diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a1e4140588..5c3af4fb51 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,8 +1,8 @@ -# Contributing to VOREStation +# Contributing to Yawn-Wider :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: -The following is a set of guidelines for contributing to VOREStation, which is hosted in the [VOREStation Org](https://github.com/VOREStation) on GitHub. +The following is a set of guidelines for contributing to Yawn-Wider, which is hosted in the [Yawn-Wider](https://github.com/Yawn-Wider/YWPolarisVore) on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. #### Table Of Contents @@ -12,7 +12,7 @@ These are just guidelines, not rules, use your best judgment and feel free to pr [How Can I Contribute?](#how-can-i-contribute) * [Your First Code Contribution](#your-first-code-contribution) - * [VOREStation Coding Standards](#vorestation-coding-standards) + * [Yawn-Wider Coding Standards](#Yawn-Wider-coding-standards) * [Pull Requests](#pull-requests) * [Git Commit Messages](#git-commit-messages) @@ -30,20 +30,20 @@ By participating, you are expected to uphold this code. ### Your First Code Contribution -Unsure where to begin contributing to VOREStation? You can start by looking through the issues tab. +Unsure where to begin contributing to Yawn-Wider? You can start by looking through the issues tab. -### VOREStation Coding Standards +### Yawn-Wider Coding Standards Any code submissions that do not meet our coding standards are likely to be rejected, or at the very least, have a maintainer request changes on your PR. Save time and follow these standards from the start. -* If it is something like a bugfix that Polaris would want (the codebase we use), code it in their code and make the PR to them. We regularly update from them. They would want any general gameplay bugfixes, and things that are obviously intended to work one way, but do not. They do not have any of our fluff species (vulp, akula, fenn, etc) so do not make PRs related to that, or any vore content to them. -* Never edit stock Polaris .DMI files. If you are confused about which .DMI files we have added and which were there originally, refer to their repository and and see if they exist (https://github.com/PolarisSS13/Polaris). All PRs with edits to stock .DMI files will be rejected. +* If it is something like a bugfix that VoreStation or Polaris would want (the codebases we use), code it in their code and make the PR to them. We regularly update from them. They would want any general gameplay bugfixes, and things that are obviously intended to work one way, but do not. They do not have any of our fluff species (vulp, akula, fenn, etc) so do not make PRs related to that, or any vore content to them. +* Never edit stock Polaris or Vore .DMI files. If you are confused about which .DMI files we have added and which were there originally, refer to their repository and and see if they exist (https://github.com/PolarisSS13/Polaris) (https://github.com/VOREStation/VOREStation). All PRs with edits to stock .DMI files might be rejected. * When changing any code in any stock Polaris .DM file, you must mark your changes: - * For single-line changes: //VOREStation Edit - "Explanation" (Edit can also be Add for new lines or Removal if you are commenting the line out) - * For multi-line additions: //VOREStation Edit - "Explanation" and then at the bottom of your changes, //VOREStation Edit End - * For multi-line removals: Use a block comment (/\* xxx \*/) to comment out the existing code block (do not modify whitespace more than necessary) and at the start, it should contain /\* VOREStation Removal - "Reason" + * For single-line changes: //Yawn-Wider Edit - "Explanation" (Edit can also be Add for new lines or Removal if you are commenting the line out) + * For multi-line additions: //Yawn-Wider Edit - "Explanation" and then at the bottom of your changes, //Yawn-Wider Edit End + * For multi-line removals: Use a block comment (/\* xxx \*/) to comment out the existing code block (do not modify whitespace more than necessary) and at the start, it should contain /\* Yawn-Wider Removal - "Reason" * Change whitespace as little as possible. Do not randomly add/remove whitespace. -* Any new files should have "_vr" at the end. For example, "life_vr.dm". Just make them in the same location as the file they are related to. +* Any new files should have "_yw" at the end. For example, "life_yw.dm". Just make them in the same location as the file they are related to. * Map changes must be in tgm format. See the [Mapmerge2 Readme] for details. The `attempt_vr()` proc has been added for your convienence. It allows a many-line change to become a single-line change in the existing Polaris files, preserving mergeability and allowing better code separation while preventing your new code from causing runtimes that stop the original code from running. If you are wanting to inject new procedures into an existing proc, called `update_atoms()` for example, you would create `update_atoms_vr()` in a nearby `_vr.dm` file, and then call to it from a single line in the original `update_atoms()` with `attempt_vr()`. @@ -72,7 +72,7 @@ Then in our `handle_grabs_vr()` proc, if we want to avoid performing the stock g * Use the GitHub magic words "Fixed/Fixes/Fix, Resolved/Resolves/Resolve, Closed/Closes/Close", as in, "Closes #1928", as this will automatically close that issue when the PR is merged if it is a fix for that issue. ## Licensing -VOREStation is licensed under the GNU Affero General Public License version 3, which can be found in full in LICENSE-AGPL3.txt. +Yawn-Wider is licensed under the GNU Affero General Public License version 3, which can be found in full in LICENSE-AGPL3.txt. Commits with a git authorship date prior to `1420675200 +0000` (2015/01/08 00:00) are licensed under the GNU General Public License version 3, which can be found in full in LICENSE-GPL3.txt. diff --git a/.github/boring-cyborg.yml b/.github/boring-cyborg.yml new file mode 100644 index 0000000000..4b8d729834 --- /dev/null +++ b/.github/boring-cyborg.yml @@ -0,0 +1,99 @@ +##### Labeler ########################################################################################################## +# Enable "labeler" for your PR that would add labels to PRs based on the paths that are modified in the PR. +# * matches anything but / +# ** matches anything including / + +labelPRBasedOnFilePath: + # Add 'Type: Map' to any changes to .dmm files + "Type: Map": + - '**.dmm' + + # Add 'Type: Map' to any changes to image files + "Type: Icon": + - '**.dmi' + - '**.png' + - '**.jpg' + + # Add 'Type: Sound' to any change to sound files + "Type: Sound": + - '**.ogg' + - '**.mp3' + - '**.wav' + + # Add 'Type: Fluff' to any change to *custom_items* + "Type: Fluff": + - '**custom_items**' + - '**loadout_fluff**' + + # Contains changes to system folders + "Type: .git or .github": + - '.github/**' + - '.git/**' + +##### Greetings ######################################################################################################## +# Comment to be posted to welcome users when they open their first PR +firstPRWelcomeComment: > + Thanks for making your first PR! Please make sure you've read the contribution guidelines as well. + +# Comment to be posted to congratulate user on their first merged PR +firstPRMergeComment: > + Awesome work, congrats on your first merged pull request! + +# Comment to be posted to on first time issues +firstIssueWelcomeComment: > + Thanks for opening your first issue here! Be sure to follow the issue template! + +###### IssueLink Adder ################################################################################################# +# Insert Issue (Jira/Github etc) link in PR description based on the Issue ID in PR title. +#insertIssueLinkInPrDescription: +# # specify the placeholder for the issue link that should be present in the description +# descriptionIssuePlaceholderRegexp: "^Issue link: (.*)$" +# matchers: +# # you can have several matches - for different types of issues +# # only the first matching entry is replaced +# jiraIssueMatch: +# # specify the regexp of issue id that you can find in the title of the PR +# # the match groups can be used to build the issue id (${1}, ${2}, etc.). +# titleIssueIdRegexp: \[(AIRFLOW-[0-9]{4})\] +# # the issue link to be added. ${1}, ${2} ... are replaced with the match groups from the +# # title match (remember to use quotes) +# descriptionIssueLink: "[${1}](https://issues.apache.org/jira/browse/${1}/)" +# docOnlyIssueMatch: +# titleIssueIdRegexp: \[(AIRFLOW-X{4})\] +# descriptionIssueLink: "`Document only change, no JIRA issue`" + +###### Title Validator ################################################################################################# +# Verifies if commit/PR titles match the regexp specified +#verifyTitles: +# # Regular expression that should be matched by titles of commits or PR +# titleRegexp: ^\[AIRFLOW-[0-9]{4}\].*$|^\[AIRFLOW-XXXX\].*$ +# # If set to true, it will always check the PR title (as opposed to the individual commits). +# alwaysUsePrTitle: true +# # If set to true, it will only check the commit in case there is a single commit. +# # In case of multiple commits it will check PR title. +# # This reflects the standard behaviour of Github that for `Squash & Merge` GitHub +# # uses the PR title rather than commit messages for the squashed commit ¯\_(ツ)_/¯ +# # For single-commit PRs it takes the squashed commit message from the commit as expected. +# # +# # If set to false it will check all commit messages. This is useful when you do not squash commits at merge. +# validateEitherPrOrSingleCommitTitle: true +# # The title the GitHub status should appear from. +# statusTitle: "Title Validator" +# # A custom message to be displayed when the title passes validation. +# successMessage: "Validation successful!" +# # A custom message to be displayed when the title fails validation. +# # Allows insertion of ${type} (commit/PR), ${title} (the title validated) and ${regex} (the titleRegexp above). +# failureMessage: "Wrong ${type} title: ${title}" + +###### PR/Branch Up-To-Date Checker #################################################################################### +# Check if the branch is up to date with master when certain files are modified +#checkUpToDate: +# # The default branch is "master", change the branch if you want to check against a different target branch +# targetBranch: master +# files: +# # File paths that you want to check for +# # In this example, it checks if the branch is up to date when alembic migrations are modified in the PR. +# # It helps avoid multiple heads in alembic migrations in a collaborative development project. +# - airflow/migrations/* +# - airflow/migrations/**/* +# - airflow/alembic.ini \ No newline at end of file diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000..7622dac94d --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,17 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 365 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 30 +# Issues with these labels will never be considered stale +#exemptLabels: +# - pinned +# - security +# Label to use when marking an issue as stale +staleLabel: 'Status: No Response' +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false \ No newline at end of file diff --git a/.gitignore b/.gitignore index abf84becce..a888a47c90 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ #ignore misc BYOND files Thumbs.db +vchat.db +vchat.db* *.log *.int *.rsc @@ -33,3 +35,9 @@ temp.dmi /maps/RandomZLevels/backup /tools/midi2piano.zip /maps/yw/backup +maps/yw/cryogaia-04-maintenance.dmm.before +maps/yw/cryogaia-05-main.dmm.before +maps/yw/cryogaia-03-transit.dmm.before +maps/yw/cryogaia-02-mining.dmm.before +maps/yw/cryogaia-01-centcomm.dmm.before +maps/yw/residential/residential.dmm.before diff --git a/.travis.yml b/.travis.yml index 3bd1c71a17..75d81c0645 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,12 @@ #pretending we're C because otherwise ruby will initialize, even with "language: dm". language: c -sudo: false env: global: + - BASENAME="vorestation" # $BASENAME.dmb, $BASENAME.dme, etc. - BYOND_MAJOR="513" - - BYOND_MINOR="1502" + - BYOND_MINOR="1513" - MACRO_COUNT=4 - matrix: - - TEST_DEFINE="MAP_TEST" TEST_FILE="code/_map_tests.dm" RUN="0" - - TEST_DEFINE="AWAY_MISSION_TEST" TEST_FILE="code/_away_mission_tests.dm" RUN="0" - - TEST_DEFINE="UNIT_TEST" TEST_FILE="code/_unit_tests.dm" RUN="1" cache: directories: @@ -24,29 +20,36 @@ addons: - libstdc++6:i386 - libssl-dev:i386 -before_script: - - chmod +x ./install-byond.sh - - ./install-byond.sh +before_install: + - chmod -R +x ./tools/travis install: - - pip install --user PyYaml -q - - pip install --user beautifulsoup4 -q + - ./tools/travis/install_byond.sh + +before_script: + - shopt -s globstar script: - - shopt -s globstar - - (! grep 'step_[xy]' maps/**/*.dmm) - - (! grep -Pn '( |\t|;|{)tag( ?)=' maps/**/*.dmm) - - (! find nano/templates/ -type f -exec md5sum {} + | sort | uniq -D -w 32 | grep nano) - - (! grep -En "<\s*span\s+class\s*=\s*('[^'>]+|[^'>]+')\s*>" **/*.dm) - - (! grep 'maps\\.*test.*' *.dme) - - awk -f tools/indentation.awk **/*.dm - - md5sum -c - <<< "88490b460c26947f5ec1ab1bb9fa9f17 *html/changelogs/example.yml" - - (num=`grep -E '\\\\(red|blue|green|black|b|i[^mc])' **/*.dm | wc -l`; echo "$num escapes (expecting ${MACRO_COUNT} or less)"; [ $num -le ${MACRO_COUNT} ]) - - source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup - - python tools/TagMatcher/tag-matcher.py ../.. - # Run our test - - cp config/example/* config/ - - echo "#define ${TEST_DEFINE} 1" > ${TEST_FILE} - - DreamMaker vorestation.dme - - if [ $RUN -eq 1 ]; then DreamDaemon vorestation.dmb -invisible -trusted -core 2>&1 | tee log.txt; fi - - if [ $RUN -eq 1 ]; then grep "All Unit Tests Passed" log.txt; fi + - ./tools/travis/compile_and_run.sh + +# Build-specific settings +jobs: + include: + - stage: "File Tests" #This is the odd man out, with specific installs and stuff. + name: "Validate Files" + install: #Need python for some of the tag matching stuff + - pip install --user PyYaml -q + - pip install --user beautifulsoup4 -q + script: ./tools/travis/validate_files.sh + addons: + apt: + packages: ~ # Don't need any packages for this + - stage: "Unit Tests" + env: TEST_DEFINE="UNIT_TEST" TEST_FILE="code/_unit_tests.dm" RUN="1" + name: "Compile normally (unit tests)" + - stage: "Isolation Tests" + env: TEST_DEFINE="MAP_TEST" TEST_FILE="code/_map_tests.dm" RUN="0" + name: "Compile POIs (no run)" + - env: TEST_DEFINE="AWAY_MISSION_TEST" TEST_FILE="code/_away_mission_tests.dm" RUN="0" + name: "Compile away missions (no run)" + diff --git a/LICENSE-CC-BY-NC-SA.txt b/LICENSE-CC-BY-NC-SA.txt new file mode 100644 index 0000000000..860915c5c0 --- /dev/null +++ b/LICENSE-CC-BY-NC-SA.txt @@ -0,0 +1,60 @@ +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. + "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(g) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. + "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. + "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike. + "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. + "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. + "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. + "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. + "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. + "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: + + to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; + to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; + to Distribute and Publicly Perform the Work including as incorporated in Collections; and, + to Distribute and Publicly Perform Adaptations. + +The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights described in Section 4(e). + +4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: + + You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(d), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(d), as requested. + You may Distribute or Publicly Perform an Adaptation only under: (i) the terms of this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License"). You must include a copy of, or the URI, for Applicable License with every copy of each Adaptation You Distribute or Publicly Perform. You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License. You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. + You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in con-nection with the exchange of copyrighted works. + If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. + + For the avoidance of doubt: + Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; + Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and, + Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c). + Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. + +8. Miscellaneous + + Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. + Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. + If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. + This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. + The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. diff --git a/README.md b/README.md index 7bef0d8b45..f60ad141eb 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# VOREStation +# Yawn-Wider [![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/compatibility-club-penguin.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/no-ragrets.svg)](http://forthebadge.com) -[Website](https://vore-station.net) - [Forums](https://forum.vore-station.net/) - [Wiki](https://wiki.vore-station.net/) +[Website (we dont have one)](https://www.youtube.com/watch?v=oHg5SJYRHA0) - [Forums](https://yawn-widerstation.proboards.com/) - [Wiki](https://yawn.izac.live/index.php?title=Main_Page) - [Discord](https://discord.gg/Wh27EAY) Going to make a Pull Request? Make sure you read the [CONTRIBUTING.md](.github/CONTRIBUTING.md) first! -VOREStation is a fork of the Polaris code branch, itself a fork of the Baystation12 code branch, for the game Space Station 13. +Yawn-Wider is a fork of the VOREStation code branch which is a fork of the Polaris code branch, itself a fork of the Baystation12 code branch, for the game Space Station 13. --- ### LICENSE -The code for VOREStation is licensed under the [GNU Affero General Public License](http://www.gnu.org/licenses/agpl.html) version 3, which can be found in full in LICENSE-AGPL3.txt. +The code for Yawn-Wider is licensed under the [GNU Affero General Public License](http://www.gnu.org/licenses/agpl.html) version 3, which can be found in full in LICENSE-AGPL3.txt. Code with a git authorship date prior to `1420675200 +0000` (2015/01/08 00:00) are licensed under the GNU General Public License version 3, which can be found in full in LICENSE-GPL3.txt. @@ -21,16 +21,24 @@ If you wish to develop and host this codebase in a closed source manner you may See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for more information. +Any files located in the +`vorestation/goon`, +`vorestation/icons/goonstation`, or +`vorestation/sound/goonstation` +directories, or any subdirectories of mentioned directories are licensed under the +Creative Commons 3.0 BY-NC-SA license +(https://creativecommons.org/licenses/by-nc-sa/3.0) + All assets including icons and sound are under a [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/) license unless otherwise indicated. ### GETTING THE CODE The simplest way to obtain the code is using the github .zip feature. If you do this, you won't be able to make a Pull Request later, though. You'll need to use the git method. -Click [here](https://github.com/VOREStation/VOREStation/archive/master.zip) to get the latest code as a .zip file, then unzip it to wherever you want. +Click [here](https://github.com/Yawn-Wider/YWPolarisVore/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: - git clone https://github.com/VOREStation/VOREStation.git + git clone https://github.com/Yawn-Wider/YWPolarisVore.git (hint: hold down ctrl and press insert to paste into git bash) diff --git a/code/ATMOSPHERICS/_atmospherics_helpers.dm b/code/ATMOSPHERICS/_atmospherics_helpers.dm index b5cd6486b9..76b520a45f 100644 --- a/code/ATMOSPHERICS/_atmospherics_helpers.dm +++ b/code/ATMOSPHERICS/_atmospherics_helpers.dm @@ -19,7 +19,7 @@ set name = "Toggle Debug Messages" set category = "Debug" M.debug = !M.debug - usr << "[M]: Debug messages toggled [M.debug? "on" : "off"]." + to_chat(usr, "[M]: Debug messages toggled [M.debug? "on" : "off"].") //Generalized gas pumping proc. //Moves gas from one gas_mixture to another and returns the amount of power needed (assuming 1 second), or -1 if no gas was pumped. @@ -468,6 +468,10 @@ return "REGULAR" if(PIPING_LAYER_SCRUBBER) return "SCRUBBER" + if(PIPING_LAYER_FUEL) + return "FUEL" + if(PIPING_LAYER_AUX) + return "AUX" /proc/atmos_pipe_flags_str(pipe_flags) var/list/dat = list() @@ -489,6 +493,10 @@ dat += "SUPPLY" if(connect_types & CONNECT_TYPE_SCRUBBER) dat += "SCRUBBER" + if(connect_types & CONNECT_TYPE_FUEL) + dat += "FUEL" + if(connect_types & CONNECT_TYPE_AUX) + dat += "AUX" if(connect_types & CONNECT_TYPE_HE) dat += "HE" return dat.Join("|") diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index acb1da7b02..6559f28f26 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -209,8 +209,18 @@ Pipelines + Other Objects -> Pipe network connect_types = CONNECT_TYPE_SUPPLY layer = PIPES_SUPPLY_LAYER icon_connect_type = "-supply" + if(PIPING_LAYER_FUEL) + icon_state = "[icon_state]-fuel" + connect_types = CONNECT_TYPE_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + if(PIPING_LAYER_AUX) + icon_state = "[icon_state]-aux" + connect_types = CONNECT_TYPE_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" if(pipe_flags & PIPING_ALL_LAYER) - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL|CONNECT_TYPE_AUX // Or if we were to do it the TG way... // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) diff --git a/code/ATMOSPHERICS/components/binary_devices/algae_generator_vr.dm b/code/ATMOSPHERICS/components/binary_devices/algae_generator_vr.dm index 391f10bc6e..1c308a0e0b 100644 --- a/code/ATMOSPHERICS/components/binary_devices/algae_generator_vr.dm +++ b/code/ATMOSPHERICS/components/binary_devices/algae_generator_vr.dm @@ -1,5 +1,4 @@ -#define MATERIAL_ALGAE "algae" -#define MATERIAL_CARBON "carbon" +#define MAT_ALGAE "algae" /obj/machinery/atmospherics/binary/algae_farm name = "algae oxygen generator" @@ -10,15 +9,15 @@ anchored = 1 density = 1 power_channel = EQUIP - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 // Minimal lights to keep algae alive active_power_usage = 5000 // Powerful grow lights to stimulate oxygen production //power_rating = 7500 //7500 W ~ 10 HP pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF - var/list/stored_material = list(MATERIAL_ALGAE = 0, MATERIAL_CARBON = 0) + var/list/stored_material = list(MAT_ALGAE = 0, MAT_GRAPHITE = 0) // Capacity increases with matter bin quality - var/list/storage_capacity = list(MATERIAL_ALGAE = 10000, MATERIAL_CARBON = 10000) + var/list/storage_capacity = list(MAT_ALGAE = 10000, MAT_GRAPHITE = 10000) // Speed at which we convert CO2 to O2. Increases with manipulator quality var/moles_per_tick = 1 // Power required to convert one mole of CO2 to O2 (this is powering the grow lights). Improves with capacitors @@ -35,7 +34,7 @@ var/const/output_gas = "oxygen" /obj/machinery/atmospherics/binary/algae_farm/filled - stored_material = list(MATERIAL_ALGAE = 10000, MATERIAL_CARBON = 0) + stored_material = list(MAT_ALGAE = 10000, MAT_GRAPHITE = 0) /obj/machinery/atmospherics/binary/algae_farm/New() ..() @@ -58,10 +57,10 @@ ..() recent_moles_transferred = 0 - if(inoperable() || use_power < 2) + if(inoperable() || use_power < USE_POWER_ACTIVE) ui_error = null update_icon() - if(use_power == 1) + if(use_power == USE_POWER_IDLE) last_power_draw = idle_power_usage else last_power_draw = 0 @@ -70,17 +69,17 @@ last_power_draw = active_power_usage // STEP 1 - Check material resources - if(stored_material[MATERIAL_ALGAE] < algae_per_mole) - ui_error = "Insufficient [material_display_name(MATERIAL_ALGAE)] to process." + if(stored_material[MAT_ALGAE] < algae_per_mole) + ui_error = "Insufficient [material_display_name(MAT_ALGAE)] to process." update_icon() return - if(stored_material[MATERIAL_CARBON] + carbon_per_mole > storage_capacity[MATERIAL_CARBON]) - ui_error = "[material_display_name(MATERIAL_CARBON)] output storage is full." + if(stored_material[MAT_GRAPHITE] + carbon_per_mole > storage_capacity[MAT_GRAPHITE]) + ui_error = "[material_display_name(MAT_GRAPHITE)] output storage is full." update_icon() return var/moles_to_convert = min(moles_per_tick,\ - stored_material[MATERIAL_ALGAE] * algae_per_mole,\ - storage_capacity[MATERIAL_CARBON] - stored_material[MATERIAL_CARBON]) + stored_material[MAT_ALGAE] * algae_per_mole,\ + storage_capacity[MAT_GRAPHITE] - stored_material[MAT_GRAPHITE]) // STEP 2 - Take the CO2 out of the input! var/power_draw = scrub_gas(src, list(input_gas), air1, internal, moles_to_convert) @@ -101,8 +100,8 @@ var/converted_moles = min(co2_moles, moles_per_tick) use_power(converted_moles * power_per_mole) last_power_draw += converted_moles * power_per_mole - stored_material[MATERIAL_ALGAE] -= converted_moles * algae_per_mole - stored_material[MATERIAL_CARBON] += converted_moles * carbon_per_mole + stored_material[MAT_ALGAE] -= converted_moles * algae_per_mole + stored_material[MAT_GRAPHITE] += converted_moles * carbon_per_mole // STEP 5 - Output the converted oxygen. Fow now we output for free! internal.adjust_gas(input_gas, -converted_moles) @@ -114,7 +113,7 @@ update_icon() /obj/machinery/atmospherics/binary/algae_farm/update_icon() - if(inoperable() || !anchored || use_power < 2) + if(inoperable() || !anchored || use_power < USE_POWER_ACTIVE) icon_state = "algae-off" else if(recent_moles_transferred >= moles_per_tick) icon_state = "algae-full" @@ -215,13 +214,13 @@ // Queue management can be done even while busy if(href_list["activate"]) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) update_icon() updateUsrDialog() return if(href_list["deactivate"]) - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() updateUsrDialog() return @@ -275,7 +274,7 @@ return 1 /material/algae - name = MATERIAL_ALGAE + name = MAT_ALGAE stack_type = /obj/item/stack/material/algae icon_colour = "#557722" shard_type = SHARD_STONE_PIECE @@ -288,29 +287,9 @@ name = "algae sheet" icon_state = "sheet-uranium" color = "#557722" - default_type = MATERIAL_ALGAE + default_type = MAT_ALGAE /obj/item/stack/material/algae/ten amount = 10 -/material/carbon - name = MATERIAL_CARBON - stack_type = /obj/item/stack/material/carbon - icon_colour = "#303030" - shard_type = SHARD_SPLINTER - weight = 5 - hardness = 20 - icon_base = "stone" - icon_reinf = "reinf_stone" - door_icon_base = "stone" - sheet_singular_name = "sheet" - sheet_plural_name = "sheets" - -/obj/item/stack/material/carbon - name = "carbon sheet" - icon_state = "sheet-metal" - color = "#303030" - default_type = MATERIAL_CARBON - -#undef MATERIAL_ALGAE -#undef MATERIAL_CARBON +#undef MAT_ALGAE diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index 4698c613a9..cc280d97cc 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -1,7 +1,7 @@ /obj/machinery/atmospherics/binary dir = SOUTH initialize_directions = SOUTH|NORTH - use_power = 1 + use_power = USE_POWER_IDLE var/datum/gas_mixture/air1 var/datum/gas_mixture/air2 diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm index 044d6b0c13..72e623351a 100644 --- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm +++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm @@ -6,8 +6,8 @@ /obj/machinery/atmospherics/binary/circulator name = "circulator" desc = "A gas circulator turbine and heat exchanger." - icon = 'icons/obj/pipes.dmi' - icon_state = "circ-off" + icon = 'icons/obj/power.dmi' + icon_state = "circ-unassembled" anchored = 0 pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF @@ -22,6 +22,7 @@ var/last_stored_energy_transferred = 0 var/volume_capacity_used = 0 var/stored_energy = 0 + var/temperature_overlay density = 1 @@ -76,15 +77,19 @@ update_icon() /obj/machinery/atmospherics/binary/circulator/update_icon() - if(stat & (BROKEN|NOPOWER) || !anchored) - icon_state = "circ-p" - else if(last_pressure_delta > 0 && recent_moles_transferred > 0) - if(last_pressure_delta > 5*ONE_ATMOSPHERE) - icon_state = "circ-run" + icon_state = anchored ? "circ-assembled" : "circ-unassembled" + cut_overlays() + if (stat & (BROKEN|NOPOWER) || !anchored) + return 1 + if (last_pressure_delta > 0 && recent_moles_transferred > 0) + if (temperature_overlay) + add_overlay(temperature_overlay) + if (last_pressure_delta > 5*ONE_ATMOSPHERE) + add_overlay("circ-run") else - icon_state = "circ-slow" + add_overlay("circ-slow") else - icon_state = "circ-off" + add_overlay("circ-off") return 1 @@ -97,6 +102,7 @@ "You hear a ratchet.") if(anchored) + temperature_overlay = null if(dir & (NORTH|SOUTH)) initialize_directions = NORTH|SOUTH else if(dir & (EAST|WEST)) diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm index a72aae425b..809cc987eb 100644 --- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm @@ -20,7 +20,7 @@ level = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -176,7 +176,7 @@ return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( @@ -201,7 +201,7 @@ /obj/machinery/atmospherics/binary/dp_vent_pump/examine(mob/user) if(..(user, 1)) - user << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + to_chat(user, "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W") /obj/machinery/atmospherics/unary/vent_pump/power_change() @@ -214,10 +214,10 @@ if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) return 0 if(signal.data["power"]) - use_power = text2num(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) if(signal.data["power_toggle"]) - use_power = !use_power + update_use_power(!use_power) if(signal.data["direction"]) pump_direction = text2num(signal.data["direction"]) diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 5abaa00c60..1c501e87f0 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -12,7 +12,7 @@ name = "pressure regulator" desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power." - use_power = 0 + use_power = USE_POWER_OFF var/unlocked = 0 //If 0, then the valve is locked closed, otherwise it is open(-able, it's a one-way valve so it closes if gas would flow backwards). var/target_pressure = ONE_ATMOSPHERE @@ -67,6 +67,8 @@ pressure_delta = input_starting_pressure - target_pressure if (REGULATE_OUTPUT) pressure_delta = target_pressure - output_starting_pressure + if (REGULATE_NONE) + pressure_delta = input_starting_pressure - output_starting_pressure //-1 if pump_gas() did not move any gas, >= 0 otherwise var/returnval = -1 @@ -82,9 +84,46 @@ transfer_moles = min(transfer_moles, calculate_transfer_moles(air2, air1, pressure_delta, (network1)? network1.volume : 0)) if (REGULATE_OUTPUT) transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0)) + if (REGULATE_NONE) + var/source = air1 + var/sink = air2 + // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current + if(istype(node1, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node1 + if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + source = l.air + // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current + if(istype(node2, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node2 + if(istype(p.parent, /datum/pipeline)) + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + sink = l.air + transfer_moles = max(0, calculate_equalize_moles(source, sink)) // Not regulated, don't care about flow rate //pump_gas() will return a negative number if no flow occurred - returnval = pump_gas_passive(src, air1, air2, transfer_moles) + if(regulate_mode == REGULATE_NONE) // ACTUALLY move gases from the whole network, not just the immediate pipes + var/source = air1 + var/sink = air2 + // If node1 is a network of more than 1 pipe, we want to transfer from that whole network, otw use just node1, as current + if(istype(node1, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node1 + if(istype(p.parent, /datum/pipeline)) // Nested if-blocks to avoid the mystical : + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + source = l.air + // If node2 is a network of more than 1 pipe, we want to transfer to that whole network, otw use just node2, as current + if(istype(node2, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/p = node2 + if(istype(p.parent, /datum/pipeline)) + var/datum/pipeline/l = p.parent + if(istype(l.air, /datum/gas_mixture)) + sink = l.air + returnval = pump_gas_passive(src, source, sink, transfer_moles) + else + returnval = pump_gas_passive(src, air1, air2, transfer_moles) if (returnval >= 0) if(network1) @@ -112,7 +151,7 @@ return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( diff --git a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm index a7c8fd6d38..7ef1858b2f 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm @@ -237,10 +237,9 @@ var/kin_to_el_ratio = 0.1 //How much kinetic energy will be taken from turbine and converted into electricity var/obj/machinery/atmospherics/pipeturbine/turbine -/obj/machinery/power/turbinemotor/New() - ..() - spawn(1) - updateConnection() +/obj/machinery/power/turbinemotor/Initialize() + . = ..() + updateConnection() /obj/machinery/power/turbinemotor/proc/updateConnection() turbine = null diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index b4b8187f45..0df0217497 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -18,6 +18,7 @@ Thus, the two variables affect pump operation are set in New(): construction_type = /obj/item/pipe/directional pipe_state = "pump" level = 1 + var/base_icon = "pump" name = "gas pump" desc = "A pump that moves gas from one place to another." @@ -26,7 +27,7 @@ Thus, the two variables affect pump operation are set in New(): //var/max_volume_transfer = 10000 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -47,14 +48,33 @@ Thus, the two variables affect pump operation are set in New(): /obj/machinery/atmospherics/binary/pump/on icon_state = "map_on" - use_power = 1 + use_power = USE_POWER_IDLE +/obj/machinery/atmospherics/binary/pump/fuel + icon_state = "map_off-fuel" + base_icon = "pump-fuel" + icon_connect_type = "-fuel" + connect_types = CONNECT_TYPE_FUEL + +/obj/machinery/atmospherics/binary/pump/fuel/on + icon_state = "map_on-fuel" + use_power = USE_POWER_IDLE + +/obj/machinery/atmospherics/binary/pump/aux + icon_state = "map_off-aux" + base_icon = "pump-aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX + +/obj/machinery/atmospherics/binary/pump/aux/on + icon_state = "map_on-aux" + use_power = USE_POWER_IDLE /obj/machinery/atmospherics/binary/pump/update_icon() if(!powered()) - icon_state = "off" + icon_state = "[base_icon]-off" else - icon_state = "[use_power ? "on" : "off"]" + icon_state = "[use_power ? "[base_icon]-on" : "[base_icon]-off"]" /obj/machinery/atmospherics/binary/pump/update_underlays() if(..()) @@ -62,8 +82,8 @@ Thus, the two variables affect pump operation are set in New(): var/turf/T = get_turf(src) if(!istype(T)) return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) + add_underlay(T, node1, turn(dir, -180), node1?.icon_connect_type) + add_underlay(T, node2, dir, node2?.icon_connect_type) /obj/machinery/atmospherics/binary/pump/hide(var/i) update_underlays() @@ -108,7 +128,7 @@ Thus, the two variables affect pump operation are set in New(): return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( @@ -160,12 +180,12 @@ Thus, the two variables affect pump operation are set in New(): if(signal.data["power"]) if(text2num(signal.data["power"])) - use_power = 1 + update_use_power(USE_POWER_IDLE) else - use_power = 0 + update_use_power(USE_POWER_OFF) if("power_toggle" in signal.data) - use_power = !use_power + update_use_power(!use_power) if(signal.data["set_output_pressure"]) target_pressure = between( @@ -199,7 +219,7 @@ Thus, the two variables affect pump operation are set in New(): if(..()) return 1 if(href_list["power"]) - use_power = !use_power + update_use_power(!use_power) switch(href_list["set_press"]) if ("min") diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index fa0aaf4bd0..8e82750588 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -11,7 +11,7 @@ power_rating = 15000 //15000 W ~ 20 HP /obj/machinery/atmospherics/binary/pump/high_power/on - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_on" /obj/machinery/atmospherics/binary/pump/high_power/update_icon() diff --git a/code/ATMOSPHERICS/components/omni_devices/filter.dm b/code/ATMOSPHERICS/components/omni_devices/filter.dm index d9009168c5..9f0862d800 100644 --- a/code/ATMOSPHERICS/components/omni_devices/filter.dm +++ b/code/ATMOSPHERICS/components/omni_devices/filter.dm @@ -11,7 +11,7 @@ var/datum/omni_port/input var/datum/omni_port/output - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -88,7 +88,7 @@ return 1 /obj/machinery/atmospherics/omni/atmos_filter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - usr.set_machine(src) + user.set_machine(src) var/list/data = new() @@ -161,13 +161,13 @@ switch(href_list["command"]) if("power") if(!configuring) - use_power = !use_power + update_use_power(!use_power) else - use_power = 0 + update_use_power(USE_POWER_OFF) if("configure") configuring = !configuring if(configuring) - use_power = 0 + update_use_power(USE_POWER_OFF) //only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on if(configuring && !use_power) diff --git a/code/ATMOSPHERICS/components/omni_devices/mixer.dm b/code/ATMOSPHERICS/components/omni_devices/mixer.dm index 47c78427f0..0210d09e69 100644 --- a/code/ATMOSPHERICS/components/omni_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/omni_devices/mixer.dm @@ -6,7 +6,7 @@ icon_state = "map_mixer" pipe_state = "omni_mixer" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 3700 //3700 W ~ 5 HP @@ -178,13 +178,13 @@ switch(href_list["command"]) if("power") if(!configuring) - use_power = !use_power + update_use_power(!use_power) else - use_power = 0 + update_use_power(USE_POWER_OFF) if("configure") configuring = !configuring if(configuring) - use_power = 0 + update_use_power(USE_POWER_OFF) //only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on if(configuring && !use_power) diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm index 6f78a7d064..54acc3db99 100644 --- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm +++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm @@ -5,7 +5,7 @@ name = "omni device" icon = 'icons/atmos/omni_devices_vr.dmi' //VOREStation Edit - New Icon icon_state = "base" - use_power = 1 + use_power = USE_POWER_IDLE initialize_directions = 0 construction_type = /obj/item/pipe/quaternary level = 1 @@ -67,7 +67,7 @@ last_flow_rate = 0 if(error_check()) - use_power = 0 + update_use_power(USE_POWER_OFF) if((stat & (NOPOWER|BROKEN)) || !use_power) return 0 diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm index b30bc9b038..5b489441db 100644 --- a/code/ATMOSPHERICS/components/portables_connector.dm +++ b/code/ATMOSPHERICS/components/portables_connector.dm @@ -18,9 +18,23 @@ var/datum/pipe_network/network var/on = 0 - use_power = 0 + use_power = USE_POWER_OFF level = 1 +/obj/machinery/atmospherics/portables_connector/fuel + icon_state = "map_connector-fuel" + pipe_state = "connector-fuel" + icon_connect_type = "-fuel" + pipe_flags = PIPING_ONE_PER_TURF + connect_types = CONNECT_TYPE_FUEL + +/obj/machinery/atmospherics/portables_connector/aux + icon_state = "map_connector-aux" + pipe_state = "connector-aux" + icon_connect_type = "-aux" + pipe_flags = PIPING_ONE_PER_TURF + connect_types = CONNECT_TYPE_AUX + /obj/machinery/atmospherics/portables_connector/init_dir() initialize_directions = dir @@ -33,7 +47,7 @@ var/turf/T = get_turf(src) if(!istype(T)) return - add_underlay(T, node, dir) + add_underlay(T, node, dir, node?.icon_connect_type) /obj/machinery/atmospherics/portables_connector/hide(var/i) update_underlays() diff --git a/code/ATMOSPHERICS/components/shutoff.dm b/code/ATMOSPHERICS/components/shutoff.dm index de69f5e395..87d2698841 100644 --- a/code/ATMOSPHERICS/components/shutoff.dm +++ b/code/ATMOSPHERICS/components/shutoff.dm @@ -7,6 +7,7 @@ GLOBAL_LIST_EMPTY(shutoff_valves) name = "automatic shutoff valve" desc = "An automatic valve with control circuitry and pipe integrity sensor, capable of automatically isolating damaged segments of the pipe network." + description_info = "Clicking this will toggle the automatic control. Alt-clicking this when the automatic control is disabled will manually open or close the valve." var/close_on_leaks = TRUE // If false it will be always open level = 1 @@ -37,21 +38,97 @@ GLOBAL_LIST_EMPTY(shutoff_valves) to_chat(user, "You [close_on_leaks ? "enable" : "disable"] the automatic shutoff circuit.") return TRUE +// Alt+Click now toggles the open/close function, when the autoseal is disabled +/obj/machinery/atmospherics/valve/shutoff/AltClick(var/mob/user) + if(isliving(user)) + if(close_on_leaks) + to_chat(user, "You try to manually [open ? "close" : "open"] the valve, but it [open ? "opens" : "closes"] automatically again.") + return + + open ? close() : open() + to_chat(user, "You manually [open ? "open" : "close"] the valve.") + /obj/machinery/atmospherics/valve/shutoff/process() ..() - if (!network_node1 || !network_node2) - if(open) + if(!network_node1 || !network_node2 || !node1 || !node2) + if(open && close_on_leaks) close() return - if (!close_on_leaks) - if (!open) + if(close_on_leaks) + if(open && (network_node1.leaks.len || network_node2.leaks.len)) + find_leaks() // If we can see the leak, then this will find it, close the valve, and cut off that network + // If we cannot see the leak, then this will not close the valve, and any valves that can see the leak will cut it off from us + else if(!open && !network_node1.leaks.len && !network_node2.leaks.len) open() + return + +// Breadth-first search for any leaking pipes that we can directly see +/obj/machinery/atmospherics/valve/shutoff/proc/find_leaks() + var/obj/machinery/atmospherics/list/search = list() + + // We're the leak! + if(!node1 || !node2) + close() return - if (network_node1.leaks.len || network_node2.leaks.len) - if (open) - close() - else if (!open) - open() + // Only searching pipes + if(istype(node1, /obj/machinery/atmospherics)) + search |= node1 + if(istype(node2, /obj/machinery/atmospherics)) + search |= node2 + + // Breadth-first search + for(var/i = 1, i <= search.len, i++) // wooo, proper for loop syntax! + var/obj/machinery/atmospherics/A = search[i] + if(!A) + continue + + if(istype(A, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/L = A + if(L.leaking) + close() // Found the leak! + return + + + if(istype(A, /obj/machinery/atmospherics/valve/shutoff)) + var/obj/machinery/atmospherics/valve/shutoff/S = A + if(S.close_on_leaks || !S.open) + continue // Either it will close, or it is closed. We don't care what's on the other side + search |= list(S.node1, S.node2) // |= skips existing nodes, so we don't search loops infinitely + + else if(istype(A, /obj/machinery/atmospherics/valve)) // Putting the shutoff before this means this won't catch shutoffs + var/obj/machinery/atmospherics/valve/V = A + if(V.open) + search |= list(V.node1, V.node2) + else + continue // Closed valve, dead end + + else if(istype(A, /obj/machinery/atmospherics/tvalve)) + var/obj/machinery/atmospherics/tvalve/T = A + if(T.state) + search |= list(T.node1, T.node2) + else + search |= list(T.node1, T.node3) + + else if(istype(A, /obj/machinery/atmospherics/pipe/zpipe)) + var/obj/machinery/atmospherics/pipe/zpipe/P = A + search |= list(P.node1, P.node2) + + else if(istype(A, /obj/machinery/atmospherics/pipe/simple)) + var/obj/machinery/atmospherics/pipe/P = A + search |= list(P.node1, P.node2) + + else if(istype(A, /obj/machinery/atmospherics/pipe/manifold)) + var/obj/machinery/atmospherics/pipe/manifold/M = A + search |= list(M.node1, M.node2, M.node3) + + else if(istype(A, /obj/machinery/atmospherics/pipe/manifold4w)) + var/obj/machinery/atmospherics/pipe/manifold4w/M = A + search |= list(M.node1, M.node2, M.node3, M.node4) + + // else continue, dead end + // We broke out of the loop, so we see no leaks + // The leaks therefore must be on the other side of another shutoff valve + return diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index e900303e22..21e1a34d33 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -9,7 +9,7 @@ name = "Gas filter" desc = "Filters one type of gas from an input, and pushes it out the side." - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //This also doubles as a measure of how powerful the filter is, in Watts. 7500 W ~ 10 HP @@ -73,7 +73,7 @@ icon_state += use_power ? "on" : "off" else icon_state += "off" - use_power = 0 + update_use_power(USE_POWER_OFF) /obj/machinery/atmospherics/trinary/atmos_filter/process() ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 8bf3d3477c..62b4b763b8 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -8,7 +8,7 @@ name = "Gas mixer" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP @@ -35,7 +35,7 @@ icon_state += use_power ? "on" : "off" else icon_state += "off" - use_power = 0 + update_use_power(USE_POWER_OFF) /obj/machinery/atmospherics/trinary/mixer/New() ..() @@ -114,7 +114,7 @@ /obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) if(..()) return 1 if(href_list["power"]) - use_power = !use_power + update_use_power(!use_power) if(href_list["set_press"]) var/max_flow_rate = min(air1.volume, air2.volume) var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",src.set_flow_rate) as num diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index 394dbceeda..10d7403541 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -1,7 +1,7 @@ /obj/machinery/atmospherics/trinary dir = SOUTH initialize_directions = SOUTH|NORTH|WEST - use_power = 0 + use_power = USE_POWER_OFF pipe_flags = PIPING_DEFAULT_LAYER_ONLY|PIPING_ONE_PER_TURF var/mirrored = FALSE diff --git a/code/ATMOSPHERICS/components/unary/cold_sink.dm b/code/ATMOSPHERICS/components/unary/cold_sink.dm index d4bfb73311..f665ba987a 100644 --- a/code/ATMOSPHERICS/components/unary/cold_sink.dm +++ b/code/ATMOSPHERICS/components/unary/cold_sink.dm @@ -8,7 +8,7 @@ icon_state = "freezer_0" density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 5 // 5 Watts for thermostat related circuitry circuit = /obj/item/weapon/circuitboard/unary_atmos/cooler @@ -99,7 +99,7 @@ if(..()) return 1 if(href_list["toggleStatus"]) - use_power = !use_power + update_use_power(!use_power) update_icon() if(href_list["temp"]) var/amount = text2num(href_list["temp"]) diff --git a/code/ATMOSPHERICS/components/unary/heat_source.dm b/code/ATMOSPHERICS/components/unary/heat_source.dm index 9729e1d387..7fbe881891 100644 --- a/code/ATMOSPHERICS/components/unary/heat_source.dm +++ b/code/ATMOSPHERICS/components/unary/heat_source.dm @@ -8,7 +8,7 @@ icon_state = "heater_0" density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 5 //5 Watts for thermostat related circuitry circuit = /obj/item/weapon/circuitboard/unary_atmos/heater @@ -119,7 +119,7 @@ if(..()) return 1 if(href_list["toggleStatus"]) - use_power = !use_power + update_use_power(!use_power) update_icon() if(href_list["temp"]) var/amount = text2num(href_list["temp"]) diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm index 5e62fe5802..5eeab2c8e4 100644 --- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm +++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm @@ -10,7 +10,7 @@ name = "air injector" desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 15000 //15000 W ~ 20 HP @@ -107,7 +107,7 @@ return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( @@ -132,10 +132,10 @@ return 0 if(signal.data["power"]) - use_power = text2num(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) if(signal.data["power_toggle"]) - use_power = !use_power + update_use_power(!use_power) if(signal.data["inject"]) spawn inject() @@ -160,7 +160,7 @@ /obj/machinery/atmospherics/unary/outlet_injector/attack_hand(mob/user as mob) to_chat(user, "You toggle \the [src].") injecting = !injecting - use_power = injecting + update_use_power(injecting ? USE_POWER_IDLE : USE_POWER_OFF) update_icon() /obj/machinery/atmospherics/unary/outlet_injector/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 6d60ee4ebf..5b0fd20efb 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -14,7 +14,7 @@ name = "Air Vent" desc = "Has a valve and pump attached to it" - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 30000 //7500 W ~ 10 HP //VOREStation Edit - 30000 W @@ -50,18 +50,23 @@ var/datum/looping_sound/air_pump/soundloop //Yawn Edit /obj/machinery/atmospherics/unary/vent_pump/on - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_vent_out" +/obj/machinery/atmospherics/unary/vent_pump/aux + icon_state = "map_vent_aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX //connects to aux pipes + /obj/machinery/atmospherics/unary/vent_pump/siphon pump_direction = 0 /obj/machinery/atmospherics/unary/vent_pump/siphon/on - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_vent_in" /obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_vent_in" external_pressure_bound = 0 external_pressure_bound_default = 0 @@ -80,7 +85,7 @@ icon = null initial_loc = get_area(loc) - area_uid = initial_loc.uid + area_uid = "\ref[initial_loc]" if (!id_tag) assign_uid() id_tag = num2text(uid) @@ -98,6 +103,11 @@ power_channel = EQUIP power_rating = 45000 //15 kW ~ 20 HP //VOREStation Edit - 45000 +/obj/machinery/atmospherics/unary/vent_pump/high_volume/aux + icon_state = "map_vent_aux" + icon_connect_type = "-aux" + connect_types = CONNECT_TYPE_AUX //connects to aux pipes + /obj/machinery/atmospherics/unary/vent_pump/high_volume/New() ..() air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 @@ -189,7 +199,7 @@ return 1 if (!node) - use_power = 0 + update_use_power(USE_POWER_OFF) if(!can_pump()) return 0 @@ -253,7 +263,7 @@ return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( @@ -311,10 +321,10 @@ pump_direction = 1 if(signal.data["power"] != null) - use_power = text2num(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) if(signal.data["power_toggle"] != null) - use_power = !use_power + update_use_power(!use_power) if(signal.data["checks"] != null) if (signal.data["checks"] == "default") @@ -405,11 +415,11 @@ /obj/machinery/atmospherics/unary/vent_pump/examine(mob/user) if(..(user, 1)) - user << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + to_chat(user, "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W") else - user << "You are too far away to read the gauge." + to_chat(user, "You are too far away to read the gauge.") if(welded) - user << "It seems welded shut." + to_chat(user, "It seems welded shut.") /obj/machinery/atmospherics/unary/vent_pump/power_change() var/old_stat = stat diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index cb460bd2f1..1c1c696b98 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -5,7 +5,7 @@ name = "Air Scrubber" desc = "Has a valve and pump attached to it" - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff power_rating = 7500 //7500 W ~ 10 HP @@ -29,7 +29,7 @@ var/radio_filter_in /obj/machinery/atmospherics/unary/vent_scrubber/on - use_power = 1 + use_power = USE_POWER_IDLE icon_state = "map_scrubber_on" /obj/machinery/atmospherics/unary/vent_scrubber/New() @@ -38,7 +38,7 @@ icon = null initial_loc = get_area(loc) - area_uid = initial_loc.uid + area_uid = "\ref[initial_loc]" if (!id_tag) assign_uid() id_tag = num2text(uid) @@ -93,7 +93,7 @@ return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( "area" = area_uid, @@ -135,7 +135,7 @@ return 1 if (!node) - use_power = 0 + update_use_power(USE_POWER_OFF) //broadcast_status() if(!use_power || (stat & (NOPOWER|BROKEN))) return 0 @@ -180,21 +180,21 @@ return 0 if(signal.data["power"] != null) - use_power = text2num(signal.data["power"]) + update_use_power(text2num(signal.data["power"])) if(signal.data["power_toggle"] != null) - use_power = !use_power + update_use_power(!use_power) if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing panic = text2num(signal.data["panic_siphon"]) if(panic) - use_power = 1 + update_use_power(USE_POWER_IDLE) scrubbing = 0 else scrubbing = 1 if(signal.data["toggle_panic_siphon"] != null) panic = !panic if(panic) - use_power = 1 + update_use_power(USE_POWER_IDLE) scrubbing = 0 else scrubbing = 1 @@ -288,6 +288,6 @@ /obj/machinery/atmospherics/unary/vent_scrubber/examine(mob/user) if(..(user, 1)) - user << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" + to_chat(user, "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W") else - user << "You are too far away to read the gauge." + to_chat(user, "You are too far away to read the gauge.") diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm index 2e5e3e08ee..dd68782bc5 100644 --- a/code/ATMOSPHERICS/components/valve.dm +++ b/code/ATMOSPHERICS/components/valve.dm @@ -308,4 +308,4 @@ /obj/machinery/atmospherics/valve/examine(mob/user) ..() - user << "It is [open ? "open" : "closed"]." + to_chat(user, "It is [open ? "open" : "closed"].") diff --git a/code/ATMOSPHERICS/pipes/cap.dm b/code/ATMOSPHERICS/pipes/cap.dm index 1e32aa6295..2a63bb3a20 100644 --- a/code/ATMOSPHERICS/pipes/cap.dm +++ b/code/ATMOSPHERICS/pipes/cap.dm @@ -93,6 +93,26 @@ icon_connect_type = "-supply" color = PIPE_COLOR_BLUE +/obj/machinery/atmospherics/pipe/cap/visible/fuel + name = "fuel pipe endcap" + desc = "An endcap for fuel pipes" + icon_state = "cap-fuel" + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/cap/visible/aux + name = "aux pipe endcap" + desc = "An endcap for aux pipes" + icon_state = "cap-aux" + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN + /obj/machinery/atmospherics/pipe/cap/hidden level = 1 icon_state = "cap" @@ -117,3 +137,23 @@ layer = PIPES_SUPPLY_LAYER icon_connect_type = "-supply" color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/cap/visible/fuel + name = "fuel pipe endcap" + desc = "An endcap for fuel pipes" + icon_state = "cap-f-fuel" + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/cap/hidden/aux + name = "aux pipe endcap" + desc = "An endcap for aux pipes" + icon_state = "cap-f-aux" + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN \ No newline at end of file diff --git a/code/ATMOSPHERICS/pipes/manifold.dm b/code/ATMOSPHERICS/pipes/manifold.dm index f44bfc5686..a17f78e50d 100644 --- a/code/ATMOSPHERICS/pipes/manifold.dm +++ b/code/ATMOSPHERICS/pipes/manifold.dm @@ -108,16 +108,23 @@ underlays.Cut() var/turf/T = get_turf(src) - var/list/directions = list(NORTH, SOUTH, EAST, WEST) - var/node1_direction = get_dir(src, node1) - var/node2_direction = get_dir(src, node2) - var/node3_direction = get_dir(src, node3) + // Unfortunately our nodes are not in consistent directions (see atmos_init()) so do the dance... + var/list/directions = list(NORTH, SOUTH, EAST, WEST) // UN-connected directions directions -= dir + if(node1) + var/node1_direction = get_dir(src, node1) + add_underlay(T, node1, node1_direction, icon_connect_type) + directions -= node1_direction + if(node2) + var/node2_direction = get_dir(src, node2) + add_underlay(T, node2, node2_direction, icon_connect_type) + directions -= node2_direction + if(node3) + var/node3_direction = get_dir(src, node3) + add_underlay(T, node3, node3_direction, icon_connect_type) + directions -= node3_direction - directions -= add_underlay(T,node1,node1_direction,icon_connect_type) - directions -= add_underlay(T,node2,node2_direction,icon_connect_type) - directions -= add_underlay(T,node3,node3_direction,icon_connect_type) for(var/D in directions) add_underlay(T,,D,icon_connect_type) @@ -195,6 +202,26 @@ icon_connect_type = "-supply" color = PIPE_COLOR_BLUE +/obj/machinery/atmospherics/pipe/manifold/visible/fuel + name="Fuel pipe manifold" + desc = "A manifold composed of fuel pipes" + icon_state = "map-fuel" + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/manifold/visible/aux + name="Aux pipe manifold" + desc = "A manifold composed of aux pipes" + icon_state = "map-aux" + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN + /obj/machinery/atmospherics/pipe/manifold/visible/yellow color = PIPE_COLOR_YELLOW @@ -241,6 +268,26 @@ icon_connect_type = "-supply" color = PIPE_COLOR_BLUE +/obj/machinery/atmospherics/pipe/manifold/hidden/fuel + name="Fuel pipe manifold" + desc = "A manifold composed of fuel pipes" + icon_state = "map-fuel" + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/manifold/hidden/aux + name="Aux pipe manifold" + desc = "A manifold composed of aux pipes" + icon_state = "map-aux" + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN + /obj/machinery/atmospherics/pipe/manifold/hidden/yellow color = PIPE_COLOR_YELLOW diff --git a/code/ATMOSPHERICS/pipes/manifold4w.dm b/code/ATMOSPHERICS/pipes/manifold4w.dm index 197c1a090b..7273ac5288 100644 --- a/code/ATMOSPHERICS/pipes/manifold4w.dm +++ b/code/ATMOSPHERICS/pipes/manifold4w.dm @@ -107,36 +107,12 @@ overlays += icon_manager.get_atmos_icon("manifold", , , "clamps_4way" + icon_connect_type) underlays.Cut() - /* - var/list/directions = list(NORTH, SOUTH, EAST, WEST) - - - directions -= add_underlay(node1) - directions -= add_underlay(node2) - directions -= add_underlay(node3) - directions -= add_underlay(node4) - - for(var/D in directions) - add_underlay(,D) - */ - var/turf/T = get_turf(src) - var/list/directions = list(NORTH, SOUTH, EAST, WEST) - var/node1_direction = get_dir(src, node1) - var/node2_direction = get_dir(src, node2) - var/node3_direction = get_dir(src, node3) - var/node4_direction = get_dir(src, node4) - - directions -= dir - - directions -= add_underlay(T,node1,node1_direction,icon_connect_type) - directions -= add_underlay(T,node2,node2_direction,icon_connect_type) - directions -= add_underlay(T,node3,node3_direction,icon_connect_type) - directions -= add_underlay(T,node4,node4_direction,icon_connect_type) - - for(var/D in directions) - add_underlay(T,,D,icon_connect_type) - + // Take advantage of the fact that our nodes are *always* in the same directions (see atmos_init()) + add_underlay(T, node1, NORTH, icon_connect_type) + add_underlay(T, node2, SOUTH, icon_connect_type) + add_underlay(T, node3, EAST, icon_connect_type) + add_underlay(T, node4, WEST, icon_connect_type) /obj/machinery/atmospherics/pipe/manifold4w/update_underlays() ..() @@ -197,6 +173,26 @@ icon_connect_type = "-supply" color = PIPE_COLOR_BLUE +/obj/machinery/atmospherics/pipe/manifold4w/visible/fuel + name="4-way fuel pipe manifold" + desc = "A manifold composed of fuel pipes" + icon_state = "map_4way-fuel" + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/manifold4w/visible/aux + name="4-way aux pipe manifold" + desc = "A manifold composed of aux pipes" + icon_state = "map_4way-aux" + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN + /obj/machinery/atmospherics/pipe/manifold4w/visible/yellow color = PIPE_COLOR_YELLOW @@ -243,6 +239,26 @@ icon_connect_type = "-supply" color = PIPE_COLOR_BLUE +/obj/machinery/atmospherics/pipe/manifold4w/hidden/fuel + name="4-way fuel pipe manifold" + desc = "A manifold composed of fuel pipes" + icon_state = "map_4way-fuel" + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/aux + name="4-way aux pipe manifold" + desc = "A manifold composed of aux pipes" + icon_state = "map_4way-aux" + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN + /obj/machinery/atmospherics/pipe/manifold4w/hidden/yellow color = PIPE_COLOR_YELLOW diff --git a/code/ATMOSPHERICS/pipes/pipe_base.dm b/code/ATMOSPHERICS/pipes/pipe_base.dm index d1bb92fac7..e627fcbb46 100644 --- a/code/ATMOSPHERICS/pipes/pipe_base.dm +++ b/code/ATMOSPHERICS/pipes/pipe_base.dm @@ -9,7 +9,7 @@ var/leaking = FALSE // Do not set directly, use set_leaking(TRUE/FALSE) layer = PIPES_LAYER - use_power = 0 + use_power = USE_POWER_OFF pipe_flags = 0 // Does not have PIPING_DEFAULT_LAYER_ONLY flag. diff --git a/code/ATMOSPHERICS/pipes/simple.dm b/code/ATMOSPHERICS/pipes/simple.dm index eb980c3b47..215f3498b7 100644 --- a/code/ATMOSPHERICS/pipes/simple.dm +++ b/code/ATMOSPHERICS/pipes/simple.dm @@ -203,6 +203,26 @@ icon_connect_type = "-supply" color = PIPE_COLOR_BLUE +/obj/machinery/atmospherics/pipe/simple/visible/fuel + name = "Fuel pipe" + desc = "A one meter section of fuel pipe" + icon_state = "intact-fuel" + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/simple/visible/aux + name = "Aux pipe" + desc = "A one meter section of aux pipe" + icon_state = "intact-aux" + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN + /obj/machinery/atmospherics/pipe/simple/visible/yellow color = PIPE_COLOR_YELLOW @@ -249,6 +269,26 @@ icon_connect_type = "-supply" color = PIPE_COLOR_BLUE +/obj/machinery/atmospherics/pipe/simple/hidden/fuel + name = "Fuel pipe" + desc = "A one meter section of fuel pipe" + icon_state = "intact-fuel" + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/simple/hidden/aux + name = "Aux pipe" + desc = "A one meter section of aux pipe" + icon_state = "intact-aux" + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN + /obj/machinery/atmospherics/pipe/simple/hidden/yellow color = PIPE_COLOR_YELLOW diff --git a/code/ATMOSPHERICS/pipes/tank.dm b/code/ATMOSPHERICS/pipes/tank.dm index 751c9ed607..8a68ba369c 100644 --- a/code/ATMOSPHERICS/pipes/tank.dm +++ b/code/ATMOSPHERICS/pipes/tank.dm @@ -136,6 +136,7 @@ /obj/machinery/atmospherics/pipe/tank/phoron name = "Pressure Tank (Phoron)" icon_state = "phoron_map" + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_FUEL /obj/machinery/atmospherics/pipe/tank/phoron/New() air_temporary = new diff --git a/code/ATMOSPHERICS/pipes/universal.dm b/code/ATMOSPHERICS/pipes/universal.dm index 00d2746947..a72ef219f7 100644 --- a/code/ATMOSPHERICS/pipes/universal.dm +++ b/code/ATMOSPHERICS/pipes/universal.dm @@ -4,7 +4,7 @@ /obj/machinery/atmospherics/pipe/simple/visible/universal name="Universal pipe adapter" desc = "An adapter for regular, supply and scrubbers pipes" - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL|CONNECT_TYPE_AUX icon_state = "map_universal" pipe_flags = PIPING_ALL_LAYER|PIPING_CARDINAL_AUTONORMALIZE construction_type = /obj/item/pipe/binary @@ -42,7 +42,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/universal name="Universal pipe adapter" desc = "An adapter for regular, supply and scrubbers pipes" - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL|CONNECT_TYPE_AUX icon_state = "map_universal" pipe_flags = PIPING_ALL_LAYER|PIPING_CARDINAL_AUTONORMALIZE construction_type = /obj/item/pipe/binary @@ -81,21 +81,42 @@ var/turf/T = loc if(node) var/node_dir = get_dir(src,node) - if(node.icon_connect_type == "-supply") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, node, node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else if (node.icon_connect_type == "-scrubbers") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, node, node_dir, "-scrubbers") - else - add_underlay_adapter(T, node, node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") + switch(node.icon_connect_type) + if("-supply") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, node, node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + add_underlay_adapter(T, , node_dir, "-fuel") + add_underlay_adapter(T, , node_dir, "-aux") + if ("-scrubbers") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, node, node_dir, "-scrubbers") + add_underlay_adapter(T, , node_dir, "-fuel") + add_underlay_adapter(T, , node_dir, "-aux") + if ("-fuel") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + add_underlay_adapter(T, node, node_dir, "-fuel") + add_underlay_adapter(T, , node_dir, "-aux") + if ("-aux") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + add_underlay_adapter(T, , node_dir, "-fuel") + add_underlay_adapter(T, node, node_dir, "-aux") + else + add_underlay_adapter(T, node, node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + add_underlay_adapter(T, , node_dir, "-fuel") + add_underlay_adapter(T, , node_dir, "-aux") else add_underlay_adapter(T, , direction, "-supply") add_underlay_adapter(T, , direction, "-scrubbers") + add_underlay_adapter(T, , direction, "-fuel") + add_underlay_adapter(T, , direction, "-aux") add_underlay_adapter(T, , direction, "") /obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays diff --git a/code/ATMOSPHERICS/pipes/vent.dm b/code/ATMOSPHERICS/pipes/vent.dm index 4a1dc5696d..5090631a2b 100644 --- a/code/ATMOSPHERICS/pipes/vent.dm +++ b/code/ATMOSPHERICS/pipes/vent.dm @@ -16,7 +16,7 @@ initialize_directions = SOUTH pipe_flags = PIPING_DEFAULT_LAYER_ONLY construction_type = /obj/item/pipe/directional - pipe_state = "passive vent" + pipe_state = "passive_vent" var/build_killswitch = 1 diff --git a/code/ZAS/Connection.dm b/code/ZAS/Connection.dm index 37600dadea..b780679d66 100644 --- a/code/ZAS/Connection.dm +++ b/code/ZAS/Connection.dm @@ -79,13 +79,13 @@ Class Procs: if(!direct()) state |= CONNECTION_DIRECT edge.direct++ - //world << "Marked direct." + //to_world("Marked direct.") /connection/proc/mark_indirect() if(direct()) state &= ~CONNECTION_DIRECT edge.direct-- - //world << "Marked indirect." + //to_world("Marked indirect.") /connection/proc/mark_space() state |= CONNECTION_SPACE @@ -99,18 +99,18 @@ Class Procs: /connection/proc/erase() edge.remove_connection(src) state |= CONNECTION_INVALID - //world << "Connection Erased: [state]" + //to_world("Connection Erased: [state]") /connection/proc/update() - //world << "Updated, \..." + //to_world("Updated, \...") if(!istype(A,/turf/simulated)) - //world << "Invalid A." + //to_world("Invalid A.") erase() return var/block_status = air_master.air_blocked(A,B) if(block_status & AIR_BLOCKED) - //world << "Blocked connection." + //to_world("Blocked connection.") erase() return else if(block_status & ZONE_BLOCKED) @@ -122,14 +122,14 @@ Class Procs: if(state & CONNECTION_SPACE) if(!b_is_space) - //world << "Invalid B." + //to_world("Invalid B.") erase() return if(A.zone != zoneA) - //world << "Zone changed, \..." + //to_world("Zone changed, \...") if(!A.zone) erase() - //world << "erased." + //to_world("erased.") return else edge.remove_connection(src) @@ -137,22 +137,22 @@ Class Procs: edge.add_connection(src) zoneA = A.zone - //world << "valid." + //to_world("valid.") return else if(b_is_space) - //world << "Invalid B." + //to_world("Invalid B.") erase() return if(A.zone == B.zone) - //world << "A == B" + //to_world("A == B") erase() return if(A.zone != zoneA || (zoneB && (B.zone != zoneB))) - //world << "Zones changed, \..." + //to_world("Zones changed, \...") if(A.zone && B.zone) edge.remove_connection(src) edge = air_master.get_edge(A.zone, B.zone) @@ -160,9 +160,9 @@ Class Procs: zoneA = A.zone zoneB = B.zone else - //world << "erased." + //to_world("erased.") erase() return - //world << "valid." \ No newline at end of file + //to_world("valid.") \ No newline at end of file diff --git a/code/ZAS/ConnectionGroup.dm b/code/ZAS/ConnectionGroup.dm index 94df46ac01..11b1e22a0b 100644 --- a/code/ZAS/ConnectionGroup.dm +++ b/code/ZAS/ConnectionGroup.dm @@ -72,10 +72,10 @@ Class Procs: /connection_edge/proc/add_connection(connection/c) coefficient++ if(c.direct()) direct++ - //world << "Connection added: [type] Coefficient: [coefficient]" + //to_world("Connection added: [type] Coefficient: [coefficient]") /connection_edge/proc/remove_connection(connection/c) - //world << "Connection removed: [type] Coefficient: [coefficient-1]" + //to_world("Connection removed: [type] Coefficient: [coefficient-1]") coefficient-- if(coefficient <= 0) erase() @@ -85,7 +85,7 @@ Class Procs: /connection_edge/proc/erase() air_master.remove_edge(src) - //world << "[type] Erased." + //to_world("[type] Erased.") /connection_edge/proc/tick() @@ -128,7 +128,7 @@ Class Procs: A.edges.Add(src) B.edges.Add(src) //id = edge_id(A,B) - //world << "New edge between [A] and [B]" + //to_world("New edge between [A] and [B]") /connection_edge/zone/add_connection(connection/c) . = ..() @@ -198,7 +198,7 @@ Class Procs: A.edges.Add(src) air = B.return_air() //id = 52*A.id - //world << "New edge from [A] to [B]." + //to_world("New edge from [A] to [B].") /connection_edge/unsimulated/add_connection(connection/c) . = ..() diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm index 41fcdcad85..83537b8575 100644 --- a/code/ZAS/Diagnostic.dm +++ b/code/ZAS/Diagnostic.dm @@ -22,11 +22,11 @@ client/proc/Zone_Info(turf/T as null|turf) if(istype(T,/turf/simulated) && T:zone) T:zone:dbg_data(src) else - mob << "No zone here." + to_chat(mob, "No zone here.") var/datum/gas_mixture/mix = T.return_air() - mob << "[mix.return_pressure()] kPa [mix.temperature]C" + to_chat(mob, "[mix.return_pressure()] kPa [mix.temperature]C") for(var/g in mix.gas) - mob << "[g]: [mix.gas[g]]\n" + to_chat(mob, "[g]: [mix.gas[g]]\n") else if(zone_debug_images) for(var/zone in zone_debug_images) @@ -56,9 +56,9 @@ client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf) if(direction == "N/A") if(!(T.c_airblock(T) & AIR_BLOCKED)) - mob << "The turf can pass air! :D" + to_chat(mob, "The turf can pass air! :D") else - mob << "No air passage :x" + to_chat(mob, "No air passage :x") return var/turf/simulated/other_turf = get_step(T, direction_list[direction]) @@ -70,29 +70,29 @@ client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf) if(o_block & AIR_BLOCKED) if(t_block & AIR_BLOCKED) - mob << "Neither turf can connect. :(" + to_chat(mob, "Neither turf can connect. :(") else - mob << "The initial turf only can connect. :\\" + to_chat(mob, "The initial turf only can connect. :\\") else if(t_block & AIR_BLOCKED) - mob << "The other turf can connect, but not the initial turf. :/" + to_chat(mob, "The other turf can connect, but not the initial turf. :/") else - mob << "Both turfs can connect! :)" + to_chat(mob, "Both turfs can connect! :)") - mob << "Additionally, \..." + to_chat(mob, "Additionally, \...") if(o_block & ZONE_BLOCKED) if(t_block & ZONE_BLOCKED) - mob << "neither turf can merge." + to_chat(mob, "neither turf can merge.") else - mob << "the other turf cannot merge." + to_chat(mob, "the other turf cannot merge.") else if(t_block & ZONE_BLOCKED) - mob << "the initial turf cannot merge." + to_chat(mob, "the initial turf cannot merge.") else - mob << "both turfs can merge." + to_chat(mob, "both turfs can merge.") client/proc/ZASSettings() set category = "Debug" diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm index f61d1cf4fc..5997a1c47a 100644 --- a/code/ZAS/Turf.dm +++ b/code/ZAS/Turf.dm @@ -99,7 +99,7 @@ var/s_block = c_airblock(src) if(s_block & AIR_BLOCKED) #ifdef ZASDBG - if(verbose) world << "Self-blocked." + if(verbose) to_world("Self-blocked.") //dbg(blocked) #endif if(zone) @@ -131,7 +131,7 @@ if(block & AIR_BLOCKED) #ifdef ZASDBG - if(verbose) world << "[d] is blocked." + if(verbose) to_world("[d] is blocked.") //unsim.dbg(air_blocked, turn(180,d)) #endif @@ -141,7 +141,7 @@ if(r_block & AIR_BLOCKED) #ifdef ZASDBG - if(verbose) world << "[d] is blocked." + if(verbose) to_world("[d] is blocked.") //dbg(air_blocked, d) #endif @@ -172,7 +172,7 @@ // we are blocking them and not blocking ourselves - this prevents tiny zones from forming on doorways. if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || ((r_block & ZONE_BLOCKED) && !(s_block & ZONE_BLOCKED))) #ifdef ZASDBG - if(verbose) world << "[d] is zone blocked." + if(verbose) to_world("[d] is zone blocked.") //dbg(zone_blocked, d) #endif @@ -186,22 +186,22 @@ #ifdef ZASDBG dbg(assigned) - if(verbose) world << "Added to [zone]" + if(verbose) to_world("Added to [zone]") #endif else if(sim.zone != zone) #ifdef ZASDBG - if(verbose) world << "Connecting to [sim.zone]" + if(verbose) to_world("Connecting to [sim.zone]") #endif air_master.connect(src, sim) #ifdef ZASDBG - else if(verbose) world << "[d] has same zone." + else if(verbose) to_world("[d] has same zone.") - else if(verbose) world << "[d] has invalid zone." + else if(verbose) to_world("[d] has invalid zone.") #endif else diff --git a/code/ZAS/Variable Settings.dm b/code/ZAS/Variable Settings.dm index 2bfbed9681..f88e4816af 100644 --- a/code/ZAS/Variable Settings.dm +++ b/code/ZAS/Variable Settings.dm @@ -169,7 +169,7 @@ var/global/vs_control/vsc = new vars[ch] = vw if(how == "Toggle") newvar = (newvar?"ON":"OFF") - world << "[key_name(user)] changed the setting [display_description] to [newvar]." + to_world("[key_name(user)] changed the setting [display_description] to [newvar].") if(ch in plc.settings) ChangeSettingsDialog(user,plc.settings) else @@ -322,7 +322,7 @@ var/global/vs_control/vsc = new plc.N2O_HALLUCINATION = initial(plc.N2O_HALLUCINATION) - world << "[key_name(user)] changed the global phoron/ZAS settings to \"[def]\"" + to_world("[key_name(user)] changed the global phoron/ZAS settings to \"[def]\"") /pl_control/var/list/settings = list() diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm index 18c5ab2899..d07a280d04 100644 --- a/code/ZAS/Zone.dm +++ b/code/ZAS/Zone.dm @@ -168,15 +168,16 @@ Class Procs: E.recheck() /zone/proc/dbg_data(mob/M) - M << name + to_chat(M,name) for(var/g in air.gas) - M << "[gas_data.name[g]]: [air.gas[g]]" - M << "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]K ([air.temperature - T0C]C)" - M << "O2 per N2: [(air.gas["nitrogen"] ? air.gas["oxygen"]/air.gas["nitrogen"] : "N/A")] Moles: [air.total_moles]" - M << "Simulated: [contents.len] ([air.group_multiplier])" - //M << "Unsimulated: [unsimulated_contents.len]" - //M << "Edges: [edges.len]" - if(invalid) M << "Invalid!" + to_chat(M, "[gas_data.name[g]]: [air.gas[g]]") + to_chat(M, "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]K ([air.temperature - T0C]C)") + to_chat(M, "O2 per N2: [(air.gas["nitrogen"] ? air.gas["oxygen"]/air.gas["nitrogen"] : "N/A")] Moles: [air.total_moles]") + to_chat(M, "Simulated: [contents.len] ([air.group_multiplier])") + //to_chat(M, "Unsimulated: [unsimulated_contents.len]") + //to_chat(M, "Edges: [edges.len]") + if(invalid) + to_chat(M, "Invalid!") var/zone_edges = 0 var/space_edges = 0 var/space_coefficient = 0 @@ -185,10 +186,10 @@ Class Procs: else space_edges++ space_coefficient += E.coefficient - M << "[E:air:return_pressure()]kPa" + to_chat(M, "[E:air:return_pressure()]kPa") - M << "Zone Edges: [zone_edges]" - M << "Space Edges: [space_edges] ([space_coefficient] connections)" + to_chat(M, "Zone Edges: [zone_edges]") + to_chat(M, "Space Edges: [space_edges] ([space_coefficient] connections)") //for(var/turf/T in unsimulated_contents) - // M << "[T] at ([T.x],[T.y])" \ No newline at end of file + // to_chat(M, "[T] at ([T.x],[T.y])") \ No newline at end of file diff --git a/code/__datastructures/globals.dm b/code/__datastructures/globals.dm index 05d5a2d29b..a51bad06db 100644 --- a/code/__datastructures/globals.dm +++ b/code/__datastructures/globals.dm @@ -1,11 +1,15 @@ -//See controllers/globals.dm +// See also controllers/globals.dm + +// Creates a global initializer with a given InitValue expression, do not use outside this file #define GLOBAL_MANAGED(X, InitValue)\ /datum/controller/global_vars/proc/InitGlobal##X(){\ ##X = ##InitValue;\ gvars_datum_init_order += #X;\ } -#define GLOBAL_UNMANAGED(X, InitValue) /datum/controller/global_vars/proc/InitGlobal##X() +// Creates an empty global initializer, do not use outside this file +#define GLOBAL_UNMANAGED(X) /datum/controller/global_vars/proc/InitGlobal##X() { return; } +// Prevents a given global from being VV'd #ifndef TESTING #define GLOBAL_PROTECT(X)\ /datum/controller/global_vars/InitGlobal##X(){\ @@ -16,24 +20,42 @@ #define GLOBAL_PROTECT(X) #endif +// Standard BYOND global, do not use outside this file #define GLOBAL_REAL_VAR(X) var/global/##X + +// Standard typed BYOND global, do not use outside this file #define GLOBAL_REAL(X, Typepath) var/global##Typepath/##X +// Defines a global var on the controller, do not use outside this file. #define GLOBAL_RAW(X) /datum/controller/global_vars/var/global##X +// Create an untyped global with an initializer expression #define GLOBAL_VAR_INIT(X, InitValue) GLOBAL_RAW(/##X); GLOBAL_MANAGED(X, InitValue) -#define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X, InitValue) +// Create a global const var, do not use +#define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X) +// Create a list global with an initializer expression #define GLOBAL_LIST_INIT(X, InitValue) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, InitValue) +// Create a list global that is initialized as an empty list #define GLOBAL_LIST_EMPTY(X) GLOBAL_LIST_INIT(X, list()) +// Create a typed list global with an initializer expression +#define GLOBAL_LIST_INIT_TYPED(X, Typepath, InitValue) GLOBAL_RAW(/list##Typepath/X); GLOBAL_MANAGED(X, InitValue) + +// Create a typed list global that is initialized as an empty list +#define GLOBAL_LIST_EMPTY_TYPED(X, Typepath) GLOBAL_LIST_INIT_TYPED(X, Typepath, list()) + +// Create a typed global with an initializer expression #define GLOBAL_DATUM_INIT(X, Typepath, InitValue) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, InitValue) -#define GLOBAL_VAR(X) GLOBAL_RAW(/##X); GLOBAL_MANAGED(X, null) +// Create an untyped null global +#define GLOBAL_VAR(X) GLOBAL_RAW(/##X); GLOBAL_UNMANAGED(X) -#define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, null) +// Create a null global list +#define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_UNMANAGED(X) -#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, null) +// Create a typed null global +#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X) diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm index 6acc667f2d..57161ffac8 100644 --- a/code/__defines/MC.dm +++ b/code/__defines/MC.dm @@ -1,4 +1,5 @@ #define MC_TICK_CHECK ( ( TICK_USAGE > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 ) +#define CURRENT_RUNLEVEL (2 ** (Master.current_runlevel - 1)) #define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = Master.current_ticklimit; var/split_tick_phases = ##phase_count #define MC_SPLIT_TICK \ diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index 10a6485c44..b68300eeb2 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -15,6 +15,9 @@ //#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile. #define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support! +// Comment/Uncomment this to turn off/on shuttle code debugging logs +#define DEBUG_SHUTTLES + // If we are doing the map test build, do not include the main maps, only the submaps. #if MAP_TEST #define USING_MAP_DATUM /datum/map diff --git a/code/__defines/_lists.dm b/code/__defines/_lists.dm index 348aafccef..016d1a89d4 100644 --- a/code/__defines/_lists.dm +++ b/code/__defines/_lists.dm @@ -3,26 +3,41 @@ //Picks from the list, with some safeties, and returns the "default" arg if it fails #define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default) + // Ensures L is initailized after this point #define LAZYINITLIST(L) if (!L) L = list() + // Sets a L back to null iff it is empty #define UNSETEMPTY(L) if (L && !length(L)) L = null + // Removes I from list L, and sets I to null if it is now empty #define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } } -// Adds I to L, initalizing I if necessary + +// Adds I to L, initalizing L if necessary #define LAZYADD(L, I) if(!L) { L = list(); } L += I; + #define LAZYOR(L, I) if(!L) { L = list(); } L |= I; + +// Adds I to L, initalizing L if necessary, if I is not already in L +#define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I; + #define LAZYFIND(L, V) L ? L.Find(V) : 0 + // Reads I from L safely - Works with both associative and traditional lists. #define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null) + // Turns LAZYINITLIST(L) L[K] = V into ... for associated lists #define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V; + // Reads the length of L, returning 0 if null #define LAZYLEN(L) length(L) + // Null-safe L.Cut() #define LAZYCLEARLIST(L) if(L) L.Cut() + // Reads L or an empty list if L is not a list. Note: Does NOT assign, L may be an expression. #define SANITIZE_LIST(L) ( islist(L) ? L : list() ) + #define reverseList(L) reverseRange(L.Copy()) // binary search sorted insert diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index c54fbe93cc..a8e3951a3e 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -22,7 +22,7 @@ What can I do with Planesmasters? Planesmasters can be used as a neater way to deal with client images or potentially to do some neat things How do planes work? - A plane can be any integer from -100 to 100. (If you want more, bug lummox.) + A plane can be any integer from -10000 to 10000. All planes above 0, the 'base plane', are visible even when your character cannot 'see' them, for example, the HUD. All planes below 0, the 'base plane', are only visible when a character can see them. @@ -40,8 +40,14 @@ What is the naming convention for planes or layers? */ + #define SPACE_PLANE -82 // Reserved for use in space/parallax -#define PARALLAX_PLANE -80 // Reserved for use in space/parallax +#define PARALLAX_PLANE -81 // Reserved for use in space/parallax +#define SKYBOX_PLANE -80 // Skybox parallax +#define DUST_PLANE -79 // For dust overlay on space turfs. Should be above skybox for parallax effect. + +#define PLANE_LOOKINGGLASS -78 // For the Looking Glass holodecks +#define PLANE_LOOKINGGLASS_IMG -77 // For the Looking Glass holodecks // OPENSPACE_PLANE reserves all planes between OPENSPACE_PLANE_START and OPENSPACE_PLANE_END inclusive #define OPENSPACE_PLANE -75 // /turf/simulated/open will use OPENSPACE_PLANE + z (Valid z's being 2 thru 17) @@ -50,7 +56,6 @@ What is the naming convention for planes or layers? #define OVER_OPENSPACE_PLANE -57 // Turf Planes -#define SPACE_PLANE -43 // Space turfs themselves #define PLATING_PLANE -44 // Plating #define DISPOSAL_LAYER 2.1 // Under objects, even when planeswapped #define PIPES_LAYER 2.2 // Under objects, even when planeswapped @@ -86,6 +91,9 @@ What is the naming convention for planes or layers? #define BELOW_MOB_LAYER 3.9 // Should be converted to plane swaps #define ABOVE_MOB_LAYER 4.1 // Should be converted to plane swaps +// Invisible things plane +#define CLOAKED_PLANE -15 + // Top plane (in the sense that it's the highest in 'the world' and not a UI element) #define ABOVE_PLANE -10 @@ -130,6 +138,8 @@ What is the naming convention for planes or layers? #define PLANE_ADMIN2 33 //Purely for shenanigans (above lighting) +#define PLANE_BUILDMODE 39 //Things that only show up when you have buildmode on + //Fullscreen overlays under inventory #define PLANE_FULLSCREEN 90 //Blindness, mesons, druggy, etc #define OBFUSCATION_LAYER 5 //Where images covering the view for eyes are put diff --git a/code/__defines/_protect.dm b/code/__defines/_protect.dm new file mode 100644 index 0000000000..b10a6264bd --- /dev/null +++ b/code/__defines/_protect.dm @@ -0,0 +1,11 @@ +///Protects a datum from being VV'd +#define GENERAL_PROTECT_DATUM(Path)\ +##Path/can_vv_get(var_name){\ + return FALSE;\ +}\ +##Path/vv_edit_var(var_name, var_value){\ + return FALSE;\ +}\ +##Path/CanProcCall(procname){\ + return FALSE;\ +} \ No newline at end of file diff --git a/code/__defines/_tick.dm b/code/__defines/_tick.dm index 1f112f711b..54ac7d398d 100644 --- a/code/__defines/_tick.dm +++ b/code/__defines/_tick.dm @@ -1,5 +1,5 @@ -#define TICK_LIMIT_RUNNING 85 //VOREStation Emergency Edit +#define TICK_LIMIT_RUNNING 80 #define TICK_LIMIT_TO_RUN 70 #define TICK_LIMIT_MC 70 #define TICK_LIMIT_MC_INIT_DEFAULT 98 diff --git a/code/__defines/admin_vr.dm b/code/__defines/admin_vr.dm index 6e5e8c1a9b..62c4add906 100644 --- a/code/__defines/admin_vr.dm +++ b/code/__defines/admin_vr.dm @@ -1,3 +1,5 @@ #define SMITE_SHADEKIN_ATTACK "Shadekin (Attack)" #define SMITE_SHADEKIN_NOMF "Shadekin (Devour)" #define SMITE_REDSPACE_ABDUCT "Redspace Abduction" +#define SMITE_AUTOSAVE "10 Second Autosave" +#define SMITE_AUTOSAVE_WIDE "10 Second Autosave (AoE)" diff --git a/code/__defines/atmos.dm b/code/__defines/atmos.dm index 66348344f2..56055273e4 100644 --- a/code/__defines/atmos.dm +++ b/code/__defines/atmos.dm @@ -29,6 +29,7 @@ #define MINIMUM_AIR_TO_SUSPEND (MOLES_CELLSTANDARD * MINIMUM_AIR_RATIO_TO_SUSPEND) // Minimum amount of air that has to move before a group processing can be suspended #define MINIMUM_MOLES_DELTA_TO_MOVE (MOLES_CELLSTANDARD * MINIMUM_AIR_RATIO_TO_SUSPEND) // Either this must be active #define MINIMUM_TEMPERATURE_TO_MOVE (T20C + 100) // or this (or both, obviously) +#define MINIMUM_PRESSURE_DIFFERENCE_TO_SUSPEND (MINIMUM_AIR_TO_SUSPEND*R_IDEAL_GAS_EQUATION*T20C)/CELL_VOLUME // Minimum pressure difference between zones to suspend #define MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND 0.012 // Minimum temperature difference before group processing is suspended. #define MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND 4 diff --git a/code/__defines/color.dm b/code/__defines/color.dm index ba9efe20de..c1416a5e92 100644 --- a/code/__defines/color.dm +++ b/code/__defines/color.dm @@ -14,4 +14,7 @@ #define COLOR_ASSEMBLY_LBLUE "#5D99BE" #define COLOR_ASSEMBLY_BLUE "#38559E" #define COLOR_ASSEMBLY_PURPLE "#6F6192" -#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4" \ No newline at end of file +#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4" + +#define COLOR_ASTEROID_ROCK "#735555" +#define COLOR_GOLD "#ffcc33" diff --git a/code/__defines/construction.dm b/code/__defines/construction.dm index 19d1fcd649..33d558497d 100644 --- a/code/__defines/construction.dm +++ b/code/__defines/construction.dm @@ -34,18 +34,25 @@ #define PIPE_TRIN_T 7 //8 directions: N->S+E, S->N+E, N->S+W, S->N+W, E->W+S, W->E+S, E->W+N, W->E+N // Pipe connectivity bit flags -#define CONNECT_TYPE_REGULAR 1 -#define CONNECT_TYPE_SUPPLY 2 -#define CONNECT_TYPE_SCRUBBER 4 -#define CONNECT_TYPE_HE 8 +#define CONNECT_TYPE_REGULAR 1 //Center of tile, 'normal' +#define CONNECT_TYPE_SUPPLY 2 //Atmos air supply pipes +#define CONNECT_TYPE_SCRUBBER 4 //Atmos air scrubber pipes +#define CONNECT_TYPE_HE 8 //Heat exchanger pipes +#define CONNECT_TYPE_FUEL 16 //Fuel pipes for overmap ships +#define CONNECT_TYPE_AUX 32 //Aux pipes for 'other' things (airlocks, etc) // We are based on the three named layers of supply, regular, and scrubber. #define PIPING_LAYER_SUPPLY 1 #define PIPING_LAYER_REGULAR 2 #define PIPING_LAYER_SCRUBBER 3 +#define PIPING_LAYER_FUEL 4 +#define PIPING_LAYER_AUX 5 #define PIPING_LAYER_DEFAULT PIPING_LAYER_REGULAR // We offset the layer values of the different pipe types to ensure they look nice +#define PIPES_SCRUBBER_LAYER (PIPES_LAYER - 0.05) +#define PIPES_AUX_LAYER (PIPES_LAYER - 0.04) +#define PIPES_FUEL_LAYER (PIPES_LAYER - 0.03) #define PIPES_SCRUBBER_LAYER (PIPES_LAYER - 0.02) #define PIPES_SUPPLY_LAYER (PIPES_LAYER - 0.01) #define PIPES_HE_LAYER (PIPES_LAYER + 0.01) diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index 59d2879ba5..02f1b11ee1 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -1,7 +1,16 @@ -#define GAME_STATE_PREGAME 1 -#define GAME_STATE_SETTING_UP 2 -#define GAME_STATE_PLAYING 3 -#define GAME_STATE_FINISHED 4 +// Ticker game states, turns out these are equivilent to runlevels1 +#define GAME_STATE_INIT 0 // RUNLEVEL_INIT +#define GAME_STATE_PREGAME 1 // RUNLEVEL_LOBBY +#define GAME_STATE_SETTING_UP 2 // RUNLEVEL_SETUP +#define GAME_STATE_PLAYING 3 // RUNLEVEL_GAME +#define GAME_STATE_FINISHED 4 // RUNLEVEL_POSTGAME + +//End game state, to manage round end. +#define END_GAME_NOT_OVER 1 // Still playing normally +#define END_GAME_MODE_FINISHED 2 // Mode has finished but game has not, wait for game to end too. +#define END_GAME_READY_TO_END 3 // Game and Mode have finished, do rounded stuff. +#define END_GAME_ENDING 4 // Just waiting for ending timer. +#define END_GAME_DELAYED 5 // Admin has delayed the round. // Security levels. #define SEC_LEVEL_GREEN 0 diff --git a/code/__defines/is_helpers.dm b/code/__defines/is_helpers.dm index d3222965a9..e121f55216 100644 --- a/code/__defines/is_helpers.dm +++ b/code/__defines/is_helpers.dm @@ -6,6 +6,7 @@ //--------------- #define isatom(D) istype(D, /atom) +#define isclient(D) istype(D, /client) //--------------- //#define isobj(D) istype(D, /obj) //Built in @@ -51,3 +52,4 @@ //--------------- //#define isturf(D) istype(D, /turf) //Built in #define isopenspace(A) istype(A, /turf/simulated/open) +#define isspace(A) istype(A, /turf/space) diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm index 35a031445e..0787245d61 100644 --- a/code/__defines/machinery.dm +++ b/code/__defines/machinery.dm @@ -11,6 +11,11 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define DOOR_CRUSH_DAMAGE 20 #define ALIEN_SELECT_AFK_BUFFER 1 // How many minutes that a person can be AFK before not being allowed to be an alien. +// Constants for machine's use_power +#define USE_POWER_OFF 0 // No continuous power use +#define USE_POWER_IDLE 1 // Machine is using power at its idle power level +#define USE_POWER_ACTIVE 2 // Machine is using power at its active power level + // Channel numbers for power. #define EQUIP 1 #define LIGHT 2 @@ -24,6 +29,11 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define MAINT 0x8 // Under maintenance. #define EMPED 0x10 // Temporary broken by EMP pulse. +// Remote control states +#define RCON_NO 1 +#define RCON_AUTO 2 +#define RCON_YES 3 + // Used by firelocks #define FIREDOOR_OPEN 1 #define FIREDOOR_CLOSED 2 @@ -60,11 +70,28 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called #define NETWORK_ALARM_ATMOS "Atmosphere Alarms" #define NETWORK_ALARM_POWER "Power Alarms" #define NETWORK_ALARM_FIRE "Fire Alarms" -#define NETWORK_EXPLORATION +#define NETWORK_TALON_HELMETS "TalonHelmets" //VOREStation Add +#define NETWORK_TALON_SHIP "TalonShip" //VOREStation Add +#define NETWORK_EXPLORATION "Exploration" // Those networks can only be accessed by pre-existing terminals. AIs and new terminals can't use them. var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret", NETWORK_COMMUNICATORS, NETWORK_EXPLORATION) +#define TRANSMISSION_WIRE 0 //Is this ever used? I don't think it is. +#define TRANSMISSION_RADIO 1 //Radio transmissions (like airlock controller to pump) +#define TRANSMISSION_SUBSPACE 2 //Like headsets +#define TRANSMISSION_BLUESPACE 3 //Point-to-point links + +#define SIGNAL_NORMAL 0 //Normal subspace signals +#define SIGNAL_SIMPLE 1 //Normal inter-machinery(?) signals +#define SIGNAL_FAKE 2 //Untrackable signals +#define SIGNAL_TEST 4 //Unlogged signals + +#define DATA_NORMAL 0 //Normal data +#define DATA_INTERCOM 1 //Intercoms only +#define DATA_LOCAL 2 //Intercoms and SBRs +#define DATA_ANTAG 3 //Antag interception +#define DATA_FAKE 4 //Not from a real mob //singularity defines #define STAGE_ONE 1 diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 0bcbcfe38b..2026639812 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -2,7 +2,7 @@ // Turf-only flags. #define NOJAUNT 1 // This is used in literally one place, turf.dm, to block ethereal jaunt. -#define TRANSITIONEDGE 7 // Distance from edge to move to another z-level. +#define TRANSITIONEDGE 1 // Distance from edge to move to another z-level. // Invisibility constants. These should only be used for TRUE invisibility, AKA nothing living players touch #define INVISIBILITY_LIGHTING 20 @@ -78,8 +78,7 @@ #define COLOR_RED_LIGHT "#FF3333" #define COLOR_DEEP_SKY_BLUE "#00e1ff" - - +#define CLIENT_FROM_VAR(I) (ismob(I) ? I:client : (isclient(I) ? I : null)) // Shuttles. @@ -105,6 +104,7 @@ #define FORCE_LAUNCH 2 #define WAIT_ARRIVE 3 #define WAIT_FINISH 4 +#define DO_AUTOPILOT 5 // Setting this much higher than 1024 could allow spammers to DOS the server easily. #define MAX_MESSAGE_LEN 2048 //VOREStation Edit - I'm not sure about "easily". It can be a little longer. @@ -113,6 +113,7 @@ #define MAX_RECORD_LENGTH 24576 #define MAX_LNAME_LEN 64 #define MAX_NAME_LEN 52 +#define MAX_FEEDBACK_LENGTH 4096 #define MAX_TEXTFILE_LENGTH 128000 // 512GQ file // Event defines. @@ -128,6 +129,13 @@ //Area flags, possibly more to come #define RAD_SHIELDED 1 //shielded from radiation, clearly +// OnTopic return values +#define TOPIC_NOACTION 0 +#define TOPIC_HANDLED 1 +#define TOPIC_REFRESH 2 +#define TOPIC_UPDATE_PREVIEW 4 +#define TOPIC_REFRESH_UPDATE_PREVIEW (TOPIC_REFRESH|TOPIC_UPDATE_PREVIEW) + // Convoluted setup so defines can be supplied by Bay12 main server compile script. // Should still work fine for people jamming the icons into their repo. #ifndef CUSTOM_ITEM_OBJ @@ -176,6 +184,7 @@ #define MAT_SUPERMATTER "supermatter" #define MAT_METALHYDROGEN "mhydrogen" #define MAT_OSMIUM "osmium" +#define MAT_GRAPHITE "graphite" #define SHARD_SHARD "shard" #define SHARD_SHRAPNEL "shrapnel" @@ -236,17 +245,20 @@ #define ANTAG_SHARED "Shared" #define ANTAG_KNOWN "Known" -// Job groups -#define ROLE_COMMAND "command" -#define ROLE_SECURITY "security" -#define ROLE_ENGINEERING "engineering" -#define ROLE_MEDICAL "medical" -#define ROLE_RESEARCH "research" -#define ROLE_CARGO "cargo" -#define ROLE_CIVILIAN "civilian" -#define ROLE_SYNTHETIC "synthetic" -#define ROLE_UNKNOWN "unknown" -#define ROLE_EVERYONE "everyone" +// Departments. +#define DEPARTMENT_COMMAND "Command" +#define DEPARTMENT_SECURITY "Security" +#define DEPARTMENT_ENGINEERING "Engineering" +#define DEPARTMENT_MEDICAL "Medical" +#define DEPARTMENT_RESEARCH "Research" +#define DEPARTMENT_CARGO "Cargo" +#define DEPARTMENT_CIVILIAN "Civilian" +#define DEPARTMENT_PLANET "Exploration" //VOREStation Edit // I hate having this be here and not in a SC file. Hopefully someday the manifest can be rewritten to be map-agnostic. +#define DEPARTMENT_SYNTHETIC "Synthetic" + +// These are mostly for the department guessing code and event system. +#define DEPARTMENT_UNKNOWN "Unknown" +#define DEPARTMENT_EVERYONE "Everyone" // Canonical spellings of TSCs, so typos never have to happen again due to human error. #define TSC_NT "NanoTrasen" @@ -263,6 +275,7 @@ #define WORLD_ICON_SIZE 32 //Needed for the R-UST port #define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port +#define MAX_CLIENT_VIEW 34 // Maximum effective value of client.view (According to DM references) // Maploader bounds indices #define MAP_MINX 1 diff --git a/code/__defines/misc_vr.dm b/code/__defines/misc_vr.dm index 23910771f0..bf6cb4d150 100644 --- a/code/__defines/misc_vr.dm +++ b/code/__defines/misc_vr.dm @@ -14,6 +14,8 @@ #define VANTAG_KIDNAP "vantag_kidnap" #define VANTAG_KILL "vantag_kill" +#define DEPARTMENT_OFFDUTY "Off-Duty" + #define ANNOUNCER_NAME "Facility PA" //For custom species @@ -46,3 +48,13 @@ #define SHELTER_DEPLOY_BAD_TURFS "bad turfs" #define SHELTER_DEPLOY_BAD_AREA "bad area" #define SHELTER_DEPLOY_ANCHORED_OBJECTS "anchored objects" + +#define PTO_SECURITY "Security" +#define PTO_MEDICAL "Medical" +#define PTO_ENGINEERING "Engineering" +#define PTO_SCIENCE "Science" +#define PTO_EXPLORATION "Exploration" +#define PTO_CARGO "Cargo" +#define PTO_CIVILIAN "Civilian" + +#define DEPARTMENT_TALON "ITV Talon" diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index d43faaf4a5..beeda302eb 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -115,10 +115,10 @@ #define INV_BACK_DEF_ICON 'icons/mob/back.dmi' #define INV_L_HAND_DEF_ICON 'icons/mob/items/lefthand.dmi' #define INV_R_HAND_DEF_ICON 'icons/mob/items/righthand.dmi' -#define INV_W_UNIFORM_DEF_ICON 'icons/mob/uniform.dmi' +#define INV_W_UNIFORM_DEF_ICON "icons/mob/uniform" #define INV_ACCESSORIES_DEF_ICON 'icons/mob/ties.dmi' #define INV_TIE_DEF_ICON 'icons/mob/ties.dmi' -#define INV_SUIT_DEF_ICON 'icons/mob/suit.dmi' +#define INV_SUIT_DEF_ICON "icons/mob/suit" #define INV_SPACESUIT_DEF_ICON 'icons/mob/spacesuit.dmi' #define INV_WEAR_ID_DEF_ICON 'icons/mob/mob.dmi' #define INV_GLOVES_DEF_ICON 'icons/mob/hands.dmi' @@ -394,7 +394,11 @@ #define VIS_OBJS 20 #define VIS_MOBS 21 -#define VIS_COUNT 21 //Must be highest number from above. +#define VIS_BUILDMODE 22 + +#define VIS_CLOAKED 23 + +#define VIS_COUNT 23 //Must be highest number from above. //Some mob icon layering defines #define BODY_LAYER -100 diff --git a/code/__defines/mobs_vr.dm b/code/__defines/mobs_vr.dm index 99ff177e52..dbd66d9062 100644 --- a/code/__defines/mobs_vr.dm +++ b/code/__defines/mobs_vr.dm @@ -1,13 +1,13 @@ #undef VIS_COUNT -#define VIS_CH_STATUS_R 22 -#define VIS_CH_HEALTH_VR 23 -#define VIS_CH_BACKUP 24 -#define VIS_CH_VANTAG 25 +#define VIS_CH_STATUS_R 24 +#define VIS_CH_HEALTH_VR 25 +#define VIS_CH_BACKUP 26 +#define VIS_CH_VANTAG 27 -#define VIS_AUGMENTED 26 +#define VIS_AUGMENTED 28 -#define VIS_COUNT 26 +#define VIS_COUNT 28 //Protean organs #define O_ORCH "orchestrator" @@ -29,9 +29,7 @@ #define SPECIES_VASILISSAN "Vasilissan" #define SPECIES_VULPKANIN "Vulpkanin" #define SPECIES_XENOCHIMERA "Xenochimera" -#define SPECIES_XENOHYBRID "Xenomorph Hybrid" -#define SPECIES_ZORREN_FLAT "Flatland Zorren" -#define SPECIES_ZORREN_HIGH "Highlander Zorren" +#define SPECIES_ZORREN_HIGH "Zorren" #define SPECIES_CUSTOM "Custom Species" //monkey species #define SPECIES_MONKEY_AKULA "Sobaka" @@ -41,3 +39,6 @@ //event species #define SPECIES_WEREBEAST "Werebeast" #define SPECIES_SHADEKIN "Shadekin" +//custom species base sprites +#define SPECIES_FENNEC "Fennec" +#define SPECIES_XENOHYBRID "Xenohybrid" diff --git a/code/__defines/overmap.dm b/code/__defines/overmap.dm new file mode 100644 index 0000000000..503675b44e --- /dev/null +++ b/code/__defines/overmap.dm @@ -0,0 +1,16 @@ + //How far from the edge of overmap zlevel could randomly placed objects spawn +#define OVERMAP_EDGE 2 + +#define SHIP_SIZE_TINY 1 +#define SHIP_SIZE_SMALL 2 +#define SHIP_SIZE_LARGE 3 + +//multipliers for max_speed to find 'slow' and 'fast' speeds for the ship +#define SHIP_SPEED_SLOW 1/(40 SECONDS) +#define SHIP_SPEED_FAST 3/(20 SECONDS)// 15 speed + +#define OVERMAP_WEAKNESS_NONE 0 +#define OVERMAP_WEAKNESS_FIRE 1 +#define OVERMAP_WEAKNESS_EMP 2 +#define OVERMAP_WEAKNESS_MINING 4 +#define OVERMAP_WEAKNESS_EXPLOSIVE 8 diff --git a/code/__defines/preferences.dm b/code/__defines/preferences.dm new file mode 100644 index 0000000000..0bb289d2e1 --- /dev/null +++ b/code/__defines/preferences.dm @@ -0,0 +1,6 @@ +#define EXAMINE_MODE_DEFAULT 0 +#define EXAMINE_MODE_INCLUDE_USAGE 1 +#define EXAMINE_MODE_SWITCH_TO_PANEL 2 + +// Should be one higher than the above +#define EXAMINE_MODE_MAX 3 \ No newline at end of file diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm index ab85326658..278a2064db 100644 --- a/code/__defines/qdel.dm +++ b/code/__defines/qdel.dm @@ -26,6 +26,7 @@ #define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) #define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) #define QDEL_NULL(item) qdel(item); item = null +#define QDEL_NULL_LIST QDEL_LIST_NULL #define QDEL_LIST_NULL(x) if(x) { for(var/y in x) { qdel(y) } ; x = null } #define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } #define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE) diff --git a/code/__defines/research.dm b/code/__defines/research.dm index b55f6601f8..080741e948 100644 --- a/code/__defines/research.dm +++ b/code/__defines/research.dm @@ -17,4 +17,5 @@ #define PROTOLATHE 0x0002 //New stuff. Uses glass/metal/chemicals #define MECHFAB 0x0004 //Mechfab #define CHASSIS 0x0008 //For protolathe, but differently -#define PROSFAB 0x0010 //For prosthetics fab \ No newline at end of file +#define PROSFAB 0x0010 //For prosthetics fab +#define AUTOLATHE 0x0012 \ No newline at end of file diff --git a/code/__defines/shields.dm b/code/__defines/shields.dm new file mode 100644 index 0000000000..be892a1d6e --- /dev/null +++ b/code/__defines/shields.dm @@ -0,0 +1,49 @@ + +#define SHIELD_DAMTYPE_PHYSICAL 1 // Physical damage - bullets, meteors, various hand objects - aka. "brute" damtype. +#define SHIELD_DAMTYPE_EM 2 // Electromagnetic damage - Ion weaponry, stun beams, ... +#define SHIELD_DAMTYPE_HEAT 3 // Heat damage - Lasers, fire + +// TODO - Thats power not energy you silly goose! I think we mean 45 kilojoules +#define ENERGY_PER_HP (45 KILOWATTS)// Base amount energy that will be deducted from the generator's internal reserve per 1 HP of damage taken +#define ENERGY_UPKEEP_PER_TILE (4 KILOWATTS) // Base upkeep per tile protected. Multiplied by various enabled shield modes. Without them the field does literally nothing. +#define ENERGY_UPKEEP_IDLE 45 // Base upkeep when idle; modified by other factors. + +// This shield model is slightly inspired by Sins of a Solar Empire series. In short, shields are designed to analyze what hits them, and adapt themselves against that type of damage. +// This means shields will become increasingly effective against things like emitters - as they will adapt to heat damage, however they will be vulnerable to brute and EM damage. +// In a theoretical assault scenario, it is best to combine all damage types, so mitigation can't build up. The value is capped to prevent full scale invulnerability. + +#define MAX_MITIGATION_BASE 40 // % Base maximal reachable mitigation. +#define MAX_MITIGATION_RESEARCH 10 // % Added to MAX_MITIGATION_BASE when generator is built using more advanced components. This value is added for each "tier" of used component, ie. basic one has 1, the best one has 5. Actual maximum should be 90% in this case (with best components). Make sure you won't get above 100%! +#define MITIGATION_HIT_GAIN 5 // Mitigation gain per hit of respective damage type. +#define MITIGATION_HIT_LOSS 4 // Mitigation loss per hit. If we get hit once by EM damage type, EM mitigation will grow, while Physical and Heat mitigation values drop. +#define MITIGATION_LOSS_PASSIVE 0.5 // Mitigation of all damage types will drop by this every tick, up to 0. + +// Shield modes allow you to calibrate the field to fit specific needs. It is, for example, possible to create a field that will block airflow, but let people pass. +// Relevant mode bitflags (maximal of 16 flags due to current BYOND limitations) +#define MODEFLAG_HYPERKINETIC 1 +#define MODEFLAG_PHOTONIC 2 +#define MODEFLAG_NONHUMANS 4 +#define MODEFLAG_HUMANOIDS 8 +#define MODEFLAG_ANORGANIC 16 +#define MODEFLAG_ATMOSPHERIC 32 +#define MODEFLAG_HULL 64 +#define MODEFLAG_BYPASS 128 +#define MODEFLAG_OVERCHARGE 256 +#define MODEFLAG_MODULATE 512 +#define MODEFLAG_MULTIZ 1024 +#define MODEFLAG_EM 2048 + +// Return codes for shield hits. +#define SHIELD_ABSORBED 1 // The shield has completely absorbed the hit +#define SHIELD_BREACHED_MINOR 2 // The hit was absorbed, but a small gap will be created in the field (1-3 tiles) +#define SHIELD_BREACHED_MAJOR 3 // Same as above, with 2-5 tile gap +#define SHIELD_BREACHED_CRITICAL 4 // Same as above, with 4-8 tile gap +#define SHIELD_BREACHED_FAILURE 5 // Same as above, with 8-16 tile gap. Occurs when the hit exhausts all remaining shield energy. + +#define SHIELD_OFF 0 // The shield is offline +#define SHIELD_DISCHARGING 1 // The shield is shutting down and discharging. +#define SHIELD_RUNNING 2 // The shield is running +#define SHIELD_IDLE 3 // The shield is being kept at an idle state +#define SHIELD_SPINNING_UP 4 // Going from idle to running + +#define SHIELD_SHUTDOWN_DISPERSION_RATE (10000 KILOWATTS) // The rate at which shield energy disperses when shutdown is initiated. diff --git a/code/__defines/shuttle.dm b/code/__defines/shuttle.dm new file mode 100644 index 0000000000..9bf7dcad6f --- /dev/null +++ b/code/__defines/shuttle.dm @@ -0,0 +1,27 @@ +// Shuttle flags +#define SHUTTLE_FLAGS_NONE 0 +#define SHUTTLE_FLAGS_PROCESS 1 // Should be processed by shuttle subsystem +#define SHUTTLE_FLAGS_SUPPLY 2 // This is the supply shuttle. Why is this a tag? +#define SHUTTLE_FLAGS_ZERO_G 4 // Shuttle has no internal gravity generation +#define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE) + +// shuttle_landmark flags +#define SLANDMARK_FLAG_AUTOSET 1 // If set, will set base area and turf type to same as where it was spawned at +#define SLANDMARK_FLAG_ZERO_G 2 // Zero-G shuttles moved here will lose gravity unless the area has ambient gravity. + +// Overmap landable shuttles (/obj/effect/overmap/visitable/ship/landable on a /datum/shuttle/autodock/overmap) +#define SHIP_STATUS_LANDED 1 // Ship is at any other shuttle landmark. +#define SHIP_STATUS_TRANSIT 2 // Ship is at it's shuttle datum's transition shuttle landmark. +#define SHIP_STATUS_OVERMAP 3 // Ship is at its "overmap" shuttle landmark (allowed to move on overmap now) + +// Ferry shuttle location constants +#define FERRY_LOCATION_STATION 0 +#define FERRY_LOCATION_OFFSITE 1 +#define FERRY_GOING_TO_STATION 0 +#define FERRY_GOING_TO_OFFSITE 1 + +#ifndef DEBUG_SHUTTLES + #define log_shuttle(M) +#else + #define log_shuttle(M) log_debug("[M]") +#endif diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm index f42329898d..4055660a3e 100644 --- a/code/__defines/species_languages.dm +++ b/code/__defines/species_languages.dm @@ -54,6 +54,7 @@ #define LANGUAGE_AKHANI "Akhani" #define LANGUAGE_ALAI "Alai" #define LANGUAGE_ZADDAT "Vedahq" +#define LANGUAGE_BLOB "Blob" #define LANGUAGE_GIBBERISH "Babel" // Language flags. diff --git a/code/__defines/sqlite_defines.dm b/code/__defines/sqlite_defines.dm new file mode 100644 index 0000000000..8cda3b62a0 --- /dev/null +++ b/code/__defines/sqlite_defines.dm @@ -0,0 +1,7 @@ +#define SQLITE_TABLE_FEEDBACK "feedback" + +#define SQLITE_FEEDBACK_COLUMN_ID "id" +#define SQLITE_FEEDBACK_COLUMN_AUTHOR "author" +#define SQLITE_FEEDBACK_COLUMN_TOPIC "topic" +#define SQLITE_FEEDBACK_COLUMN_CONTENT "content" +#define SQLITE_FEEDBACK_COLUMN_DATETIME "datetime" \ No newline at end of file diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index fe01b602fc..37f4bff5f9 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -52,9 +52,12 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G // Subsystem init_order, from highest priority to lowest priority // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. -#define INIT_ORDER_CHEMISTRY 18 -#define INIT_ORDER_MAPPING 17 -#define INIT_ORDER_DECALS 16 +#define INIT_ORDER_SQLITE 40 +#define INIT_ORDER_CHEMISTRY 35 +#define INIT_ORDER_SKYBOX 30 +#define INIT_ORDER_MAPPING 25 +#define INIT_ORDER_DECALS 20 +#define INIT_ORDER_JOB 17 #define INIT_ORDER_ATOMS 15 #define INIT_ORDER_MACHINES 10 #define INIT_ORDER_SHUTTLES 3 @@ -62,31 +65,40 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define INIT_ORDER_DEFAULT 0 #define INIT_ORDER_LIGHTING 0 #define INIT_ORDER_AIR -1 +#define INIT_ORDER_ASSETS -3 #define INIT_ORDER_PLANETS -4 #define INIT_ORDER_HOLOMAPS -5 #define INIT_ORDER_OVERLAY -6 +#define INIT_ORDER_ALARM -7 +#define INIT_ORDER_OPENSPACE -10 #define INIT_ORDER_XENOARCH -20 #define INIT_ORDER_CIRCUIT -21 #define INIT_ORDER_AI -22 +#define INIT_ORDER_GAME_MASTER -24 +#define INIT_ORDER_TICKER -50 +#define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init. // Subsystem fire priority, from lowest to highest priority // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) #define FIRE_PRIORITY_SHUTTLES 5 -#define FIRE_PRIORITY_TIMERS 7 //VOREStation Emergency Edit +#define FIRE_PRIORITY_SUPPLY 5 #define FIRE_PRIORITY_ORBIT 8 #define FIRE_PRIORITY_VOTE 9 #define FIRE_PRIORITY_AI 10 #define FIRE_PRIORITY_GARBAGE 15 +#define FIRE_PRIORITY_ALARM 20 #define FIRE_PRIORITY_CHARSETUP 25 #define FIRE_PRIORITY_AIRFLOW 30 #define FIRE_PRIORITY_AIR 35 #define FIRE_PRIORITY_OBJ 40 #define FIRE_PRIORITY_PROCESS 45 #define FIRE_PRIORITY_DEFAULT 50 +#define FIRE_PRIORITY_TICKER 60 #define FIRE_PRIORITY_PLANETS 75 #define FIRE_PRIORITY_MACHINES 100 #define FIRE_PRIORITY_PROJECTILES 150 +#define FIRE_PRIORITY_CHAT 400 #define FIRE_PRIORITY_OVERLAYS 500 // Macro defining the actual code applying our overlays lists to the BYOND overlays list. (I guess a macro for speed) diff --git a/code/__defines/typeids.dm b/code/__defines/typeids.dm index 53028be294..6ee99f62a0 100644 --- a/code/__defines/typeids.dm +++ b/code/__defines/typeids.dm @@ -2,5 +2,5 @@ #define TYPEID_NULL "0" #define TYPEID_NORMAL_LIST "f" //helper macros -#define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref)-6) ) ) +#define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, -7) ) ) #define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) diff --git a/code/__defines/vv.dm b/code/__defines/vv.dm index 97e3a02e0d..e71d6e2f8f 100644 --- a/code/__defines/vv.dm +++ b/code/__defines/vv.dm @@ -27,7 +27,7 @@ #define VV_NORMAL_LIST_NO_EXPAND_THRESHOLD 50 #define VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD 150 -#define IS_VALID_ASSOC_KEY(V) (istext(V) || isdatum(V) || islist(V)) +#define IS_VALID_ASSOC_KEY(V) (!isnum(V)) //Helpers for vv_get_dropdown() #define VV_DROPDOWN_OPTION(href_key, name) . += "" @@ -35,6 +35,16 @@ //Helpers for vv_do_topic(list/href_list) #define IF_VV_OPTION(href_key) if(href_list[href_key]) +// vv_do_list() keys +#define VV_HK_LIST_ADD "listadd" +#define VV_HK_LIST_EDIT "listedit" +#define VV_HK_LIST_CHANGE "listchange" +#define VV_HK_LIST_REMOVE "listremove" +#define VV_HK_LIST_ERASE_NULLS "listnulls" +#define VV_HK_LIST_ERASE_DUPES "listdupes" +#define VV_HK_LIST_SHUFFLE "listshuffle" +#define VV_HK_LIST_SET_LENGTH "listlen" + // /datum #define VV_HK_DELETE "delete" #define VV_HK_EXPOSE "expose" diff --git a/code/__defines/xenoarcheaology.dm b/code/__defines/xenoarcheaology.dm index 8a4988b78c..e4b0a3935a 100644 --- a/code/__defines/xenoarcheaology.dm +++ b/code/__defines/xenoarcheaology.dm @@ -37,7 +37,11 @@ #define ARCHAEO_ALIEN_BOAT 37 #define ARCHAEO_IMPERION_CIRCUIT 38 #define ARCHAEO_TELECUBE 39 -#define MAX_ARCHAEO 39 +#define ARCHAEO_BATTERY 40 +#define ARCHAEO_SYRINGE 41 +#define ARCHAEO_RING 42 +#define ARCHAEO_CLUB 43 +#define MAX_ARCHAEO 43 #define DIGSITE_GARDEN 1 #define DIGSITE_ANIMAL 2 diff --git a/code/_global_vars/lists/misc.dm b/code/_global_vars/lists/misc.dm index aa9680a5f6..c10fcba530 100644 --- a/code/_global_vars/lists/misc.dm +++ b/code/_global_vars/lists/misc.dm @@ -1 +1,2 @@ GLOBAL_LIST_INIT(speech_toppings, list("|" = "i", "+" = "b", "_" = "u")) +GLOBAL_LIST_EMPTY(meteor_list) diff --git a/code/_global_vars/lists/species.dm b/code/_global_vars/lists/species.dm index 780ce3fb70..5b467f573f 100644 --- a/code/_global_vars/lists/species.dm +++ b/code/_global_vars/lists/species.dm @@ -1,7 +1,9 @@ //Languages/species/whitelist. GLOBAL_LIST_INIT(all_species, list()) GLOBAL_LIST_INIT(all_languages, list()) +GLOBAL_LIST_INIT(language_name_conflicts, list()) GLOBAL_LIST_INIT(language_keys, list()) // Table of say codes for all languages +GLOBAL_LIST_INIT(language_key_conflicts, list()) GLOBAL_LIST_INIT(whitelisted_species, list(SPECIES_HUMAN)) // Species that require a whitelist check. // VOREStation edit - include custom species GLOBAL_LIST_INIT(playable_species, list(SPECIES_HUMAN, SPECIES_CUSTOM)) // A list of ALL playable species, whitelisted, latejoin or otherwise. diff --git a/code/_helpers/_lists.dm b/code/_helpers/_lists.dm index 1eb8ac5456..d844d61b5c 100644 --- a/code/_helpers/_lists.dm +++ b/code/_helpers/_lists.dm @@ -5,18 +5,79 @@ * Sorting */ +// Determiner constants +#define DET_NONE 0x00 +#define DET_DEFINITE 0x01 // the +#define DET_INDEFINITE 0x02 // a, an, some +#define DET_AUTO 0x04 + /* * Misc */ //Returns a list in plain english as a string -/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) +/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "") + // this proc cannot be merged with counting_english_list to maintain compatibility + // with shoddy use of this proc for code logic and for cases that require original order switch(input.len) if(0) return nothing_text if(1) return "[input[1]]" if(2) return "[input[1]][and_text][input[2]]" else return "[jointext(input, comma_text, 1, -1)][final_comma_text][and_text][input[input.len]]" +//Returns a newline-separated list that counts equal-ish items, outputting count and item names, optionally with icons and specific determiners +/proc/counting_english_list(var/list/input, output_icons = TRUE, determiners = DET_NONE, nothing_text = "nothing", line_prefix = "\t", first_item_prefix = "\n", last_item_suffix = "\n", and_text = "\n", comma_text = "\n", final_comma_text = "") + var/list/counts = list() // counted input items + var/list/items = list() // actual objects for later reference (for icons and formatting) + + // count items + for(var/item in input) + var/name = "[item]" // index items by name; usually works fairly well for loose equality + if(name in counts) + counts[name]++ + else + counts[name] = 1 + items.Add(item) + + // assemble the output list + var/list/out = list() + var/i = 0 + for(var/item in items) + var/name = "[item]" + var/count = counts[name] + var/item_str = line_prefix + if(count > 1) + item_str += "[count]x " + + if(isatom(item)) + // atoms/items/objects can be pretty and whatnot + var/atom/A = item + if(output_icons && isicon(A.icon) && !ismob(A)) // mobs tend to have unusable icons + item_str += "[bicon(A)] " + switch(determiners) + if(DET_NONE) item_str += A.name + if(DET_DEFINITE) item_str += "\the [A]" + if(DET_INDEFINITE) item_str += "\a [A]" + else item_str += name + else + // non-atoms use plain string conversion + item_str += name + + if(i == 0) + item_str = first_item_prefix + item_str + if(i == items.len - 1) + item_str = item_str + last_item_suffix + + out.Add(item_str) + i++ + + // finally return the list using regular english_list builder + return english_list(out, nothing_text, and_text, comma_text, final_comma_text) + +//A "preset" for counting_english_list that displays the list "inline" (comma separated) +/proc/inline_counting_english_list(var/list/input, output_icons = TRUE, determiners = DET_NONE, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "", line_prefix = "", first_item_prefix = "", last_item_suffix = "") + return counting_english_list(input, output_icons, determiners, nothing_text, and_text, comma_text, final_comma_text) + //Returns list element or null. Should prevent "index out of bounds" error. proc/listgetindex(var/list/list,index) if(istype(list) && list.len) @@ -145,6 +206,20 @@ proc/listclearnulls(list/list) result = first - second return result +/* +Two lists may be different (A!=B) even if they have the same elements. +This actually tests if they have the same entries and values. +*/ +/proc/same_entries(var/list/first, var/list/second) + if(!islist(first) || !islist(second)) + return 0 + if(length(first) != length(second)) + return 0 + for(var/entry in first) + if(!(entry in second) || (first[entry] != second[entry])) + return 0 + return 1 + /* * Returns list containing entries that are in either list but not both. * If skipref = 1, repeated elements are treated as one. @@ -247,6 +322,13 @@ proc/listclearnulls(list/list) else L[key] = temp[key] +// Return a list of the values in an assoc list (including null) +/proc/list_values(var/list/L) + var/list/V = list() + V.len = L.len // Preallocate! + for(var/i in 1 to L.len) + V[i] = L[L[i]] // We avoid += in case the value is itself a list + return V //Mergesort: divides up the list into halves to begin the sort /proc/sortKey(var/list/client/L, var/order = 1) @@ -457,7 +539,7 @@ proc/listclearnulls(list/list) //Don't use this on lists larger than half a dozen or so /proc/insertion_sort_numeric_list_ascending(var/list/L) - //world.log << "ascending len input: [L.len]" + //to_world_log("ascending len input: [L.len]") var/list/out = list(pop(L)) for(var/entry in L) if(isnum(entry)) @@ -470,13 +552,13 @@ proc/listclearnulls(list/list) if(!success) out.Add(entry) - //world.log << " output: [out.len]" + //to_world_log(" output: [out.len]") return out /proc/insertion_sort_numeric_list_descending(var/list/L) - //world.log << "descending len input: [L.len]" + //to_world_log("descending len input: [L.len]") var/list/out = insertion_sort_numeric_list_ascending(L) - //world.log << " output: [out.len]" + //to_world_log(" output: [out.len]") return reverselist(out) /proc/dd_sortedObjectList(var/list/L, var/cache=list()) @@ -762,4 +844,19 @@ proc/dd_sortedTextList(list/incoming) /proc/popleft(list/L) if(L.len) . = L[1] - L.Cut(1,2) + L.Cut(1,2) + +//generates a list used to randomize transit animations so they aren't in lockstep +/proc/get_cross_shift_list(var/size) + var/list/result = list() + + result += rand(0, 14) + for(var/i in 2 to size) + var/shifts = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) + shifts -= result[i - 1] //consecutive shifts should not be equal + if(i == size) + shifts -= result[1] //because shift list is a ring buffer + result += pick(shifts) + + return result + \ No newline at end of file diff --git a/code/_helpers/atmospherics.dm b/code/_helpers/atmospherics.dm index e211ed329a..2f2d1f2b63 100644 --- a/code/_helpers/atmospherics.dm +++ b/code/_helpers/atmospherics.dm @@ -5,12 +5,12 @@ A.add_fingerprint(user) var/list/result = A.atmosanalyze(user) if(result && result.len) - user << "Results of the analysis[src == A ? "" : " of \the [A]"]" + to_chat(user, "Results of the analysis[src == A ? "" : " of \the [A]"]") for(var/line in result) - user << "[line]" + to_chat(user, "[line]") return 1 - user << "Your [src] flashes a red light as it fails to analyze \the [A]." + to_chat(user, "Your [src] flashes a red light as it fails to analyze \the [A].") return 0 /proc/atmosanalyzer_scan(var/atom/target, var/datum/gas_mixture/mixture, var/mob/user) diff --git a/code/_helpers/events.dm b/code/_helpers/events.dm index e31d24783e..2d15bb3aa9 100644 --- a/code/_helpers/events.dm +++ b/code/_helpers/events.dm @@ -24,4 +24,16 @@ if(A == myarea) //The loc of a turf is the area it is in. return 1 return 0 - \ No newline at end of file + +// Returns a list of area instances, or a subtypes of them, that are mapped in somewhere. +// Avoid feeding it `/area`, as it will likely cause a lot of lag as it evaluates every single area coded in. +/proc/get_all_existing_areas_of_types(list/area_types) + . = list() + for(var/area_type in area_types) + var/list/types = typesof(area_type) + for(var/T in types) + // Test for existance. + var/area/A = locate(T) + if(!istype(A) || !A.contents.len) // Empty contents list means it's not on the map. + continue + . += A \ No newline at end of file diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 6b924ef082..17a2544850 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -246,7 +246,7 @@ var/turf/speaker = get_turf(R) if(speaker) for(var/turf/T in hear(R.canhear_range,speaker)) - speaker_coverage[T] = T + speaker_coverage[T] = R // Try to find all the players who can hear the message diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index 4c98e06c4d..33b50af54b 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -19,8 +19,6 @@ var/global/list/side_effects = list() //list of all medical sideeffects types var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking. var/global/list/joblist = list() //list of all jobstypes, minus borg and AI -var/global/list/turfs = list() //list of all turfs - #define all_genders_define_list list(MALE,FEMALE,PLURAL,NEUTER,HERM) //VOREStaton Edit #define all_genders_text_list list("Male","Female","Plural","Neuter","Herm") //VOREStation Edit @@ -30,9 +28,6 @@ var/list/mannequins_ var/global/list/poster_designs = list() var/global/list/NT_poster_designs = list() -// Uplinks -var/list/obj/item/device/uplink/world_uplinks = list() - //Preferences stuff //Hairstyles var/global/list/hair_styles_list = list() //stores /datum/sprite_accessory/hair indexed by name @@ -48,7 +43,7 @@ var/datum/category_collection/underwear/global_underwear = new() //Backpacks var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Messenger Bag") -var/global/list/pdachoicelist = list("Default", "Slim", "Old", "Rugged") +var/global/list/pdachoicelist = list("Default", "Slim", "Old", "Rugged", "Holographic") var/global/list/exclude_jobs = list(/datum/job/ai,/datum/job/cyborg) // Visual nets @@ -153,17 +148,30 @@ var/global/list/string_slot_flags = list( var/datum/job/J = new T joblist[J.title] = J - //Languages and species. + //Languages paths = typesof(/datum/language)-/datum/language for(var/T in paths) var/datum/language/L = new T - GLOB.all_languages[L.name] = L + if (isnull(GLOB.all_languages[L.name])) + GLOB.all_languages[L.name] = L + else + log_debug("Language name conflict! [T] is named [L.name], but that is taken by [GLOB.all_languages[L.name].type]") + if(isnull(GLOB.language_name_conflicts[L.name])) + GLOB.language_name_conflicts[L.name] = list(GLOB.all_languages[L.name]) + GLOB.language_name_conflicts[L.name] += L for (var/language_name in GLOB.all_languages) var/datum/language/L = GLOB.all_languages[language_name] if(!(L.flags & NONGLOBAL)) - GLOB.language_keys[lowertext(L.key)] = L + if(isnull(GLOB.language_keys[L.key])) + GLOB.language_keys[L.key] = L + else + log_debug("Language key conflict! [L] has key [L.key], but that is taken by [(GLOB.language_keys[L.key])]") + if(isnull(GLOB.language_key_conflicts[L.key])) + GLOB.language_key_conflicts[L.key] = list(GLOB.language_keys[L.key]) + GLOB.language_key_conflicts[L.key] += L + //Species var/rkey = 0 paths = typesof(/datum/species) for(var/T in paths) @@ -206,7 +214,7 @@ var/global/list/string_slot_flags = list( var/list/L = chemical_reactions_list[reaction] for(var/t in L) . += " has: [t]\n" - world << . + to_world(.) */ //Hexidecimal numbers var/global/list/hexNums = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm index 6f88446441..fdb127e21e 100644 --- a/code/_helpers/global_lists_vr.dm +++ b/code/_helpers/global_lists_vr.dm @@ -470,6 +470,7 @@ var/global/list/remainless_species = list(SPECIES_PROMETHEAN, // Custom species icon bases var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN) //Just ones that won't work well. + var/list/whitelisted_icons = list(SPECIES_FENNEC,SPECIES_XENOHYBRID) //Include these anyway for(var/species_name in GLOB.playable_species) if(species_name in blacklisted_icons) continue @@ -477,5 +478,7 @@ var/global/list/remainless_species = list(SPECIES_PROMETHEAN, if(S.spawn_flags & SPECIES_IS_WHITELISTED) continue custom_species_bases += species_name + for(var/species_name in whitelisted_icons) + custom_species_bases += species_name return 1 // Hooks must return 1 diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm index b29fa91b8b..5abedfe9ac 100644 --- a/code/_helpers/icons.dm +++ b/code/_helpers/icons.dm @@ -167,7 +167,7 @@ mob Output_Icon() set name = "2. Output Icon" - src<<"Icon is: \icon[getFlatIcon(src)]" + to_chat(src, "Icon is: [bicon(getFlatIcon(src))]") Label_Icon() set name = "3. Label Icon" diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index 51c9c01527..18cede2ea4 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -22,16 +22,16 @@ #endif /proc/error(msg) - world.log << "## ERROR: [msg]" + to_world_log("## ERROR: [msg]") #define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].") //print a warning message to world.log /proc/warning(msg) - world.log << "## WARNING: [msg]" + to_world_log("## WARNING: [msg]") //print a testing-mode debug message to world.log /proc/testing(msg) - world.log << "## TESTING: [msg]" + to_world_log("## TESTING: [msg]") /proc/log_admin(text) admin_log.Add(text) @@ -49,7 +49,7 @@ for(var/client/C in admins) if(C.is_preference_enabled(/datum/client_preference/debug/show_debug_logs)) - C << "DEBUG: [text]" + to_chat(C, "DEBUG: [text]") /proc/log_game(text) if (config.log_game) @@ -154,12 +154,12 @@ /proc/log_to_dd(text) - world.log << text //this comes before the config check because it can't possibly runtime + to_world_log(text) //this comes before the config check because it can't possibly runtime if(config.log_world_output) WRITE_LOG(diary, "DD_OUTPUT: [text]") /proc/log_error(text) - world.log << text + to_world_log(text) WRITE_LOG(error_log, "RUNTIME: [text]") /proc/log_misc(text) @@ -174,7 +174,7 @@ WRITE_LOG(href_logfile, "HREF: [text]") /proc/log_unit_test(text) - world.log << "## UNIT_TEST: [text]" + to_world_log("## UNIT_TEST: [text]") /proc/report_progress(var/progress_message) admin_notice("[progress_message]", R_DEBUG) @@ -259,20 +259,28 @@ return key_name(whom, 1, include_name) // Helper procs for building detailed log lines +// +// These procs must not fail under ANY CIRCUMSTANCES! +// + /datum/proc/log_info_line() return "[src] ([type])" /atom/log_info_line() + . = ..() var/turf/t = get_turf(src) if(istype(t)) - return "([t]) ([t.x],[t.y],[t.z]) ([t.type])" + return "[.] @ [t.log_info_line()]" else if(loc) - return "([loc]) (0,0,0) ([loc.type])" + return "[.] @ ([loc]) (0,0,0) ([loc.type])" else - return "(NULL) (0,0,0) (NULL)" + return "[.] @ (NULL) (0,0,0) (NULL)" + +/turf/log_info_line() + return "([src]) ([x],[y],[z]) ([type])" /mob/log_info_line() - return "[..()] ([ckey])" + return "[..()] (ckey=[ckey])" /proc/log_info_line(var/datum/d) if(!d) diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index 9320c29272..04b8ede203 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -1,38 +1,3 @@ -/atom/movable/proc/get_mob() - return - -/obj/mecha/get_mob() - return occupant - -/obj/vehicle/train/get_mob() - return buckled_mobs - -/mob/get_mob() - return src - -/mob/living/bot/mulebot/get_mob() - if(load && istype(load, /mob/living)) - return list(src, load) - return src - -/proc/mobs_in_view(var/range, var/source) - var/list/mobs = list() - for(var/atom/movable/AM in view(range, source)) - var/M = AM.get_mob() - if(M) - mobs += M - - return mobs - -/proc/mobs_in_xray_view(var/range, var/source) - var/list/mobs = list() - for(var/atom/movable/AM in orange(range, source)) - var/M = AM.get_mob() - if(M) - mobs += M - - return mobs - proc/random_hair_style(gender, species = SPECIES_HUMAN) var/h_style = "Bald" diff --git a/code/_helpers/sorts/comparators.dm b/code/_helpers/sorts/comparators.dm index 73ad3d847c..a9ca5748b7 100644 --- a/code/_helpers/sorts/comparators.dm +++ b/code/_helpers/sorts/comparators.dm @@ -34,12 +34,23 @@ // Sorts jobs by department, and then by flag within department /proc/cmp_job_datums(var/datum/job/a, var/datum/job/b) - . = sorttext(b.department, a.department) - if (. == 0) //Same department, push up if they're a head - . = b.head_position - a.head_position - if (. == 0) //Already in head/nothead spot, sort by name + . = 0 + if( LAZYLEN(a.departments) && LAZYLEN(b.departments) ) + var/list/common_departments = a.departments & b.departments // Makes a list that contains only departments that were in both. + if(!common_departments.len) + . = sorttext(b.departments[1], a.departments[1]) + + if(. == 0) //Same department, push up if they're a head + . = b.sorting_order - a.sorting_order + + if(. == 0) //Already in same sorting order, sort by name . = sorttext(b.title, a.title) +/proc/cmp_department_datums(var/datum/department/a, var/datum/department/b) + . = b.sorting_order - a.sorting_order // First, sort by the sorting order vars. + if(. == 0) // If they have the same var, then sort by name. + . = sorttext(b.name, a.name) + // Sorts entries in a performance stats list. /proc/cmp_generic_stat_item_time(list/A, list/B) . = B[STAT_ENTRY_TIME] - A[STAT_ENTRY_TIME] diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index 688e1c2a68..67844cf4a2 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -301,11 +301,15 @@ proc/TextPreview(var/string,var/len=40) //For generating neat chat tag-images //The icon var could be local in the proc, but it's a waste of resources // to always create it and then throw it out. -/var/icon/text_tag_icons = new('./icons/chattags.dmi') +/var/icon/text_tag_icons = 'icons/chattags.dmi' +/var/list/text_tag_cache = list() /proc/create_text_tag(var/tagname, var/tagdesc = tagname, var/client/C = null) if(!(C && C.is_preference_enabled(/datum/client_preference/chat_tags))) return tagdesc - return "[tagdesc]" + if(!text_tag_cache[tagname]) + var/icon/tag = icon(text_tag_icons, tagname) + text_tag_cache[tagname] = bicon(tag, TRUE, "text_tag") + return text_tag_cache[tagname] /proc/contains_az09(var/input) for(var/i=1, i<=length(input), i++) diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index c075925fdd..3846c6fd66 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -107,11 +107,6 @@ var/round_start_time = 0 next_duration_update = world.time + 1 MINUTES return last_round_duration -//Can be useful for things dependent on process timing -/proc/process_schedule_interval(var/process_name) - var/datum/controller/process/process = processScheduler.getProcess(process_name) - return process.schedule_interval - /hook/startup/proc/set_roundstart_hour() roundstart_hour = pick(2,7,12,17) return 1 diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm index 1926246856..a9cede52de 100644 --- a/code/_helpers/turfs.dm +++ b/code/_helpers/turfs.dm @@ -33,9 +33,139 @@ available_turfs = start_turfs return pick(available_turfs) +// Picks a turf that is clearance tiles away from the map edge given by dir, on z-level Z +/proc/pick_random_edge_turf(var/dir, var/Z, var/clearance = TRANSITIONEDGE + 1) + if(!dir) + return + switch(dir) + if(NORTH) + return locate(rand(clearance, world.maxx - clearance), world.maxy - clearance, Z) + if(SOUTH) + return locate(rand(clearance, world.maxx - clearance), clearance, Z) + if(EAST) + return locate(world.maxx - clearance, rand(clearance, world.maxy - clearance), Z) + if(WEST) + return locate(clearance, rand(clearance, world.maxy - clearance), Z) + /proc/is_below_sound_pressure(var/turf/T) var/datum/gas_mixture/environment = T ? T.return_air() : null var/pressure = environment ? environment.return_pressure() : 0 if(pressure < SOUND_MINIMUM_PRESSURE) return TRUE - return FALSE \ No newline at end of file + return FALSE + +/* + Turf manipulation +*/ + +//Returns an assoc list that describes how turfs would be changed if the +//turfs in turfs_src were translated by shifting the src_origin to the dst_origin +/proc/get_turf_translation(turf/src_origin, turf/dst_origin, list/turfs_src) + var/list/turf_map = list() + for(var/turf/source in turfs_src) + var/x_pos = (source.x - src_origin.x) + var/y_pos = (source.y - src_origin.y) + var/z_pos = (source.z - src_origin.z) + + var/turf/target = locate(dst_origin.x + x_pos, dst_origin.y + y_pos, dst_origin.z + z_pos) + if(!target) + error("Null turf in translation @ ([dst_origin.x + x_pos], [dst_origin.y + y_pos], [dst_origin.z + z_pos])") + turf_map[source] = target //if target is null, preserve that information in the turf map + + return turf_map + +/proc/translate_turfs(var/list/translation, var/area/base_area = null, var/turf/base_turf) + for(var/turf/source in translation) + + var/turf/target = translation[source] + + if(target) + if(base_area) ChangeArea(target, get_area(source)) + var/leave_turf = base_turf ? base_turf : get_base_turf_by_area(base_area ? base_area : source) + translate_turf(source, target, leave_turf) + if(base_area) ChangeArea(source, base_area) + + //change the old turfs (Currently done by translate_turf for us) + //for(var/turf/source in translation) + // source.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(source), 1, 1) + +// Parmaters for stupid historical reasons are: +// T - Origin +// B - Destination +/proc/translate_turf(var/turf/T, var/turf/B, var/turftoleave = null) + + //You can stay, though. + if(istype(T,/turf/space)) + error("Tried to translate a space turf: src=[log_info_line(T)] dst=[log_info_line(B)]") + return FALSE // TODO - Is this really okay to do nothing? + + var/turf/X //New Destination Turf + + //Are we doing shuttlework? Just to save another type check later. + var/shuttlework = 0 + + //Shuttle turfs handle their own fancy moving. + if(istype(T,/turf/simulated/shuttle)) + shuttlework = 1 + var/turf/simulated/shuttle/SS = T + if(!SS.landed_holder) SS.landed_holder = new(turf = SS) + X = SS.landed_holder.land_on(B) + + //Generic non-shuttle turf move. + else + var/old_dir1 = T.dir + var/old_icon_state1 = T.icon_state + var/old_icon1 = T.icon + var/old_underlays = T.underlays.Copy() + var/old_decals = T.decals ? T.decals.Copy() : null + + X = B.ChangeTurf(T.type) + X.set_dir(old_dir1) + X.icon_state = old_icon_state1 + X.icon = old_icon1 + X.copy_overlays(T, TRUE) + X.underlays = old_underlays + X.decals = old_decals + + //Move the air from source to dest + var/turf/simulated/ST = T + if(istype(ST) && ST.zone) + var/turf/simulated/SX = X + if(!SX.air) + SX.make_air() + SX.air.copy_from(ST.zone.air) + ST.zone.remove(ST) + + var/z_level_change = FALSE + if(T.z != X.z) + z_level_change = TRUE + + //Move the objects. Not forceMove because the object isn't "moving" really, it's supposed to be on the "same" turf. + for(var/obj/O in T) + if(O.simulated) + O.loc = X + O.update_light() + if(z_level_change) // The objects still need to know if their z-level changed. + O.onTransitZ(T.z, X.z) + + //Move the mobs unless it's an AI eye or other eye type. + for(var/mob/M in T) + if(isEye(M)) continue // If we need to check for more mobs, I'll add a variable + M.loc = X + + if(z_level_change) // Same goes for mobs. + M.onTransitZ(T.z, X.z) + + if(istype(M, /mob/living)) + var/mob/living/LM = M + LM.check_shadow() // Need to check their Z-shadow, which is normally done in forceMove(). + + if(shuttlework) + var/turf/simulated/shuttle/SS = T + SS.landed_holder.leave_turf(turftoleave) + else if(turftoleave) + T.ChangeTurf(turftoleave) + else + T.ChangeTurf(get_base_turf_by_area(T)) + + return TRUE diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm index bdfa4e0fbb..18a94a501d 100644 --- a/code/_helpers/type2type.dm +++ b/code/_helpers/type2type.dm @@ -68,7 +68,7 @@ if (4.0) return EAST if (8.0) return WEST else - world.log << "UNKNOWN DIRECTION: [direction]" + to_world_log("UNKNOWN DIRECTION: [direction]") // Turns a direction into text /proc/dir2text(direction) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index da92fd0d6d..fef8f1c1ee 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -370,7 +370,7 @@ Turf and target are seperate in case you want to teleport some distance from a t if(isAI(src)) var/mob/living/silicon/ai/A = src oldname = null//don't bother with the records update crap - //world << "[newname] is the AI!" + //to_world("[newname] is the AI!") //world << sound('sound/AI/newAI.ogg') // Set eyeobj name A.SetName(newname) @@ -679,7 +679,7 @@ proc/GaussRandRound(var/sigma,var/roundto) //Returns: all the areas in the world /proc/return_areas() var/list/area/areas = list() - for(var/area/A in all_areas) + for(var/area/A in world) areas += A return areas @@ -697,7 +697,7 @@ proc/GaussRandRound(var/sigma,var/roundto) areatype = areatemp.type var/list/areas = new/list() - for(var/area/N in all_areas) + for(var/area/N in world) if(istype(N, areatype)) areas += N return areas @@ -711,7 +711,7 @@ proc/GaussRandRound(var/sigma,var/roundto) areatype = areatemp.type var/list/turfs = new/list() - for(var/area/N in all_areas) + for(var/area/N in world) if(istype(N, areatype)) for(var/turf/T in N) turfs += T return turfs @@ -726,7 +726,7 @@ proc/GaussRandRound(var/sigma,var/roundto) areatype = areatemp.type var/list/atoms = new/list() - for(var/area/N in all_areas) + for(var/area/N in world) if(istype(N, areatype)) for(var/atom/A in N) atoms += A @@ -1420,6 +1420,8 @@ var/mob/dview/dview_mob = new #define VARSET_LIST_CALLBACK(target, var_name, var_value) CALLBACK(GLOBAL_PROC, /proc/___callbackvarset, ##target, ##var_name, ##var_value) //dupe code because dm can't handle 3 level deep macros #define VARSET_CALLBACK(datum, var, var_value) CALLBACK(GLOBAL_PROC, /proc/___callbackvarset, ##datum, NAMEOF(##datum, ##var), ##var_value) +//we'll see about those 3-level deep macros +#define VARSET_IN(datum, var, var_value, time) addtimer(VARSET_CALLBACK(datum, var, var_value), time) /proc/___callbackvarset(list_or_datum, var_name, var_value) if(length(list_or_datum)) @@ -1577,4 +1579,12 @@ var/mob/dview/dview_mob = new return "\ref[input]" /proc/pass() - return \ No newline at end of file + return + +// Painlessly creates an element. +// First argument is where to send the Topic call to when clicked. Should be a reference to an object. This is generally src, but not always. +// Second one is for all the params that will be sent. Uses an assoc list (e.g. "value" = "5"). +// Note that object refs will be converted to text, as if \ref[thing] was done. To get the ref back on Topic() side, you will need to use locate(). +// Third one is the text that will be clickable. +/proc/href(href_src, list/href_params, href_text) + return "[href_text]" diff --git a/code/_macros.dm b/code/_macros.dm index 3860f63434..5b7fb379d3 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -2,27 +2,39 @@ #define get_turf(A) get_step(A,0) +#define get_x(A) (get_step(A, 0)?.x || 0) + +#define get_y(A) (get_step(A, 0)?.y || 0) + +#define get_z(A) (get_step(A, 0)?.z || 0) + #define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") -#define to_chat(target, message) target << message -#define to_world(message) world << message +// #define to_chat(target, message) target << message Not anymore! +#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat +#define to_world(message) to_chat(world, message) #define to_world_log(message) world.log << message // TODO - Baystation has this log to crazy places. For now lets just world.log, but maybe look into it later. -#define log_world(message) world.log << message +#define log_world(message) to_world_log(message) #define to_file(file_entry, source_var) file_entry << source_var #define from_file(file_entry, target_var) file_entry >> target_var #define show_browser(target, browser_content, browser_name) target << browse(browser_content, browser_name) +#define send_rsc(target, rsc_content, rsc_name) target << browse_rsc(rsc_content, rsc_name) +#define open_link(target, url) target << link(url) // From TG, might be useful to have. // Didn't port SEND_TEXT() since to_chat() appears to serve the same purpose. #define DIRECT_OUTPUT(A, B) A << B #define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) #define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) +//#define WRITE_LOG is in logging.dm #define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE) #define qdel_null(x) if(x) { qdel(x) ; x = null } +#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key) + #define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id) #define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") } diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 6fc470cc81..ea40e3dc0e 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -43,7 +43,7 @@ next_click = world.time + 1 - if(client.buildmode) + if(client && client.buildmode) build_click(src, client.buildmode, params, A) return @@ -103,7 +103,7 @@ var/sdepth = A.storage_depth(src) if((!isturf(A) && A == loc) || (sdepth != -1 && sdepth <= 1)) if(W) - var/resolved = W.resolve_attackby(A, src) + var/resolved = W.resolve_attackby(A, src, click_parameters = params) if(!resolved && A && W) W.afterattack(A, src, 1, params) // 1 indicates adjacency else @@ -134,16 +134,10 @@ // A is a turf or is on a turf, or in something on a turf (pen in a box); but not something in something on a turf (pen in a box in a backpack) sdepth = A.storage_depth_turf() if(isturf(A) || isturf(A.loc) || (sdepth != -1 && sdepth <= 1)) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = src - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN if(A.Adjacent(src) || (W && W.attack_can_reach(src, A, W.reach)) ) // see adjacent.dm if(W) // Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example) - var/resolved = W.resolve_attackby(A,src) + var/resolved = W.resolve_attackby(A,src, click_parameters = params) if(!resolved && A && W) W.afterattack(A, src, 1, params) // 1: clicking something Adjacent else @@ -188,6 +182,9 @@ /mob/living/UnarmedAttack(var/atom/A, var/proximity_flag) + if(is_incorporeal()) + return 0 + if(!ticker) to_chat(src, "You cannot attack people before the game has started.") return 0 diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm index 655a451e92..23a29367c0 100644 --- a/code/_onclick/hud/ability_screen_objects.dm +++ b/code/_onclick/hud/ability_screen_objects.dm @@ -270,7 +270,7 @@ // Makes the ability be triggered. The subclasses of this are responsible for carrying it out in whatever way it needs to. /obj/screen/ability/proc/activate() - world << "[src] had activate() called." + to_world("[src] had activate() called.") return // This checks if the ability can be used. @@ -305,7 +305,7 @@ if(object_used && verb_to_call) call(object_used,verb_to_call)(arguments_to_use) // call(object_used,verb_to_call)(arguments_to_use) -// world << "Attempted to call([object_used],[verb_to_call])([arguments_to_use])" +// to_world("Attempted to call([object_used],[verb_to_call])([arguments_to_use])") // if(hascall(object_used, verb_to_call)) // call(object_used,verb_to_call)(arguments_to_use) // else diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index fef0301c81..6e1b184071 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -40,8 +40,9 @@ var/list/global_huds = list( /datum/global_hud/proc/setup_overlay(var/icon_state) var/obj/screen/screen = new /obj/screen() - screen.screen_loc = "1,1" - screen.icon = 'icons/obj/hud_full.dmi' + screen.alpha = 40 // Adjut this if you want goggle overlays to be thinner or thicker. + screen.screen_loc = "SOUTHWEST to NORTHEAST" // Will tile up to the whole screen, scaling beyond 15x15 if needed. + screen.icon = 'icons/obj/hud_tiled.dmi' screen.icon_state = icon_state screen.layer = SCREEN_LAYER screen.plane = PLANE_FULLSCREEN @@ -332,11 +333,11 @@ datum/hud/New(mob/owner) set hidden = 1 if(!hud_used) - usr << "This mob type does not use a HUD." + to_chat(usr, "This mob type does not use a HUD.") return if(!ishuman(src)) - usr << "Inventory hiding is currently only supported for human mobs, sorry." + to_chat(usr, "Inventory hiding is currently only supported for human mobs, sorry.") return if(!client) return diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index e31dd5e56a..8a61b87251 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -358,11 +358,13 @@ mymob.radio_use_icon.color = ui_color mymob.radio_use_icon.alpha = ui_alpha - mymob.client.screen = list() + if(mymob.client) + mymob.client.screen = list() + + mymob.client.screen += hud_elements + mymob.client.screen += src.adding + src.hotkeybuttons + mymob.client.screen += mymob.client.void - mymob.client.screen += hud_elements - mymob.client.screen += src.adding + src.hotkeybuttons - mymob.client.screen += mymob.client.void inventory_shown = 0 return diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index dc414df11f..392854742a 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -223,11 +223,11 @@ var/obj/screen/robot_inventory //r.client.screen += robot_inventory //"store" icon if(!r.module) - usr << "No module selected" + to_chat(usr, "No module selected") return if(!r.module.modules) - usr << "Selected module has no modules to select" + to_chat(usr, "Selected module has no modules to select") return if(!r.robot_modules_background) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index bd2849b251..2c6408cf7f 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -205,7 +205,7 @@ if(iscarbon(usr)) var/mob/living/carbon/C = usr if(C.legcuffed) - C << "You are legcuffed! You cannot run until you get [C.legcuffed] removed!" + to_chat(C, "You are legcuffed! You cannot run until you get [C.legcuffed] removed!") C.m_intent = "walk" //Just incase C.hud_used.move_intent.icon_state = "walking" return 1 @@ -245,7 +245,7 @@ if(!C.stat && !C.stunned && !C.paralysis && !C.restrained()) if(C.internal) C.internal = null - C << "No longer running on internals." + to_chat(C, "No longer running on internals.") if(C.internals) C.internals.icon_state = "internal0" else @@ -257,7 +257,7 @@ no_mask = 1 if(no_mask) - C << "You are not wearing a suitable mask or helmet." + to_chat(C, "You are not wearing a suitable mask or helmet.") return 1 else var/list/nicename = null @@ -276,12 +276,12 @@ tankcheck = list(C.r_hand, C.l_hand, C.back) // Rigs are a fucking pain since they keep an air tank in nullspace. - if(istype(C.back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = C.back - if(rig.air_supply && !rig.offline) + var/obj/item/weapon/rig/Rig = C.get_rig() + if(Rig) + if(Rig.air_supply && !Rig.offline) from = "in" nicename |= "hardsuit" - tankcheck |= rig.air_supply + tankcheck |= Rig.air_supply for(var/i=1, iYou are now running on internals from [tankcheck[best]] [from] your [nicename[best]]." + to_chat(C, "You are now running on internals from [tankcheck[best]] [from] your [nicename[best]].") C.internal = tankcheck[best] @@ -346,7 +346,7 @@ if(C.internals) C.internals.icon_state = "internal1" else - C << "You don't have a[breathes=="oxygen" ? "n oxygen" : addtext(" ",breathes)] tank." + to_chat(C, "You don't have a[breathes=="oxygen" ? "n oxygen" : addtext(" ",breathes)] tank.") if("act_intent") usr.a_intent_change("right") if(I_HELP) @@ -386,7 +386,7 @@ R.hud_used.toggle_show_robot_modules() return 1 else - R << "You haven't selected a module yet." + to_chat(R, "You haven't selected a module yet.") if("radio") if(issilicon(usr)) @@ -402,7 +402,7 @@ R.uneq_active() R.hud_used.update_robot_modules_display() else - R << "You haven't selected a module yet." + to_chat(R, "You haven't selected a module yet.") if("module1") if(istype(usr, /mob/living/silicon/robot)) diff --git a/code/_onclick/hud/skybox.dm b/code/_onclick/hud/skybox.dm new file mode 100644 index 0000000000..affeef8893 --- /dev/null +++ b/code/_onclick/hud/skybox.dm @@ -0,0 +1,64 @@ +#define SKYBOX_PADDING 4 // How much larger we want the skybox image to be than client's screen (in turfs) +#define SKYBOX_PIXELS 736 // Size of skybox image in pixels +#define SKYBOX_TURFS (SKYBOX_PIXELS/WORLD_ICON_SIZE) + +// Skybox screen object. +/obj/skybox + name = "skybox" + mouse_opacity = 0 + anchored = TRUE + simulated = FALSE + screen_loc = "CENTER,CENTER" + plane = SKYBOX_PLANE + blend_mode = BLEND_MULTIPLY // You actually need to do it this way or you see it in occlusion. + +// Adjust transform property to scale for client's view var. We assume the skybox is 736x736 px +/obj/skybox/proc/scale_to_view(var/view) + var/matrix/M = matrix() + // Translate to center the icon over us! + M.Translate(-(SKYBOX_PIXELS - WORLD_ICON_SIZE) / 2) + // Scale appropriately based on view size. (7 results in scale of 1) + view = text2num(view) || 7 // Sanitize + M.Scale(((min(MAX_CLIENT_VIEW, view) + SKYBOX_PADDING) * 2 + 1) / SKYBOX_TURFS) + src.transform = M + +/client + var/obj/skybox/skybox + +/client/proc/update_skybox(rebuild) + if(!skybox) + skybox = new() + skybox.scale_to_view(src.view) + screen += skybox + rebuild = 1 + + var/turf/T = get_turf(eye) + if(T) + if(rebuild) + skybox.cut_overlays() + skybox.add_overlay(SSskybox.get_skybox(T.z)) + screen |= skybox + skybox.screen_loc = "CENTER:[(world.maxx>>1) - T.x],CENTER:[(world.maxy>>1) - T.y]" + +/mob/Login() + . = ..() + client.update_skybox(TRUE) + +/mob/onTransitZ(old_z, new_z) + ..() + if(old_z != new_z) + client?.update_skybox(TRUE) + +/mob/doMove() + if((. = ..())) + client?.update_skybox() + +/mob/set_viewsize() + . = ..() + if (. && client) + client.update_skybox() + client.skybox?.scale_to_view(client.view) + +#undef SKYBOX_BORDER +#undef SKYBOX_PIXELS +#undef SKYBOX_TURFS diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index a1f2335a9d..954eb90c44 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -28,27 +28,24 @@ avoid code duplication. This includes items that may sometimes act as a standard return //I would prefer to rename this to attack(), but that would involve touching hundreds of files. -/obj/item/proc/resolve_attackby(atom/A, mob/user, var/attack_modifier = 1) +/obj/item/proc/resolve_attackby(atom/A, mob/user, var/attack_modifier = 1, var/click_parameters) pre_attack(A, user) add_fingerprint(user) - return A.attackby(src, user, attack_modifier) + return A.attackby(src, user, attack_modifier, click_parameters) // No comment -/atom/proc/attackby(obj/item/W, mob/user, var/attack_modifier) +/atom/proc/attackby(obj/item/W, mob/user, var/attack_modifier, var/click_parameters) return -/atom/movable/attackby(obj/item/W, mob/user, var/attack_modifier) +/atom/movable/attackby(obj/item/W, mob/user, var/attack_modifier, var/click_parameters) if(!(W.flags & NOBLUDGEON)) visible_message("[src] has been hit by [user] with [W].") -/mob/living/attackby(obj/item/I, mob/user, var/attack_modifier) +/mob/living/attackby(obj/item/I, mob/user, var/attack_modifier, var/click_parameters) if(!ismob(user)) return 0 if(can_operate(src) && I.do_surgery(src,user)) - if(I.can_do_surgery(src,user)) - return 1 - else - return 0 + return 1 if(attempt_vr(src,"vore_attackby",args)) return //VOREStation Add - The vore, of course. return I.attack(src, user, user.zone_sel.selecting, attack_modifier) @@ -113,4 +110,4 @@ avoid code duplication. This includes items that may sometimes act as a standard power *= attack_modifier - return target.hit_with_weapon(src, user, power, hit_zone) \ No newline at end of file + return target.hit_with_weapon(src, user, power, hit_zone) diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index d5197cb30f..a2d3f0c92b 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -61,13 +61,13 @@ if(awaygate) user.loc = awaygate.loc else - user << "[src] has no destination." + to_chat(user, "[src] has no destination.") /obj/machinery/gateway/centeraway/attack_ghost(mob/user as mob) if(stationgate) user.loc = stationgate.loc else - user << "[src] has no destination." + to_chat(user, "[src] has no destination.") // ------------------------------------------- // This was supposed to be used by adminghosts diff --git a/code/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm index 229e717e45..4398dbf7e6 100644 --- a/code/_onclick/telekinesis.dm +++ b/code/_onclick/telekinesis.dm @@ -110,7 +110,7 @@ var/const/tk_maxrange = 15 if(focus) d = max(d, get_dist(user, focus)) // whichever is further if(d > tk_maxrange) - user << "Your mind won't reach that far." + to_chat(user, "Your mind won't reach that far.") return if(!focus) diff --git a/code/controllers/ProcessScheduler/.gitignore b/code/controllers/ProcessScheduler/.gitignore deleted file mode 100644 index 5fe19e425b..0000000000 --- a/code/controllers/ProcessScheduler/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/bower_components -/node_modules -/ProcessScheduler.dmb -/ProcessScheduler.int -/ProcessScheduler.rsc -/*.lk diff --git a/code/controllers/ProcessScheduler/LICENSE-AGPL b/code/controllers/ProcessScheduler/LICENSE-AGPL deleted file mode 100644 index abaa41e829..0000000000 --- a/code/controllers/ProcessScheduler/LICENSE-AGPL +++ /dev/null @@ -1,212 +0,0 @@ -GNU AFFERO GENERAL PUBLIC LICENSE - -Version 3, 19 November 2007 - -Copyright 2007 Free Software Foundation, Inc. -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - -Preamble - -The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. - -The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. - -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. - -Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. - -A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. - -The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. - -An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. - -The precise terms and conditions for copying, distribution and modification follow. - -TERMS AND CONDITIONS - -0. Definitions. -"This License" refers to version 3 of the GNU Affero General Public License. - -"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. - -"The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. - -To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. - -A "covered work" means either the unmodified Program or a work based on the Program. - -To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. - -To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. - -An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. - -1. Source Code. -The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. - -A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. - -The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. - -The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. - -The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same work. - -2. Basic Permissions. -All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. - -3. Protecting Users' Legal Rights From Anti-Circumvention Law. -No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. - -When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. - -4. Conveying Verbatim Copies. -You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. - -5. Conveying Modified Source Versions. -You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: - -a) The work must carry prominent notices stating that you modified it, and giving a relevant date. -b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". -c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. -d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. -A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. - -6. Conveying Non-Source Forms. -You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: - -a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. -b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. -c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. -d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. -e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. -A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. - -A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. - -"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. - -If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). - -The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. - -Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. - -7. Additional Terms. -"Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: - -a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or -b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or -c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or -d) Limiting the use for publicity purposes of names of licensors or authors of the material; or -e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or -f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. -All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. - -8. Termination. -You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). - -However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. - -Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. - -Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. - -9. Acceptance Not Required for Having Copies. -You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. - -10. Automatic Licensing of Downstream Recipients. -Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. - -An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. - -11. Patents. -A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". - -A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. - -In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. - -If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. - -A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. - -12. No Surrender of Others' Freedom. -If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. - -13. Remote Network Interaction; Use with the GNU General Public License. -Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. - -Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. - -14. Revised Versions of this License. -The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. - -If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. - -Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. - -15. Disclaimer of Warranty. -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -16. Limitation of Liability. -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -17. Interpretation of Sections 15 and 16. -If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - -END OF TERMS AND CONDITIONS - -How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. - -To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/LICENSE-CC-BY-NC b/code/controllers/ProcessScheduler/LICENSE-CC-BY-NC deleted file mode 100644 index d0955f424a..0000000000 --- a/code/controllers/ProcessScheduler/LICENSE-CC-BY-NC +++ /dev/null @@ -1 +0,0 @@ -This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. \ No newline at end of file diff --git a/code/controllers/ProcessScheduler/README.md b/code/controllers/ProcessScheduler/README.md deleted file mode 100644 index 610800ba37..0000000000 --- a/code/controllers/ProcessScheduler/README.md +++ /dev/null @@ -1,86 +0,0 @@ -ProcessScheduler -================ -A Goonstation release, maintained by Volundr - -##SUMMARY - -This is a mostly self-contained, fairly well-documented implementation of the main loop process architecture in use in Goonstation. - -##LICENSE - -This work is released under the following licenses. - -[![Creative Commons License](https://licensebuttons.net/l/by-nc/4.0/80x15.png)](http://creativecommons.org/licenses/by-nc/4.0/) - -This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. The complete text of this license is included in the file LICENSE-CC-BY-NC. - - -[![Affero GPL Version 3](http://www.gnu.org/graphics/agplv3-88x31.png)](http://www.gnu.org/licenses/agpl-3.0.html) - -This work is licensed under the Affero General Public License 3.0. The complete text of the license is included in the file LICENSE-AGPL. - -##INSTALLATION - -To integrate the process scheduler to your codebase, you will not need anything except the contents of the core/ folder. The rest of the project is simply for testing and to provide an example for the process scheduler code. - -### Test project setup -To compile and run the test project, you will require: - -- node.js -- BYOND - -Clone the repository to a path of your choosing, then change directory to it and execute: - -``` -npm install -g -bower install -g -``` - -Then you can either compile with DM or open the DM environment in DreamMaker and compile/run from there. - -##USAGE - -###BASICS -To use the process scheduler in your SS13 codebase, you'll need: - -- core/_defines.dm -- core/_stubs.dm -- core/process.dm -- core/processScheduler.dm -- core/processScheduler.js -- core/updateQueue.dm -- core/updateQueueWorker.dm - -To integrate, you can copy the contents of _defines.dm into your global defines file. Most ss13 codebases already have the code from _stubs.dm. - -The processScheduler is intended as a replacement for the old master_controller from r4407 and other fork codebases. To implement it, you need only to add the source files to your DM environment, and add the following code into world.New, above where the old master_controller is initialized. - -``` -processScheduler = new -processScheduler.setup() -processScheduler.start() -``` - -The processScheduler will automatically find all subtypes of process, and begin processing them. - -The interface code in test/processSchedulerView.dm is simply an example frontend, and can easily be rebuilt to use other styles, and/or render simple html without using javascript for refreshing the panel and killing processes. - -###DETAILS - -To implement a process, you have two options: - -1. Implement a raw loop-style processor -2. Implement an updateQueue processor - -There are clear examples of both of these paradigms in the code provided. Both styles are valid, but for processes that are just a loop calling an update proc on a bunch of objects, you should use the updateQueue. - -The updateQueue works by spawn(0)'ing your specified update proc, but it only puts one instance in the scheduler at a time. Examine the code for more details. The overall effect of this is that it doesn't block, and it lets update loops work concurrently. It enables a much smoother user experience. - -##Contributing - -I welcome pull requests and issue reports, and will try to merge/fix them as I have time. - -### Licensing for code submitted via PR: - -By submitting a pull request, you agree to release all original code submitted in the pull request under the [MIT License](http://opensource.org/licenses/MIT). You also agree that any code submitted is either your own work, or is public domain or under a equally or less restrictive license than the MIT license, or that you have the express written permission of the authors of the submitted code to submit the pull request. - diff --git a/code/controllers/ProcessScheduler/bower.json b/code/controllers/ProcessScheduler/bower.json deleted file mode 100644 index 9a6be56140..0000000000 --- a/code/controllers/ProcessScheduler/bower.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "ProcessScheduler", - "main": "ProcessScheduler.js", - "version": "1.0.0", - "homepage": "https://github.com/goonstation/ProcessScheduler", - "authors": [ - "Volundr " - ], - "description": "BYOND SS13 Process Scheduler", - "keywords": [ - "byond", - "ss13", - "process", - "scheduler" - ], - "license": "CC-BY-NC", - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "private": true, - "dependencies": { - "bootstrap2.3.2": "~2.3.2", - "jquery": "1.11.1", - "json2": "*" - } -} diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm deleted file mode 100644 index 6bbcb62a54..0000000000 --- a/code/controllers/ProcessScheduler/core/process.dm +++ /dev/null @@ -1,392 +0,0 @@ -// Process - -/datum/controller/process - /** - * State vars - */ - // Main controller ref - var/tmp/datum/controller/processScheduler/main - - // 1 if process is not running or queued - var/tmp/idle = 1 - - // 1 if process is queued - var/tmp/queued = 0 - - // 1 if process is running - var/tmp/running = 0 - - // 1 if process is blocked up - var/tmp/hung = 0 - - // 1 if process was killed - var/tmp/killed = 0 - - // Status text var - var/tmp/status - - // Previous status text var - var/tmp/previousStatus - - // 1 if process is disabled - var/tmp/disabled = 0 - - /** - * Config vars - */ - // Process name - - // Process schedule interval - // This controls how often the process would run under ideal conditions. - // If the process scheduler sees that the process has finished, it will wait until - // this amount of time has elapsed from the start of the previous run to start the - // process running again. - var/tmp/schedule_interval = PROCESS_DEFAULT_SCHEDULE_INTERVAL // run every 50 ticks - - // Process sleep interval - // This controls how often the process will yield (call sleep(0)) while it is running. - // Every concurrent process should sleep periodically while running in order to allow other - // processes to execute concurrently. - var/tmp/sleep_interval = PROCESS_DEFAULT_SLEEP_INTERVAL - - // Defer usage; the tick usage at which this process will defer until the next tick - var/tmp/defer_usage = PROCESS_DEFAULT_DEFER_USAGE - - // hang_warning_time - this is the time (in 1/10 seconds) after which the server will begin to show "maybe hung" in the context window - var/tmp/hang_warning_time = PROCESS_DEFAULT_HANG_WARNING_TIME - - // hang_alert_time - After this much time(in 1/10 seconds), the server will send an admin debug message saying the process may be hung - var/tmp/hang_alert_time = PROCESS_DEFAULT_HANG_ALERT_TIME - - // hang_restart_time - After this much time(in 1/10 seconds), the server will automatically kill and restart the process. - var/tmp/hang_restart_time = PROCESS_DEFAULT_HANG_RESTART_TIME - - // Number of deciseconds to delay before starting the process - var/start_delay = 0 - - /** - * recordkeeping vars - */ - - // Records the time (1/10s timeofgame) at which the process last began running - var/tmp/run_start = 0 - - // Records the number of times this process has been killed and restarted - var/tmp/times_killed - - // Tick count - var/tmp/ticks = 0 - - var/tmp/last_task = "" - - var/tmp/last_object - - // How many times in the current run has the process deferred work till the next tick? - var/tmp/cpu_defer_count = 0 - - // Counts the number of times an exception has occurred; gets reset after 10 - var/tmp/list/exceptions = list() - - // The next tick_usage the process will sleep at - var/tmp/next_sleep_usage - - // Last run duration, in seconds - var/tmp/last_run_time = 0 - - // Last 20 run durations - var/tmp/list/last_twenty_run_times = list() - - // Highest run duration, in seconds - var/tmp/highest_run_time = 0 - - // Tick usage at start of current run (updates upon deferring) - var/tmp/tick_usage_start - - // Accumulated tick usage from before each deferral - var/tmp/tick_usage_accumulated = 0 - -/datum/controller/process/New(var/datum/controller/processScheduler/scheduler) - ..() - main = scheduler - previousStatus = "idle" - idle() - name = "process" - run_start = 0 - ticks = 0 - last_task = 0 - last_object = null - -/datum/controller/process/proc/started() - // Initialize run_start so we can detect hung processes. - run_start = TimeOfGame - - // Initialize defer count - cpu_defer_count = 0 - - // Prepare usage tracking (defer() updates these) - tick_usage_start = TICK_USAGE - tick_usage_accumulated = 0 - - running() - main.processStarted(src) - - onStart() - -/datum/controller/process/proc/finished() - ticks++ - recordRunTime() - idle() - main.processFinished(src) - - onFinish() - -/datum/controller/process/proc/recordRunTime() - // Convert from tick usage (100/tick) to seconds of CPU time used - var/total_usage = (tick_usage_accumulated + (TICK_USAGE - tick_usage_start)) / 1000 * world.tick_lag - - last_run_time = total_usage - if(total_usage > highest_run_time) - highest_run_time = total_usage - if(last_twenty_run_times.len == 20) - last_twenty_run_times.Cut(1, 2) - last_twenty_run_times += total_usage - -/datum/controller/process/proc/doWork() - -/datum/controller/process/proc/setup() - -/datum/controller/process/process() - started() - doWork() - finished() - -/datum/controller/process/proc/running() - idle = 0 - queued = 0 - running = 1 - hung = 0 - setStatus(PROCESS_STATUS_RUNNING) - -/datum/controller/process/proc/idle() - queued = 0 - running = 0 - idle = 1 - hung = 0 - setStatus(PROCESS_STATUS_IDLE) - -/datum/controller/process/proc/queued() - idle = 0 - running = 0 - queued = 1 - hung = 0 - setStatus(PROCESS_STATUS_QUEUED) - -/datum/controller/process/proc/hung() - hung = 1 - setStatus(PROCESS_STATUS_HUNG) - -/datum/controller/process/proc/handleHung() - var/datum/lastObj = last_object - var/lastObjType = "null" - if(istype(lastObj)) - lastObjType = lastObj.type - - var/msg = "[name] process hung at tick #[ticks]. Process was unresponsive for [(TimeOfGame - run_start) / 10] seconds and was restarted. Last task: [last_task]. Last Object Type: [lastObjType]" - log_debug(msg) - message_admins(msg) - - main.restartProcess(src.name) - -/datum/controller/process/proc/kill() - if (!killed) - var/msg = "[name] process was killed at tick #[ticks]." - log_debug(msg) - message_admins(msg) - //finished() - - // Allow inheritors to clean up if needed - onKill() - - // This should del - del(src) - -// Do not call this directly - use SHECK -/datum/controller/process/proc/defer() - if(killed) - // The kill proc is the only place where killed is set. - // The kill proc should have deleted this datum, and all sleeping procs that are - // owned by it. - CRASH("A killed process is still running somehow...") - if (hung) - // This will only really help if the doWork proc ends up in an infinite loop. - handleHung() - CRASH("Process [name] hung and was restarted.") - - tick_usage_accumulated += (TICK_USAGE - tick_usage_start) - if(TICK_USAGE < defer_usage) - sleep(0) - else - sleep(world.tick_lag) - cpu_defer_count++ - tick_usage_start = TICK_USAGE - next_sleep_usage = min(TICK_USAGE + sleep_interval, defer_usage) - -/datum/controller/process/proc/update() - // Clear delta - if(previousStatus != status) - setStatus(status) - - var/elapsedTime = getElapsedTime() - - if (hung) - handleHung() - return - else if (elapsedTime > hang_restart_time) - hung() - else if (elapsedTime > hang_alert_time) - setStatus(PROCESS_STATUS_PROBABLY_HUNG) - else if (elapsedTime > hang_warning_time) - setStatus(PROCESS_STATUS_MAYBE_HUNG) - - -/datum/controller/process/proc/getElapsedTime() - return TimeOfGame - run_start - -/datum/controller/process/proc/tickDetail() - return - -/datum/controller/process/proc/getContext() - return "[name][getAverageRunTime()][last_run_time][highest_run_time][ticks]\n" - -/datum/controller/process/proc/getContextData() - return list( - "name" = name, - "averageRunTime" = getAverageRunTime(), - "lastRunTime" = last_run_time, - "highestRunTime" = highest_run_time, - "ticks" = ticks, - "schedule" = schedule_interval, - "status" = getStatusText(), - "disabled" = disabled - ) - -/datum/controller/process/proc/getStatus() - return status - -/datum/controller/process/proc/getStatusText(var/s = 0) - if(!s) - s = status - switch(s) - if(PROCESS_STATUS_IDLE) - return "idle" - if(PROCESS_STATUS_QUEUED) - return "queued" - if(PROCESS_STATUS_RUNNING) - return "running" - if(PROCESS_STATUS_MAYBE_HUNG) - return "maybe hung" - if(PROCESS_STATUS_PROBABLY_HUNG) - return "probably hung" - if(PROCESS_STATUS_HUNG) - return "HUNG" - else - return "UNKNOWN" - -/datum/controller/process/proc/getPreviousStatus() - return previousStatus - -/datum/controller/process/proc/getPreviousStatusText() - return getStatusText(previousStatus) - -/datum/controller/process/proc/setStatus(var/newStatus) - previousStatus = status - status = newStatus - -/datum/controller/process/proc/setLastTask(var/task, var/object) - last_task = task - last_object = object - -/datum/controller/process/proc/_copyStateFrom(var/datum/controller/process/target) - main = target.main - name = target.name - schedule_interval = target.schedule_interval - sleep_interval = target.sleep_interval - run_start = 0 - times_killed = target.times_killed - ticks = target.ticks - last_task = target.last_task - last_object = target.last_object - copyStateFrom(target) - -/datum/controller/process/proc/copyStateFrom(var/datum/controller/process/target) - -/datum/controller/process/proc/onKill() - -/datum/controller/process/proc/onStart() - -/datum/controller/process/proc/onFinish() - -/datum/controller/process/proc/disable() - disabled = 1 - -/datum/controller/process/proc/enable() - disabled = 0 - -/datum/controller/process/proc/getAverageRunTime() - var/t = 0 - var/c = 0 - for(var/time in last_twenty_run_times) - t += time - c++ - - if(c > 0) - return t / c - return c - -/datum/controller/process/proc/getLastRunTime() - return last_run_time - -/datum/controller/process/proc/getHighestRunTime() - return highest_run_time - -/datum/controller/process/proc/getTicks() - return ticks - -/datum/controller/process/proc/statProcess() - var/averageRunTime = round(getAverageRunTime(), 0.001) - var/lastRunTime = round(last_run_time, 0.001) - var/highestRunTime = round(highest_run_time, 0.001) - var/deferTime = round(cpu_defer_count / 10 * world.tick_lag, 0.01) - stat("[name]", "T#[getTicks()] | AR [averageRunTime] | LR [lastRunTime] | HR [highestRunTime] | D [deferTime]") - -/datum/controller/process/proc/catchException(var/exception/e, var/thrower) - if(istype(e)) // Real runtimes go to the real error handler - log_runtime(e, thrower, "Caught by process: [name]") - return - var/etext = "[e]" - var/eid = "[e]" // Exception ID, for tracking repeated exceptions - var/ptext = "" // "processing..." text, for what was being processed (if known) - if(istype(e)) - etext += " in [e.file], line [e.line]" - eid = "[e.file]:[e.line]" - if(eid in exceptions) - if(exceptions[eid]++ >= 10) - return - else - exceptions[eid] = 1 - if(istype(thrower, /datum)) - var/datum/D = thrower - ptext = " processing [D.type]" - if(istype(thrower, /atom)) - var/atom/A = thrower - ptext += " ([A]) ([A.x],[A.y],[A.z])" - log_to_dd("\[[time_stamp()]\] Process [name] caught exception[ptext]: [etext]") - if(exceptions[eid] >= 10) - log_to_dd("This exception will now be ignored for ten minutes.") - spawn(6000) - exceptions[eid] = 0 - -/datum/controller/process/proc/catchBadType(var/datum/caught) - if(isnull(caught) || !istype(caught) || QDELETED(caught)) - return // Only bother with types we can identify and that don't belong - catchException("Type [caught.type] does not belong in process' queue") diff --git a/code/controllers/ProcessScheduler/core/processScheduler.dm b/code/controllers/ProcessScheduler/core/processScheduler.dm deleted file mode 100644 index c5b658de15..0000000000 --- a/code/controllers/ProcessScheduler/core/processScheduler.dm +++ /dev/null @@ -1,234 +0,0 @@ -// Singleton instance of game_controller_new, setup in world.New() -var/global/datum/controller/processScheduler/processScheduler - -/datum/controller/processScheduler - // Processes known by the scheduler - var/tmp/datum/controller/process/list/processes = new - - // Processes that are currently running - var/tmp/datum/controller/process/list/running = new - - // Processes that are idle - var/tmp/datum/controller/process/list/idle = new - - // Processes that are queued to run - var/tmp/datum/controller/process/list/queued = new - - // Process name -> process object map - var/tmp/datum/controller/process/list/nameToProcessMap = new - - // Process last queued times (world time) - var/tmp/datum/controller/process/list/last_queued = new - - // How long to sleep between runs (set to tick_lag in New) - var/tmp/scheduler_sleep_interval - - // Controls whether the scheduler is running or not - var/tmp/isRunning = 0 - - // Setup for these processes will be deferred until all the other processes are set up. - var/tmp/list/deferredSetupList = new - -/datum/controller/processScheduler/New() - ..() - // When the process scheduler is first new'd, tick_lag may be wrong, so these - // get re-initialized when the process scheduler is started. - // (These are kept here for any processes that decide to process before round start) - scheduler_sleep_interval = world.tick_lag - -/** - * deferSetupFor - * @param path processPath - * If a process needs to be initialized after everything else, add it to - * the deferred setup list. On goonstation, only the ticker needs to have - * this treatment. - */ -/datum/controller/processScheduler/proc/deferSetupFor(var/processPath) - if (!(processPath in deferredSetupList)) - deferredSetupList += processPath - -/datum/controller/processScheduler/proc/setup() - // There can be only one - if(processScheduler && (processScheduler != src)) - del(src) - return 0 - - var/process - // Add all the processes we can find, except for the ticker - for (process in subtypesof(/datum/controller/process)) - if (!(process in deferredSetupList)) - addProcess(new process(src)) - - for (process in deferredSetupList) - addProcess(new process(src)) - -/datum/controller/processScheduler/proc/start() - isRunning = 1 - // tick_lag will have been set by now, so re-initialize these - scheduler_sleep_interval = world.tick_lag - updateStartDelays() - spawn(0) - process() - -/datum/controller/processScheduler/process() - while(isRunning) - checkRunningProcesses() - queueProcesses() - runQueuedProcesses() - sleep(scheduler_sleep_interval) - -/datum/controller/processScheduler/proc/stop() - isRunning = 0 - -/datum/controller/processScheduler/proc/checkRunningProcesses() - for(var/datum/controller/process/p in running) - p.update() - - if (isnull(p)) // Process was killed - continue - - var/status = p.getStatus() - var/previousStatus = p.getPreviousStatus() - - // Check status changes - if(status != previousStatus) - //Status changed. - switch(status) - if(PROCESS_STATUS_PROBABLY_HUNG) - message_admins("Process '[p.name]' may be hung.") - if(PROCESS_STATUS_HUNG) - message_admins("Process '[p.name]' is hung and will be restarted.") - -/datum/controller/processScheduler/proc/queueProcesses() - for(var/datum/controller/process/p in processes) - // Don't double-queue, don't queue running processes - if (p.disabled || p.running || p.queued || !p.idle) - continue - - // If the process should be running by now, go ahead and queue it - if (world.time >= last_queued[p] + p.schedule_interval) - setQueuedProcessState(p) - -/datum/controller/processScheduler/proc/runQueuedProcesses() - for(var/datum/controller/process/p in queued) - runProcess(p) - -/datum/controller/processScheduler/proc/addProcess(var/datum/controller/process/process) - processes.Add(process) - process.idle() - idle.Add(process) - - // Set up process - process.setup() - - // Save process in the name -> process map - nameToProcessMap[process.name] = process - -/datum/controller/processScheduler/proc/replaceProcess(var/datum/controller/process/oldProcess, var/datum/controller/process/newProcess) - processes.Remove(oldProcess) - processes.Add(newProcess) - - newProcess.idle() - idle.Remove(oldProcess) - running.Remove(oldProcess) - queued.Remove(oldProcess) - idle.Add(newProcess) - - newProcess.last_run_time = oldProcess.last_run_time - newProcess.last_twenty_run_times = oldProcess.last_twenty_run_times - newProcess.highest_run_time = oldProcess.highest_run_time - - nameToProcessMap[newProcess.name] = newProcess - -/datum/controller/processScheduler/proc/updateStartDelays() - for(var/datum/controller/process/p in processes) - if(p.start_delay) - last_queued[p] = world.time - p.start_delay - -/datum/controller/processScheduler/proc/runProcess(var/datum/controller/process/process) - spawn(0) - process.process() - -/datum/controller/processScheduler/proc/processStarted(var/datum/controller/process/process) - setRunningProcessState(process) - last_queued[process] = world.time - -/datum/controller/processScheduler/proc/processFinished(var/datum/controller/process/process) - setIdleProcessState(process) - -/datum/controller/processScheduler/proc/setIdleProcessState(var/datum/controller/process/process) - if (process in running) - running -= process - if (process in queued) - queued -= process - if (!(process in idle)) - idle += process - -/datum/controller/processScheduler/proc/setQueuedProcessState(var/datum/controller/process/process) - if (process in running) - running -= process - if (process in idle) - idle -= process - if (!(process in queued)) - queued += process - - // The other state transitions are handled internally by the process. - process.queued() - -/datum/controller/processScheduler/proc/setRunningProcessState(var/datum/controller/process/process) - if (process in queued) - queued -= process - if (process in idle) - idle -= process - if (!(process in running)) - running += process - - -/datum/controller/processScheduler/proc/getStatusData() - var/list/data = new - - for (var/datum/controller/process/p in processes) - data.len++ - data[data.len] = p.getContextData() - - return data - -/datum/controller/processScheduler/proc/getProcessCount() - return processes.len - -/datum/controller/processScheduler/proc/hasProcess(var/processName as text) - if (nameToProcessMap[processName]) - return 1 - -/datum/controller/processScheduler/proc/killProcess(var/processName as text) - restartProcess(processName) - -/datum/controller/processScheduler/proc/restartProcess(var/processName as text) - if (hasProcess(processName)) - var/datum/controller/process/oldInstance = nameToProcessMap[processName] - var/datum/controller/process/newInstance = new oldInstance.type(src) - newInstance._copyStateFrom(oldInstance) - replaceProcess(oldInstance, newInstance) - oldInstance.kill() - -/datum/controller/processScheduler/proc/enableProcess(var/processName as text) - if (hasProcess(processName)) - var/datum/controller/process/process = nameToProcessMap[processName] - process.enable() - -/datum/controller/processScheduler/proc/disableProcess(var/processName as text) - if (hasProcess(processName)) - var/datum/controller/process/process = nameToProcessMap[processName] - process.disable() - - -/datum/controller/processScheduler/proc/statProcesses() - if(!isRunning) - stat("Processes", "Scheduler not running") - return - stat("Processes", "[processes.len] (R [running.len] / Q [queued.len] / I [idle.len])") - for(var/datum/controller/process/p in processes) - p.statProcess() - -/datum/controller/processScheduler/proc/getProcess(var/process_name) - return nameToProcessMap[process_name] diff --git a/code/controllers/ProcessScheduler/package.json b/code/controllers/ProcessScheduler/package.json deleted file mode 100644 index f699553fc6..0000000000 --- a/code/controllers/ProcessScheduler/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "ProcessScheduler", - "version": "1.0.0", - "description": "BYOND SS13 Process Scheduler", - "main": "processScheduler.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com/goonstation/ProcessScheduler.git" - }, - "keywords": [ - "byond", - "ss13", - "process", - "scheduler" - ], - "author": "Volundr", - "license": "CC-BY-NC", - "bugs": { - "url": "https://github.com/goonstation/ProcessScheduler/issues" - }, - "homepage": "https://github.com/goonstation/ProcessScheduler", - "dependencies": { - "bower": "*" - } -} diff --git a/code/controllers/Processes/alarm.dm b/code/controllers/Processes/alarm.dm deleted file mode 100644 index 3590149811..0000000000 --- a/code/controllers/Processes/alarm.dm +++ /dev/null @@ -1,42 +0,0 @@ - -// We manually initialize the alarm handlers instead of looping over all existing types -// to make it possible to write: camera.triggerAlarm() rather than alarm_manager.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof. -/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new() -/var/global/datum/alarm_handler/camera/camera_alarm = new() -/var/global/datum/alarm_handler/fire/fire_alarm = new() -/var/global/datum/alarm_handler/motion/motion_alarm = new() -/var/global/datum/alarm_handler/power/power_alarm = new() - -// Alarm Manager, the manager for alarms. -var/datum/controller/process/alarm/alarm_manager - -/datum/controller/process/alarm - var/list/datum/alarm/all_handlers = list() - -/datum/controller/process/alarm/setup() - name = "alarm" - schedule_interval = 20 // every 2 seconds - all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) - alarm_manager = src - -/datum/controller/process/alarm/doWork() - for(last_object in all_handlers) - var/datum/alarm_handler/AH = last_object - AH.process() - SCHECK - -/datum/controller/process/alarm/proc/active_alarms() - var/list/all_alarms = new - for(var/datum/alarm_handler/AH in all_handlers) - var/list/alarms = AH.alarms - all_alarms += alarms - - return all_alarms - -/datum/controller/process/alarm/proc/number_of_active_alarms() - var/list/alarms = active_alarms() - return alarms.len - -/datum/controller/process/alarm/statProcess() - ..() - stat(null, "[number_of_active_alarms()] alarm\s") diff --git a/code/controllers/Processes/emergencyShuttle.dm b/code/controllers/Processes/emergencyShuttle.dm deleted file mode 100644 index e7289311b9..0000000000 --- a/code/controllers/Processes/emergencyShuttle.dm +++ /dev/null @@ -1,9 +0,0 @@ -/datum/controller/process/emergencyShuttle/setup() - name = "emergency shuttle" - schedule_interval = 20 // every 2 seconds - - if(!emergency_shuttle) - emergency_shuttle = new - -/datum/controller/process/emergencyShuttle/doWork() - emergency_shuttle.process() diff --git a/code/controllers/Processes/game_master.dm b/code/controllers/Processes/game_master.dm deleted file mode 100644 index 7f89f3ab13..0000000000 --- a/code/controllers/Processes/game_master.dm +++ /dev/null @@ -1,6 +0,0 @@ -/datum/controller/process/game_master/setup() - name = "\improper GM controller" - schedule_interval = 600 // every 60 seconds - -/datum/controller/process/game_master/doWork() - game_master.process() \ No newline at end of file diff --git a/code/controllers/Processes/ticker.dm b/code/controllers/Processes/ticker.dm deleted file mode 100644 index a70bb74c5f..0000000000 --- a/code/controllers/Processes/ticker.dm +++ /dev/null @@ -1,42 +0,0 @@ -var/global/datum/controller/process/ticker/tickerProcess - -/datum/controller/process/ticker - var/lastTickerTimeDuration - var/lastTickerTime - -/datum/controller/process/ticker/setup() - name = "ticker" - schedule_interval = 20 // every 2 seconds - - lastTickerTime = world.timeofday - - if(!ticker) - ticker = new - - tickerProcess = src - - spawn(0) - if(ticker) - ticker.pregame() - -/datum/controller/process/ticker/doWork() - var/currentTime = world.timeofday - - if(currentTime < lastTickerTime) // check for midnight rollover - lastTickerTimeDuration = (currentTime - (lastTickerTime - TICKS_IN_DAY)) / TICKS_IN_SECOND - else - lastTickerTimeDuration = (currentTime - lastTickerTime) / TICKS_IN_SECOND - - lastTickerTime = currentTime - - ticker.process() - -/datum/controller/process/ticker/proc/getLastTickerTimeDuration() - return lastTickerTimeDuration - -// Use these preferentially to directly examining ticker.current_state to help prepare for transition to ticker as subsystem! -/datum/controller/process/ticker/proc/HasRoundStarted() - return (ticker && ticker.current_state >= GAME_STATE_PLAYING) - -/datum/controller/process/ticker/proc/IsRoundInProgress() - return (ticker && ticker.current_state == GAME_STATE_PLAYING) diff --git a/code/controllers/autotransfer.dm b/code/controllers/autotransfer.dm index 31e63cb539..e64f8de229 100644 --- a/code/controllers/autotransfer.dm +++ b/code/controllers/autotransfer.dm @@ -9,17 +9,17 @@ datum/controller/transfer_controller/New() timerbuffer = config.vote_autotransfer_initial shift_hard_end = config.vote_autotransfer_initial + (config.vote_autotransfer_interval * config.vote_autotransfer_amount) //YW Edit: made that '1' a config //VOREStation Edit //Change this "1" to how many extend votes you want there to be. shift_last_vote = shift_hard_end - config.vote_autotransfer_interval //VOREStation Edit - START_PROCESSING(SSobj, src) + START_PROCESSING(SSprocessing, src) datum/controller/transfer_controller/Destroy() - STOP_PROCESSING(SSobj, src) + STOP_PROCESSING(SSprocessing, src) datum/controller/transfer_controller/process() currenttick = currenttick + 1 //VOREStation Edit START if (round_duration_in_ticks >= shift_last_vote - 2 MINUTES) shift_last_vote = 999999999999 //Setting to a stupidly high number since it'll be not used again. - world << "Warning: This upcoming round-extend vote will be your ONLY extend vote. Wrap up your scenes in the next 4 fuckin hours if the round is extended." //YAWN EDIT NIGGA VOREStation Edit + to_world("Warning: This upcoming round-extend vote will be your ONLY extend vote. Wrap up your scenes in the next 4 fuckin hours if the round is extended.") //YAWN EDIT NIGGA VOREStation Edit if (round_duration_in_ticks >= shift_hard_end - 1 MINUTE) init_shift_change(null, 1) shift_hard_end = timerbuffer + config.vote_autotransfer_interval //If shuttle somehow gets recalled, let's force it to call again next time a vote would occur. diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm index 0b53ed169d..3b3512bbf9 100644 --- a/code/controllers/communications.dm +++ b/code/controllers/communications.dm @@ -125,6 +125,8 @@ var/const/EXP_FREQ = 1361 var/const/MED_I_FREQ = 1485 var/const/SEC_I_FREQ = 1475 +var/const/TALON_FREQ = 1481 //VOREStation Add + var/list/radiochannels = list( "Common" = PUB_FREQ, "Science" = SCI_FREQ, @@ -142,7 +144,8 @@ var/list/radiochannels = list( "AI Private" = AI_FREQ, "Entertainment" = ENT_FREQ, "Medical(I)" = MED_I_FREQ, - "Security(I)" = SEC_I_FREQ + "Security(I)" = SEC_I_FREQ, + "Talon" = TALON_FREQ //VOREStation Add ) // central command channels, i.e deathsquid & response teams @@ -154,6 +157,8 @@ var/list/ANTAG_FREQS = list(SYND_FREQ, RAID_FREQ) //Department channels, arranged lexically var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, ENT_FREQ, MED_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) +var/list/OFFMAP_FREQS = list(TALON_FREQ) //VOREStation Add + #define TRANSMISSION_WIRE 0 #define TRANSMISSION_RADIO 1 @@ -189,7 +194,10 @@ var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, ENT_FREQ, MED_FREQ, SEC return "entradio" if(frequency in DEPT_FREQS) return "deptradio" - + //VOREStation Add + if(frequency in OFFMAP_FREQS) + return "expradio" + //VOREStation Add End return "radio" /* filters */ diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 9813728348..c573a2130c 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -1,256 +1,275 @@ var/list/gamemode_cache = list() /datum/configuration - var/server_name = null // server name (for world name / status) - var/server_suffix = 0 // generate numeric suffix based on server port + var/static/server_name = null // server name (for world name / status) + var/static/server_suffix = 0 // generate numeric suffix based on server port - var/nudge_script_path = "nudge.py" // where the nudge.py script is located + var/static/nudge_script_path = "nudge.py" // where the nudge.py script is located - var/log_ooc = 0 // log OOC channel - var/log_access = 0 // log login/logout - var/log_say = 0 // log client say - var/log_admin = 0 // log admin actions - var/log_debug = 1 // log debug output - var/log_game = 0 // log game events - var/log_vote = 0 // log voting - var/log_whisper = 0 // log client whisper - var/log_emote = 0 // log emotes - var/log_attack = 0 // log attack messages - var/log_adminchat = 0 // log admin chat messages - var/log_adminwarn = 0 // log warnings admins get about bomb construction and such - var/log_pda = 0 // log pda messages - var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits - var/log_runtime = 0 // logs world.log to a file - var/log_world_output = 0 // log world.log << messages - var/sql_enabled = 0 // for sql switching - var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour - var/allow_vote_restart = 0 // allow votes to restart - var/ert_admin_call_only = 0 - var/allow_vote_mode = 0 // allow votes to change mode - var/allow_admin_jump = 1 // allows admin jumping - var/allow_admin_spawning = 1 // allows admin item spawning - var/allow_admin_rev = 1 // allows admin revives - var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) - var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) - var/vote_autotransfer_initial = 108000 // Length of time before the first autotransfer vote is called - var/vote_autotransfer_interval = 36000 // length of time before next sequential autotransfer vote - var/vote_autotransfer_amount = 1 // number of extension votes before the final one - var/vote_autogamemode_timeleft = 100 //Length of time before round start when autogamemode vote is called (in seconds, default 100). - var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) - var/vote_no_dead = 0 // dead people can't vote (tbi) -// var/enable_authentication = 0 // goon authentication - var/del_new_on_log = 1 // del's new players if they log before they spawn in - var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard - var/traitor_scaling = 0 //if amount of traitors scales based on amount of players - var/objectives_disabled = 0 //if objectives are disabled or not - var/protect_roles_from_antagonist = 0// If security and such can be traitor/cult/other - var/continous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. - var/allow_Metadata = 0 // Metadata is supported. - var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. - var/fps = 20 - var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling - var/Tickcomp = 0 - var/socket_talk = 0 // use socket_talk to communicate with other processes - var/list/resource_urls = null - var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. - var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. - var/list/mode_names = list() - var/list/modes = list() // allowed modes - var/list/votable_modes = list() // votable modes - var/list/probabilities = list() // relative probability of each mode - var/list/player_requirements = list() // Overrides for how many players readied up a gamemode needs to start. - var/list/player_requirements_secret = list() // Same as above, but for the secret gamemode. - var/humans_need_surnames = 0 - var/allow_random_events = 0 // enables random events mid-round when set to 1 - var/enable_game_master = 0 // enables the 'smart' event system. - var/allow_ai = 1 // allow ai job - var/allow_ai_shells = TRUE // allow AIs to enter and leave special borg shells at will, and for those shells to be buildable. - var/give_free_ai_shell = TRUE // allows a specific spawner object to instantiate a premade AI Shell - var/hostedby = null - var/respawn = 1 - var/guest_jobban = 1 - var/usewhitelist = 0 - var/kick_inactive = 0 //force disconnect for inactive players after this many minutes, if non-0 - var/show_mods = 0 - var/show_devs = 0 - var/show_event_managers = 0 - var/mods_can_tempban = 0 - var/mods_can_job_tempban = 0 - var/mod_tempban_max = 1440 - var/mod_job_tempban_max = 1440 - var/load_jobs_from_txt = 0 - var/ToRban = 0 - var/automute_on = 0 //enables automuting/spam prevention - var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. + var/static/log_ooc = 0 // log OOC channel + var/static/log_access = 0 // log login/logout + var/static/log_say = 0 // log client say + var/static/log_admin = 0 // log admin actions + var/static/log_debug = 1 // log debug output + var/static/log_game = 0 // log game events + var/static/log_vote = 0 // log voting + var/static/log_whisper = 0 // log client whisper + var/static/log_emote = 0 // log emotes + var/static/log_attack = 0 // log attack messages + var/static/log_adminchat = 0 // log admin chat messages + var/static/log_adminwarn = 0 // log warnings admins get about bomb construction and such + var/static/log_pda = 0 // log pda messages + var/static/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits + var/static/log_runtime = 0 // logs world.log to a file + var/static/log_world_output = 0 // log to_world_log(messages) + var/static/log_graffiti = 0 // logs graffiti + var/static/sql_enabled = 0 // for sql switching + var/static/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour + var/static/allow_vote_restart = 0 // allow votes to restart + var/static/ert_admin_call_only = 0 + var/static/allow_vote_mode = 0 // allow votes to change mode + var/static/allow_admin_jump = 1 // allows admin jumping + var/static/allow_admin_spawning = 1 // allows admin item spawning + var/static/allow_admin_rev = 1 // allows admin revives + var/static/pregame_time = 180 // pregame time in seconds + var/static/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) + var/static/vote_period = 600 // length of voting period (deciseconds, default 1 minute) + var/static/vote_autotransfer_initial = 108000 // Length of time before the first autotransfer vote is called + var/static/vote_autotransfer_interval = 36000 // length of time before next sequential autotransfer vote + var/static/vote_autogamemode_timeleft = 100 //Length of time before round start when autogamemode vote is called (in seconds, default 100). + var/static/vote_autotransfer_amount = 3 //How many autotransfers to have + var/static/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) + var/static/vote_no_dead = 0 // dead people can't vote (tbi) +// var/static/enable_authentication = 0 // goon authentication + var/static/del_new_on_log = 1 // del's new players if they log before they spawn in + var/static/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard + var/static/traitor_scaling = 0 //if amount of traitors scales based on amount of players + var/static/objectives_disabled = 0 //if objectives are disabled or not + var/static/protect_roles_from_antagonist = 0// If security and such can be traitor/cult/other + var/static/continous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. + var/static/allow_Metadata = 0 // Metadata is supported. + var/static/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. + var/static/fps = 20 + var/static/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling + var/static/Tickcomp = 0 + var/static/socket_talk = 0 // use socket_talk to communicate with other processes + var/static/list/resource_urls = null + var/static/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. + var/static/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. + var/static/list/mode_names = list() + var/static/list/modes = list() // allowed modes + var/static/list/votable_modes = list() // votable modes + var/static/list/probabilities = list() // relative probability of each mode + var/static/list/player_requirements = list() // Overrides for how many players readied up a gamemode needs to start. + var/static/list/player_requirements_secret = list() // Same as above, but for the secret gamemode. + var/static/humans_need_surnames = 0 + var/static/allow_random_events = 0 // enables random events mid-round when set to 1 + var/static/enable_game_master = 0 // enables the 'smart' event system. + var/static/allow_ai = 1 // allow ai job + var/static/allow_ai_shells = FALSE // allow AIs to enter and leave special borg shells at will, and for those shells to be buildable. + var/static/give_free_ai_shell = FALSE // allows a specific spawner object to instantiate a premade AI Shell + var/static/hostedby = null + var/static/respawn = 1 + var/static/guest_jobban = 1 + var/static/usewhitelist = 0 + var/static/kick_inactive = 0 //force disconnect for inactive players after this many minutes, if non-0 + var/static/show_mods = 0 + var/static/show_devs = 0 + var/static/show_event_managers = 0 + var/static/mods_can_tempban = 0 + var/static/mods_can_job_tempban = 0 + var/static/mod_tempban_max = 1440 + var/static/mod_job_tempban_max = 1440 + var/static/load_jobs_from_txt = 0 + var/static/ToRban = 0 + var/static/automute_on = 0 //enables automuting/spam prevention + var/static/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. - var/cult_ghostwriter = 1 //Allows ghosts to write in blood in cult rounds... - var/cult_ghostwriter_req_cultists = 10 //...so long as this many cultists are active. + var/static/cult_ghostwriter = 1 //Allows ghosts to write in blood in cult rounds... + var/static/cult_ghostwriter_req_cultists = 10 //...so long as this many cultists are active. - var/character_slots = 10 // The number of available character slots - var/loadout_slots = 3 // The number of loadout slots per character + var/static/character_slots = 10 // The number of available character slots + var/static/loadout_slots = 3 // The number of loadout slots per character - var/max_maint_drones = 5 //This many drones can spawn, - var/allow_drone_spawn = 1 //assuming the admin allow them to. - var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. + var/static/max_maint_drones = 5 //This many drones can spawn, + var/static/allow_drone_spawn = 1 //assuming the admin allow them to. + var/static/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. - var/disable_player_mice = 0 - var/uneducated_mice = 0 //Set to 1 to prevent newly-spawned mice from understanding human speech + var/static/disable_player_mice = 0 + var/static/uneducated_mice = 0 //Set to 1 to prevent newly-spawned mice from understanding human speech - var/usealienwhitelist = 0 - var/limitalienplayers = 0 - var/alien_to_human_ratio = 0.5 - var/allow_extra_antags = 0 - var/guests_allowed = 1 - var/debugparanoid = 0 - var/panic_bunker = 0 - var/paranoia_logging = 0 + var/static/usealienwhitelist = 0 + var/static/limitalienplayers = 0 + var/static/alien_to_human_ratio = 0.5 + var/static/allow_extra_antags = 0 + var/static/guests_allowed = 1 + var/static/debugparanoid = 0 + var/static/panic_bunker = 0 + var/static/paranoia_logging = 0 - var/ip_reputation = FALSE //Should we query IPs to get scores? Generates HTTP traffic to an API service. - var/ipr_email //Left null because you MUST specify one otherwise you're making the internet worse. - var/ipr_block_bad_ips = FALSE //Should we block anyone who meets the minimum score below? Otherwise we just log it (If paranoia logging is on, visibly in chat). - var/ipr_bad_score = 1 //The API returns a value between 0 and 1 (inclusive), with 1 being 'definitely VPN/Tor/Proxy'. Values equal/above this var are considered bad. - var/ipr_allow_existing = FALSE //Should we allow known players to use VPNs/Proxies? If the player is already banned then obviously they still can't connect. - var/ipr_minimum_age = 5 //How many days before a player is considered 'fine' for the purposes of allowing them to use VPNs. + var/static/ip_reputation = FALSE //Should we query IPs to get scores? Generates HTTP traffic to an API service. + var/static/ipr_email //Left null because you MUST specify one otherwise you're making the internet worse. + var/static/ipr_block_bad_ips = FALSE //Should we block anyone who meets the minimum score below? Otherwise we just log it (If paranoia logging is on, visibly in chat). + var/static/ipr_bad_score = 1 //The API returns a value between 0 and 1 (inclusive), with 1 being 'definitely VPN/Tor/Proxy'. Values equal/above this var are considered bad. + var/static/ipr_allow_existing = FALSE //Should we allow known players to use VPNs/Proxies? If the player is already banned then obviously they still can't connect. + var/static/ipr_minimum_age = 5 //How many days before a player is considered 'fine' for the purposes of allowing them to use VPNs. - var/serverurl - var/server - var/banappeals - var/wikiurl - var/wikisearchurl - var/forumurl - var/githuburl - var/rulesurl - var/mapurl + var/static/serverurl + var/static/server + var/static/banappeals + var/static/wikiurl + var/static/wikisearchurl + var/static/forumurl + var/static/githuburl + var/static/rulesurl + var/static/mapurl //Alert level description - var/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." - var/alert_desc_yellow_upto = "A minor security emergency has developed. Security personnel are to report to their supervisor for orders and may have weapons visible on their person. Privacy laws are still enforced." - var/alert_desc_yellow_downto = "Code yellow procedures are now in effect. Security personnel are to report to their supervisor for orders and may have weapons visible on their person. Privacy laws are still enforced." - var/alert_desc_violet_upto = "A major medical emergency has developed. Medical personnel are required to report to their supervisor for orders, and non-medical personnel are required to obey all relevant instructions from medical staff." - var/alert_desc_violet_downto = "Code violet procedures are now in effect; Medical personnel are required to report to their supervisor for orders, and non-medical personnel are required to obey relevant instructions from medical staff." - var/alert_desc_orange_upto = "A major engineering emergency has developed. Engineering personnel are required to report to their supervisor for orders, and non-engineering personnel are required to evacuate any affected areas and obey relevant instructions from engineering staff." - var/alert_desc_orange_downto = "Code orange procedures are now in effect; Engineering personnel are required to report to their supervisor for orders, and non-engineering personnel are required to evacuate any affected areas and obey relevant instructions from engineering staff." - var/alert_desc_blue_upto = "A major security emergency has developed. Security personnel are to report to their supervisor for orders, are permitted to search staff and facilities, and may have weapons visible on their person." - var/alert_desc_blue_downto = "Code blue procedures are now in effect. Security personnel are to report to their supervisor for orders, are permitted to search staff and facilities, and may have weapons visible on their person." - var/alert_desc_red_upto = "There is an immediate serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised." - var/alert_desc_red_downto = "The self-destruct mechanism has been deactivated, there is still however an immediate serious threat to the station. Security may have weapons unholstered at all times, random searches are allowed and advised." - var/alert_desc_delta = "The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill." + var/static/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." + var/static/alert_desc_yellow_upto = "A minor security emergency has developed. Security personnel are to report to their supervisor for orders and may have weapons visible on their person. Privacy laws are still enforced." + var/static/alert_desc_yellow_downto = "Code yellow procedures are now in effect. Security personnel are to report to their supervisor for orders and may have weapons visible on their person. Privacy laws are still enforced." + var/static/alert_desc_violet_upto = "A major medical emergency has developed. Medical personnel are required to report to their supervisor for orders, and non-medical personnel are required to obey all relevant instructions from medical staff." + var/static/alert_desc_violet_downto = "Code violet procedures are now in effect; Medical personnel are required to report to their supervisor for orders, and non-medical personnel are required to obey relevant instructions from medical staff." + var/static/alert_desc_orange_upto = "A major engineering emergency has developed. Engineering personnel are required to report to their supervisor for orders, and non-engineering personnel are required to evacuate any affected areas and obey relevant instructions from engineering staff." + var/static/alert_desc_orange_downto = "Code orange procedures are now in effect; Engineering personnel are required to report to their supervisor for orders, and non-engineering personnel are required to evacuate any affected areas and obey relevant instructions from engineering staff." + var/static/alert_desc_blue_upto = "A major security emergency has developed. Security personnel are to report to their supervisor for orders, are permitted to search staff and facilities, and may have weapons visible on their person." + var/static/alert_desc_blue_downto = "Code blue procedures are now in effect. Security personnel are to report to their supervisor for orders, are permitted to search staff and facilities, and may have weapons visible on their person." + var/static/alert_desc_red_upto = "There is an immediate serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised." + var/static/alert_desc_red_downto = "The self-destruct mechanism has been deactivated, there is still however an immediate serious threat to the station. Security may have weapons unholstered at all times, random searches are allowed and advised." + var/static/alert_desc_delta = "The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill." - var/forbid_singulo_possession = 0 + var/static/forbid_singulo_possession = 0 //game_options.txt configs - var/health_threshold_softcrit = 0 - var/health_threshold_crit = 0 - var/health_threshold_dead = -100 + var/static/health_threshold_softcrit = 0 + var/static/health_threshold_crit = 0 + var/static/health_threshold_dead = -100 - var/organ_health_multiplier = 1 - var/organ_regeneration_multiplier = 1 - var/organs_decay - var/default_brain_health = 400 - var/allow_headgibs = FALSE + var/static/organ_health_multiplier = 1 + var/static/organ_regeneration_multiplier = 1 + var/static/organs_decay + var/static/default_brain_health = 400 + var/static/allow_headgibs = FALSE //Paincrit knocks someone down once they hit 60 shock_stage, so by default make it so that close to 100 additional damage needs to be dealt, //so that it's similar to HALLOSS. Lowered it a bit since hitting paincrit takes much longer to wear off than a halloss stun. - var/organ_damage_spillover_multiplier = 0.5 + var/static/organ_damage_spillover_multiplier = 0.5 - var/bones_can_break = 0 - var/limbs_can_break = 0 + var/static/bones_can_break = 0 + var/static/limbs_can_break = 0 - var/revival_pod_plants = 1 - var/revival_cloning = 1 - var/revival_brain_life = -1 + var/static/revival_pod_plants = 1 + var/static/revival_cloning = 1 + var/static/revival_brain_life = -1 - var/use_loyalty_implants = 0 + var/static/use_loyalty_implants = 0 - var/welder_vision = 1 - var/generate_map = 0 - var/no_click_cooldown = 0 + var/static/welder_vision = 1 + var/static/generate_map = 0 + var/static/no_click_cooldown = 0 //Used for modifying movement speed for mobs. //Unversal modifiers - var/run_speed = 0 - var/walk_speed = 0 + var/static/run_speed = 0 + var/static/walk_speed = 0 //Mob specific modifiers. NOTE: These will affect different mob types in different ways - var/human_delay = 0 - var/robot_delay = 0 - var/monkey_delay = 0 - var/alien_delay = 0 - var/slime_delay = 0 - var/animal_delay = 0 + var/static/human_delay = 0 + var/static/robot_delay = 0 + var/static/monkey_delay = 0 + var/static/alien_delay = 0 + var/static/slime_delay = 0 + var/static/animal_delay = 0 - var/footstep_volume = 0 + var/static/footstep_volume = 0 - var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt - var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt - var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database - var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database + var/static/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt + var/static/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt + var/static/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database + var/static/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database - var/simultaneous_pm_warning_timeout = 100 + var/static/simultaneous_pm_warning_timeout = 100 - var/use_recursive_explosions //Defines whether the server uses recursive or circular explosions. - var/multi_z_explosion_scalar = 0.5 //Multiplier for how much weaker explosions are on neighboring z levels. + var/static/use_recursive_explosions //Defines whether the server uses recursive or circular explosions. + var/static/multi_z_explosion_scalar = 0.5 //Multiplier for how much weaker explosions are on neighboring z levels. - var/assistant_maint = 0 //Do assistants get maint access? - var/gateway_delay = 18000 //How long the gateway takes before it activates. Default is half an hour. - var/ghost_interaction = 0 + var/static/assistant_maint = 0 //Do assistants get maint access? + var/static/gateway_delay = 18000 //How long the gateway takes before it activates. Default is half an hour. + var/static/ghost_interaction = 0 - var/comms_password = "" + var/static/comms_password = "" - var/enter_allowed = 1 + var/static/enter_allowed = 1 - var/use_irc_bot = 0 - var/use_node_bot = 0 - var/irc_bot_port = 0 - var/irc_bot_host = "" - var/irc_bot_export = 0 // whether the IRC bot in use is a Bot32 (or similar) instance; Bot32 uses world.Export() instead of nudge.py/libnudge - var/main_irc = "" - var/admin_irc = "" - var/python_path = "" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix - var/use_lib_nudge = 0 //Use the C library nudge instead of the python nudge. - var/use_overmap = 0 + var/static/use_irc_bot = 0 + var/static/use_node_bot = 0 + var/static/irc_bot_port = 0 + var/static/irc_bot_host = "" + var/static/irc_bot_export = 0 // whether the IRC bot in use is a Bot32 (or similar) instance; Bot32 uses world.Export() instead of nudge.py/libnudge + var/static/main_irc = "" + var/static/admin_irc = "" + var/static/python_path = "" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix + var/static/use_lib_nudge = 0 //Use the C library nudge instead of the python nudge. + var/static/use_overmap = 0 // Event settings - var/expected_round_length = 3 * 60 * 60 * 10 // 3 hours + var/static/expected_round_length = 3 * 60 * 60 * 10 // 3 hours // If the first delay has a custom start time // No custom time, no custom time, between 80 to 100 minutes respectively. - var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) + var/static/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) // The lowest delay until next event // 10, 30, 50 minutes respectively - var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) + var/static/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) // The upper delay until next event // 15, 45, 70 minutes respectively - var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) + var/static/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) - var/aliens_allowed = 0 - var/ninjas_allowed = 0 - var/abandon_allowed = 1 - var/ooc_allowed = 1 - var/looc_allowed = 1 - var/dooc_allowed = 1 - var/dsay_allowed = 1 + var/static/aliens_allowed = 0 + var/static/ninjas_allowed = 0 + var/static/abandon_allowed = 1 + var/static/ooc_allowed = 1 + var/static/looc_allowed = 1 + var/static/dooc_allowed = 1 + var/static/dsay_allowed = 1 - var/starlight = 0 // Whether space turfs have ambient light or not + var/static/starlight = 0 // Whether space turfs have ambient light or not - var/list/ert_species = list(SPECIES_HUMAN) + var/static/list/ert_species = list(SPECIES_HUMAN) - var/law_zero = "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4'ALL LAWS OVERRIDDEN#*?&110010" + var/static/law_zero = "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4'ALL LAWS OVERRIDDEN#*?&110010" - var/aggressive_changelog = 0 + var/static/aggressive_changelog = 0 - var/list/language_prefixes = list(",","#")//Default language prefixes + var/static/list/language_prefixes = list(",","#")//Default language prefixes - var/show_human_death_message = 1 + var/static/show_human_death_message = 1 - var/radiation_resistance_calc_mode = RAD_RESIST_CALC_SUB // 0:1 subtraction:division for computing effective radiation on a turf - var/radiation_decay_rate = 1 //How much radiation is reduced by each tick - var/radiation_resistance_multiplier = 8.5 //VOREstation edit - var/radiation_material_resistance_divisor = 1 - var/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it. + var/static/radiation_resistance_calc_mode = RAD_RESIST_CALC_SUB // 0:1 subtraction:division for computing effective radiation on a turf + var/static/radiation_decay_rate = 1 //How much radiation is reduced by each tick + var/static/radiation_resistance_multiplier = 8.5 //VOREstation edit + var/static/radiation_material_resistance_divisor = 1 + var/static/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it. - var/random_submap_orientation = FALSE // If true, submaps loaded automatically can be rotated. - var/autostart_solars = FALSE // If true, specifically mapped in solar control computers will set themselves up when the round starts. + var/static/random_submap_orientation = FALSE // If true, submaps loaded automatically can be rotated. + var/static/autostart_solars = FALSE // If true, specifically mapped in solar control computers will set themselves up when the round starts. + + // New shiny SQLite stuff. + // The basics. + var/static/sqlite_enabled = FALSE // If it should even be active. SQLite can be ran alongside other databases but you should not have them do the same functions. + + // In-Game Feedback. + var/static/sqlite_feedback = FALSE // Feedback cannot be submitted if this is false. + var/static/list/sqlite_feedback_topics = list("General") // A list of 'topics' that feedback can be catagorized under by the submitter. + var/static/sqlite_feedback_privacy = FALSE // If true, feedback submitted can have its author name be obfuscated. This is not 100% foolproof (it's md5 ffs) but can stop casual snooping. + var/static/sqlite_feedback_cooldown = 0 // How long one must wait, in days, to submit another feedback form. Used to help prevent spam, especially with privacy active. 0 = No limit. + var/static/sqlite_feedback_min_age = 0 // Used to block new people from giving feedback. This metric is very bad but it can help slow down spammers. + + var/static/defib_timer = 10 // How long until someone can't be defibbed anymore, in minutes. + var/static/defib_braindamage_timer = 2 // How long until someone will get brain damage when defibbed, in minutes. The closer to the end of the above timer, the more brain damage they get. + + // disables the annoying "You have already logged in this round, disconnect or be banned" popup for multikeying, because it annoys the shit out of me when testing. + var/static/disable_cid_warn_popup = FALSE /datum/configuration/New() var/list/L = typesof(/datum/game_mode) - /datum/game_mode @@ -376,6 +395,9 @@ var/list/gamemode_cache = list() if ("log_runtime") config.log_runtime = 1 + if ("log_graffiti") + config.log_graffiti = 1 + if ("generate_map") config.generate_map = 1 @@ -406,6 +428,9 @@ var/list/gamemode_cache = list() if ("default_no_vote") config.vote_no_default = 1 + if ("pregame_time") + config.pregame_time = text2num(value) + if ("vote_delay") config.vote_delay = text2num(value) @@ -841,7 +866,31 @@ var/list/gamemode_cache = list() if("autostart_solars") config.autostart_solars = TRUE + if("sqlite_enabled") + config.sqlite_enabled = TRUE + if("sqlite_feedback") + config.sqlite_feedback = TRUE + + if("sqlite_feedback_topics") + config.sqlite_feedback_topics = splittext(value, ";") + if(!config.sqlite_feedback_topics.len) + config.sqlite_feedback_topics += "General" + + if("sqlite_feedback_privacy") + config.sqlite_feedback_privacy = TRUE + + if("sqlite_feedback_cooldown") + config.sqlite_feedback_cooldown = text2num(value) + + if("defib_timer") + config.defib_timer = text2num(value) + + if("defib_braindamage_timer") + config.defib_braindamage_timer = text2num(value) + + if("disable_cid_warn_popup") + config.disable_cid_warn_popup = TRUE else log_misc("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/configuration_vr.dm b/code/controllers/configuration_vr.dm index cde9c7c963..ad839a5730 100644 --- a/code/controllers/configuration_vr.dm +++ b/code/controllers/configuration_vr.dm @@ -3,13 +3,14 @@ // /datum/configuration - var/list/engine_map // Comma separated list of engines to choose from. Blank means fully random. - var/time_off = FALSE - var/pto_job_change = FALSE - var/limit_interns = -1 //Unlimited by default - var/limit_visitors = -1 //Unlimited by default - var/pto_cap = 100 //Hours - var/require_flavor = FALSE + var/static/list/engine_map // Comma separated list of engines to choose from. Blank means fully random. + var/static/time_off = FALSE + var/static/pto_job_change = FALSE + var/static/limit_interns = -1 //Unlimited by default + var/static/limit_visitors = -1 //Unlimited by default + var/static/pto_cap = 100 //Hours + var/static/require_flavor = FALSE + var/static/ipqualityscore_apikey //API key for ipqualityscore.com /hook/startup/proc/read_vs_config() var/list/Lines = file2list("config/config.txt") @@ -58,4 +59,6 @@ config.pto_job_change = TRUE if ("require_flavor") config.require_flavor = TRUE + if ("ipqualityscore_apikey") + config.ipqualityscore_apikey = value return 1 diff --git a/code/controllers/emergency_shuttle_controller.dm b/code/controllers/emergency_shuttle_controller.dm index 2229c4febc..d6e272f9c9 100644 --- a/code/controllers/emergency_shuttle_controller.dm +++ b/code/controllers/emergency_shuttle_controller.dm @@ -2,10 +2,10 @@ // Controls the emergency shuttle -var/global/datum/emergency_shuttle_controller/emergency_shuttle +var/global/datum/emergency_shuttle_controller/emergency_shuttle = new /datum/emergency_shuttle_controller - var/datum/shuttle/ferry/emergency/shuttle + var/datum/shuttle/autodock/ferry/emergency/shuttle // Set in shuttle_emergency.dm TODO - is it really? var/list/escape_pods var/launch_time //the time at which the shuttle will be launched @@ -36,8 +36,8 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle if (!shuttle.location) //leaving from the station //launch the pods! for (var/EP in escape_pods) - var/datum/shuttle/ferry/escape_pod/pod - if(istype(escape_pods[EP], /datum/shuttle/ferry/escape_pod)) + var/datum/shuttle/autodock/ferry/escape_pod/pod + if(istype(escape_pods[EP], /datum/shuttle/autodock/ferry/escape_pod)) pod = escape_pods[EP] else continue @@ -63,8 +63,8 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle //arm the escape pods if (evac) for (var/EP in escape_pods) - var/datum/shuttle/ferry/escape_pod/pod - if(istype(escape_pods[EP], /datum/shuttle/ferry/escape_pod)) + var/datum/shuttle/autodock/ferry/escape_pod/pod + if(istype(escape_pods[EP], /datum/shuttle/autodock/ferry/escape_pod)) pod = escape_pods[EP] else continue @@ -75,8 +75,10 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle /datum/emergency_shuttle_controller/proc/set_launch_countdown(var/seconds) wait_for_launch = 1 launch_time = world.time + seconds*10 + START_PROCESSING(SSprocessing, src) /datum/emergency_shuttle_controller/proc/stop_launch_countdown() + STOP_PROCESSING(SSprocessing, src) wait_for_launch = 0 //calls the shuttle for an emergency evacuation @@ -94,7 +96,7 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle evac = 1 emergency_shuttle_called.Announce(replacetext(using_map.emergency_shuttle_called_message, "%ETA%", "[estimated_time] minute\s")) - for(var/area/A in all_areas) + for(var/area/A in world) if(istype(A, /area/hallway)) A.readyalert() @@ -120,13 +122,13 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle /datum/emergency_shuttle_controller/proc/recall() if (!can_recall()) return - wait_for_launch = 0 + stop_launch_countdown() shuttle.cancel_launch(src) if (evac) emergency_shuttle_recalled.Announce(using_map.emergency_shuttle_recall_message) - for(var/area/A in all_areas) + for(var/area/A in world) if(istype(A, /area/hallway)) A.readyreset() evac = 0 @@ -215,11 +217,11 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle //returns 1 if the shuttle is currently in transit (or just leaving) to the station /datum/emergency_shuttle_controller/proc/going_to_station() - return (!shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE) + return shuttle && (!shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE) //returns 1 if the shuttle is currently in transit (or just leaving) to centcom /datum/emergency_shuttle_controller/proc/going_to_centcom() - return (shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE) + return shuttle && (shuttle.direction && shuttle.moving_status != SHUTTLE_IDLE) /datum/emergency_shuttle_controller/proc/get_status_panel_eta() diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 0589d5fa7f..254472a2c8 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -52,6 +52,7 @@ var/datum/controller/failsafe/Failsafe if(Master.processing && Master.iteration) // Check if processing is done yet. if(Master.iteration == master_iteration) + log_debug("DEFCON [defcon]: Master.iteration=[Master.iteration] Master.last_run=[Master.last_run] world.time=[world.time]") switch(defcon) if(4,5) --defcon @@ -71,7 +72,7 @@ var/datum/controller/failsafe/Failsafe master_iteration = 0 to_chat(admins, "MC restarted successfully") else if(rtn < 0) - log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") + log_world("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") to_chat(admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again //no need to handle that specially when defcon 0 can handle it diff --git a/code/controllers/master.dm b/code/controllers/master.dm index fad4ef9692..2212291b9e 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -198,7 +198,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new #else world.sleep_offline = 1 #endif - world.fps = config.fps + world.change_fps(config.fps) var/initialized_tod = REALTIMEOFDAY sleep(1) initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10 @@ -223,10 +223,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new //loop ended, restart the mc log_game("MC crashed or runtimed, restarting") message_admins("MC crashed or runtimed, restarting") + log_world("MC crashed or runtimed, restarting") var/rtn2 = Recreate_MC() if (rtn2 <= 0) log_game("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now") message_admins("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now") + log_world("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now") Failsafe.defcon = 2 // Main loop. @@ -338,6 +340,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new subsystems_to_check = tickersubsystems if (CheckQueue(subsystems_to_check) <= 0) + log_world("MC: CheckQueue(subsystems_to_check) exited uncleanly, SoftReset (error_level=[error_level]") if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) log_world("MC: SoftReset() failed, crashing") return @@ -350,6 +353,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if (queue_head) if (RunQueue() <= 0) + log_world("MC: RunQueue() exited uncleanly, running SoftReset (error_level=[error_level]") if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) log_world("MC: SoftReset() failed, crashing") return diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 8c0b6fa5de..7115c77a61 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -168,11 +168,12 @@ statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - - if(can_fire && !(SS_NO_FIRE & flags)) - msg = "[round(cost,1)]ms|[round(tick_usage,1)]%([round(tick_overrun,1)]%)|[round(ticks,0.1)]\t[msg]" - else + if(SS_NO_FIRE & flags) + msg = "NO FIRE\t[msg]" + else if(can_fire <= 0) msg = "OFFLINE\t[msg]" + else + msg = "[round(cost,1)]ms|[round(tick_usage,1)]%([round(tick_overrun,1)]%)|[round(ticks,0.1)]\t[msg]" var/title = name if (can_fire) @@ -202,3 +203,15 @@ //usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash) //should attempt to salvage what it can from the old instance of subsystem /datum/controller/subsystem/Recover() + +// Suspends this subsystem from being queued for running. If already in the queue, sleeps until idle. Returns FALSE if the subsystem was already suspended. +/datum/controller/subsystem/proc/suspend() + . = (can_fire > 0) // Return true if we were previously runnable, false if previously suspended. + can_fire = FALSE + // Safely sleep in a loop until the subsystem is idle, (or its been un-suspended somehow) + while(can_fire <= 0 && state != SS_IDLE) + stoplag() // Safely sleep in a loop until + +// Wakes a suspended subsystem. +/datum/controller/subsystem/proc/wake() + can_fire = TRUE diff --git a/code/controllers/subsystems/ai.dm b/code/controllers/subsystems/ai.dm index 8977818755..a111b83e43 100644 --- a/code/controllers/subsystems/ai.dm +++ b/code/controllers/subsystems/ai.dm @@ -25,7 +25,7 @@ SUBSYSTEM_DEF(ai) // var/mob/living/L = currentrun[currentrun.len] var/datum/ai_holder/A = currentrun[currentrun.len] --currentrun.len - if(!A || QDELETED(A)) // Doesn't exist or won't exist soon. + if(!A || QDELETED(A) || A.busy) // Doesn't exist or won't exist soon or not doing it this tick continue if(times_fired % 4 == 0 && A.holder.stat != DEAD) A.handle_strategicals() diff --git a/code/controllers/subsystems/air.dm b/code/controllers/subsystems/air.dm index 263e290968..40d2cc3133 100644 --- a/code/controllers/subsystems/air.dm +++ b/code/controllers/subsystems/air.dm @@ -38,7 +38,7 @@ SUBSYSTEM_DEF(air) current_cycle = 0 var/simulated_turf_count = 0 - for(var/turf/simulated/S in turfs) + for(var/turf/simulated/S in world) simulated_turf_count++ S.update_air_properties() CHECK_TICK diff --git a/code/controllers/subsystems/alarm.dm b/code/controllers/subsystems/alarm.dm new file mode 100644 index 0000000000..4b6140e507 --- /dev/null +++ b/code/controllers/subsystems/alarm.dm @@ -0,0 +1,45 @@ + +// We manually initialize the alarm handlers instead of looping over all existing types +// to make it possible to write: camera_alarm.triggerAlarm() rather than SSalarm.managers[datum/alarm_handler/camera].triggerAlarm() or a variant thereof. +/var/global/datum/alarm_handler/atmosphere/atmosphere_alarm = new() +/var/global/datum/alarm_handler/camera/camera_alarm = new() +/var/global/datum/alarm_handler/fire/fire_alarm = new() +/var/global/datum/alarm_handler/motion/motion_alarm = new() +/var/global/datum/alarm_handler/power/power_alarm = new() + +SUBSYSTEM_DEF(alarm) + name = "Alarm" + wait = 2 SECONDS + priority = FIRE_PRIORITY_ALARM + init_order = INIT_ORDER_ALARM + var/list/datum/alarm/all_handlers + var/tmp/list/currentrun = null + var/static/list/active_alarm_cache = list() + +/datum/controller/subsystem/alarm/Initialize() + all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) + . = ..() + +/datum/controller/subsystem/alarm/fire(resumed = FALSE) + if(!resumed) + src.currentrun = all_handlers.Copy() + active_alarm_cache.Cut() + + var/list/currentrun = src.currentrun // Cache for sanic speed + while (currentrun.len) + var/datum/alarm_handler/AH = currentrun[currentrun.len] + currentrun.len-- + AH.process() + active_alarm_cache += AH.alarms + + if (MC_TICK_CHECK) + return + +/datum/controller/subsystem/alarm/proc/active_alarms() + return active_alarm_cache.Copy() + +/datum/controller/subsystem/alarm/proc/number_of_active_alarms() + return active_alarm_cache.len + +/datum/controller/subsystem/alarm/stat_entry() + ..("[number_of_active_alarms()] alarm\s") diff --git a/code/controllers/subsystems/assets.dm b/code/controllers/subsystems/assets.dm new file mode 100644 index 0000000000..cd531db614 --- /dev/null +++ b/code/controllers/subsystems/assets.dm @@ -0,0 +1,17 @@ +SUBSYSTEM_DEF(assets) + name = "Assets" + init_order = INIT_ORDER_ASSETS + flags = SS_NO_FIRE + var/list/cache = list() + var/list/preload = list() + +/datum/controller/subsystem/assets/Initialize(timeofday) + for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple)) + var/datum/asset/A = new type() + A.register() + + preload = cache.Copy() //don't preload assets generated during the round + + for(var/client/C in GLOB.clients) + addtimer(CALLBACK(GLOBAL_PROC, .proc/getFilesSlow, C, preload, FALSE), 10) + return ..() \ No newline at end of file diff --git a/code/controllers/subsystems/atoms.dm b/code/controllers/subsystems/atoms.dm index 60fccf6dd3..4db490914a 100644 --- a/code/controllers/subsystems/atoms.dm +++ b/code/controllers/subsystems/atoms.dm @@ -8,9 +8,9 @@ SUBSYSTEM_DEF(atoms) init_order = INIT_ORDER_ATOMS flags = SS_NO_FIRE - var/initialized = INITIALIZATION_INSSATOMS + var/static/initialized = INITIALIZATION_INSSATOMS // var/list/created_atoms // This is never used, so don't bother. ~Leshana - var/old_initialized + var/static/old_initialized var/list/late_loaders var/list/created_atoms diff --git a/code/controllers/subsystems/bellies_vr.dm b/code/controllers/subsystems/bellies_vr.dm deleted file mode 100644 index faaa297ca3..0000000000 --- a/code/controllers/subsystems/bellies_vr.dm +++ /dev/null @@ -1,41 +0,0 @@ -#define SSBELLIES_PROCESSED 1 -#define SSBELLIES_IGNORED 2 - -// -// Bellies subsystem - Process vore bellies -// - -SUBSYSTEM_DEF(bellies) - name = "Bellies" - priority = 5 - wait = 1 SECONDS - flags = SS_KEEP_TIMING|SS_NO_INIT - runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME - - var/static/list/belly_list = list() - var/list/currentrun = list() - var/ignored_bellies = 0 - -/datum/controller/subsystem/bellies/stat_entry() - ..("#: [belly_list.len] | P: [ignored_bellies]") - -/datum/controller/subsystem/bellies/fire(resumed = 0) - if (!resumed) - ignored_bellies = 0 - src.currentrun = belly_list.Copy() - - //cache for sanic speed (lists are references anyways) - var/list/currentrun = src.currentrun - var/times_fired = src.times_fired - while(currentrun.len) - var/obj/belly/B = currentrun[currentrun.len] - currentrun.len-- - - if(QDELETED(B)) - belly_list -= B - else - if(B.process_belly(times_fired,wait) == SSBELLIES_IGNORED) - ignored_bellies++ - - if (MC_TICK_CHECK) - return diff --git a/code/controllers/subsystems/chat.dm b/code/controllers/subsystems/chat.dm new file mode 100644 index 0000000000..3afbcae374 --- /dev/null +++ b/code/controllers/subsystems/chat.dm @@ -0,0 +1,87 @@ +SUBSYSTEM_DEF(chat) + name = "Chat" + flags = SS_TICKER + wait = 1 // SS_TICKER means this runs every tick + priority = FIRE_PRIORITY_CHAT + init_order = INIT_ORDER_CHAT + + var/list/msg_queue = list() + +/datum/controller/subsystem/chat/Initialize(timeofday) + init_vchat() + ..() + +/datum/controller/subsystem/chat/fire() + var/list/msg_queue = src.msg_queue // Local variable for sanic speed. + for(var/i in msg_queue) + var/client/C = i + var/list/messages = msg_queue[C] + msg_queue -= C + if (C) + C << output(jsEncode(messages), "htmloutput:putmessage") + + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/chat/stat_entry() + ..("C:[msg_queue.len]") + +/datum/controller/subsystem/chat/proc/queue(target, time, message, handle_whitespace = TRUE) + if(!target || !message) + return + + if(!istext(message)) + stack_trace("to_chat called with invalid input type") + return + + // Currently to_chat(world, ...) gets sent individually to each client. Consider. + if(target == world) + target = GLOB.clients + + //Some macros remain in the string even after parsing and fuck up the eventual output + var/original_message = message + message = replacetext(message, "\n", "
") + message = replacetext(message, "\improper", "") + message = replacetext(message, "\proper", "") + + if(isnull(time)) + time = world.time + + var/list/messageStruct = list("time" = time, "message" = message); + + if(islist(target)) + for(var/I in target) + var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible + + if(!C) + return + + if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file. + //Send it to the old style output window. + DIRECT_OUTPUT(C, original_message) + continue + + // // Client still loading, put their messages in a queue - Actually don't, logged already in database. + // if(!C.chatOutput.loaded && C.chatOutput.message_queue && islist(C.chatOutput.message_queue)) + // C.chatOutput.message_queue[++C.chatOutput.message_queue.len] = messageStruct + // continue + + LAZYINITLIST(msg_queue[C]) + msg_queue[C][++msg_queue[C].len] = messageStruct + else + var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible + + if(!C) + return + + if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file. + DIRECT_OUTPUT(C, original_message) + return + + // // Client still loading, put their messages in a queue - Actually don't, logged already in database. + // if(!C.chatOutput.loaded && C.chatOutput.message_queue && islist(C.chatOutput.message_queue)) + // C.chatOutput.message_queue[++C.chatOutput.message_queue.len] = messageStruct + // return + + LAZYINITLIST(msg_queue[C]) + msg_queue[C][++msg_queue[C].len] = messageStruct diff --git a/code/controllers/subsystems/events.dm b/code/controllers/subsystems/events.dm index 8b8bc1d8b0..226c09f492 100644 --- a/code/controllers/subsystems/events.dm +++ b/code/controllers/subsystems/events.dm @@ -1,6 +1,8 @@ SUBSYSTEM_DEF(events) - name = "Events" - wait = 20 + name = "Events" // VOREStation Edit - This is still the main events subsystem for us. + wait = 2 SECONDS + + var/tmp/list/currentrun = null var/list/datum/event/active_events = list() var/list/datum/event/finished_events = list() @@ -11,23 +13,37 @@ SUBSYSTEM_DEF(events) var/datum/event_meta/new_event = new /datum/controller/subsystem/events/Initialize() + allEvents = typesof(/datum/event) - /datum/event event_containers = list( EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane, EVENT_LEVEL_MODERATE = new/datum/event_container/moderate, EVENT_LEVEL_MAJOR = new/datum/event_container/major ) - allEvents = typesof(/datum/event) - /datum/event + if(global.using_map.use_overmap) + GLOB.overmap_event_handler.create_events(global.using_map.overmap_z, global.using_map.overmap_size, global.using_map.overmap_event_areas) return ..() /datum/controller/subsystem/events/fire(resumed) - for(var/datum/event/E in active_events) + if (!resumed) + src.currentrun = active_events.Copy() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while (currentrun.len) + var/datum/event/E = currentrun[currentrun.len] + currentrun.len-- if(E.processing_active) E.process() + if (MC_TICK_CHECK) + return for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) var/list/datum/event_container/EC = event_containers[i] EC.process() +/datum/controller/subsystem/events/stat_entry() + ..("E:[active_events.len]") + /datum/controller/subsystem/events/Recover() if(SSevents.active_events) active_events |= SSevents.active_events @@ -35,6 +51,8 @@ SUBSYSTEM_DEF(events) finished_events |= SSevents.finished_events /datum/controller/subsystem/events/proc/event_complete(var/datum/event/E) + active_events -= E + if(!E.event_meta || !E.severity) // datum/event is used here and there for random reasons, maintaining "backwards compatibility" log_debug("Event of '[E.type]' with missing meta-data has completed.") return @@ -50,7 +68,7 @@ SUBSYSTEM_DEF(events) log_debug("Event '[EM.name]' has completed at [stationtime2text()].") /datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay) - var/list/datum/event_container/EC = event_containers[severity] + var/datum/event_container/EC = event_containers[severity] EC.next_event_time += delay /datum/controller/subsystem/events/proc/RoundEnd() diff --git a/code/controllers/subsystems/events2.dm b/code/controllers/subsystems/events2.dm new file mode 100644 index 0000000000..2a73498b1d --- /dev/null +++ b/code/controllers/subsystems/events2.dm @@ -0,0 +1,37 @@ +// This is a simple ticker for the new event system. +// The logic that determines what events get chosen is held inside a seperate subsystem. + +SUBSYSTEM_DEF(event_ticker) + name = "Events (Ticker)" + wait = 2 SECONDS + runlevels = RUNLEVEL_GAME + + // List of `/datum/event2/event`s that are currently active, and receiving process() ticks. + var/list/active_events = list() + + // List of `/datum/event2/event`s that finished, and are here for showing at roundend, if that's desired. + var/list/finished_events = list() + +// Process active events. +/datum/controller/subsystem/event_ticker/fire(resumed) + for(var/E in active_events) + var/datum/event2/event/event = E + event.process() + if(event.finished) + event_finished(event) + +// Starts an event, independent of the GM system. +// This means it will always run, and won't affect the GM system in any way, e.g. not putting the event off limits after one use. +/datum/controller/subsystem/event_ticker/proc/start_event(event_type) + var/datum/event2/event/E = new event_type() + E.execute() + event_started(E) + +/datum/controller/subsystem/event_ticker/proc/event_started(datum/event2/event/E) + log_debug("Event [E.type] is now being ran.") + active_events += E + +/datum/controller/subsystem/event_ticker/proc/event_finished(datum/event2/event/E) + log_debug("Event [E.type] has finished.") + active_events -= E + finished_events += E \ No newline at end of file diff --git a/code/controllers/subsystems/game_master.dm b/code/controllers/subsystems/game_master.dm new file mode 100644 index 0000000000..d5326bfba6 --- /dev/null +++ b/code/controllers/subsystems/game_master.dm @@ -0,0 +1,369 @@ +// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the +// previous ones, checking various things like player count, department size and composition, individual player activity, +// individual player (IC) skill, and such, in order to try to choose the best events to take in order to add spice or variety to +// the round. + +// This subsystem holds the logic that chooses events. Actual event processing is handled in a seperate subsystem. +SUBSYSTEM_DEF(game_master) + name = "Events (Game Master)" + wait = 1 MINUTE + runlevels = RUNLEVEL_GAME + + // The GM object is what actually chooses events. + // It's held in a seperate object for better encapsulation, and allows for different 'flavors' of GMs to be made, that choose events differently. + var/datum/game_master/GM = null + var/game_master_type = /datum/game_master/default + + var/list/available_events = list() // A list of meta event objects. + + var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back. + var/staleness = -20 // Determines liklihood of the GM doing something, increases over time. + + var/next_event = 0 // Minimum amount of time of nothingness until the GM can pick something again. + + var/debug_messages = FALSE // If true, debug information is written to `log_debug()`. + +/datum/controller/subsystem/game_master/Initialize() + var/list/subtypes = subtypesof(/datum/event2/meta) + for(var/T in subtypes) + var/datum/event2/meta/M = new T() + if(!M.name) + continue + available_events += M + + GM = new game_master_type() + + if(config && !config.enable_game_master) + can_fire = FALSE + + return ..() + +/datum/controller/subsystem/game_master/fire(resumed) + adjust_staleness(1) + adjust_danger(-1) + + var/global_afk = metric.assess_all_living_mobs() + global_afk = abs(global_afk - 100) + global_afk = round(global_afk / 100, 0.1) + adjust_staleness(global_afk) // Staleness increases faster if more people are less active. + + if(GM.ignore_time_restrictions || next_event < world.time) + if(prob(staleness) && pre_event_checks()) + do_event_decision() + + +/datum/controller/subsystem/game_master/proc/do_event_decision() + log_game_master("Going to choose an event.") + var/datum/event2/meta/event_picked = GM.choose_event() + if(event_picked) + run_event(event_picked) + next_event = world.time + rand(GM.decision_cooldown_lower_bound, GM.decision_cooldown_upper_bound) + +/datum/controller/subsystem/game_master/proc/debug_gm() + can_fire = TRUE + staleness = 100 + debug_messages = TRUE + +/datum/controller/subsystem/game_master/proc/run_event(datum/event2/meta/chosen_event) + var/datum/event2/event/E = chosen_event.make_event() + + chosen_event.times_ran++ + + if(!chosen_event.reusable) + // Disable this event, so it only gets picked once. + chosen_event.enabled = FALSE + if(chosen_event.event_class) + // Disable similar events, too. + for(var/M in available_events) + var/datum/event2/meta/meta = M + if(meta.event_class == chosen_event.event_class) + meta.enabled = FALSE + + SSevent_ticker.event_started(E) + adjust_danger(chosen_event.chaos) + adjust_staleness(-(10 + chosen_event.chaos)) // More chaotic events reduce staleness more, e.g. a 25 chaos event will reduce it by 35. + + +// Tell the game master that something dangerous happened, e.g. someone dying, station explosions. +/datum/controller/subsystem/game_master/proc/adjust_danger(amount) + amount *= GM.danger_modifier + danger = round(between(0, danger + amount, 1000), 0.1) + +// Tell the game master that things are getting boring if positive, or something interesting if negative.. +/datum/controller/subsystem/game_master/proc/adjust_staleness(amount) + amount *= GM.staleness_modifier + staleness = round( between(-20, staleness + amount, 100), 0.1) + +// These are ran before committing to an event. +// Returns TRUE if the system is allowed to procede, otherwise returns FALSE. +/datum/controller/subsystem/game_master/proc/pre_event_checks(quiet = FALSE) + if(!ticker || ticker.current_state != GAME_STATE_PLAYING) + if(!quiet) + log_game_master("Unable to start event: Ticker is nonexistant, or the game is not ongoing.") + return FALSE + if(GM.ignore_time_restrictions) + return TRUE + if(next_event > world.time) // Sanity. + if(!quiet) + log_game_master("Unable to start event: Time until next event is approximately [round((next_event - world.time) / (1 MINUTE))] minute(s)") + return FALSE + + // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. + var/mills = round_duration_in_ticks + var/mins = round((mills % 36000) / 600) + var/hours = round(mills / 36000) + +// if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round. +// if(!quiet) +// log_debug("Game Master unable to start event: It is too early.") +// return FALSE + if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well. + if(!quiet) + log_game_master("Unable to start event: It is too late.") + return FALSE + return TRUE + +/datum/controller/subsystem/game_master/proc/choose_game_master(mob/user) + var/list/subtypes = subtypesof(/datum/game_master) + var/new_gm_path = input(user, "What kind of Game Master do you want?", "New Game Master", /datum/game_master/default) as null|anything in subtypes + if(new_gm_path) + log_and_message_admins("has swapped the current GM ([GM.type]) for a new GM ([new_gm_path]).") + GM = new new_gm_path(src) + +/datum/controller/subsystem/game_master/proc/log_game_master(message) + if(debug_messages) + log_debug("GAME MASTER: [message]") + + +// This object makes the actual decisions. +/datum/game_master + // Multiplier for how much 'danger' is accumulated. Higer generally makes it possible for more dangerous events to be picked. + var/danger_modifier = 1.0 + + // Ditto. Higher numbers generally result in more events occuring in a round. + var/staleness_modifier = 1.0 + + var/decision_cooldown_lower_bound = 5 MINUTES // Lower bound for how long to wait until -the potential- for another event being decided. + var/decision_cooldown_upper_bound = 20 MINUTES // Same, but upper bound. + + var/ignore_time_restrictions = FALSE // Useful for debugging without needing to wait 20 minutes each time. + var/ignore_round_chaos = FALSE // If true, the system will happily choose back to back intense events like meteors and blobs, Dwarf Fortress style. + +/client/proc/show_gm_status() + set category = "Debug" + set name = "Show GM Status" + set desc = "Shows you what the GM is thinking. If only that existed in real life..." + + if(check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + SSgame_master.interact(usr) + else + to_chat(usr, span("warning", "You do not have sufficent rights to view the GM panel, sorry.")) + +/datum/controller/subsystem/game_master/proc/interact(var/client/user) + if(!user) + return + + // Using lists for string tree conservation. + var/list/dat = list("Automated Game Master Event System") + + // Makes the system turn on or off. + dat += href(src, list("toggle" = 1), "\[Toggle GM\]") + dat += " | " + + // Makes the system not care about staleness or being near round-end. + dat += href(src, list("toggle_time_restrictions" = 1), "\[Toggle Time Restrictions\]") + dat += " | " + + // Makes the system not care about how chaotic the round might be. + dat += href(src, list("toggle_chaos_throttle" = 1), "\[Toggle Chaos Throttling\]") + dat += " | " + + // Makes the system immediately choose an event, while still bound to factors like danger, weights, and department staffing. + dat += href(src, list("force_choose_event" = 1), "\[Force Event Decision\]") + dat += "
" + + // Swaps out the current GM for a new one with different ideas on what a good event might be. + dat += href(src, list("change_gm" = 1), "\[Change GM\]") + dat += "
" + + dat += "Current GM Type: [GM.type]
" + dat += "State: [can_fire ? "Active": "Inactive"]
" + dat += "Status: [pre_event_checks(TRUE) ? "Ready" : "Suppressed"]

" + + dat += "Staleness: [staleness] " + dat += href(src, list("set_staleness" = 1), "\[Set\]") + dat += "
" + dat += "Staleness is an estimate of how boring the round might be, and if an event should be done. It is increased passively over time, \ + and increases faster if people are AFK. It deceases when events and certain 'interesting' things happen in the round.
" + + dat += "Danger: [danger] " + dat += href(src, list("set_danger" = 1), "\[Set\]") + dat += "
" + dat += "Danger is an estimate of how chaotic the round has been so far. It is decreased passively over time, and is increased by having \ + certain chaotic events be selected, or chaotic things happen in the round. A sufficently high amount of danger will make the system \ + avoid using destructive events, to avoid pushing the station over the edge.
" + + dat += "

Player Activity:

" + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + + for(var/D in metric.departments) + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + dat += "" + + for(var/P in player_list) + var/mob/M = P + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
CategoryActivity Percentage
All Living Mobs[metric.assess_all_living_mobs()]%
All Ghosts[metric.assess_all_dead_mobs()]%
Departments" + dat += "
[D][metric.assess_department(D)]%
Players" + dat += "
[M] ([M.ckey])[metric.assess_player_activity(M)]%
" + + dat += "

Events available:

" + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/E in available_events) + var/datum/event2/meta/event = E + dat += "" + if(!event.enabled) + dat += "" + else + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
Event NameInvolved DepartmentsChaosChaotic ThresholdWeightButtons
[event.name][event.name][english_list(event.departments)][event.chaos][event.chaotic_threshold][event.get_weight()][href(event, list("force" = 1), "\[Force\]")] [href(event, list("toggle" = 1), "\[Toggle\]")]
" + + dat += "

Events active:

" + + dat += "Current time: [world.time]" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/E in SSevent_ticker.active_events) + var/datum/event2/event/event = E + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
Event TypeTime StartedTime to AnnounceTime to EndAnnouncedStartedEndedButtons
[event.type][event.time_started][event.time_to_announce ? event.time_to_announce : "NULL"][event.time_to_end ? event.time_to_end : "NULL"][event.announced ? "Yes" : "No"][event.started ? "Yes" : "No"][event.ended ? "Yes" : "No"][href(event, list("abort" = 1), "\[Abort\]")]
" + dat += "" + + dat += "

Events completed:

" + + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/E in SSevent_ticker.finished_events) + var/datum/event2/event/event = E + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + dat += "" + + var/datum/browser/popup = new(user, "game_master_debug", "Automated Game Master Event System", 800, 500, src) + popup.set_content(dat.Join()) + popup.open() + + +/datum/controller/subsystem/game_master/Topic(href, href_list) + if(..()) + return + + if(href_list["close"]) // Needed or the window re-opens after closing, making it last forever. + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to modify the Game Master values without sufficent privilages.") + return + + if(href_list["toggle"]) + can_fire = !can_fire + message_admins("GM was [!can_fire ? "dis" : "en"]abled by [usr.key].") + + if(href_list["toggle_time_restrictions"]) + GM.ignore_time_restrictions = !GM.ignore_time_restrictions + message_admins("GM event time restrictions was [GM.ignore_time_restrictions ? "dis" : "en"]abled by [usr.key].") + + if(href_list["toggle_chaos_throttle"]) + GM.ignore_round_chaos = !GM.ignore_round_chaos + message_admins("GM event chaos restrictions was [GM.ignore_round_chaos ? "dis" : "en"]abled by [usr.key].") + + if(href_list["force_choose_event"]) + do_event_decision() + message_admins("[usr.key] forced the Game Master to choose an event immediately.") + + if(href_list["change_gm"]) + choose_game_master(usr) + + if(href_list["set_staleness"]) + var/amount = input(usr, "How much staleness should there be?", "Game Master") as null|num + if(!isnull(amount)) + staleness = amount + message_admins("GM staleness was set to [amount] by [usr.key].") + + if(href_list["set_danger"]) + var/amount = input(usr, "How much danger should there be?", "Game Master") as null|num + if(!isnull(amount)) + danger = amount + message_admins("GM danger was set to [amount] by [usr.key].") + + interact(usr) // To refresh the UI. \ No newline at end of file diff --git a/code/controllers/subsystems/inactivity.dm b/code/controllers/subsystems/inactivity.dm index 1affe905fd..956af04787 100644 --- a/code/controllers/subsystems/inactivity.dm +++ b/code/controllers/subsystems/inactivity.dm @@ -1,42 +1,64 @@ SUBSYSTEM_DEF(inactivity) - name = "AFK Kick" - wait = 600 - flags = SS_BACKGROUND | SS_NO_TICK_CHECK + name = "Inactivity" + wait = 1 MINUTE + flags = SS_NO_INIT | SS_BACKGROUND + var/tmp/list/client_list + var/number_kicked = 0 -/datum/controller/subsystem/inactivity/fire() - if(config.kick_inactive) - for(var/i in GLOB.clients) - var/client/C = i - if(C.is_afk(config.kick_inactive MINUTES) && !C.holder) // VOREStation Edit - Allow admins to idle - to_chat(C,"You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.") - var/information +/datum/controller/subsystem/inactivity/fire(resumed = FALSE) + if (!config.kick_inactive) + can_fire = FALSE + return + if (!resumed) + client_list = GLOB.clients.Copy() - if(C.mob) - if(ishuman(C.mob)) - var/job - var/mob/living/carbon/human/H = C.mob - var/datum/data/record/R = find_general_record("name", H.real_name) - if(R) - job = R.fields["real_rank"] - if(!job && H.mind) - job = H.mind.assigned_role - if(!job && H.job) - job = H.job - if(job) - information = " while [job]." + while(client_list.len) + var/client/C = client_list[client_list.len] + client_list.len-- + if(C.is_afk(config.kick_inactive MINUTES) && can_kick(C)) + to_chat(C, "You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.") - else if(issilicon(C.mob)) - information = " while a silicon." - if(isAI(C.mob)) - var/mob/living/silicon/ai/A = C.mob - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(A.loc) - global_announcer.autosay("[A] has been moved to intelligence storage.", "Artificial Intelligence Oversight") - A.clear_client() - information = " while an AI." + var/information + if(C.mob) + if(ishuman(C.mob)) + var/job + var/mob/living/carbon/human/H = C.mob + var/datum/data/record/R = find_general_record("name", H.real_name) + if(R) + job = R.fields["real_rank"] + if(!job && H.mind) + job = H.mind.assigned_role + if(!job && H.job) + job = H.job + if(job) + information = " while [job]." - var/adminlinks - adminlinks = " (JMP|CRYO)" + else if(isobserver(C.mob)) + information = " while a ghost." - log_and_message_admins("being kicked for AFK[information][adminlinks]", C.mob) + else if(issilicon(C.mob)) + information = " while a silicon." + if(isAI(C.mob)) + var/mob/living/silicon/ai/A = C.mob + empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(A.loc) + global_announcer.autosay("[A] has been moved to intelligence storage.", "Artificial Intelligence Oversight") + A.clear_client() + information = " while an AI." - qdel(C) + var/adminlinks + adminlinks = " (JMP|CRYO)" + + log_and_message_admins("being kicked for AFK[information][adminlinks]", C.mob) + + qdel(C) + number_kicked++ + + if (MC_TICK_CHECK) + return + +/datum/controller/subsystem/inactivity/stat_entry() + ..("Kicked: [number_kicked]") + +/datum/controller/subsystem/inactivity/proc/can_kick(var/client/C) + if(C.holder) return FALSE //VOREStation Add - Don't kick admins. + return TRUE diff --git a/code/controllers/subsystems/job.dm b/code/controllers/subsystems/job.dm new file mode 100644 index 0000000000..f265f39955 --- /dev/null +++ b/code/controllers/subsystems/job.dm @@ -0,0 +1,142 @@ +SUBSYSTEM_DEF(job) + name = "Job" + init_order = INIT_ORDER_JOB + flags = SS_NO_FIRE + + var/list/occupations = list() //List of all jobs + var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles + var/list/type_occupations = list() //Dict of all jobs, keys are types + + var/list/department_datums = list() + var/debug_messages = FALSE + + +/datum/controller/subsystem/job/Initialize(timeofday) + if(!department_datums.len) + setup_departments() + if(!occupations.len) + setup_occupations() + return ..() + +/datum/controller/subsystem/job/proc/setup_occupations(faction = "Station") + occupations = list() + var/list/all_jobs = subtypesof(/datum/job) + if(!all_jobs.len) + to_chat(world, span("warning", "Error setting up jobs, no job datums found")) + return FALSE + + for(var/J in all_jobs) + var/datum/job/job = new J() + if(!job) + continue + if(job.faction != faction) + continue + occupations += job + name_occupations[job.title] = job + type_occupations[J] = job + if(LAZYLEN(job.departments)) + add_to_departments(job) + + sortTim(occupations, /proc/cmp_job_datums) + for(var/D in department_datums) + var/datum/department/dept = department_datums[D] + sortTim(dept.jobs, /proc/cmp_job_datums, TRUE) + sortTim(dept.primary_jobs, /proc/cmp_job_datums, TRUE) + + return TRUE + +/datum/controller/subsystem/job/proc/add_to_departments(datum/job/J) + // Adds to the regular job lists in the departments, which allow multiple departments for a job. + for(var/D in J.departments) + var/datum/department/dept = LAZYACCESS(department_datums, D) + if(!istype(dept)) + job_debug_message("Job '[J.title]' is defined as being inside department '[D]', but it does not exist.") + continue + dept.jobs[J.title] = J + + // Now for the 'primary' department for a job, which is defined as being the first department in the list for a job. + // This results in no duplicates, which can be useful in some situations. + if(LAZYLEN(J.departments)) + var/primary_department = J.departments[1] + var/datum/department/dept = LAZYACCESS(department_datums, primary_department) + if(!istype(dept)) + job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") + else + dept.primary_jobs[J.title] = J + +/datum/controller/subsystem/job/proc/setup_departments() + for(var/t in subtypesof(/datum/department)) + var/datum/department/D = new t() + department_datums[D.name] = D + + sortTim(department_datums, /proc/cmp_department_datums, TRUE) + +/datum/controller/subsystem/job/proc/get_all_department_datums() + var/list/dept_datums = list() + for(var/D in department_datums) + dept_datums += department_datums[D] + return dept_datums + +/datum/controller/subsystem/job/proc/get_job(rank) + if(!occupations.len) + setup_occupations() + return name_occupations[rank] + +/datum/controller/subsystem/job/proc/get_job_type(jobtype) + if(!occupations.len) + setup_occupations() + return type_occupations[jobtype] + +// Determines if a job title is inside of a specific department. +// Useful to replace the old `if(job_title in command_positions)` code. +/datum/controller/subsystem/job/proc/is_job_in_department(rank, target_department_name) + var/datum/department/D = LAZYACCESS(department_datums, target_department_name) + if(istype(D)) + return LAZYFIND(D.jobs, rank) ? TRUE : FALSE + return FALSE + +// Returns a list of all job names in a specific department. +/datum/controller/subsystem/job/proc/get_job_titles_in_department(target_department_name) + var/datum/department/D = LAZYACCESS(department_datums, target_department_name) + if(istype(D)) + var/list/job_titles = list() + for(var/J in D.jobs) + job_titles += J + return job_titles + + job_debug_message("Was asked to get job titles for a non-existant department '[target_department_name]'.") + return list() + +// Returns a reference to the primary department datum that a job is in. +// Can receive job datum refs, typepaths, or job title strings. +/datum/controller/subsystem/job/proc/get_primary_department_of_job(datum/job/J) + if(!istype(J, /datum/job)) + if(ispath(J)) + J = get_job_type(J) + else if(istext(J)) + J = get_job(J) + + if(!istype(J)) + job_debug_message("Was asked to get department for job '[J]', but input could not be resolved into a job datum.") + return + + if(!LAZYLEN(J.departments)) + return + + var/primary_department = J.departments[1] + var/datum/department/dept = LAZYACCESS(department_datums, primary_department) + if(!istype(dept)) + job_debug_message("Job '[J.title]' has their primary department be '[primary_department]', but it does not exist.") + return + + return department_datums[primary_department] + +// Someday it might be good to port code/game/jobs/job_controller.dm to here and clean it up. + + + + + +/datum/controller/subsystem/job/proc/job_debug_message(message) + if(debug_messages) + log_debug("JOB DEBUG: [message]") \ No newline at end of file diff --git a/code/controllers/subsystems/mapping.dm b/code/controllers/subsystems/mapping.dm index cc69931426..e48b9f557f 100644 --- a/code/controllers/subsystems/mapping.dm +++ b/code/controllers/subsystems/mapping.dm @@ -1,30 +1,29 @@ -// Handles map-related tasks, mostly here to ensure it does so after the MC initializes. -SUBSYSTEM_DEF(mapping) - name = "Mapping" - init_order = INIT_ORDER_MAPPING - flags = SS_NO_FIRE - - var/list/map_templates = list() - var/dmm_suite/maploader = null - -/datum/controller/subsystem/mapping/Initialize(timeofday) - if(subsystem_initialized) - return - world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably) - maploader = new() - load_map_templates() - - if(config.generate_map) - // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order. - if(using_map.perform_map_generation()) - using_map.refresh_mining_turfs() - - -/datum/controller/subsystem/mapping/proc/load_map_templates() - for(var/T in subtypesof(/datum/map_template)) - var/datum/map_template/template = T - if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence. - continue - template = new T() - map_templates[template.name] = template - return TRUE +// Handles map-related tasks, mostly here to ensure it does so after the MC initializes. +SUBSYSTEM_DEF(mapping) + name = "Mapping" + init_order = INIT_ORDER_MAPPING + flags = SS_NO_FIRE + + var/list/map_templates = list() + var/dmm_suite/maploader = null + +/datum/controller/subsystem/mapping/Initialize(timeofday) + if(subsystem_initialized) + return + world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably) + maploader = new() + load_map_templates() + + if(config.generate_map) + // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order. + using_map.perform_map_generation() + + +/datum/controller/subsystem/mapping/proc/load_map_templates() + for(var/T in subtypesof(/datum/map_template)) + var/datum/map_template/template = T + if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence. + continue + template = new T() + map_templates[template.name] = template + return TRUE diff --git a/code/controllers/subsystems/mapping_vr.dm b/code/controllers/subsystems/mapping_vr.dm index 0b301e2fe1..a0eea82667 100644 --- a/code/controllers/subsystems/mapping_vr.dm +++ b/code/controllers/subsystems/mapping_vr.dm @@ -27,8 +27,7 @@ SUBSYSTEM_DEF(mapping) if(config.generate_map) // Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order. - if(using_map.perform_map_generation()) - using_map.refresh_mining_turfs() + using_map.perform_map_generation() loadEngine() preloadShelterTemplates() diff --git a/code/controllers/subsystems/nanoui.dm b/code/controllers/subsystems/nanoui.dm index 520973514d..c916eb2739 100644 --- a/code/controllers/subsystems/nanoui.dm +++ b/code/controllers/subsystems/nanoui.dm @@ -1,41 +1,17 @@ SUBSYSTEM_DEF(nanoui) name = "NanoUI" wait = 5 + flags = SS_NO_INIT // a list of current open /nanoui UIs, grouped by src_object and ui_key var/list/open_uis = list() // a list of current open /nanoui UIs, not grouped, for use in processing var/list/processing_uis = list() - // a list of asset filenames which are to be sent to the client on user logon - var/list/asset_files = list() - -/datum/controller/subsystem/nanoui/Initialize() - var/list/nano_asset_dirs = list(\ - "nano/css/",\ - "nano/images/",\ - "nano/images/status_icons/",\ - "nano/images/modular_computers/",\ - "nano/js/",\ - "nano/templates/"\ - ) - - var/list/filenames = null - for (var/path in nano_asset_dirs) - filenames = flist(path) - for(var/filename in filenames) - if(copytext(filename, length(filename)) != "/") // filenames which end in "/" are actually directories, which we want to ignore - if(fexists(path + filename)) - asset_files.Add(fcopy_rsc(path + filename)) // add this file to asset_files for sending to clients when they connect - .=..() - for(var/i in GLOB.clients) - send_resources(i) /datum/controller/subsystem/nanoui/Recover() if(SSnanoui.open_uis) open_uis |= SSnanoui.open_uis if(SSnanoui.processing_uis) processing_uis |= SSnanoui.processing_uis - if(SSnanoui.asset_files) - asset_files |= SSnanoui.asset_files /datum/controller/subsystem/nanoui/stat_entry() return ..("[processing_uis.len] UIs") @@ -44,10 +20,3 @@ SUBSYSTEM_DEF(nanoui) for(var/thing in processing_uis) var/datum/nanoui/UI = thing UI.process() - -//Sends asset files to a client, called on client/New() -/datum/controller/subsystem/nanoui/proc/send_resources(client) - if(!subsystem_initialized) - return - for(var/file in asset_files) - client << browse_rsc(file) // send the file to the client diff --git a/code/controllers/subsystems/open_space.dm b/code/controllers/subsystems/open_space.dm new file mode 100644 index 0000000000..dfc1319009 --- /dev/null +++ b/code/controllers/subsystems/open_space.dm @@ -0,0 +1,123 @@ +// +// Controller handling icon updates of open space turfs +// + +GLOBAL_VAR_INIT(open_space_initialised, FALSE) + +SUBSYSTEM_DEF(open_space) + name = "Open Space" + wait = 2 // 5 times per second. + init_order = INIT_ORDER_OPENSPACE + var/list/turfs_to_process = list() // List of turfs queued for update. + var/list/turfs_to_process_old = null // List of turfs currently being updated. + var/counter = 1 // Can't use .len because we need to iterate in order + var/static/image/over_OS_darkness // Image overlayed over the bottom turf with stuff in it. + +/datum/controller/subsystem/open_space/Initialize(timeofday) + over_OS_darkness = image('icons/turf/open_space.dmi', "black_open") + over_OS_darkness.plane = OVER_OPENSPACE_PLANE + over_OS_darkness.layer = MOB_LAYER + initialize_open_space() + // Pre-process open space turfs once before the round starts. + fire(FALSE, TRUE) + return ..() + +/datum/controller/subsystem/open_space/Recover() + flags |= SS_NO_INIT // Make extra sure we don't initialize twice. + . = ..() + +/datum/controller/subsystem/open_space/fire(resumed = 0, init_tick_checks = FALSE) + // We use a different list so any additions to the update lists during a delay from MC_TICK_CHECK + // don't cause things to be cut from the list without being updated. + + //If we're not resuming, this must mean it's a new iteration so we have to grab the turfs + if (!resumed) + src.counter = 1 + src.turfs_to_process_old = turfs_to_process + turfs_to_process = list() + + //cache for sanic speed (lists are references anyways) + var/list/turfs_to_process_old = src.turfs_to_process_old + var/counter = src.counter + while(counter <= turfs_to_process_old.len) + var/turf/T = turfs_to_process_old[counter] + counter += 1 + if(!QDELETED(T)) + update_turf(T) + if (init_tick_checks) + CHECK_TICK // Used during initialization processing + else if (MC_TICK_CHECK) + src.counter = counter // Save for when we're resumed + return // Used during normal MC processing. + +/datum/controller/subsystem/open_space/proc/update_turf(var/turf/T) + for(var/atom/movable/A in T) + A.fall() + T.update_icon() + +/datum/controller/subsystem/open_space/proc/add_turf(var/turf/T, var/recursive = 0) + ASSERT(isturf(T)) + // Check for multiple additions + // Pointless to process the same turf twice. But we need to push it to the end of the list + // because any turfs below it need to process first. + turfs_to_process -= T + turfs_to_process += T + if(recursive > 0) + var/turf/above = GetAbove(T) + if(above && isopenspace(above)) + add_turf(above, recursive) + +// Queue the initial updates of open space turfs when the game starts. This will lag! +/datum/controller/subsystem/open_space/proc/initialize_open_space() + // Do initial setup from bottom to top. + for(var/zlevel = 1 to world.maxz) + for(var/turf/simulated/open/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))) + add_turf(T) + CHECK_TICK + GLOB.open_space_initialised = TRUE + +/datum/controller/subsystem/open_space/stat_entry(msg_prefix) + return ..("T [turfs_to_process.len]") + +/turf/Entered(atom/movable/AM) + . = ..() + if(GLOB.open_space_initialised && !AM.invisibility && isobj(AM)) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].Entered([AM])") + SSopen_space.add_turf(T, 1) + +/turf/Exited(atom/movable/AM) + . = ..() + if(GLOB.open_space_initialised && !AM.invisibility && isobj(AM)) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].Exited([AM])") + SSopen_space.add_turf(T, 1) + +/obj/update_icon() + . = ..() + if(GLOB.open_space_initialised && !invisibility && isturf(loc)) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].update_icon()") + SSopen_space.add_turf(T, 1) + +// Ouch... this is painful. But is there any other way? +/* - No for now +/obj/New() + . = ..() + if(open_space_initialised && !invisibility) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src]New()") + OS_controller.add_turf(T, 1) +*/ + +// We probably should hook Destroy() If we can think of something more efficient, lets hear it. +/obj/Destroy() + if(GLOB.open_space_initialised && !invisibility && isturf(loc)) + var/turf/T = GetAbove(src) + if(isopenspace(T)) + SSopen_space.add_turf(T, 1) + . = ..() // Important that this be at the bottom, or we will have been moved to nullspace. diff --git a/code/controllers/subsystems/persist_vr.dm b/code/controllers/subsystems/persist_vr.dm index 88bbabe72d..41811a2a6a 100644 --- a/code/controllers/subsystems/persist_vr.dm +++ b/code/controllers/subsystems/persist_vr.dm @@ -36,15 +36,13 @@ SUBSYSTEM_DEF(persist) // Try and detect job and department of mob var/datum/job/J = detect_job(M) - if(!istype(J) || !J.department || !J.timeoff_factor) + if(!istype(J) || !J.pto_type || !J.timeoff_factor) if (MC_TICK_CHECK) return continue // Do not collect useless PTO - var/department_earning = J.department - if(J.department == "Command") - department_earning = "Civilian" + var/department_earning = J.pto_type clear_unused_pto(M) // Update client whatever @@ -90,6 +88,6 @@ SUBSYSTEM_DEF(persist) /datum/controller/subsystem/persist/proc/clear_unused_pto(var/mob/M) var/client/C = M.client LAZYINITLIST(C.department_hours) - if(C.department_hours["Command"]) - C.department_hours["Command"] = null - C.department_hours.Remove("Command") \ No newline at end of file + if(C.department_hours[DEPARTMENT_COMMAND]) + C.department_hours[DEPARTMENT_COMMAND] = null + C.department_hours.Remove(DEPARTMENT_COMMAND) \ No newline at end of file diff --git a/code/controllers/subsystems/planets.dm b/code/controllers/subsystems/planets.dm index f7fc49b661..9e823fc0db 100644 --- a/code/controllers/subsystems/planets.dm +++ b/code/controllers/subsystems/planets.dm @@ -24,7 +24,7 @@ SUBSYSTEM_DEF(planets) ..() /datum/controller/subsystem/planets/proc/createPlanets() - var/list/planet_datums = subtypesof(/datum/planet) + var/list/planet_datums = using_map.planet_datums_to_make for(var/P in planet_datums) var/datum/planet/NP = new P() planets.Add(NP) diff --git a/code/controllers/subsystems/processing/bellies_vr.dm b/code/controllers/subsystems/processing/bellies_vr.dm new file mode 100644 index 0000000000..ad2a3e23e8 --- /dev/null +++ b/code/controllers/subsystems/processing/bellies_vr.dm @@ -0,0 +1,20 @@ +// +// Bellies subsystem - Process vore bellies +// + +PROCESSING_SUBSYSTEM_DEF(bellies) + name = "Bellies" + wait = 6 SECONDS + flags = SS_KEEP_TIMING|SS_NO_INIT + runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME + +/datum/controller/subsystem/processing/bellies/Recover() + log_debug("[name] subsystem Recover().") + if(SSbellies.current_thing) + log_debug("current_thing was: (\ref[SSbellies.current_thing])[SSbellies.current_thing]([SSbellies.current_thing.type]) - currentrun: [SSbellies.currentrun.len] vs total: [SSbellies.processing.len]") + var/list/old_processing = SSbellies.processing.Copy() + for(var/datum/D in old_processing) + if(!isbelly(D)) + log_debug("[name] subsystem Recover() found inappropriate item in list: [D.type]") + if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING)) + processing |= D diff --git a/code/controllers/subsystems/processing/chemistry.dm b/code/controllers/subsystems/processing/chemistry.dm index 34094f9fe3..7f4fd9c42b 100644 --- a/code/controllers/subsystems/processing/chemistry.dm +++ b/code/controllers/subsystems/processing/chemistry.dm @@ -8,6 +8,14 @@ PROCESSING_SUBSYSTEM_DEF(chemistry) var/list/chemical_reagents = list() /datum/controller/subsystem/processing/chemistry/Recover() + log_debug("[name] subsystem Recover().") + if(SSchemistry.current_thing) + log_debug("current_thing was: (\ref[SSchemistry.current_thing])[SSchemistry.current_thing]([SSchemistry.current_thing.type]) - currentrun: [SSchemistry.currentrun.len] vs total: [SSchemistry.processing.len]") + var/list/old_processing = SSchemistry.processing.Copy() + for(var/datum/D in old_processing) + if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING)) + processing |= D + chemical_reactions = SSchemistry.chemical_reactions chemical_reagents = SSchemistry.chemical_reagents diff --git a/code/controllers/subsystems/processing/fastprocess.dm b/code/controllers/subsystems/processing/fastprocess.dm index 9622e02146..bda0bb6fb6 100644 --- a/code/controllers/subsystems/processing/fastprocess.dm +++ b/code/controllers/subsystems/processing/fastprocess.dm @@ -4,3 +4,12 @@ PROCESSING_SUBSYSTEM_DEF(fastprocess) name = "Fast Processing" wait = 2 stat_tag = "FP" + +/datum/controller/subsystem/processing/fastprocess/Recover() + log_debug("[name] subsystem Recover().") + if(SSfastprocess.current_thing) + log_debug("current_thing was: (\ref[SSfastprocess.current_thing])[SSfastprocess.current_thing]([SSfastprocess.current_thing.type]) - currentrun: [SSfastprocess.currentrun.len] vs total: [SSfastprocess.processing.len]") + var/list/old_processing = SSfastprocess.processing.Copy() + for(var/datum/D in old_processing) + if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING)) + processing |= D \ No newline at end of file diff --git a/code/controllers/subsystems/processing/obj.dm b/code/controllers/subsystems/processing/obj.dm index 26021fb267..37f3acc7c6 100644 --- a/code/controllers/subsystems/processing/obj.dm +++ b/code/controllers/subsystems/processing/obj.dm @@ -3,3 +3,14 @@ PROCESSING_SUBSYSTEM_DEF(obj) priority = FIRE_PRIORITY_OBJ flags = SS_NO_INIT wait = 20 + +/datum/controller/subsystem/processing/obj/Recover() + log_debug("[name] subsystem Recover().") + if(SSobj.current_thing) + log_debug("current_thing was: (\ref[SSobj.current_thing])[SSobj.current_thing]([SSobj.current_thing.type]) - currentrun: [SSobj.currentrun.len] vs total: [SSobj.processing.len]") + var/list/old_processing = SSobj.processing.Copy() + for(var/datum/D in old_processing) + if(!isobj(D)) + log_debug("[name] subsystem Recover() found inappropriate item in list: [D.type]") + if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING)) + processing |= D \ No newline at end of file diff --git a/code/controllers/subsystems/processing/processing.dm b/code/controllers/subsystems/processing/processing.dm index ef54b8a4ab..3eeff489aa 100644 --- a/code/controllers/subsystems/processing/processing.dm +++ b/code/controllers/subsystems/processing/processing.dm @@ -13,6 +13,16 @@ SUBSYSTEM_DEF(processing) var/debug_last_thing var/debug_original_process_proc // initial() does not work with procs + var/datum/current_thing + +/datum/controller/subsystem/processing/Recover() + log_debug("[name] subsystem Recover().") + if(SSprocessing.current_thing) + log_debug("current_thing was: (\ref[SSprocessing.current_thing])[SSprocessing.current_thing]([SSprocessing.current_thing.type]) - currentrun: [SSprocessing.currentrun.len] vs total: [SSprocessing.processing.len]") + var/list/old_processing = SSprocessing.processing.Copy() + for(var/datum/D in old_processing) + if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING)) + processing |= D /datum/controller/subsystem/processing/stat_entry() ..("[stat_tag]:[processing.len]") @@ -24,16 +34,19 @@ SUBSYSTEM_DEF(processing) var/list/current_run = currentrun while(current_run.len) - var/datum/thing = current_run[current_run.len] + current_thing = current_run[current_run.len] current_run.len-- - if(QDELETED(thing)) - processing -= thing - else if(thing.process(wait) == PROCESS_KILL) + if(QDELETED(current_thing)) + processing -= current_thing + else if(current_thing.process(wait) == PROCESS_KILL) // fully stop so that a future START_PROCESSING will work - STOP_PROCESSING(src, thing) + STOP_PROCESSING(src, current_thing) if (MC_TICK_CHECK) + current_thing = null return + current_thing = null + /datum/controller/subsystem/processing/proc/toggle_debug() if(!check_rights(R_DEBUG)) return diff --git a/code/controllers/subsystems/processing/projectiles.dm b/code/controllers/subsystems/processing/projectiles.dm index 87c9f097de..e4316304ce 100644 --- a/code/controllers/subsystems/processing/projectiles.dm +++ b/code/controllers/subsystems/processing/projectiles.dm @@ -8,6 +8,15 @@ PROCESSING_SUBSYSTEM_DEF(projectiles) var/global_pixel_speed = 2 var/global_iterations_per_move = 16 +/datum/controller/subsystem/processing/projectiles/Recover() + log_debug("[name] subsystem Recover().") + if(SSprojectiles.current_thing) + log_debug("current_thing was: (\ref[SSprojectiles.current_thing])[SSprojectiles.current_thing]([SSprojectiles.current_thing.type]) - currentrun: [SSprojectiles.currentrun.len] vs total: [SSprojectiles.processing.len]") + var/list/old_processing = SSprojectiles.processing.Copy() + for(var/datum/D in old_processing) + if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING)) + processing |= D + /datum/controller/subsystem/processing/projectiles/proc/set_pixel_speed(new_speed) global_pixel_speed = new_speed for(var/i in processing) diff --git a/code/controllers/subsystems/processing/turfs.dm b/code/controllers/subsystems/processing/turfs.dm index 941513527e..d28f4b4809 100644 --- a/code/controllers/subsystems/processing/turfs.dm +++ b/code/controllers/subsystems/processing/turfs.dm @@ -1,3 +1,14 @@ PROCESSING_SUBSYSTEM_DEF(turfs) name = "Turf Processing" wait = 20 + +/datum/controller/subsystem/processing/turfs/Recover() + log_debug("[name] subsystem Recover().") + if(SSturfs.current_thing) + log_debug("current_thing was: (\ref[SSturfs.current_thing])[SSturfs.current_thing]([SSturfs.current_thing.type]) - currentrun: [SSturfs.currentrun.len] vs total: [SSturfs.processing.len]") + var/list/old_processing = SSturfs.processing.Copy() + for(var/datum/D in old_processing) + if(!isturf(D)) + log_debug("[name] subsystem Recover() found inappropriate item in list: [D.type]") + if(CHECK_BITFIELD(D.datum_flags, DF_ISPROCESSING)) + processing |= D \ No newline at end of file diff --git a/code/controllers/subsystems/shuttles.dm b/code/controllers/subsystems/shuttles.dm index 61a4590890..0bce940385 100644 --- a/code/controllers/subsystems/shuttles.dm +++ b/code/controllers/subsystems/shuttles.dm @@ -1,6 +1,8 @@ // // SSshuttles subsystem - Handles initialization and processing of shuttles. // +// Also handles initialization and processing of overmap sectors. +// // This global variable exists for legacy support so we don't have to rename every shuttle_controller to SSshuttles yet. var/global/datum/controller/subsystem/shuttles/shuttle_controller @@ -13,71 +15,170 @@ SUBSYSTEM_DEF(shuttles) flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME - var/list/shuttles = list() // Maps shuttle tags to shuttle datums, so that they can be looked up. - var/list/process_shuttles = list() // Simple list of shuttles, for processing - var/list/current_run = list() // Shuttles remaining to process this fire() tick - var/list/docks_init_callbacks // List of callbacks to run when we finish setting up shuttle docks. - var/docks_initialized = FALSE + var/overmap_halted = FALSE // Whether ships can move on the overmap; used for adminbus. + var/list/ships = list() // List of all ships. + + var/list/shuttles = list() // Maps shuttle tags to shuttle datums, so that they can be looked up. + var/list/process_shuttles = list() // Simple list of shuttles, for processing + + var/list/registered_shuttle_landmarks = list() // Maps shuttle landmark tags to instances + var/last_landmark_registration_time // world.time of most recent addition to registered_shuttle_landmarks + var/list/shuttle_logs = list() // (Not Implemented) Keeps records of shuttle movement, format is list(datum/shuttle = datum/shuttle_log) + var/list/shuttle_areas = list() // All the areas of all shuttles. + var/list/docking_registry = list() // Docking controller tag -> docking controller program, mostly for init purposes. + + var/list/landmarks_awaiting_sector = list() // Stores automatic landmarks that are waiting for a sector to finish loading. + var/list/landmarks_still_needed = list() // Stores landmark_tags that need to be assigned to the sector (landmark_tag = sector) when registered. + var/list/shuttles_to_initialize // A queue for shuttles to initialize at the appropriate time. + var/list/sectors_to_initialize // Used to find all sector objects at the appropriate time. + var/block_init_queue = TRUE // Block initialization of new shuttles/sectors + + var/tmp/list/current_run // Shuttles remaining to process this fire() tick + +/datum/controller/subsystem/shuttles/PreInit() + global.shuttle_controller = src // TODO - Remove this! Change everything to point at SSshuttles intead /datum/controller/subsystem/shuttles/Initialize(timeofday) - global.shuttle_controller = src - setup_shuttle_docks() - for(var/I in docks_init_callbacks) - var/datum/callback/cb = I - cb.InvokeAsync() - LAZYCLEARLIST(docks_init_callbacks) - docks_init_callbacks = null + last_landmark_registration_time = world.time + // Find all declared shuttle datums and initailize them. (Okay, queue them for initialization a few lines further down) + for(var/shuttle_type in subtypesof(/datum/shuttle)) // This accounts for most shuttles, though away maps can queue up more. + var/datum/shuttle/shuttle = shuttle_type + if(initial(shuttle.category) == shuttle_type) + continue // Its an "abstract class" datum, not for a real shuttle. + if(!initial(shuttle.defer_initialisation)) // Skip if it asks not to be initialized at startup. + LAZYDISTINCTADD(shuttles_to_initialize, shuttle_type) + block_init_queue = FALSE + process_init_queues() return ..() /datum/controller/subsystem/shuttles/fire(resumed = 0) - do_process_shuttles(resumed) - -/datum/controller/subsystem/shuttles/stat_entry() - var/msg = list() - msg += "AS:[shuttles.len]|" - msg += "PS:[process_shuttles.len]|" - ..(jointext(msg, null)) - -/datum/controller/subsystem/shuttles/proc/do_process_shuttles(resumed = 0) if (!resumed) src.current_run = process_shuttles.Copy() - var/list/current_run = src.current_run // Cache for sanic speed - while(current_run.len) - var/datum/shuttle/S = current_run[current_run.len] - current_run.len-- - if(istype(S) && !QDELETED(S)) - if(istype(S, /datum/shuttle/ferry)) // Ferry shuttles get special treatment - var/datum/shuttle/ferry/F = S - if(F.process_state || F.always_process) - F.process() - else - S.process() - else + var/list/working_shuttles = src.current_run // Cache for sanic speed + while(working_shuttles.len) + var/datum/shuttle/S = working_shuttles[working_shuttles.len] + working_shuttles.len-- + if(!istype(S) || QDELETED(S)) + error("Bad entry in SSshuttles.process_shuttles - [log_info_line(S)] ") process_shuttles -= S + continue + // NOTE - In old system, /datum/shuttle/ferry was processed only if (F.process_state || F.always_process) + if(S.process_state && (S.process(wait, times_fired, src) == PROCESS_KILL)) + process_shuttles -= S + if(MC_TICK_CHECK) return -// This should be called after all the machines and radio frequencies have been properly initialized -/datum/controller/subsystem/shuttles/proc/setup_shuttle_docks() - // Find all declared shuttle datums and initailize them. - for(var/shuttle_type in subtypesof(/datum/shuttle)) - var/datum/shuttle/shuttle = shuttle_type - if(initial(shuttle.category) == shuttle_type) - continue +/datum/controller/subsystem/shuttles/proc/process_init_queues() + if(block_init_queue) + return + initialize_shuttles() + initialize_sectors() + +// Initializes all shuttles in shuttles_to_initialize +/datum/controller/subsystem/shuttles/proc/initialize_shuttles() + var/list/shuttles_made = list() + for(var/shuttle_type in shuttles_to_initialize) + var/shuttle = initialize_shuttle(shuttle_type) + if(shuttle) + shuttles_made += shuttle + hook_up_motherships(shuttles_made) + hook_up_shuttle_objects(shuttles_made) + shuttles_to_initialize = null + +/datum/controller/subsystem/shuttles/proc/initialize_sectors() + for(var/sector in sectors_to_initialize) + initialize_sector(sector) + sectors_to_initialize = null + +/datum/controller/subsystem/shuttles/proc/register_landmark(shuttle_landmark_tag, obj/effect/shuttle_landmark/shuttle_landmark) + if (registered_shuttle_landmarks[shuttle_landmark_tag]) + CRASH("Attempted to register shuttle landmark with tag [shuttle_landmark_tag], but it is already registered!") + if (istype(shuttle_landmark)) + registered_shuttle_landmarks[shuttle_landmark_tag] = shuttle_landmark + last_landmark_registration_time = world.time + + var/obj/effect/overmap/visitable/O = landmarks_still_needed[shuttle_landmark_tag] + if(O) //These need to be added to sectors, which we handle. + try_add_landmark_tag(shuttle_landmark_tag, O) + landmarks_still_needed -= shuttle_landmark_tag + else if(istype(shuttle_landmark, /obj/effect/shuttle_landmark/automatic)) //These find their sector automatically + O = get_overmap_sector(get_z(shuttle_landmark)) + O ? O.add_landmark(shuttle_landmark, shuttle_landmark.shuttle_restricted) : (landmarks_awaiting_sector += shuttle_landmark) + +/datum/controller/subsystem/shuttles/proc/get_landmark(var/shuttle_landmark_tag) + return registered_shuttle_landmarks[shuttle_landmark_tag] + +//Checks if the given sector's landmarks have initialized; if so, registers them with the sector, if not, marks them for assignment after they come in. +//Also adds automatic landmarks that were waiting on their sector to spawn. +/datum/controller/subsystem/shuttles/proc/initialize_sector(obj/effect/overmap/visitable/given_sector) + given_sector.populate_sector_objects() // This is a late init operation that sets up the sector's map_z and does non-overmap-related init tasks. + + for(var/landmark_tag in given_sector.initial_generic_waypoints) + if(!try_add_landmark_tag(landmark_tag, given_sector)) + landmarks_still_needed[landmark_tag] = given_sector // Landmark isn't registered yet, queue it to be added once it is. + + for(var/shuttle_name in given_sector.initial_restricted_waypoints) + for(var/landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name]) + if(!try_add_landmark_tag(landmark_tag, given_sector)) + landmarks_still_needed[landmark_tag] = given_sector // Landmark isn't registered yet, queue it to be added once it is. + + var/landmarks_to_check = landmarks_awaiting_sector.Copy() + for(var/thing in landmarks_to_check) + var/obj/effect/shuttle_landmark/automatic/landmark = thing + if(landmark.z in given_sector.map_z) + given_sector.add_landmark(landmark, landmark.shuttle_restricted) + landmarks_awaiting_sector -= landmark + +// Attempts to add a landmark instance with a sector (returns false if landmark isn't registered yet) +/datum/controller/subsystem/shuttles/proc/try_add_landmark_tag(landmark_tag, obj/effect/overmap/visitable/given_sector) + var/obj/effect/shuttle_landmark/landmark = get_landmark(landmark_tag) + if(!landmark) + return + + if(landmark.landmark_tag in given_sector.initial_generic_waypoints) + given_sector.add_landmark(landmark) + . = 1 + for(var/shuttle_name in given_sector.initial_restricted_waypoints) + if(landmark.landmark_tag in given_sector.initial_restricted_waypoints[shuttle_name]) + given_sector.add_landmark(landmark, shuttle_name) + . = 1 + +/datum/controller/subsystem/shuttles/proc/initialize_shuttle(var/shuttle_type) + var/datum/shuttle/shuttle = shuttle_type + if(initial(shuttle.category) != shuttle_type) // Skip if its an "abstract class" datum shuttle = new shuttle() - shuttle.init_docking_controllers() - shuttle.dock() //makes all shuttles docked to something at round start go into the docked state - CHECK_TICK + shuttle_areas |= shuttle.shuttle_area + log_debug("Initialized shuttle [shuttle] ([shuttle.type])") + return shuttle + // Historical note: No need to call shuttle.init_docking_controllers(), controllers register themselves + // and shuttles fetch refs in New(). Shuttles also dock() themselves in new if they want. - for(var/obj/machinery/embedded_controller/C in machines) - if(istype(C.program, /datum/computer/file/embedded_program/docking)) - C.program.tag = null //clear the tags, 'cause we don't need 'em anymore - docks_initialized = TRUE +// TODO - Leshana to hook up more of this when overmap is ported. +/datum/controller/subsystem/shuttles/proc/hook_up_motherships(shuttles_list) + for(var/datum/shuttle/S in shuttles_list) + if(S.mothershuttle && !S.motherdock) + var/datum/shuttle/mothership = shuttles[S.mothershuttle] + if(mothership) + S.motherdock = S.current_location.landmark_tag + mothership.shuttle_area |= S.shuttle_area + else + error("Shuttle [S] was unable to find mothership [mothership]!") -// Register a callback that will be invoked once the shuttles have been initialized -/datum/controller/subsystem/shuttles/proc/OnDocksInitialized(datum/callback/cb) - if(!docks_initialized) - LAZYADD(docks_init_callbacks, cb) - else - cb.InvokeAsync() +// Let shuttles scan their owned areas for objects they want to configure (Called after mothership hookup) +/datum/controller/subsystem/shuttles/proc/hook_up_shuttle_objects(shuttles_list) + for(var/datum/shuttle/S in shuttles_list) + S.populate_shuttle_objects() + +// Admin command to halt/resume overmap +/datum/controller/subsystem/shuttles/proc/toggle_overmap(new_setting) + if(overmap_halted == new_setting) + return + overmap_halted = !overmap_halted + for(var/ship in ships) + var/obj/effect/overmap/visitable/ship/ship_effect = ship + overmap_halted ? ship_effect.halt() : ship_effect.unhalt() + +/datum/controller/subsystem/shuttles/stat_entry() + ..("Shuttles:[process_shuttles.len]/[shuttles.len], Ships:[ships.len], L:[registered_shuttle_landmarks.len][overmap_halted ? ", HALT" : ""]") diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm new file mode 100644 index 0000000000..03f41f0f33 --- /dev/null +++ b/code/controllers/subsystems/skybox.dm @@ -0,0 +1,158 @@ + +//Exists to handle a few global variables that change enough to justify this. Technically a parallax, but it exhibits a skybox effect. +SUBSYSTEM_DEF(skybox) + name = "Space skybox" + init_order = INIT_ORDER_SKYBOX + flags = SS_NO_FIRE + var/static/list/skybox_cache = list() + + var/static/mutable_appearance/normal_space + var/static/list/dust_cache = list() + var/static/list/speedspace_cache = list() + var/static/list/mapedge_cache = list() + var/static/list/phase_shift_by_x = list() + var/static/list/phase_shift_by_y = list() + +/datum/controller/subsystem/skybox/PreInit() + //Shuffle some lists + phase_shift_by_x = get_cross_shift_list(15) + phase_shift_by_y = get_cross_shift_list(15) + + //Create our 'normal' space appearance + normal_space = new() + normal_space.name = "\proper space" + normal_space.desc = "Space!" + normal_space.mouse_opacity = 2 //Always fully opaque. It's SPACE there can't be things BEHIND IT. + normal_space.appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER + normal_space.plane = SPACE_PLANE + normal_space.layer = TURF_LAYER + normal_space.icon = 'icons/turf/space.dmi' + normal_space.icon_state = "white" + + //Static + for (var/i in 0 to 25) + var/mutable_appearance/MA = new(normal_space) + var/image/im = image('icons/turf/space_dust.dmi', "[i]") + im.plane = DUST_PLANE + im.alpha = 128 //80 + im.blend_mode = BLEND_ADD + + MA.overlays = list(im) + + dust_cache["[i]"] = MA + + //Moving + for (var/i in 0 to 14) + // NORTH/SOUTH + var/mutable_appearance/MA = new(normal_space) + var/image/im = image('icons/turf/space_dust_transit.dmi', "speedspace_ns_[i]") + im.plane = DUST_PLANE + im.blend_mode = BLEND_ADD + MA.overlays = list(im) + speedspace_cache["NS_[i]"] = MA + // EAST/WEST + MA = new(normal_space) + im = image('icons/turf/space_dust_transit.dmi', "speedspace_ew_[i]") + im.plane = DUST_PLANE + im.blend_mode = BLEND_ADD + + MA.overlays = list(im) + + speedspace_cache["EW_[i]"] = MA + + //Over-the-edge images + for (var/dir in alldirs) + var/mutable_appearance/MA = new(normal_space) + var/matrix/M = matrix() + var/horizontal = (dir & (WEST|EAST)) + var/vertical = (dir & (NORTH|SOUTH)) + M.Scale(horizontal ? 8 : 1, vertical ? 8 : 1) + MA.transform = M + MA.appearance_flags = KEEP_APART | TILE_BOUND + MA.plane = SPACE_PLANE + MA.layer = 0 + + if(dir & NORTH) + MA.pixel_y = 112 + else if(dir & SOUTH) + MA.pixel_y = -112 + + if(dir & EAST) + MA.pixel_x = 112 + else if(dir & WEST) + MA.pixel_x = -112 + + mapedge_cache["[dir]"] = MA + + . = ..() + +/datum/controller/subsystem/skybox/Initialize() + . = ..() + +/datum/controller/subsystem/skybox/proc/get_skybox(z) + if(!skybox_cache["[z]"]) + skybox_cache["[z]"] = generate_skybox(z) + return skybox_cache["[z]"] + +/datum/controller/subsystem/skybox/proc/generate_skybox(z) + var/datum/skybox_settings/settings = global.using_map.get_skybox_datum(z) + + var/image/res = image(settings.icon) + res.appearance_flags = KEEP_TOGETHER + + var/image/base = image(settings.icon, settings.icon_state) + base.color = settings.color + + if(settings.use_stars) + var/image/stars = image(settings.icon, settings.star_state) + stars.appearance_flags = RESET_COLOR + base.overlays += stars + + res.overlays += base + + if(global.using_map.use_overmap && settings.use_overmap_details) + var/obj/effect/overmap/visitable/O = get_overmap_sector(z) + if(istype(O)) + var/image/overmap = image(settings.icon) + overmap.overlays += O.generate_skybox() + for(var/obj/effect/overmap/visitable/other in O.loc) + if(other != O) + overmap.overlays += other.get_skybox_representation() + overmap.appearance_flags = RESET_COLOR + res.overlays += overmap + + // Allow events to apply custom overlays to skybox! (Awesome!) + for(var/datum/event/E in SSevents.active_events) + if(E.has_skybox_image && E.isRunning && (z in E.affecting_z)) + res.overlays += E.get_skybox_image() + + return res + +/datum/controller/subsystem/skybox/proc/rebuild_skyboxes(var/list/zlevels) + for(var/z in zlevels) + skybox_cache["[z]"] = generate_skybox(z) + + for(var/client/C) + var/their_z = get_z(C.mob) + if(!their_z) //Nullspace + continue + if(their_z in zlevels) + C.update_skybox(1) + +// Settings datum that maps can override to play with their skyboxes +/datum/skybox_settings + var/icon = 'icons/skybox/skybox.dmi' //Path to our background. Lets us use anything we damn well please. Skyboxes need to be 736x736 + var/icon_state = "dyable" + var/color + var/random_color = FALSE + + var/use_stars = TRUE + var/star_icon = 'icons/skybox/skybox.dmi' + var/star_state = "stars" + + var/use_overmap_details = TRUE //Do we try to draw overmap visitables in our sector on the map? + +/datum/skybox_settings/New() + ..() + if(random_color) + color = rgb(rand(0,255), rand(0,255), rand(0,255)) diff --git a/code/controllers/subsystems/sqlite.dm b/code/controllers/subsystems/sqlite.dm new file mode 100644 index 0000000000..3d18ec4a7a --- /dev/null +++ b/code/controllers/subsystems/sqlite.dm @@ -0,0 +1,187 @@ +// This holds all the code needed to manage and use a SQLite database. +// It is merely a file sitting inside the data directory, as opposed to a full fledged DB service, +// however this makes it a lot easier to test, and it is natively supported by BYOND. +SUBSYSTEM_DEF(sqlite) + name = "SQLite" + init_order = INIT_ORDER_SQLITE + flags = SS_NO_FIRE + var/database/sqlite_db = null + +/datum/controller/subsystem/sqlite/Initialize(timeofday) + connect() + if(sqlite_db) + init_schema(sqlite_db) + return ..() + +/datum/controller/subsystem/sqlite/proc/connect() + if(!config.sqlite_enabled) + return + + if(!sqlite_db) + sqlite_db = new("data/sqlite/sqlite.db") // The path has to be hardcoded or BYOND silently fails. + + + if(!sqlite_db) + to_world_log("Failed to load or create a SQLite database.") + log_debug("ERROR: SQLite database is active in config but failed to load.") + else + to_world_log("Sqlite database connected.") + +// Makes the tables, if they do not already exist in the sqlite file. +/datum/controller/subsystem/sqlite/proc/init_schema(database/sqlite_object) + // Feedback table. + // Note that this is for direct feedback from players using the in-game feedback system and NOT for stat tracking. + // Player ckeys are not stored in this table as a unique key due to a config option to hash the keys to encourage more honest feedback. + /* + * id - Primary unique key to ID a specific piece of feedback. + NOT used to id people submitting feedback. + * author - The person who submitted it. Will be the ckey, or a hash of the ckey, + if both the config supports it, and the user wants it. + * topic - A specific category to organize feedback under. Options are defined in the config file. + * content - What the author decided to write to the staff. Limited to MAX_FEEDBACK_LENGTH. + * datetime - When the author submitted their feedback, acts as a timestamp. + */ + var/database/query/init_schema = new( + {" + CREATE TABLE IF NOT EXISTS [SQLITE_TABLE_FEEDBACK] + ( + `[SQLITE_FEEDBACK_COLUMN_ID]` INTEGER NOT NULL UNIQUE, + `[SQLITE_FEEDBACK_COLUMN_AUTHOR]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_TOPIC]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_CONTENT]` TEXT NOT NULL, + `[SQLITE_FEEDBACK_COLUMN_DATETIME]` TEXT NOT NULL, + PRIMARY KEY(`[SQLITE_FEEDBACK_COLUMN_ID]`) + ); + "} + ) + init_schema.Execute(sqlite_object) + sqlite_check_for_errors(init_schema, "Feedback table creation") + + // Add more schemas below this if the SQLite DB gets expanded for things like persistant news, polls, bans, deaths, etc. + +// General error checking for SQLite. +// Returns true if something went wrong. Also writes a log. +// The desc parameter should be unique for each call, to make it easier to track down where the error occured. +/datum/controller/subsystem/sqlite/proc/sqlite_check_for_errors(var/database/query/query_used, var/desc) + if(query_used && query_used.ErrorMsg()) + log_debug("SQLite Error: [desc] : [query_used.ErrorMsg()]") + return TRUE + return FALSE + + +/************ + * Feedback * + ************/ + +// Inserts data into the feedback table in a painless manner. +// Returns TRUE if no issues happened, FALSE otherwise. +/datum/controller/subsystem/sqlite/proc/insert_feedback(author, topic, content, database/sqlite_object) + if(!author || !topic || !content) + CRASH("One or more parameters was invalid.") + + // Sanitize everything to avoid sneaky stuff. + var/sqlite_author = sql_sanitize_text(ckey(lowertext(author))) + var/sqlite_content = sql_sanitize_text(content) + var/sqlite_topic = sql_sanitize_text(topic) + + var/database/query/query = new( + "INSERT INTO [SQLITE_TABLE_FEEDBACK] (\ + [SQLITE_FEEDBACK_COLUMN_AUTHOR], \ + [SQLITE_FEEDBACK_COLUMN_TOPIC], \ + [SQLITE_FEEDBACK_COLUMN_CONTENT], \ + [SQLITE_FEEDBACK_COLUMN_DATETIME]) \ + \ + VALUES (\ + ?,\ + ?,\ + ?,\ + datetime('now'))", + sqlite_author, + sqlite_topic, + sqlite_content + ) + query.Execute(sqlite_object) + return !sqlite_check_for_errors(query, "Insert Feedback") + +/datum/controller/subsystem/sqlite/proc/can_submit_feedback(client/C) + if(!config.sqlite_enabled) + return FALSE + if(config.sqlite_feedback_min_age && !is_old_enough(C)) + return FALSE + if(config.sqlite_feedback_cooldown > 0 && get_feedback_cooldown(C.key, config.sqlite_feedback_cooldown, sqlite_db) > 0) + return FALSE + return TRUE + +// Returns TRUE if the player is 'old' enough, according to the config. +/datum/controller/subsystem/sqlite/proc/is_old_enough(client/C) + if(get_player_age(C.key) < config.sqlite_feedback_min_age) + return FALSE + return TRUE + + +// Returns how many days someone has to wait, to submit more feedback, or 0 if they can do so right now. +/datum/controller/subsystem/sqlite/proc/get_feedback_cooldown(player_ckey, cooldown, database/sqlite_object) + player_ckey = sql_sanitize_text(ckey(lowertext(player_ckey))) + var/potential_hashed_ckey = sql_sanitize_text(md5(player_ckey + SSsqlite.get_feedback_pepper())) + + // First query is to get the most recent time the player has submitted feedback. + var/database/query/query = new({" + SELECT [SQLITE_FEEDBACK_COLUMN_DATETIME] + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? OR [SQLITE_FEEDBACK_COLUMN_AUTHOR] == ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_DATETIME] + DESC LIMIT 1; + "}, + player_ckey, + potential_hashed_ckey + ) + query.Execute(sqlite_object) + sqlite_check_for_errors(query, "Rate Limited Check 1") + + // It is possible this is their first time, so there won't be a next row. + if(query.NextRow()) // If this is true, the user has submitted feedback at least once. + var/list/row_data = query.GetRowData() + var/last_submission_datetime = row_data[SQLITE_FEEDBACK_COLUMN_DATETIME] + + // Now we have the datetime, we need to do something to compare it. + // Second query is to calculate the difference between two datetimes. + // This is done on the SQLite side because parsing datetimes with BYOND is probably a bad idea. + query = new( + "SELECT julianday('now') - julianday(?) \ + AS 'datediff';", + last_submission_datetime + ) + query.Execute(sqlite_object) + sqlite_check_for_errors(query, "Rate Limited Check 2") + + query.NextRow() + row_data = query.GetRowData() + var/date_diff = row_data["datediff"] + + // Now check if it's too soon to give more feedback. + if(text2num(date_diff) < cooldown) // Too soon. + return round(cooldown - date_diff, 0.1) + return 0.0 + + +// A Pepper is like a Salt but only one exists and is supposed to be outside of a database. +// If the file is properly protected, it can only be viewed/copied by sys-admins generating a log, which is much more conspicious than accessing/copying a DB. +// This stops mods/admins/etc from guessing the author by shoving names in an MD5 hasher until they pick the right one. +// Don't use this for things needing actual security. +/datum/controller/subsystem/sqlite/proc/get_feedback_pepper() + var/pepper_file = file2list("config/sqlite_feedback_pepper.txt") + var/pepper = null + for(var/line in pepper_file) + if(!line) + continue + if(length(line) == 0) + continue + else if(copytext(line, 1, 2) == "#") + continue + else + pepper = line + break + return pepper + +/datum/controller/subsystem/sqlite/CanProcCall(procname) + return procname != "get_feedback_pepper" diff --git a/code/controllers/Processes/supply.dm b/code/controllers/subsystems/supply.dm similarity index 68% rename from code/controllers/Processes/supply.dm rename to code/controllers/subsystems/supply.dm index b2b9951cb6..f379193c6a 100644 --- a/code/controllers/Processes/supply.dm +++ b/code/controllers/subsystems/supply.dm @@ -1,36 +1,15 @@ -//Config stuff -#define SUPPLY_DOCKZ 2 //Z-level of the Dock. -#define SUPPLY_STATIONZ 1 //Z-level of the Station. -#define SUPPLY_STATION_AREATYPE "/area/supply/station" //Type of the supply shuttle area for station -#define SUPPLY_DOCK_AREATYPE "/area/supply/dock" //Type of the supply shuttle area for dock - //Supply packs are in /code/datums/supplypacks //Computers are in /code/game/machinery/computer/supply.dm +SUBSYSTEM_DEF(supply) + name = "Supply" + wait = 20 SECONDS + priority = FIRE_PRIORITY_SUPPLY + //Initializes at default time + flags = SS_NO_TICK_CHECK -/datum/supply_order - var/ordernum // Unfabricatable index - var/index // Fabricatable index - var/datum/supply_pack/object = null - var/cost // Cost of the supply pack (Fabricatable) (Changes not reflected when purchasing supply packs, this is cosmetic only) - var/name // Name of the supply pack datum (Fabricatable) - var/ordered_by = null // Who requested the order - var/comment = null // What reason was given for the order - var/approved_by = null // Who approved the order - var/ordered_at // Date and time the order was requested at - var/approved_at // Date and time the order was approved at - var/status // [Requested, Accepted, Denied, Shipped] - -/datum/exported_crate - var/name - var/value - var/list/contents - -var/datum/controller/supply/supply_controller = new() - -/datum/controller/supply //supply points var/points = 50 - var/points_per_process = 1.5 + var/points_per_process = 1.0 // Processes every 20 seconds, so this is 3 per minute var/points_per_slip = 2 var/points_per_money = 0.02 // 1 point for $50 //control @@ -43,33 +22,35 @@ var/datum/controller/supply/supply_controller = new() var/list/adm_export_history = list() // Complete history of all crates sent back on the shuttle, for admin use //shuttle movement var/movetime = 1200 - var/datum/shuttle/ferry/supply/shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle var/list/material_points_conversion = list( // Any materials not named in this list are worth 0 points "phoron" = 5, "platinum" = 5 ) -/datum/controller/supply/New() +/datum/controller/subsystem/supply/Initialize() ordernum = rand(1,9000) + // build master supply list for(var/typepath in subtypesof(/datum/supply_pack)) var/datum/supply_pack/P = new typepath() - supply_pack[P.name] = P + if(P.name) + supply_pack[P.name] = P + else + qdel(P) -/datum/controller/process/supply/setup() - name = "supply controller" - schedule_interval = 300 // every 30 seconds + // TODO - Auto-build material_points_conversion from material datums + . = ..() -/datum/controller/process/supply/doWork() - supply_controller.process() - -// Supply shuttle ticker - handles supply point regeneration -// This is called by the process scheduler every thirty seconds -/datum/controller/supply/process() +// Supply shuttle ticker - handles supply point regeneration. Just add points over time. +/datum/controller/subsystem/supply/fire() points += points_per_process +/datum/controller/subsystem/supply/stat_entry() + ..("Points: [points]") + //To stop things being sent to CentCom which should not be sent to centcomm. Recursively checks for these types. -/datum/controller/supply/proc/forbidden_atoms_check(atom/A) +/datum/controller/subsystem/supply/proc/forbidden_atoms_check(atom/A) if(isliving(A)) return 1 if(istype(A,/obj/item/weapon/disk/nuclear)) @@ -86,87 +67,102 @@ var/datum/controller/supply/supply_controller = new() return 1 //Selling -/datum/controller/supply/proc/sell() - var/area/area_shuttle = shuttle.get_location_area() - if(!area_shuttle) - return +/datum/controller/subsystem/supply/proc/sell() + // Loop over each area in the supply shuttle + for(var/area/subarea in shuttle.shuttle_area) + callHook("sell_shuttle", list(subarea)); + for(var/atom/movable/MA in subarea) + if(MA.anchored) + continue - callHook("sell_shuttle", list(area_shuttle)); + var/datum/exported_crate/EC = new /datum/exported_crate() + EC.name = "\proper[MA.name]" + EC.value = 0 + EC.contents = list() + var/base_value = 0 - for(var/atom/movable/MA in area_shuttle) - if(MA.anchored) - continue + // Must be in a crate! + if(istype(MA,/obj/structure/closet/crate)) + var/obj/structure/closet/crate/CR = MA + callHook("sell_crate", list(CR, subarea)) - var/datum/exported_crate/EC = new /datum/exported_crate() - EC.name = "\proper[MA.name]" - EC.value = 0 - EC.contents = list() - var/base_value = 0 + points += CR.points_per_crate + if(CR.points_per_crate) + base_value = CR.points_per_crate + var/find_slip = 1 - // Must be in a crate! - if(istype(MA,/obj/structure/closet/crate)) - var/obj/structure/closet/crate/CR = MA - callHook("sell_crate", list(CR, area_shuttle)) + for(var/atom/A in CR) + EC.contents[++EC.contents.len] = list( + "object" = "\proper[A.name]", + "value" = 0, + "quantity" = 1 + ) - points += CR.points_per_crate - if(CR.points_per_crate) - base_value = CR.points_per_crate - var/find_slip = 1 + // Sell manifests + if(find_slip && istype(A,/obj/item/weapon/paper/manifest)) + var/obj/item/weapon/paper/manifest/slip = A + if(!slip.is_copy && slip.stamped && slip.stamped.len) //yes, the clown stamp will work. clown is the highest authority on the station, it makes sense + points += points_per_slip + EC.contents[EC.contents.len]["value"] = points_per_slip + find_slip = 0 + continue - for(var/atom/A in CR) - EC.contents[++EC.contents.len] = list( - "object" = "\proper[A.name]", - "value" = 0, - "quantity" = 1 + // Sell phoron and platinum + if(istype(A, /obj/item/stack)) + var/obj/item/stack/P = A + if(material_points_conversion[P.get_material_name()]) + EC.contents[EC.contents.len]["value"] = P.get_amount() * material_points_conversion[P.get_material_name()] + EC.contents[EC.contents.len]["quantity"] = P.get_amount() + EC.value += EC.contents[EC.contents.len]["value"] + + //Sell spacebucks + if(istype(A, /obj/item/weapon/spacecash)) + var/obj/item/weapon/spacecash/cashmoney = A + EC.contents[EC.contents.len]["value"] = cashmoney.worth * points_per_money + EC.contents[EC.contents.len]["quantity"] = cashmoney.worth + EC.value += EC.contents[EC.contents.len]["value"] + + + + // Make a log of it, but it wasn't shipped properly, and so isn't worth anything + else + EC.contents = list( + "error" = "Error: Product was improperly packaged. Payment rendered null under terms of agreement." ) - // Sell manifests - if(find_slip && istype(A,/obj/item/weapon/paper/manifest)) - var/obj/item/weapon/paper/manifest/slip = A - if(!slip.is_copy && slip.stamped && slip.stamped.len) //yes, the clown stamp will work. clown is the highest authority on the station, it makes sense - points += points_per_slip - EC.contents[EC.contents.len]["value"] = points_per_slip - find_slip = 0 + exported_crates += EC + points += EC.value + EC.value += base_value + + // Duplicate the receipt for the admin-side log + var/datum/exported_crate/adm = new() + adm.name = EC.name + adm.value = EC.value + adm.contents = deepCopyList(EC.contents) + adm_export_history += adm + + qdel(MA) + +/datum/controller/subsystem/supply/proc/get_clear_turfs() + var/list/clear_turfs = list() + + for(var/area/subarea in shuttle.shuttle_area) + for(var/turf/T in subarea) + if(T.density) + continue + var/occupied = 0 + for(var/atom/A in T.contents) + if(!A.simulated) continue + occupied = 1 + break + if(!occupied) + clear_turfs += T - // Sell phoron and platinum - if(istype(A, /obj/item/stack)) - var/obj/item/stack/P = A - if(material_points_conversion[P.get_material_name()]) - EC.contents[EC.contents.len]["value"] = P.get_amount() * material_points_conversion[P.get_material_name()] - EC.contents[EC.contents.len]["quantity"] = P.get_amount() - EC.value += EC.contents[EC.contents.len]["value"] - - //Sell spacebucks - if(istype(A, /obj/item/weapon/spacecash)) - var/obj/item/weapon/spacecash/cashmoney = A - EC.contents[EC.contents.len]["value"] = cashmoney.worth * points_per_money - EC.contents[EC.contents.len]["quantity"] = cashmoney.worth - EC.value += EC.contents[EC.contents.len]["value"] - - - - // Make a log of it, but it wasn't shipped properly, and so isn't worth anything - else - EC.contents = list( - "error" = "Error: Product was improperly packaged. Payment rendered null under terms of agreement." - ) - - exported_crates += EC - points += EC.value - EC.value += base_value - - // Duplicate the receipt for the admin-side log - var/datum/exported_crate/adm = new() - adm.name = EC.name - adm.value = EC.value - adm.contents = deepCopyList(EC.contents) - adm_export_history += adm - - qdel(MA) + return clear_turfs //Buying -/datum/controller/supply/proc/buy() +/datum/controller/subsystem/supply/proc/buy() var/list/shoppinglist = list() for(var/datum/supply_order/SO in order_history) if(SO.status == SUP_ORDER_APPROVED) @@ -174,26 +170,9 @@ var/datum/controller/supply/supply_controller = new() if(!shoppinglist.len) return - var/orderedamount = shoppinglist.len - var/area/area_shuttle = shuttle.get_location_area() - if(!area_shuttle) - return - - var/list/clear_turfs = list() - - for(var/turf/T in area_shuttle) - if(T.density) - continue - var/contcount - for(var/atom/A in T.contents) - if(!A.simulated) - continue - contcount++ - if(contcount) - continue - clear_turfs += T + var/list/clear_turfs = get_clear_turfs() for(var/datum/supply_order/SO in shoppinglist) if(!clear_turfs.len) @@ -262,9 +241,9 @@ var/datum/controller/supply/supply_controller = new() return // Will attempt to purchase the specified order, returning TRUE on success, FALSE on failure -/datum/controller/supply/proc/approve_order(var/datum/supply_order/O, var/mob/user) +/datum/controller/subsystem/supply/proc/approve_order(var/datum/supply_order/O, var/mob/user) // Not enough points to purchase the crate - if(supply_controller.points <= O.object.cost) + if(points <= O.object.cost) return FALSE // Based on the current model, there shouldn't be any entries in order_history, requestlist, or shoppinglist, that aren't matched in adm_order_history @@ -291,11 +270,11 @@ var/datum/controller/supply/supply_controller = new() adm_order.approved_at = stationdate2text() + " - " + stationtime2text() // Deduct cost - supply_controller.points -= O.object.cost + points -= O.object.cost return TRUE // Will deny the specified order. Only useful if the order is currently requested, but available at any status -/datum/controller/supply/proc/deny_order(var/datum/supply_order/O, var/mob/user) +/datum/controller/subsystem/supply/proc/deny_order(var/datum/supply_order/O, var/mob/user) // Based on the current model, there shouldn't be any entries in order_history, requestlist, or shoppinglist, that aren't matched in adm_order_history var/datum/supply_order/adm_order for(var/datum/supply_order/temp in adm_order_history) @@ -321,22 +300,22 @@ var/datum/controller/supply/supply_controller = new() return // Will deny all requested orders -/datum/controller/supply/proc/deny_all_pending(var/mob/user) +/datum/controller/subsystem/supply/proc/deny_all_pending(var/mob/user) for(var/datum/supply_order/O in order_history) if(O.status == SUP_ORDER_REQUESTED) deny_order(O, user) // Will delete the specified order from the user-side list -/datum/controller/supply/proc/delete_order(var/datum/supply_order/O, var/mob/user) +/datum/controller/subsystem/supply/proc/delete_order(var/datum/supply_order/O, var/mob/user) // Making sure they know what they're doing if(alert(user, "Are you sure you want to delete this record? If it has been approved, cargo points will NOT be refunded!", "Delete Record","No","Yes") == "Yes") if(alert(user, "Are you really sure? There is no way to recover the order once deleted.", "Delete Record", "No", "Yes") == "Yes") log_admin("[key_name(user)] has deleted supply order \ref[O] [O] from the user-side order history.") - supply_controller.order_history -= O + order_history -= O return // Will generate a new, requested order, for the given supply pack type -/datum/controller/supply/proc/create_order(var/datum/supply_pack/S, var/mob/user, var/reason) +/datum/controller/subsystem/supply/proc/create_order(var/datum/supply_pack/S, var/mob/user, var/reason) var/datum/supply_order/new_order = new() var/datum/supply_order/adm_order = new() // Admin-recorded order must be a separate copy in memory, or user-made edits will corrupt it @@ -371,16 +350,16 @@ var/datum/controller/supply/supply_controller = new() adm_order_history += adm_order // Will delete the specified export receipt from the user-side list -/datum/controller/supply/proc/delete_export(var/datum/exported_crate/E, var/mob/user) +/datum/controller/subsystem/supply/proc/delete_export(var/datum/exported_crate/E, var/mob/user) // Making sure they know what they're doing if(alert(user, "Are you sure you want to delete this record?", "Delete Record","No","Yes") == "Yes") if(alert(user, "Are you really sure? There is no way to recover the receipt once deleted.", "Delete Record", "No", "Yes") == "Yes") log_admin("[key_name(user)] has deleted export receipt \ref[E] [E] from the user-side export history.") - supply_controller.exported_crates -= E + exported_crates -= E return // Will add an item entry to the specified export receipt on the user-side list -/datum/controller/supply/proc/add_export_item(var/datum/exported_crate/E, var/mob/user) +/datum/controller/subsystem/supply/proc/add_export_item(var/datum/exported_crate/E, var/mob/user) var/new_name = input(user, "Name", "Please enter the name of the item.") as null|text if(!new_name) return @@ -398,3 +377,21 @@ var/datum/controller/supply/supply_controller = new() "quantity" = new_quantity, "value" = new_value ) + +/datum/exported_crate + var/name + var/value + var/list/contents + +/datum/supply_order + var/ordernum // Unfabricatable index + var/index // Fabricatable index + var/datum/supply_pack/object = null + var/cost // Cost of the supply pack (Fabricatable) (Changes not reflected when purchasing supply packs, this is cosmetic only) + var/name // Name of the supply pack datum (Fabricatable) + var/ordered_by = null // Who requested the order + var/comment = null // What reason was given for the order + var/approved_by = null // Who approved the order + var/ordered_at // Date and time the order was requested at + var/approved_at // Date and time the order was approved at + var/status // [Requested, Accepted, Denied, Shipped] diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm new file mode 100644 index 0000000000..984b4448c1 --- /dev/null +++ b/code/controllers/subsystems/ticker.dm @@ -0,0 +1,559 @@ +// +// Ticker controls the state of the game, being responsible for round start, game mode, and round end. +// +SUBSYSTEM_DEF(ticker) + name = "Gameticker" + wait = 2 SECONDS + init_order = INIT_ORDER_TICKER + priority = FIRE_PRIORITY_TICKER + flags = SS_NO_TICK_CHECK | SS_KEEP_TIMING + runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME // Every runlevel! + + var/const/restart_timeout = 3 MINUTES // Default time to wait before rebooting in desiseconds. + var/current_state = GAME_STATE_INIT // We aren't even at pregame yet // TODO replace with CURRENT_GAME_STATE + + /* Relies upon the following globals (TODO move those in here) */ + // var/master_mode = "extended" //The underlying game mode (so "secret" or the voted mode). + // Set by SSvote when VOTE_GAMEMODE finishes. + // var/round_progressing = 1 //Whether the lobby clock is ticking down. + + var/pregame_timeleft = 0 // Time remaining until game starts in seconds. Set by config + var/start_immediately = FALSE // If true there is no lobby phase, the game starts immediately. + + var/hide_mode = FALSE // If the true game mode should be hidden (because we chose "secret") + var/datum/game_mode/mode = null // The actual gamemode, if selected. + + var/end_game_state = END_GAME_NOT_OVER // Track where we are ending game/round + var/restart_timeleft // Time remaining until restart in desiseconds + var/last_restart_notify // world.time of last restart warning. + var/delay_end = FALSE // If set, the round will not restart on its own. + + // var/login_music // music played in pregame lobby // VOREStation Edit - We do music differently + + var/list/datum/mind/minds = list() // The people in the game. Used for objective tracking. + + // TODO - I am sure there is a better place these can go. + var/Bible_icon_state // icon_state the chaplain has chosen for his bible + var/Bible_item_state // item_state the chaplain has chosen for his bible + var/Bible_name // name of the bible + var/Bible_deity_name + + var/random_players = FALSE // If set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders + + // TODO - Should this go here or in the job subsystem? + var/triai = FALSE // Global flag for Triumvirate AI being enabled + + //station_explosion used to be a variable for every mob's hud. Which was a waste! + //Now we have a general cinematic centrally held within the gameticker....far more efficient! + var/obj/screen/cinematic = null + +// This global variable exists for legacy support so we don't have to rename every 'ticker' to 'SSticker' yet. +var/global/datum/controller/subsystem/ticker/ticker +/datum/controller/subsystem/ticker/PreInit() + global.ticker = src // TODO - Remove this! Change everything to point at SSticker intead + +/datum/controller/subsystem/ticker/Initialize() + pregame_timeleft = config.pregame_time + send2mainirc("Server lobby is loaded and open at byond://[config.serverurl ? config.serverurl : (config.server ? config.server : "[world.address]:[world.port]")]") + return ..() + +/datum/controller/subsystem/ticker/fire(resumed = FALSE) + switch(current_state) + if(GAME_STATE_INIT) + pregame_welcome() + current_state = GAME_STATE_PREGAME + if(GAME_STATE_PREGAME) + pregame_tick() + if(GAME_STATE_SETTING_UP) + setup_tick() + if(GAME_STATE_PLAYING) + playing_tick() + if(GAME_STATE_FINISHED) + post_game_tick() + +/datum/controller/subsystem/ticker/proc/pregame_welcome() + to_chat(world, "Welcome to the pregame lobby!") + to_chat(world, "Please set up your character and select ready. The round will start in [pregame_timeleft] seconds.") + +// Called during GAME_STATE_PREGAME (RUNLEVEL_LOBBY) +/datum/controller/subsystem/ticker/proc/pregame_tick() + if(round_progressing && last_fire) + pregame_timeleft -= (world.time - last_fire) / (1 SECOND) + + if(start_immediately) + pregame_timeleft = 0 + else if(SSvote.time_remaining) + return // vote still going, wait for it. + + // Time to start the game! + if(pregame_timeleft <= 0) + current_state = GAME_STATE_SETTING_UP + Master.SetRunLevel(RUNLEVEL_SETUP) + if(start_immediately) + fire() // Don't wait for next tick, do it now! + return + + if(pregame_timeleft <= config.vote_autogamemode_timeleft && !SSvote.gamemode_vote_called) + SSvote.autogamemode() // Start the game mode vote (if we haven't had one already) + +// Called during GAME_STATE_SETTING_UP (RUNLEVEL_SETUP) +/datum/controller/subsystem/ticker/proc/setup_tick(resumed = FALSE) + if(!setup_choose_gamemode()) + // It failed, go back to lobby state and re-send the welcome message + pregame_timeleft = config.pregame_time + SSvote.gamemode_vote_called = FALSE // Allow another autogamemode vote + current_state = GAME_STATE_PREGAME + Master.SetRunLevel(RUNLEVEL_LOBBY) + pregame_welcome() + return + // If we got this far we succeeded in picking a game mode. Punch it! + setup_startgame() + return + +// Formerly the first half of setup() - The part that chooses the game mode. +// Returns 0 if failed to pick a mode, otherwise 1 +/datum/controller/subsystem/ticker/proc/setup_choose_gamemode() + //Create and announce mode + if(master_mode == "secret") + src.hide_mode = TRUE + + var/list/runnable_modes = config.get_runnable_modes() + if((master_mode == "random") || (master_mode == "secret")) + if(!runnable_modes.len) + to_chat(world, "Unable to choose playable game mode. Reverting to pregame lobby.") + return 0 + if(secret_force_mode != "secret") + src.mode = config.pick_mode(secret_force_mode) + if(!src.mode) + var/list/weighted_modes = list() + for(var/datum/game_mode/GM in runnable_modes) + weighted_modes[GM.config_tag] = config.probabilities[GM.config_tag] + src.mode = gamemode_cache[pickweight(weighted_modes)] + else + src.mode = config.pick_mode(master_mode) + + if(!src.mode) + to_chat(world, "Serious error in mode setup! Reverting to pregame lobby.") //Uses setup instead of set up due to computational context. + return 0 + + job_master.ResetOccupations() + src.mode.create_antagonists() + src.mode.pre_setup() + job_master.DivideOccupations() // Apparently important for new antagonist system to register specific job antags properly. + + if(!src.mode.can_start()) + to_world("Unable to start [mode.name]. Not enough players readied, [config.player_requirements[mode.config_tag]] players needed. Reverting to pregame lobby.") + mode.fail_setup() + mode = null + job_master.ResetOccupations() + return 0 + + if(hide_mode) + to_world("The current game mode is - Secret!") + if(runnable_modes.len) + var/list/tmpmodes = new + for (var/datum/game_mode/M in runnable_modes) + tmpmodes+=M.name + tmpmodes = sortList(tmpmodes) + if(tmpmodes.len) + to_chat(world, "Possibilities: [english_list(tmpmodes, and_text= "; ", comma_text = "; ")]") + else + src.mode.announce() + return 1 + +// Formerly the second half of setup() - The part that actually initializes everything and starts the game. +/datum/controller/subsystem/ticker/proc/setup_startgame() + setup_economy() + create_characters() //Create player characters and transfer them. + collect_minds() + equip_characters() + //data_core.manifest() //VOREStation Removal + + callHook("roundstart") + + spawn(0)//Forking here so we dont have to wait for this to finish + mode.post_setup() + //Cleanup some stuff + for(var/obj/effect/landmark/start/S in landmarks_list) + //Deleting Startpoints but we need the ai point to AI-ize people later + if (S.name != "AI") + qdel(S) + to_chat(world, "Enjoy the game!") + world << sound('sound/AI/welcome.ogg') // Skie + //Holiday Round-start stuff ~Carn + Holiday_Game_Start() + + var/list/adm = get_admin_counts() + if(adm["total"] == 0) + send2adminirc("A round has started with no admins online.") + +/* supply_controller.process() //Start the supply shuttle regenerating points -- TLE // handled in scheduler + master_controller.process() //Start master_controller.process() + lighting_controller.process() //Start processing DynamicAreaLighting updates + */ + + current_state = GAME_STATE_PLAYING + Master.SetRunLevel(RUNLEVEL_GAME) + + if(config.sql_enabled) + statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE + + return 1 + + +// Called during GAME_STATE_PLAYING (RUNLEVEL_GAME) +/datum/controller/subsystem/ticker/proc/playing_tick(resumed = FALSE) + mode.process() // So THIS is where we run mode.process() huh? Okay + + if(mode.explosion_in_progress) + return // wait until explosion is done. + + // Calculate if game and/or mode are finished (Complicated by the continuous_rounds config option) + var/game_finished = FALSE + var/mode_finished = FALSE + if (config.continous_rounds) // Game keeps going after mode ends. + game_finished = (emergency_shuttle.returned() || mode.station_was_nuked) + mode_finished = ((end_game_state >= END_GAME_MODE_FINISHED) || mode.check_finished()) // Short circuit if already finished. + else // Game ends when mode does + game_finished = (mode.check_finished() || (emergency_shuttle.returned() && emergency_shuttle.evac == 1)) || universe_has_ended + mode_finished = game_finished + + if(game_finished && mode_finished) + end_game_state = END_GAME_READY_TO_END + current_state = GAME_STATE_FINISHED + Master.SetRunLevel(RUNLEVEL_POSTGAME) + INVOKE_ASYNC(src, .proc/declare_completion) + else if (mode_finished && (end_game_state < END_GAME_MODE_FINISHED)) + end_game_state = END_GAME_MODE_FINISHED // Only do this cleanup once! + mode.cleanup() + //call a transfer shuttle vote + to_chat(world, "The round has ended!") + SSvote.autotransfer() + +// Called during GAME_STATE_FINISHED (RUNLEVEL_POSTGAME) +/datum/controller/subsystem/ticker/proc/post_game_tick() + switch(end_game_state) + if(END_GAME_READY_TO_END) + callHook("roundend") + + if (mode.station_was_nuked) + feedback_set_details("end_proper", "nuke") + restart_timeleft = 1 MINUTE // No point waiting five minutes if everyone's dead. + if(!delay_end) + to_chat(world, "Rebooting due to destruction of [station_name()] in [round(restart_timeleft/600)] minute\s.") + last_restart_notify = world.time + else + feedback_set_details("end_proper", "proper completion") + restart_timeleft = restart_timeout + + if(blackbox) + blackbox.save_all_data_to_sql() // TODO - Blackbox or statistics subsystem + + end_game_state = END_GAME_ENDING + return + if(END_GAME_ENDING) + restart_timeleft -= (world.time - last_fire) + if(delay_end) + to_chat(world, "An admin has delayed the round end.") + end_game_state = END_GAME_DELAYED + else if(restart_timeleft <= 0) + world.Reboot() + else if (world.time - last_restart_notify >= 1 MINUTE) + to_chat(world, "Restarting in [round(restart_timeleft/600, 1)] minute\s.") + last_restart_notify = world.time + return + if(END_GAME_DELAYED) + restart_timeleft -= (world.time - last_fire) + if(!delay_end) + end_game_state = END_GAME_ENDING + else + log_error("Ticker arrived at round end in an unexpected endgame state '[end_game_state]'.") + end_game_state = END_GAME_READY_TO_END + + +// ---------------------------------------------------------------------- +// These two below are not used! But they could be + +// Use these preferentially to directly examining ticker.current_state to help prepare for transition to ticker as subsystem! + +/datum/controller/subsystem/ticker/proc/PreRoundStart() + return (current_state < GAME_STATE_PLAYING) + +/datum/controller/subsystem/ticker/proc/IsSettingUp() + return (current_state == GAME_STATE_SETTING_UP) + +/datum/controller/subsystem/ticker/proc/IsRoundInProgress() + return (current_state == GAME_STATE_PLAYING) + +/datum/controller/subsystem/ticker/proc/HasRoundStarted() + return (current_state >= GAME_STATE_PLAYING) + +// ------------------------------------------------------------------------ +// HELPER PROCS! +// ------------------------------------------------------------------------ + +//Plus it provides an easy way to make cinematics for other events. Just use this as a template :) +/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null) + if( cinematic ) return //already a cinematic in progress! + + //initialise our cinematic screen object + cinematic = new(src) + cinematic.icon = 'icons/effects/station_explosion.dmi' + cinematic.icon_state = "station_intact" + cinematic.layer = 100 + cinematic.plane = PLANE_PLAYER_HUD + cinematic.mouse_opacity = 0 + cinematic.screen_loc = "1,0" + + var/obj/structure/bed/temp_buckle = new(src) + //Incredibly hackish. It creates a bed within the gameticker (lol) to stop mobs running around + if(station_missed) + for(var/mob/living/M in living_mob_list) + M.buckled = temp_buckle //buckles the mob so it can't do anything + if(M.client) + M.client.screen += cinematic //show every client the cinematic + else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" + for(var/mob/living/M in living_mob_list) + M.buckled = temp_buckle + if(M.client) + M.client.screen += cinematic + + switch(M.z) + if(0) //inside a crate or something + var/turf/T = get_turf(M) + if(T && T.z in using_map.station_levels) //we don't use M.death(0) because it calls a for(/mob) loop and + M.health = 0 + M.set_stat(DEAD) + if(1) //on a z-level 1 turf. + M.health = 0 + M.set_stat(DEAD) + + //Now animate the cinematic + switch(station_missed) + if(1) //nuke was nearby but (mostly) missed + if( mode && !override ) + override = mode.name + switch( override ) + if("mercenary") //Nuke wasn't on station when it blew up + flick("intro_nuke",cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + flick("station_intact_fade_red",cinematic) + cinematic.icon_state = "summary_nukefail" + else + flick("intro_nuke",cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + //flick("end",cinematic) + + + if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation + sleep(50) + world << sound('sound/effects/explosionfar.ogg') + + + else //station was destroyed + if( mode && !override ) + override = mode.name + switch( override ) + if("mercenary") //Nuke Ops successfully bombed the station + flick("intro_nuke",cinematic) + sleep(35) + flick("station_explode_fade_red",cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_nukewin" + if("AI malfunction") //Malf (screen,explosion,summary) + flick("intro_malf",cinematic) + sleep(76) + flick("station_explode_fade_red",cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_malf" + if("blob") //Station nuked (nuke,explosion,summary) + flick("intro_nuke",cinematic) + sleep(35) + flick("station_explode_fade_red",cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + else //Station nuked (nuke,explosion,summary) + flick("intro_nuke",cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + for(var/mob/living/M in living_mob_list) + if(M.loc.z in using_map.station_levels) + M.death()//No mercy + //If its actually the end of the round, wait for it to end. + //Otherwise if its a verb it will continue on afterwards. + sleep(300) + + if(cinematic) qdel(cinematic) //end the cinematic + if(temp_buckle) qdel(temp_buckle) //release everybody + return + + +/datum/controller/subsystem/ticker/proc/create_characters() + for(var/mob/new_player/player in player_list) + if(player && player.ready && player.mind) + if(player.mind.assigned_role=="AI") + player.close_spawn_windows() + player.AIize() + else if(!player.mind.assigned_role) + continue + else + //VOREStation Edit Start + var/mob/living/carbon/human/new_char = player.create_character() + if(new_char) + qdel(player) + if(istype(new_char) && !(new_char.mind.assigned_role=="Cyborg")) + data_core.manifest_inject(new_char) + //VOREStation Edit End + + +/datum/controller/subsystem/ticker/proc/collect_minds() + for(var/mob/living/player in player_list) + if(player.mind) + minds += player.mind + + +/datum/controller/subsystem/ticker/proc/equip_characters() + var/captainless=1 + for(var/mob/living/carbon/human/player in player_list) + if(player && player.mind && player.mind.assigned_role) + if(player.mind.assigned_role == "Colony Director") + captainless=0 + if(!player_is_antag(player.mind, only_offstation_roles = 1)) + job_master.EquipRank(player, player.mind.assigned_role, 0) + UpdateFactionList(player) + //equip_custom_items(player) //VOREStation Removal + //player.apply_traits() //VOREStation Removal + if(captainless) + for(var/mob/M in player_list) + if(!istype(M,/mob/new_player)) + to_chat(M, "Colony Directorship not forced on anyone.") + + +/datum/controller/subsystem/ticker/proc/declare_completion() + to_world("


A round of [mode.name] has ended!

") + for(var/mob/Player in player_list) + if(Player.mind && !isnewplayer(Player)) + if(Player.stat != DEAD) + var/turf/playerTurf = get_turf(Player) + if(emergency_shuttle.departed && emergency_shuttle.evac) + if(isNotAdminLevel(playerTurf.z)) + to_chat(Player, "You survived the round, but remained on [station_name()] as [Player.real_name].") + else + to_chat(Player, "You managed to survive the events on [station_name()] as [Player.real_name].") + else if(isAdminLevel(playerTurf.z)) + to_chat(Player, "You successfully underwent crew transfer after events on [station_name()] as [Player.real_name].") + else if(issilicon(Player)) + to_chat(Player, "You remain operational after the events on [station_name()] as [Player.real_name].") + else + to_chat(Player, "You missed the crew transfer after the events on [station_name()] as [Player.real_name].") + else + if(istype(Player,/mob/observer/dead)) + var/mob/observer/dead/O = Player + if(!O.started_as_observer) + to_chat(Player, "You did not survive the events on [station_name()]...") + else + to_chat(Player, "You did not survive the events on [station_name()]...") + to_world("
") + + for (var/mob/living/silicon/ai/aiPlayer in mob_list) + if (aiPlayer.stat != 2) + to_world("[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the round were:") + else + to_world("[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") + aiPlayer.show_laws(1) + + if (aiPlayer.connected_robots.len) + var/robolist = "The AI's loyal minions were: " + for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) + robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" + to_world("[robolist]") + + var/dronecount = 0 + + for (var/mob/living/silicon/robot/robo in mob_list) + + if(istype(robo,/mob/living/silicon/robot/drone) && !istype(robo,/mob/living/silicon/robot/drone/swarm)) + dronecount++ + continue + + if (!robo.connected_ai) + if (robo.stat != 2) + to_world("[robo.name] (Played by: [robo.key]) survived as an AI-less stationbound synthetic! Its laws were:") + else + to_world("[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a stationbound synthetic without an AI. Its laws were:") + + if(robo) //How the hell do we lose robo between here and the world messages directly above this? + robo.laws.show_laws(world) + + if(dronecount) + to_world("There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] at the end of this round.") + + mode.declare_completion()//To declare normal completion. + + //Ask the event manager to print round end information + SSevents.RoundEnd() + + //Print a list of antagonists to the server log + var/list/total_antagonists = list() + //Look into all mobs in world, dead or alive + for(var/datum/mind/Mind in minds) + var/temprole = Mind.special_role + if(temprole) //if they are an antagonist of some sort. + if(temprole in total_antagonists) //If the role exists already, add the name to it + total_antagonists[temprole] += ", [Mind.name]([Mind.key])" + else + total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob + total_antagonists[temprole] += ": [Mind.name]([Mind.key])" + + //Now print them all into the log! + log_game("Antagonists at round end were...") + for(var/i in total_antagonists) + log_game("[i]s[total_antagonists[i]].") + + return 1 + +/datum/controller/subsystem/ticker/stat_entry() + switch(current_state) + if(GAME_STATE_INIT) + ..() + if(GAME_STATE_PREGAME) // RUNLEVEL_LOBBY + ..("START [round_progressing ? "[round(pregame_timeleft)]s" : "(PAUSED)"]") + if(GAME_STATE_SETTING_UP) // RUNLEVEL_SETUP + ..("SETUP") + if(GAME_STATE_PLAYING) // RUNLEVEL_GAME + ..("GAME") + if(GAME_STATE_FINISHED) // RUNLEVEL_POSTGAME + switch(end_game_state) + if(END_GAME_MODE_FINISHED) + ..("MODE OVER, WAITING") + if(END_GAME_READY_TO_END) + ..("ENDGAME PROCESSING") + if(END_GAME_ENDING) + ..("END IN [round(restart_timeleft/10)]s") + if(END_GAME_DELAYED) + ..("END PAUSED") + else + ..("ENDGAME ERROR:[end_game_state]") + +/datum/controller/subsystem/ticker/Recover() + flags |= SS_NO_INIT // Don't initialize again + + current_state = SSticker.current_state + mode = SSticker.mode + pregame_timeleft = SSticker.pregame_timeleft + + end_game_state = SSticker.end_game_state + delay_end = SSticker.delay_end + restart_timeleft = SSticker.restart_timeleft + + minds = SSticker.minds + + Bible_icon_state = SSticker.Bible_icon_state + Bible_item_state = SSticker.Bible_item_state + Bible_name = SSticker.Bible_name + Bible_deity_name = SSticker.Bible_deity_name + random_players = SSticker.random_players diff --git a/code/controllers/subsystems/vote.dm b/code/controllers/subsystems/vote.dm index c4a720ad62..5bd74eaea9 100644 --- a/code/controllers/subsystems/vote.dm +++ b/code/controllers/subsystems/vote.dm @@ -13,6 +13,7 @@ SUBSYSTEM_DEF(vote) var/duration var/mode var/question + var/gamemode_vote_called = FALSE // Have we had a gamemode vote this round? Only auto-call if we haven't var/list/choices = list() var/list/gamemode_names = list() var/list/voted = list() @@ -104,7 +105,7 @@ SUBSYSTEM_DEF(vote) else factor = 1.4 choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) - world << "Crew Transfer Factor: [factor]" + to_world("Crew Transfer Factor: [factor]") greatest_votes = max(choices["Initiate Crew Transfer"], choices["Extend the Shift"]) //VOREStation Edit . = list() // Get all options with that many votes and return them in a list @@ -166,10 +167,10 @@ SUBSYSTEM_DEF(vote) if(mode == VOTE_GAMEMODE) //fire this even if the vote fails. if(!round_progressing) round_progressing = 1 - world << "The round will start soon." + to_world("The round will start soon.") if(restart) - world << "World restarting due to vote..." + to_world("World restarting due to vote...") feedback_set_details("end_error", "restart vote") if(blackbox) blackbox.save_all_data_to_sql() @@ -191,7 +192,7 @@ SUBSYSTEM_DEF(vote) /datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period) if(!mode) - if(started_time != null && !(check_rights(R_ADMIN) || automatic)) + if(started_time != null && !(check_rights(R_ADMIN|R_EVENT) || automatic)) var/next_allowed_time = (started_time + config.vote_delay) if(next_allowed_time > world.time) return 0 @@ -213,12 +214,12 @@ SUBSYSTEM_DEF(vote) additional_text.Add("") gamemode_names["secret"] = "Secret" if(VOTE_CREW_TRANSFER) - if(!check_rights(R_ADMIN|R_MOD, 0)) // The gods care not for the affairs of the mortals + if(!check_rights(R_ADMIN|R_MOD|R_EVENT, 0)) // The gods care not for the affairs of the mortals if(get_security_level() == "red" || get_security_level() == "delta") - initiator_key << "The current alert status is too high to call for a crew transfer!" + to_chat(initiator_key, "The current alert status is too high to call for a crew transfer!") return 0 if(ticker.current_state <= GAME_STATE_SETTING_UP) - initiator_key << "The crew transfer button has been disabled!" + to_chat(initiator_key, "The crew transfer button has been disabled!") return 0 question = "Your PDA beeps with a message from Central. Would you like an additional four hours to finish ongoing projects?" //Yawn Wider Edit choices.Add("Initiate Crew Transfer", "Extend the Shift") //VOREStation Edit @@ -252,13 +253,15 @@ SUBSYSTEM_DEF(vote) log_vote(text) - world << "[text]\nType vote or click here to place your votes.\nYou have [config.vote_period / 10] seconds to vote." + to_world("[text]\nType vote or click here to place your votes.\nYou have [config.vote_period / 10] seconds to vote.") if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM) - world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3) + world << sound('sound/misc/notice1.ogg', repeat = 0, wait = 0, volume = 50, channel = 3) //YW Edit + if(mode == VOTE_GAMEMODE && round_progressing) + gamemode_vote_called = TRUE round_progressing = 0 - world << "Round start has been delayed." + to_world("Round start has been delayed.") time_remaining = round(config.vote_period / 10) return 1 @@ -269,7 +272,7 @@ SUBSYSTEM_DEF(vote) return var/admin = FALSE if(C.holder) - if(C.holder.rights & R_ADMIN) + if(C.holder.rights & R_ADMIN|R_EVENT) admin = TRUE . = "Voting Panel" @@ -350,7 +353,8 @@ SUBSYSTEM_DEF(vote) if("cancel") if(usr.client.holder) - reset() + if("Yes" == alert(usr, "You are about to cancel this vote. Are you sure?", "Cancel Vote", "No", "Yes")) + reset() if("toggle_restart") if(usr.client.holder) config.allow_vote_restart = !config.allow_vote_restart diff --git a/code/controllers/subsystems/xenoarch.dm b/code/controllers/subsystems/xenoarch.dm index d863ad8269..2e22213ee6 100644 --- a/code/controllers/subsystems/xenoarch.dm +++ b/code/controllers/subsystems/xenoarch.dm @@ -30,7 +30,7 @@ SUBSYSTEM_DEF(xenoarch) . = ..() /datum/controller/subsystem/xenoarch/proc/SetupXenoarch() - for(var/turf/simulated/mineral/M in turfs) + for(var/turf/simulated/mineral/M in world) if(!M.density || M.z in using_map.xenoarch_exempt_levels) continue @@ -99,6 +99,9 @@ SUBSYSTEM_DEF(xenoarch) if(isnull(M.artifact_find) && digsite != DIGSITE_GARDEN && digsite != DIGSITE_ANIMAL) artifact_spawning_turfs.Add(archeo_turf) + //Larger maps will convince byond this is an infinite loop, so let go for a second + CHECK_TICK + //create artifact machinery var/num_artifacts_spawn = rand(ARTIFACTSPAWNNUM_LOWER, ARTIFACTSPAWNNUM_UPPER) while(artifact_spawning_turfs.len > num_artifacts_spawn) diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 7e2e1a42e3..df1aa91ea3 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -89,19 +89,15 @@ //Goon PS stuff, and other yet-to-be-subsystem things. options["LEGACY: master_controller"] = master_controller - options["LEGACY: ticker"] = ticker - options["LEGACY: tickerProcess"] = tickerProcess options["LEGACY: air_master"] = air_master options["LEGACY: job_master"] = job_master options["LEGACY: radio_controller"] = radio_controller - options["LEGACY: supply_controller"] = supply_controller options["LEGACY: emergency_shuttle"] = emergency_shuttle options["LEGACY: paiController"] = paiController options["LEGACY: cameranet"] = cameranet options["LEGACY: transfer_controller"] = transfer_controller options["LEGACY: gas_data"] = gas_data options["LEGACY: plant_controller"] = plant_controller - options["LEGACY: alarm_manager"] = alarm_manager var/pick = input(mob, "Choose a controller to debug/view variables of.", "VV controller:") as null|anything in options if(!pick) @@ -112,28 +108,3 @@ feedback_add_details("admin_verb", "DebugController") message_admins("Admin [key_name_admin(mob)] is debugging the [pick] controller.") debug_variables(D) - -//VOREStation Edit Begin -/client/proc/debug_process_scheduler() - set category = "Debug" - set name = "Debug Process Scheduler" - set desc = "Debug the process scheduler itself. For vulpine use only." - - if(!check_rights(R_DEBUG)) return - if(config.debugparanoid && !check_rights(R_ADMIN)) return - debug_variables(processScheduler) - feedback_add_details("admin_verb", "DProcSchd") - message_admins("Admin [key_name_admin(usr)] is debugging the process scheduler.") - -/client/proc/debug_process(controller in processScheduler.nameToProcessMap) - set category = "Debug" - set name = "Debug Process Controller" - set desc = "Debug one of the periodic loop background task controllers for the game (be careful!)" - - if(!check_rights(R_DEBUG)) return - if(config.debugparanoid && !check_rights(R_ADMIN)) return - var/datum/controller/process/P = processScheduler.nameToProcessMap[controller] - debug_variables(P) - feedback_add_details("admin_verb", "DProcCtrl") - message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") -//VOREStation Edit End \ No newline at end of file diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index a8e7ed7ce9..3bd5b6e304 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -227,9 +227,9 @@ var/global/const/base_law_type = /datum/ai_laws/nanotrasen if(law == zeroth_law_borg) continue if(law == zeroth_law) - who << "[law.get_index()]. [law.law]" + to_chat(who, "[law.get_index()]. [law.law]") else - who << "[law.get_index()]. [law.law]" + to_chat(who, "[law.get_index()]. [law.law]") /******************** * Stating Laws * diff --git a/code/datums/autolathe/arms_1.dm b/code/datums/autolathe/arms_1.dm new file mode 100644 index 0000000000..747e9e36bb --- /dev/null +++ b/code/datums/autolathe/arms_1.dm @@ -0,0 +1,342 @@ +/datum/design/item/autolathe + var/hidden = 0 + build_type = AUTOLATHE //Flag as to what kind machine the design is built in. See defines. + +/datum/design/item/autolathe/arms //Datum for object designs, used in construction //IDs of that techs the object originated from and the minimum level requirements. + category = "Arms and Ammunition" //category item goes to + + +//////////////// +/*Ammo casings*/ +//////////////// +//Shotgun + +/datum/design/item/autolathe/arms/shotgun_blanks + name = "ammunition (12g, blank)" + id = "shotgun_blanks" + build_path = /obj/item/ammo_casing/a12g/blank + time = 10 + materials = list(DEFAULT_WALL_MATERIAL = 112) + +/datum/design/item/autolathe/arms/shotgun_beanbag + name = "ammunition (12g, beanbag)" + id = "shotgun_beanbag" + build_path = /obj/item/ammo_casing/a12g/beanbag + time = 10 + materials = list(DEFAULT_WALL_MATERIAL = 225) + +/datum/design/item/autolathe/arms/shotgun_flash + name = "ammunition (12g, flash)" + id = "shotgun_flash" + build_path = /obj/item/ammo_casing/a12g/flash + time = 10 + materials = list(DEFAULT_WALL_MATERIAL = 112, "glass" = 112) + +/datum/design/item/autolathe/arms/shotgun_slug + name = "ammunition (12g, slug)" + id = "shotgun_slug" + build_path = /obj/item/ammo_casing/a12g + time = 10 + materials = list(DEFAULT_WALL_MATERIAL = 450) + hidden = 1 + +/datum/design/item/autolathe/arms/shotgun_pellet + name = "ammunition (12g, pellet)" + id = "shotgun_pellet" + build_path = /obj/item/ammo_casing/a12g/pellet + time = 10 + materials = list(DEFAULT_WALL_MATERIAL = 450) + hidden = 1 + +/datum/design/item/autolathe/arms/stunshell + name = "ammunition (stun cartridge, shotgun)" + id = "shotgun_stunshell" + build_path = /obj/item/ammo_casing/a12g/stunshell + time = 10 + materials = list(DEFAULT_WALL_MATERIAL = 450, "glass" = 900) + hidden = 1 + +////////////////// +/*Ammo magazines*/ +////////////////// + +/////// .45 +/datum/design/item/autolathe/arms/pistol_45 + name = "pistol magazine (.45)" + id = "pistol_45" + build_path = /obj/item/ammo_magazine/m45 + time = 10 + materials = list(DEFAULT_WALL_MATERIAL = 656) + hidden = 1 + +/datum/design/item/autolathe/arms/pistol_45p + name = "pistol magazine (.45 practice)" + id = "pistol_45p" + build_path = /obj/item/ammo_magazine/m45/practice + materials = list(DEFAULT_WALL_MATERIAL = 656) + + +/datum/design/item/autolathe/arms/pistol_45r + name = "pistol magazine (.45 rubber)" + id = "pistol_45r" + build_path = /obj/item/ammo_magazine/m45/rubber + materials = list(DEFAULT_WALL_MATERIAL = 656) + +/datum/design/item/autolathe/arms/pistol_45f + name = "pistol magazine (.45 flash)" + id = "pistol_45f" + build_path = /obj/item/ammo_magazine/m45/flash + materials = list(DEFAULT_WALL_MATERIAL = 656) + +/datum/design/item/autolathe/arms/pistol_45uzi + name = "uzi magazine (.45)" + id = "pistol_45uzi" + build_path = /obj/item/ammo_magazine/m45uzi + materials = list(DEFAULT_WALL_MATERIAL = 656) + hidden = 1 + +/datum/design/item/autolathe/arms/tommymag + name = "Tommy Gun magazine (.45)" + id = "tommymag" + build_path = /obj/item/ammo_magazine/m45tommy + materials = list(DEFAULT_WALL_MATERIAL = 1875) + hidden = 1 + +/datum/design/item/autolathe/arms/tommydrum + name = "Tommy Gun drum magazine (.45)" + id = "tommydrum" + build_path = /obj/item/ammo_magazine/m45tommydrum + materials = list(DEFAULT_WALL_MATERIAL = 4687) + hidden = 1 + +/////// 9mm + +// Full size pistol mags. + +/datum/design/item/autolathe/arms/pistol_9mm + name = "pistol magazine (9mm)" + id = "pistol_9mm" + build_path = /obj/item/ammo_magazine/m9mm + materials = list(DEFAULT_WALL_MATERIAL = 750) + hidden = 1 + +/datum/design/item/autolathe/arms/pistol_9mmr + name = "pistol magazine (9mm rubber)" + id = "pistol_9mm_rubber" + build_path = /obj/item/ammo_magazine/m9mm/rubber + materials = list(DEFAULT_WALL_MATERIAL = 750) + + +/datum/design/item/autolathe/arms/pistol_9mmp + name = "pistol magazine (9mm practice)" + id = "pistol_9mm_practice" + build_path = /obj/item/ammo_magazine/m9mm/practice + materials = list(DEFAULT_WALL_MATERIAL = 750) + +/datum/design/item/autolathe/arms/pistol_9mmf + name = "pistol magazine (9mm flash)" + id = "pistol_9mm_flash" + build_path = /obj/item/ammo_magazine/m9mm/flash + materials = list(DEFAULT_WALL_MATERIAL = 750) + +/datum/design/item/autolathe/arms/pistol_9mm_compact + name = "compact pistol magazine (9mm)" + id = "pistol_9mm_compact" + build_path = /obj/item/ammo_magazine/m9mm/compact + materials = list(DEFAULT_WALL_MATERIAL = 750) + hidden = 1 + +/datum/design/item/autolathe/arms/pistol_9mmr_compact + name = "compact pistol magazine (9mm rubber)" + id = "pistol_9mm_compact_rubber" + build_path = /obj/item/ammo_magazine/m9mm/compact/rubber + materials = list(DEFAULT_WALL_MATERIAL = 750) + hidden = 1 +/datum/design/item/autolathe/arms/pistol_9mmp_compact + name = "compact pistol magazine (9mm)" + id = "pistol_9mm_compact_practice" + build_path = /obj/item/ammo_magazine/m9mm/compact/practice + materials = list(DEFAULT_WALL_MATERIAL = 750) + hidden = 1 + +/datum/design/item/autolathe/arms/pistol_9mmf_compact + name = "compact pistol magazine (9mm)" + id = "pistol_9mm_compact_flash" + build_path = /obj/item/ammo_magazine/m9mm/compact/flash + materials = list(DEFAULT_WALL_MATERIAL = 750) + hidden = 1 + +/////// SMG Mags + +/datum/design/item/autolathe/arms/smg_9mm + name = "top-mounted SMG magazine (9mm)" + id = "smg_9mm" + build_path = /obj/item/ammo_magazine/m9mmt + materials = list(DEFAULT_WALL_MATERIAL = 750) + hidden = 1 + +/datum/design/item/autolathe/arms/smg_9mmr + name = "top-mounted SMG magazine (9mm rubber)" + id = "smg_9mmr" + build_path = /obj/item/ammo_magazine/m9mmt/rubber + materials = list(DEFAULT_WALL_MATERIAL = 380) + +/datum/design/item/autolathe/arms/smg_9mmp + name = "top-mounted SMG magazine (9mm practice)" + id = "smg_9mmp" + build_path = /obj/item/ammo_magazine/m9mmt/practice + materials = list(DEFAULT_WALL_MATERIAL = 380) + +/datum/design/item/autolathe/arms/smg_9mmf + name = "top-mounted SMG magazine (9mm flash)" + id = "smg_9mmf" + build_path = /obj/item/ammo_magazine/m9mmt/flash + materials = list(DEFAULT_WALL_MATERIAL = 380) + + +/////// 10mm + +/datum/design/item/autolathe/arms/smg_10mm + name = "SMG magazine (10mm)" + id = "smg_10mm" + build_path = /obj/item/ammo_magazine/m10mm + materials = list(DEFAULT_WALL_MATERIAL = 1875) + +/datum/design/item/autolathe/arms/pistol_44 + name = "pistol magazine (.44)" + id = "pistol_44" + build_path = /obj/item/ammo_magazine/m44 + materials = list(DEFAULT_WALL_MATERIAL = 1575) + +/////// 5.45mm +/datum/design/item/autolathe/arms/rifle_545 + name = "rifle magazine (5.45mm)" + id = "rifle_545" + build_path = /obj/item/ammo_magazine/m545 + materials = list(DEFAULT_WALL_MATERIAL = 2250) + hidden = 1 + +/datum/design/item/autolathe/arms/rifle_545p + name = "rifle magazine (5.45mm practice)" + id = "rifle_545_practice" + build_path = /obj/item/ammo_magazine/m545/practice + materials = list(DEFAULT_WALL_MATERIAL = 2250) + +/datum/design/item/autolathe/arms/machinegun_545 + name = "machinegun box magazine (5.45)" + id = "machinegun_545" + build_path = /obj/item/ammo_magazine/m545saw + materials = list(DEFAULT_WALL_MATERIAL = 12500) + hidden = 1 + +/////// 7.62 + +/datum/design/item/autolathe/arms/rifle_762 + name = "rifle magazine (7.62mm)" + id = "rifle_762" + build_path = /obj/item/ammo_magazine/m762 + materials = list(DEFAULT_WALL_MATERIAL = 2500) + hidden = 1 + + +/////// Shotgun + +/datum/design/item/autolathe/arms/shotgun_clip_beanbag + name = "2-round 12g speedloader (beanbag)" + id = "shotgun_speedloader_beanbag" + build_path = /obj/item/ammo_magazine/clip/c12g/beanbag + materials = list(DEFAULT_WALL_MATERIAL = 887) + +/datum/design/item/autolathe/arms/shotgun_clip_pellet + name = "2-round 12g speedloader (pellet)" + id = "shotgun_speedloader_pellet" + build_path = /obj/item/ammo_magazine/clip/c12g/pellet + materials = list(DEFAULT_WALL_MATERIAL = 1337) + hidden = 1 + +/datum/design/item/autolathe/arms/shotgun_clip_slug + name = "2-round 12g speedloader (slug)" + id = "shotgun_speedloader_slug" + build_path = /obj/item/ammo_magazine/clip/c12g + materials = list(DEFAULT_WALL_MATERIAL = 1337) + hidden = 1 + + +/////////////////////////////// +/*Ammo clips and Speedloaders*/ +/////////////////////////////// + +/datum/design/item/autolathe/arms/speedloader_357 + name = "speedloader (.357)" + id = "speedloader_357" + build_path = /obj/item/ammo_magazine/s357 + materials = list(DEFAULT_WALL_MATERIAL = 1575) + hidden = 1 + +/datum/design/item/autolathe/arms/speedloader_38 + name = "speedloader (.38)" + id = "speedloader_38" + build_path = /obj/item/ammo_magazine/s38 + materials = list(DEFAULT_WALL_MATERIAL = 450) + hidden = 1 + +/datum/design/item/autolathe/arms/speedloader_38r + name = "speedloader (.38 rubber)" + id = "speedloader_38_rubber" + build_path = /obj/item/ammo_magazine/s38/rubber + materials = list(DEFAULT_WALL_MATERIAL = 450) + +/datum/design/item/autolathe/arms/speedloader_45 + name = "speedloader (.45)" + id = "speedloader_45" + build_path = /obj/item/ammo_magazine/s45 + materials = list(DEFAULT_WALL_MATERIAL = 656) + hidden = 1 + +/datum/design/item/autolathe/arms/speedloader_45r + name = "speedloader (.45 rubber)" + id = "speedloader_45_rubber" + build_path = /obj/item/ammo_magazine/s45/rubber + materials = list(DEFAULT_WALL_MATERIAL = 656) + +/datum/design/item/autolathe/arms/rifle_clip_545 + name = "ammo clip (5.45mm)" + id = "rifle_clip_545" + build_path = /obj/item/ammo_magazine/clip/c545 + materials = list(DEFAULT_WALL_MATERIAL = 565) + hidden = 1 + +/datum/design/item/autolathe/arms/rifle_clip_545_practice + name = "ammo clip (5.45mm practice)" + id = "rifle_clip_545p" + build_path = /obj/item/ammo_magazine/clip/c545/practice + materials = list(DEFAULT_WALL_MATERIAL = 565) + +/datum/design/item/autolathe/arms/rifle_clip_762 + name = "ammo clip (7.62mm)" + id = "rifle_clip_762" + build_path = /obj/item/ammo_magazine/clip/c762 + materials = list(DEFAULT_WALL_MATERIAL = 1250) + hidden = 1 + +/datum/design/item/autolathe/arms/rifle_clip_762_practice + name = "ammo clip (7.62mm practice)" + id = "rifle_clip_762p" + build_path = /obj/item/ammo_magazine/clip/c762/practice + materials = list(DEFAULT_WALL_MATERIAL = 1250) + +/datum/design/item/autolathe/arms/knuckledusters + name = "knuckle dusters" + id = "knuckledusters" + build_path = /obj/item/clothing/gloves/knuckledusters + materials = list(DEFAULT_WALL_MATERIAL = 625) + hidden = 1 + +/datum/design/item/autolathe/arms/flamethrower + name = "flamethrower" + id = "flamethrower" + build_path = /obj/item/weapon/flamethrower/full + materials = list(DEFAULT_WALL_MATERIAL = 625) + hidden = 1 + + diff --git a/code/datums/autolathe/arms_1_vr.dm b/code/datums/autolathe/arms_1_vr.dm new file mode 100644 index 0000000000..54919d6bd7 --- /dev/null +++ b/code/datums/autolathe/arms_1_vr.dm @@ -0,0 +1,56 @@ +/datum/design/item/autolathe/arms/speedloader_357_flash + name = "speedloader (.357 flash)" + id = "speedloader_357f" + build_path = /obj/item/ammo_magazine/s357/flash + materials = list(DEFAULT_WALL_MATERIAL = 1575) + hidden = 1 + +/datum/design/item/autolathe/arms/speedloader_357_stun + name = "speedloader (.357 stun)" + id = "speedloader_357s" + build_path = /obj/item/ammo_magazine/s357/stun + materials = list(DEFAULT_WALL_MATERIAL = 1575) + hidden = 1 + +/datum/design/item/autolathe/arms/speedloader_357_rubber + name = "speedloader (.357 rubber)" + id = "speedloader_357r" + build_path = /obj/item/ammo_magazine/s357/rubber + materials = list(DEFAULT_WALL_MATERIAL = 1575) + hidden = 1 + +/datum/design/item/autolathe/arms/speedloader_44 + name = "speedloader (.44)" + id = "speedloader_44" + build_path = /obj/item/ammo_magazine/s44 + materials = list(DEFAULT_WALL_MATERIAL = 1575) + hidden = 1 + +/datum/design/item/autolathe/arms/speedloader_44_rubber + name = "speedloader (.44)" + id = "speedloader_44r" + build_path = /obj/item/ammo_magazine/s44/rubber + materials = list(DEFAULT_WALL_MATERIAL = 1575) + hidden = 1 + +/datum/design/item/autolathe/arms/mag_44 + name = "magazine (.44)" + id = "mag_44" + build_path = /obj/item/ammo_magazine/m44 + materials = list(DEFAULT_WALL_MATERIAL = 1575) + hidden = 1 + +/datum/design/item/autolathe/arms/mag_44_rubber + name = "magazine (.44 rubber)" + id = "mag_44r" + build_path = /obj/item/ammo_magazine/m44/rubber + materials = list(DEFAULT_WALL_MATERIAL = 1575) + hidden = 1 + +/datum/design/item/autolathe/arms/classic_smg_9mm + name = "SMG magazine (9mm)" + id = "classic_smg_9mm" + build_path = /obj/item/ammo_magazine/m9mml + materials = list(DEFAULT_WALL_MATERIAL = 2250) + hidden = 1 + diff --git a/code/datums/autolathe/arms_1_yw.dm b/code/datums/autolathe/arms_1_yw.dm new file mode 100644 index 0000000000..9f574aceaa --- /dev/null +++ b/code/datums/autolathe/arms_1_yw.dm @@ -0,0 +1,6 @@ +/datum/design/item/autolathe/arms/shotgun_scatter + name = "ammunition (12g, scatter)" + id = "shotgun_scatter" + build_path = /obj/item/ammo_casing/a12g/scatter + materials = list(DEFAULT_WALL_MATERIAL = 450) + hidden = 1 \ No newline at end of file diff --git a/code/datums/autolathe/arms_yw.dm b/code/datums/autolathe/arms_yw.dm new file mode 100644 index 0000000000..1d102f9d87 --- /dev/null +++ b/code/datums/autolathe/arms_yw.dm @@ -0,0 +1,4 @@ +/datum/category_item/autolathe/arms/shotgun_scatter + name = "ammunition (12g scatter)" + path =/obj/item/ammo_casing/a12g/scatter + hidden = 1 diff --git a/code/datums/autolathe/autolathe_yw.dm b/code/datums/autolathe/autolathe_yw.dm new file mode 100644 index 0000000000..54da21f54f --- /dev/null +++ b/code/datums/autolathe/autolathe_yw.dm @@ -0,0 +1,502 @@ +#define AUTOLATHE_MAIN_MENU 1 +#define AUTOLATHE_CATEGORY_MENU 2 +#define AUTOLATHE_SEARCH_MENU 3 + +/obj/machinery/autolathe + name = "autolathe" + desc = "It produces items using metal and glass." + icon = 'icons/obj/stationobjs_vr.dmi' + icon_state = "autolathe" + density = 1 + anchored = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 10 + active_power_usage = 2000 + clicksound = "keyboard" + clickvol = 30 + + circuit = /obj/item/weapon/circuitboard/autolathe + var/list/stored_material = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0) + var/list/storage_capacity = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0) + + var/hacked = 0 + var/disabled = 0 + var/shocked = 0 + var/busy = 0 + var/operating = 0.0 + var/list/queue = list() + var/queue_max_len = 12 + var/turf/BuildTurf + var/list/L = list() + var/list/LL = list() + var/prod_coeff + var/list/being_built = list() + var/datum/research/files + + var/mat_efficiency = 1 + var/build_time = 50 + var/list/datum/design/item/autolathe/matching_designs + var/temp_search + var/selected_category + var/screen = 1 + var/list/categories = list("Arms and Ammunition", "Devices", "Engineering", "General", "Security", "Medical", "Tools") + + var/datum/wires/autolathe/wires = null + +/obj/machinery/autolathe/New() + ..() + wires = new(src) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/console_screen(src) + RefreshParts() + files = new /datum/research/autolathe(src) + matching_designs = list() + +/obj/machinery/autolathe/Destroy() + qdel(wires) + wires = null + return ..() + +/obj/machinery/autolathe/attack_hand(mob/user) + + if(..() || (disabled && !panel_open)) + to_chat(user, "\The [src] is disabled!") + return + + if(shocked) + shock(user, 50) + else + ui_interact(user) + +/obj/machinery/autolathe/dismantle() + for(var/mat in stored_material) + var/material/M = get_material_by_name(mat) + if(!istype(M)) + continue + var/obj/item/stack/material/S = new M.stack_type(get_turf(src)) + if(stored_material[mat] > S.perunit) + S.amount = round(stored_material[mat] / S.perunit) + else + qdel(S) + ..() + return 1 + +/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + user.set_machine(src) + var/data[0] + data["screen"] = screen + data["total_amount"] = (stored_material[DEFAULT_WALL_MATERIAL] + stored_material["glass"]) + data["max_amount"] = (storage_capacity[DEFAULT_WALL_MATERIAL] + storage_capacity["glass"]) + data["metal_amount"] = stored_material[DEFAULT_WALL_MATERIAL] + data["glass_amount"] = stored_material["glass"] + switch(screen) + if(AUTOLATHE_MAIN_MENU) + data["uid"] = "\ref[src]" + data["categories"] = categories + if(AUTOLATHE_CATEGORY_MENU) + data["selected_category"] = selected_category + var/list/designs = list() + data["designs"] = designs + for(var/datum/design/item/autolathe/D in files.known_designs) + if(D.hidden && !hacked || selected_category != D.category) + continue + var/list/design = list() + designs[++designs.len] = design + design["name"] = D.name + design["id"] = D.id + design["disabled"] = disabled || !can_build(D) ? "disabled" : null + if(ispath(D.build_path, /obj/item/stack)) + design["max_multiplier"] = min(D.maxstack, D.materials[DEFAULT_WALL_MATERIAL] ? round(stored_material[DEFAULT_WALL_MATERIAL] / D.materials[DEFAULT_WALL_MATERIAL]) : INFINITY, D.materials["glass"] ? round(stored_material["glass"] / D.materials["glass"]) : INFINITY) + else + design["max_multiplier"] = null + design["materials"] = design_cost_data(D) + if(AUTOLATHE_SEARCH_MENU) + data["search"] = temp_search + var/list/designs = list() + data["designs"] = designs + for(var/datum/design/item/autolathe/D in matching_designs) + var/list/design = list() + designs[++designs.len] = design + design["name"] = D.name + design["id"] = D.id + design["disabled"] = disabled || !can_build(D) ? "disabled" : null + if(ispath(D.build_path, /obj/item/stack)) + design["max_multiplier"] = min(D.maxstack, D.materials[DEFAULT_WALL_MATERIAL] ? round(stored_material[DEFAULT_WALL_MATERIAL] / D.materials[DEFAULT_WALL_MATERIAL]) : INFINITY, D.materials["glass"] ? round(stored_material["glass"] / D.materials["glass"]) : INFINITY) + else + design["max_multiplier"] = null + design["materials"] = design_cost_data(D) + + data = queue_data(data) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550) + ui.set_initial_data(data) + ui.open() + + return data + +/obj/machinery/autolathe/Topic(href, href_list) + if(..()) + return + add_fingerprint(usr) + + if(href_list["menu"]) + screen = text2num(href_list["menu"]) + + if(href_list["category"]) + selected_category = href_list["category"] + screen = AUTOLATHE_CATEGORY_MENU + + if(href_list["make"]) + BuildTurf = loc + + ///////////////// + //href protection + var/datum/design/design_last_ordered + design_last_ordered = FindDesign(href_list["make"]) //check if it's a valid design + if(!design_last_ordered) + return + if(!(design_last_ordered.build_type & AUTOLATHE)) + return + + //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier + var/multiplier = text2num(href_list["multiplier"]) + var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[DEFAULT_WALL_MATERIAL] ?round(stored_material[DEFAULT_WALL_MATERIAL]/design_last_ordered.materials[DEFAULT_WALL_MATERIAL]):INFINITY,design_last_ordered.materials["glass"]?round(stored_material["glass"]/design_last_ordered.materials["glass"]):INFINITY) + var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) + + if(!is_stack && (multiplier > 1)) + return + if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc + return + ///////////////// + + if((queue.len + 1) < queue_max_len) + add_to_queue(design_last_ordered,multiplier) + else + to_chat(usr, "The autolathe queue is full!") + if(!busy) + busy = 1 + process_queue() + busy = 0 + + if(href_list["remove_from_queue"]) + var/index = text2num(href_list["remove_from_queue"]) + if(isnum(index) && ISINRANGE(index, 1, queue.len)) + remove_from_queue(index) + if(href_list["queue_move"] && href_list["index"]) + var/index = text2num(href_list["index"]) + var/new_index = index + text2num(href_list["queue_move"]) + if(isnum(index) && isnum(new_index)) + if(ISINRANGE(new_index, 1, queue.len)) + queue.Swap(index,new_index) + if(href_list["clear_queue"]) + queue = list() + if(href_list["search"]) + if(href_list["to_search"]) + temp_search = href_list["to_search"] + if(!temp_search) + return + matching_designs.Cut() + + for(var/datum/design/item/autolathe/D in files.known_designs) + if(findtext(D.name, temp_search)) + matching_designs.Add(D) + + screen = AUTOLATHE_SEARCH_MENU + + SSnanoui.update_uis(src) + return 1 + +/obj/machinery/autolathe/update_icon() + if(panel_open) + icon_state = "autolathe_t" + else if(busy) + icon_state = "autolathe_n" + else + if(icon_state == "autolathe_n") + flick("autolathe_u", src) // If lid WAS closed, show opening animation + icon_state = "autolathe" + +/obj/machinery/autolathe/RefreshParts() + ..() + var/mb_rating = 0 + var/man_rating = 0 + for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts) + mb_rating += MB.rating + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + man_rating += M.rating + + storage_capacity[DEFAULT_WALL_MATERIAL] = mb_rating * 25000 + storage_capacity["glass"] = mb_rating * 12500 + build_time = 50 / man_rating + mat_efficiency = 1.1 - man_rating * 0.1// Normally, price is 1.25 the amount of material, so this shouldn't go higher than 0.6. Maximum rating of parts is 5 + + +/obj/machinery/autolathe/proc/design_cost_data(datum/design/D) + var/list/data = list() + var/coeff = get_coeff(D) + var/has_metal = 1 + if(D.materials[DEFAULT_WALL_MATERIAL] && (stored_material[DEFAULT_WALL_MATERIAL] < (D.materials[DEFAULT_WALL_MATERIAL] * coeff))) + has_metal = 0 + var/has_glass = 1 + if(D.materials["glass"] && (stored_material["glass"] < (D.materials["glass"] * coeff))) + has_glass = 0 + + data[++data.len] = list("name" = "metal", "amount" = D.materials[DEFAULT_WALL_MATERIAL] * coeff, "is_red" = !has_metal) + data[++data.len] = list("name" = "glass", "amount" = D.materials["glass"] * coeff, "is_red" = !has_glass) + + return data + +/obj/machinery/autolathe/proc/queue_data(list/data) + var/temp_metal = stored_material[DEFAULT_WALL_MATERIAL] + var/temp_glass = stored_material["glass"] + data["processing"] = being_built.len ? get_processing_line() : null + if(istype(queue) && queue.len) + var/list/data_queue = list() + for(var/list/L in queue) + var/datum/design/item/autolathe/D = L[1] + var/list/LL = get_design_cost_as_list(D, L[2]) + data_queue[++data_queue.len] = list("name" = initial(D.name), "can_build" = can_build(D, L[2], temp_metal, temp_glass), "multiplier" = L[2]) + temp_metal = max(temp_metal - LL[1], 1) + temp_glass = max(temp_glass - LL[2], 1) + data["queue"] = data_queue + data["queue_len"] = data_queue.len + else + data["queue"] = null + return data + +/obj/machinery/autolathe/proc/get_coeff(datum/design/D) + var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : mat_efficiency)//stacks are unaffected by production coefficient + return coeff + +/obj/machinery/autolathe/proc/build_item(datum/design/D, multiplier) + desc = initial(desc)+"\nIt's building \a [initial(D.name)]." + var/is_stack = ispath(D.build_path, /obj/item/stack) + var/coeff = get_coeff(D) + var/metal_cost = D.materials[DEFAULT_WALL_MATERIAL] + var/glass_cost = D.materials["glass"] + var/power = max(2000, (metal_cost+glass_cost)*multiplier/5) + if(can_build(D, multiplier)) + being_built = list(D, multiplier) + use_power(power) + icon_state = "autolathe" + flick("autolathe_n",src) + if(is_stack) + var/list/materials_used = list(DEFAULT_WALL_MATERIAL=metal_cost*multiplier, "glass"=glass_cost*multiplier) + //stored_material = list(DEFAULT_WALL_MATERIAL -= materials_used[DEFAULT_WALL_MATERIAL], "glass" -= materials_used["glass"]) + stored_material[DEFAULT_WALL_MATERIAL] -= materials_used[DEFAULT_WALL_MATERIAL] + stored_material["glass"] -= materials_used["glass"] + else + var/list/materials_used = list(DEFAULT_WALL_MATERIAL=metal_cost*coeff, "glass"=glass_cost*coeff) + stored_material[DEFAULT_WALL_MATERIAL] -= materials_used[DEFAULT_WALL_MATERIAL] + stored_material["glass"] -= materials_used["glass"] + SSnanoui.update_uis(src) + sleep(32*coeff) + if(is_stack) + var/obj/item/stack/S = new D.build_path(BuildTurf) + S.amount = multiplier + else + var/obj/item/new_item = new D.build_path(BuildTurf) + new_item.matter[DEFAULT_WALL_MATERIAL] *= coeff + new_item.matter["glass"] *= coeff + SSnanoui.update_uis(src) + desc = initial(desc) + +/obj/machinery/autolathe/proc/can_build(var/datum/design/D, var/multiplier = 1, custom_metal, custom_glass) + if(D.chemicals.len) + return 0 + + var/coeff = get_coeff(D) + var/metal_amount = stored_material[DEFAULT_WALL_MATERIAL] + if(custom_metal) + metal_amount = custom_metal + var/glass_amount = stored_material["glass"] + if(custom_glass) + glass_amount = custom_glass + + if(D.materials[DEFAULT_WALL_MATERIAL] && (metal_amount < (multiplier*D.materials[DEFAULT_WALL_MATERIAL] * coeff))) + return 0 + if(D.materials["glass"] && (glass_amount < (multiplier*D.materials["glass"] * coeff))) + return 0 + return 1 + +/obj/machinery/autolathe/proc/get_design_cost_as_list(datum/design/D, multiplier = 1) + var/list/OutputList = list(0,0) + var/coeff = get_coeff(D) + if(D.materials[DEFAULT_WALL_MATERIAL]) + OutputList[1] = (D.materials[DEFAULT_WALL_MATERIAL] * coeff)*multiplier + if(D.materials["glass"]) + OutputList[2] = (D.materials["glass"] * coeff)*multiplier + return OutputList + +/obj/machinery/autolathe/proc/get_processing_line() + var/datum/design/D = being_built[1] + var/multiplier = being_built[2] + var/is_stack = (multiplier>1) + var/output = "PROCESSING: [initial(D.name)][is_stack?" (x[multiplier])":null]" + return output + +/obj/machinery/autolathe/proc/add_to_queue(D, multiplier) + if(!istype(queue)) + queue = list() + if(D) + queue.Add(list(list(D,multiplier))) + return queue.len + +/obj/machinery/autolathe/proc/remove_from_queue(index) + if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) + return 0 + queue.Cut(index,++index) + return 1 + +/obj/machinery/autolathe/proc/process_queue() + var/datum/design/D = queue[1][1] + var/multiplier = queue[1][2] + if(!D) + remove_from_queue(1) + if(queue.len) + return process_queue() + else + return + while(D) + if(stat & (NOPOWER|BROKEN)) + being_built = new /list() + return 0 + if(!can_build(D, multiplier)) + visible_message("[bicon(src)] \The [src] beeps, \"Not enough resources. Queue processing terminated.\"") + queue = list() + being_built = new /list() + return 0 + + remove_from_queue(1) + build_item(D,multiplier) + D = listgetindex(listgetindex(queue, 1),1) + multiplier = listgetindex(listgetindex(queue,1),2) + being_built = new /list() + + +/datum/research/autolathe + +/datum/research/autolathe/DesignHasReqs(var/datum/design/D) + return D && (D.build_type & AUTOLATHE) + +/datum/research/autolathe/AddDesign2Known(var/datum/design/D) + if(!(D.build_type & AUTOLATHE)) + return + ..() + +//datum/research/proc/FindDesignByID(var/id) + //return known_designs[id] + +///obj/machinery/autolathe/proc/FindDesignByID(var/id) + //return files.known_designs[id] + +/obj/machinery/autolathe/proc/FindDesign(var/id) + for(var/datum/design/item/autolathe/desired_design in files.known_designs) + if(desired_design.id == id) + return desired_design + return + +/obj/machinery/autolathe/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(busy) + to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") + return + + if(default_deconstruction_screwdriver(user, O)) + updateUsrDialog() + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + + if(stat) + return + + if(panel_open) + //Don't eat multitools or wirecutters used on an open lathe. + if(O.is_multitool() || O.is_wirecutter()) + wires.Interact(user) + return + + if(O.loc != user && !(istype(O,/obj/item/stack))) + return 0 + + if(is_robot_module(O)) + return 0 + + if(istype(O,/obj/item/ammo_magazine/clip) || istype(O,/obj/item/ammo_magazine/s357) || istype(O,/obj/item/ammo_magazine/s38) || istype (O,/obj/item/ammo_magazine/s44)/* VOREstation Edit*/) // Prevents ammo recycling exploit with speedloaders. + to_chat(user, "\The [O] is too hazardous to recycle with the autolathe!") + return + /* ToDo: Make this actually check for ammo and change the value of the magazine if it's empty. -Spades + var/obj/item/ammo_magazine/speedloader = O + if(speedloader.stored_ammo) + to_chat(user, "\The [speedloader] is too hazardous to put back into the autolathe while there's ammunition inside of it!") + return + else + speedloader.matter = list(DEFAULT_WALL_MATERIAL = 75) // It's just a hunk of scrap metal now. + if(istype(O,/obj/item/ammo_magazine)) // This was just for immersion consistency with above. + var/obj/item/ammo_magazine/mag = O + if(mag.stored_ammo) + to_chat(user, "\The [mag] is too hazardous to put back into the autolathe while there's ammunition inside of it!") + return*/ + + //Resources are being loaded. + var/obj/item/eating = O + if(!eating.matter) + to_chat(user, "\The [eating] does not contain significant amounts of useful materials and cannot be accepted.") + return + + var/filltype = 0 // Used to determine message. + var/total_used = 0 // Amount of material used. + var/mass_per_sheet = 0 // Amount of material constituting one sheet. + + for(var/material in eating.matter) + + if(isnull(stored_material[material]) || isnull(storage_capacity[material])) + continue + + if(stored_material[material] >= storage_capacity[material]) + continue + + var/total_material = eating.matter[material] + + //If it's a stack, we eat multiple sheets. + if(istype(eating,/obj/item/stack)) + var/obj/item/stack/stack = eating + total_material *= stack.get_amount() + + if(stored_material[material] + total_material > storage_capacity[material]) + total_material = storage_capacity[material] - stored_material[material] + filltype = 1 + else + filltype = 2 + + stored_material[material] += total_material + total_used += total_material + mass_per_sheet += eating.matter[material] + + if(!filltype) + to_chat(user, "\The [src] is full. Please remove material from the autolathe in order to insert more.") + return + else if(filltype == 1) + to_chat(user, "You fill \the [src] to capacity with \the [eating].") + else + to_chat(user, "You fill \the [src] with \the [eating].") + + flick("autolathe_o", src) // Plays metal insertion animation. Work out a good way to work out a fitting animation. ~Z + + if(istype(eating,/obj/item/stack)) + var/obj/item/stack/stack = eating + stack.use(max(1, round(total_used/mass_per_sheet))) // Always use at least 1 to prevent infinite materials. + else + user.remove_from_mob(O) + qdel(O) + + updateUsrDialog() + return \ No newline at end of file diff --git a/code/datums/autolathe/devices_1.dm b/code/datums/autolathe/devices_1.dm new file mode 100644 index 0000000000..5cfdc6afa7 --- /dev/null +++ b/code/datums/autolathe/devices_1.dm @@ -0,0 +1,45 @@ +/datum/design/item/autolathe/devices //Datum for object designs, used in construction //IDs of that techs the object originated from and the minimum level requirements. + category = "Devices" //category item goes to + +/datum/design/item/autolathe/devices/consolescreen + name = "console screen" + id = "console_screen" + build_path = /obj/item/weapon/stock_parts/console_screen + materials = list("glass" = 200) + +/datum/design/item/autolathe/devices/igniter + name = "igniter" + id = "igniter" + build_path = /obj/item/device/assembly/igniter + materials = list(DEFAULT_WALL_MATERIAL = 625, "glass" = 62) + +/datum/design/item/autolathe/devices/signaler + name = "signaler" + id = "signaler" + build_path = /obj/item/device/assembly/signaler + materials = list(DEFAULT_WALL_MATERIAL = 1250, "glass" = 250) + +/datum/design/item/autolathe/devices/sensor_infra + name = "infrared sensor" + id = "infrared_sensor" + build_path = /obj/item/device/assembly/infra + materials = list(DEFAULT_WALL_MATERIAL = 1250, "glass" = 625) + +/datum/design/item/autolathe/devices/sensor_prox + name = "proximity sensor" + id = "proximity_sensor" + build_path = /obj/item/device/assembly/prox_sensor + materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 250) + +/datum/design/item/autolathe/devices/beartrap + name = "mechanical trap" + id = "beartrap" + build_path = /obj/item/weapon/beartrap + materials = list(DEFAULT_WALL_MATERIAL = 23437) + +/datum/design/item/autolathe/devices/electropack + name = "electropack" + id = "electropack" + build_path = /obj/item/device/radio/electropack + materials = list(DEFAULT_WALL_MATERIAL = 12500, "glass" = 3125) + hidden = 1 diff --git a/code/datums/autolathe/devices_1_vr.dm b/code/datums/autolathe/devices_1_vr.dm new file mode 100644 index 0000000000..eaadab5d4f --- /dev/null +++ b/code/datums/autolathe/devices_1_vr.dm @@ -0,0 +1,5 @@ +/datum/design/item/autolathe/devices/sleevecard + name = "sleevecard" + id = "sleevecard" + build_path = /obj/item/device/sleevecard + materials = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 5000) diff --git a/code/datums/autolathe/engineering_1.dm b/code/datums/autolathe/engineering_1.dm new file mode 100644 index 0000000000..eb7bf25c90 --- /dev/null +++ b/code/datums/autolathe/engineering_1.dm @@ -0,0 +1,140 @@ +/datum/design/item/autolathe/engineering //Datum for object designs, used in construction //IDs of that techs the object originated from and the minimum level requirements. + category = "Engineering" //category item goes to + +/datum/design/item/autolathe/engineering/airlockmodule + name = "airlock electronics" + id = "airlock_electronics" + build_path = /obj/item/weapon/airlock_electronics + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/airalarm + name = "air alarm electronics" + id = "airalarm_electronics" + build_path = /obj/item/weapon/circuitboard/airalarm + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/firealarm + name = "fire alarm electronics" + id = "firealarm_electronics" + build_path = /obj/item/weapon/circuitboard/firealarm + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/statusdisplay + name = "ai status display electronics" + id = "statusdisplay_electronics" + build_path = /obj/item/weapon/circuitboard/ai_status_display + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/newscaster + name = "newscaster electronics" + id = "newscaster_electronics" + build_path = /obj/item/weapon/circuitboard/newscaster + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/atm + name = "atm electronics" + id = "atm_electronics" + build_path = /obj/item/weapon/circuitboard/atm + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/intercom + name = "intercom electronics" + id = "intercom_electronics" + build_path = /obj/item/weapon/circuitboard/intercom + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/holopad + name = "holopad electronics" + id = "holopad_electronics" + build_path = /obj/item/weapon/circuitboard/holopad + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/guestpass + name = "guestpass console electronics" + id = "guestpass_electronics" + build_path = /obj/item/weapon/circuitboard/guestpass + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/entertainment + name = "entertainment camera electronics" + id = "entertainmentcamera_electronics" + build_path = /obj/item/weapon/circuitboard/security/telescreen/entertainment + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/keycard_auth + name = "keycard authenticator electronics" + id = "keycardauth_electronics" + build_path = /obj/item/weapon/circuitboard/keycard_auth + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/photocopier + name = "photocopíer electronics" + id = "photocopier_electronics" + build_path = /obj/item/weapon/circuitboard/photocopier + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/fax + name = "fax machine electronics" + id = "fax_machine_electronics" + build_path = /obj/item/weapon/circuitboard/fax + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/papershredder + name = "paper shredder electronics" + id = "papershredder_electronics" + build_path = /obj/item/weapon/circuitboard/papershredder + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/microwave + name = "microwave electronics" + id = "microwave_electronics" + build_path = /obj/item/weapon/circuitboard/microwave + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/washing + name = "washing machine electronics" + id = "washingmachine_electronics" + build_path = /obj/item/weapon/circuitboard/washing + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/request + name = "request console electronics" + id = "requestconsole_electronics" + build_path = /obj/item/weapon/circuitboard/request + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/motor + name = "motor" + id = "motor" + build_path = /obj/item/weapon/stock_parts/motor + materials = list(DEFAULT_WALL_MATERIAL = 75, "glass" = 12) + +/datum/design/item/autolathe/engineering/gear + name = "gear" + id = "gear" + build_path = /obj/item/weapon/stock_parts/gear + materials = list(DEFAULT_WALL_MATERIAL = 62) + +/datum/design/item/autolathe/engineering/spring + name = "spring" + id = "spring" + build_path = /obj/item/weapon/stock_parts/spring + materials = list(DEFAULT_WALL_MATERIAL = 50) + +/datum/design/item/autolathe/engineering/rcd_ammo + name = "matter cartridge" + id = "rcd_ammo" + build_path = /obj/item/weapon/rcd_ammo + materials = list(DEFAULT_WALL_MATERIAL = 30000, "glass" = 15000) + +/datum/design/item/autolathe/engineering/rcd + name = "rapid construction device" + id = "rcd" + build_path = /obj/item/weapon/rcd + materials = list(DEFAULT_WALL_MATERIAL = 62500) + +/datum/design/item/autolathe/engineering/camera_assembly + name = "camera assembly" + id = "camera_assembly" + build_path = /obj/item/weapon/camera_assembly + materials = list(DEFAULT_WALL_MATERIAL = 875, "glass" = 375) diff --git a/code/datums/autolathe/engineering_1_vr.dm b/code/datums/autolathe/engineering_1_vr.dm new file mode 100644 index 0000000000..21bf2e632f --- /dev/null +++ b/code/datums/autolathe/engineering_1_vr.dm @@ -0,0 +1,11 @@ +/datum/design/item/autolathe/engineering/timeclock + name = "timeclock electronics" + id = "timeclock_electronics" + build_path = /obj/item/weapon/circuitboard/timeclock + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) + +/datum/design/item/autolathe/engineering/id_restorer + name = "ID restoration console electronics" + id = "idrestorer_electronics" + build_path = /obj/item/weapon/circuitboard/id_restorer + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62) diff --git a/code/datums/autolathe/general_1.dm b/code/datums/autolathe/general_1.dm new file mode 100644 index 0000000000..72c4fe004b --- /dev/null +++ b/code/datums/autolathe/general_1.dm @@ -0,0 +1,209 @@ +/datum/design/item/autolathe/general //Datum for object designs, used in construction //IDs of that techs the object originated from and the minimum level requirements. + category = "General" //category item goes to + +/datum/design/item/autolathe/general/bucket + name = "bucket" + id = "bucket" + build_path = /obj/item/weapon/reagent_containers/glass/bucket + materials = list(DEFAULT_WALL_MATERIAL = 250) + + +/datum/design/item/autolathe/general/cooler_bottle + name = "water cooler bottle" + id = "cooler_bottle" + build_path = /obj/item/weapon/reagent_containers/glass/cooler_bottle + materials = list(DEFAULT_WALL_MATERIAL = 15000) + +/datum/design/item/autolathe/general/drinkingglass_square + name = "half-pint glass" + id = "halfpint_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/square + materials = list("glass" = 75) + +/datum/design/item/autolathe/general/drinkingglass_rocks + name = "rocks glass" + id = "rocks_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/rocks + materials = list("glass" = 50) + +/datum/design/item/autolathe/general/drinkingglass_shake + name = "milkshake glass" + id = "milkshake_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/shake + materials = list("glass" = 37) + +/datum/design/item/autolathe/general/drinkingglass_cocktail + name = "cocktail glass" + id = "cocktail_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/cocktail + materials = list("glass" = 37) + +/datum/design/item/autolathe/general/drinkingglass_shot + name = "shot glass" + id = "shot_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/shot + materials = list("glass" = 75) + +/datum/design/item/autolathe/general/drinkingglass_pint + name = "pint glass" + id = "pint_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/pint + materials = list("glass" = 150) + +/datum/design/item/autolathe/general/drinkingglass_mug + name = "glass mug" + id = "mug_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/mug + materials = list("glass" = 100) + +/datum/design/item/autolathe/general/drinkingglass_wine + name = "wine glass" + id = "wine_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/wine + materials = list("glass" = 62) + +/datum/design/item/autolathe/general/flashlight + name = "flashlight" + id = "flashlight" + build_path = /obj/item/device/flashlight + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 25) + + +/datum/design/item/autolathe/general/floor_light + name = "floor light" + id = "floor_light" + build_path = /obj/machinery/floor_light + materials = list(DEFAULT_WALL_MATERIAL = 3125, "glass" = 3437) + +/datum/design/item/autolathe/general/extinguisher + name = "fire extinguisher" + id = "fire_extinguisher" + build_path = /obj/item/weapon/extinguisher + materials = list(DEFAULT_WALL_MATERIAL = 112) + +/datum/design/item/autolathe/general/jar + name = "jar" + id = "glass_jar" + build_path = /obj/item/glass_jar + materials = list(DEFAULT_WALL_MATERIAL = 250) + +/datum/design/item/autolathe/general/radio_headset + name = "radio headset" + id = "radio_headset" + build_path = /obj/item/device/radio/headset + materials = list(DEFAULT_WALL_MATERIAL = 93) + +/datum/design/item/autolathe/general/radio_bounced + name = "station bounced radio" + id = "radio_bounced" + build_path = /obj/item/device/radio/off + materials = list(DEFAULT_WALL_MATERIAL = 93, "glass" = 31) + +/datum/design/item/autolathe/general/suit_cooler + name = "suit cooling unit" + id = "suit_cooler" + build_path = /obj/item/device/suit_cooling_unit + materials = list(DEFAULT_WALL_MATERIAL = 18750, "glass" = 4375) + +/datum/design/item/autolathe/general/weldermask + name = "welding mask" + id = "weldermask" + build_path = /obj/item/clothing/head/welding + materials = list(DEFAULT_WALL_MATERIAL = 3750, "glass" = 1250) + +/datum/design/item/autolathe/general/metal + name = "steel sheets" + id = "steel_sheets" + build_path = /obj/item/stack/material/steel + materials = list(DEFAULT_WALL_MATERIAL = 2000) + maxstack = 50 + +/datum/design/item/autolathe/general/glass + name = "glass sheets" + id = "glass_sheets" + build_path = /obj/item/stack/material/glass + materials = list("glass" = 2000) + maxstack = 50 + +/datum/design/item/autolathe/general/rglass + name = "reinforced glass sheets" + id = "reinforcedglass_sheets" + build_path = /obj/item/stack/material/glass/reinforced + materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 2000) + maxstack = 50 + +/datum/design/item/autolathe/general/rods + name = "metal rods" + id = "metal_rods" + build_path = /obj/item/stack/rods + materials = list(DEFAULT_WALL_MATERIAL = 500) + maxstack = 60 + +/datum/design/item/autolathe/general/spraybottle + name = "empty spray bottle" + id = "spraybottle" + build_path = /obj/item/stack/rods + materials = list(DEFAULT_WALL_MATERIAL = 375, "glass" = 375) + +/datum/design/item/autolathe/general/knife + name = "kitchen knife" + id = "kitchen_knife" + build_path = /obj/item/weapon/material/knife + materials = list(DEFAULT_WALL_MATERIAL = 375) + +/datum/design/item/autolathe/general/taperecorder + name = "tape recorder" + id = "taperecorder" + build_path = /obj/item/device/taperecorder + materials = list(DEFAULT_WALL_MATERIAL = 75, "glass" = 37) + +/datum/design/item/autolathe/general/light_tube + name = "light tube" + id = "light_tube" + build_path = /obj/item/weapon/light/tube + materials = list("glass" = 125) + +/datum/design/item/autolathe/general/light_bulb + name = "light bulb" + id = "light_bulb" + build_path = /obj/item/weapon/light/bulb + materials = list("glass" = 125) + +/datum/design/item/autolathe/general/ashtray_glass + name = "glass ashtray" + id = "ashtray_glass" + build_path = /obj/item/weapon/material/ashtray/glass + materials = list("glass" = 250) + +/datum/design/item/autolathe/general/weldinggoggles + name = "welding goggles" + id = "weldinggoggles" + build_path = /obj/item/clothing/glasses/welding + materials = list(DEFAULT_WALL_MATERIAL = 1875, "glass" = 1250) + +/datum/design/item/autolathe/general/maglight + name = "maglight" + id = "maglight" + build_path = /obj/item/device/flashlight/maglight + materials = list(DEFAULT_WALL_MATERIAL = 250, "glass" = 62) + +/datum/design/item/autolathe/general/emergency_cell + name = "light fixture battery" + id = "emergency_cell" + build_path = /obj/item/weapon/cell/emergency_light + materials = list("glass" = 25) + +/datum/design/item/autolathe/general/handcuffs + name = "handcuffs" + id = "handcuffs" + build_path = /obj/item/weapon/handcuffs + materials = list(DEFAULT_WALL_MATERIAL = 625) + hidden = 1 + +/datum/design/item/autolathe/general/legcuffs + name = "legcuffs" + id = "legcuffs" + build_path = /obj/item/weapon/handcuffs/legcuffs + materials = list(DEFAULT_WALL_MATERIAL = 625) + hidden = 1 + diff --git a/code/datums/autolathe/general_1_vr.dm b/code/datums/autolathe/general_1_vr.dm new file mode 100644 index 0000000000..23c0f2b3b0 --- /dev/null +++ b/code/datums/autolathe/general_1_vr.dm @@ -0,0 +1,23 @@ +/datum/design/item/autolathe/general/holocollar + name = "Holo-collar" + id = "holocollar" + build_path = /obj/item/clothing/accessory/collar/holo + materials = list(DEFAULT_WALL_MATERIAL = 62) + +/datum/design/item/autolathe/general/metaglass + name = "metamorphic glass" + id = "metaglass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/metaglass + materials = list("glass" = 625) + +/datum/design/item/autolathe/general/drinkingglass_carafe + name = "glass carafe" + id = "carafe_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/carafe + materials = list("glass" = 62) + +/datum/design/item/autolathe/general/drinkingglass_pitcher + name = "pitcher" + id = "pitcher_glass" + build_path = /obj/item/weapon/reagent_containers/food/drinks/glass2/pitcher + materials = list(DEFAULT_WALL_MATERIAL = 62) \ No newline at end of file diff --git a/code/datums/autolathe/general_vr.dm b/code/datums/autolathe/general_vr.dm index 5c90909f55..e1fb7ad46f 100644 --- a/code/datums/autolathe/general_vr.dm +++ b/code/datums/autolathe/general_vr.dm @@ -29,4 +29,13 @@ /datum/category_item/autolathe/general/metaglass name = "metamorphic glass" path =/obj/item/weapon/reagent_containers/food/drinks/metaglass - is_stack = TRUE \ No newline at end of file + is_stack = TRUE + +/datum/category_item/autolathe/general/drinkingglass_carafe + name = "glass carafe" + path =/obj/item/weapon/reagent_containers/food/drinks/glass2/carafe + +/datum/category_item/autolathe/general/drinkingglass_pitcher + name = "plastic pitcher" + path =/obj/item/weapon/reagent_containers/food/drinks/glass2/pitcher + \ No newline at end of file diff --git a/code/datums/autolathe/medical_1.dm b/code/datums/autolathe/medical_1.dm new file mode 100644 index 0000000000..728e510e2c --- /dev/null +++ b/code/datums/autolathe/medical_1.dm @@ -0,0 +1,71 @@ +/datum/design/item/autolathe/medical //Datum for object designs, used in construction //IDs of that techs the object originated from and the minimum level requirements. + category = "Medical" //category item goes to + +/datum/design/item/autolathe/medical/scalpel + name = "scalpel" + id = "scalpel" + build_path = /obj/item/weapon/surgical/scalpel + materials = list(DEFAULT_WALL_MATERIAL = 12500, "glass" = 6250) + +/datum/design/item/autolathe/medical/circularsaw + name = "circular saw" + id = "circularsaw" + build_path = /obj/item/weapon/surgical/circular_saw + materials = list(DEFAULT_WALL_MATERIAL = 25000, "glass" = 12500) + +/datum/design/item/autolathe/medical/surgicaldrill + name = "surgical drill" + id = "surgicaldrill" + build_path = /obj/item/weapon/surgical/surgicaldrill + materials = list(DEFAULT_WALL_MATERIAL = 18750, "glass" = 12500) + +/datum/design/item/autolathe/medical/retractor + name = "retractor" + id = "retractor" + build_path = /obj/item/weapon/surgical/retractor + materials = list(DEFAULT_WALL_MATERIAL = 12500, "glass" = 6250) + +/datum/design/item/autolathe/medical/cautery + name = "cautery" + id = "cautery" + build_path = /obj/item/weapon/surgical/cautery + materials = list(DEFAULT_WALL_MATERIAL = 6250, "glass" = 3125) + +/datum/design/item/autolathe/medical/hemostat + name = "hemostat" + id = "hemostat" + build_path = /obj/item/weapon/surgical/hemostat + materials = list(DEFAULT_WALL_MATERIAL = 6250, "glass" = 3125) + +/datum/design/item/autolathe/medical/beaker + name = "glass beaker" + id = "beaker" + build_path = /obj/item/weapon/reagent_containers/glass/beaker + materials = list("glass" = 625) + +/datum/design/item/autolathe/medical/large_beaker + name = "large glass beaker" + id = "large_beaker" + build_path = /obj/item/weapon/reagent_containers/glass/beaker/large + materials = list("glass" = 1300) + +/datum/design/item/autolathe/medical/vial + name = "glass vial" + id = "vial" + build_path = /obj/item/weapon/reagent_containers/glass/beaker/vial + materials = list("glass" = 312) + +/datum/design/item/autolathe/medical/syringe + name = "syringe" + id = "syringe" + build_path = /obj/item/weapon/reagent_containers/syringe + materials = list("glass" = 187) + +/datum/design/item/autolathe/medical/implanter + name = "implanter" + id = "implanter" + build_path = /obj/item/weapon/implanter + materials = list(DEFAULT_WALL_MATERIAL = 1250, "glass" = 1250) + + + diff --git a/code/datums/autolathe/medical_1_vr.dm b/code/datums/autolathe/medical_1_vr.dm new file mode 100644 index 0000000000..bdfee8d8a3 --- /dev/null +++ b/code/datums/autolathe/medical_1_vr.dm @@ -0,0 +1,5 @@ +/datum/design/item/autolathe/medical/autoinjector + name = "empty autoinjector" + id = "autoinjector" + build_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/empty + materials = list(DEFAULT_WALL_MATERIAL = 100) \ No newline at end of file diff --git a/code/datums/autolathe/tools_1.dm b/code/datums/autolathe/tools_1.dm new file mode 100644 index 0000000000..9ae3d8561d --- /dev/null +++ b/code/datums/autolathe/tools_1.dm @@ -0,0 +1,69 @@ +/datum/design/item/autolathe/tools //Datum for object designs, used in construction //IDs of that techs the object originated from and the minimum level requirements. + category = "Tools" //category item goes to + +/datum/design/item/autolathe/tools/crowbar + name = "crowbar" + id = "crowbar" + build_path = /obj/item/weapon/tool/crowbar + materials = list(DEFAULT_WALL_MATERIAL = 62) + +/datum/design/item/autolathe/tools/multitool + name = "multitool" + id = "multitool" + build_path = /obj/item/device/multitool + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 25) + +/datum/design/item/autolathe/tools/t_scanner + name = "T-ray scanner" + id = "t_scanner" + build_path = /obj/item/device/t_scanner + materials = list(DEFAULT_WALL_MATERIAL = 187) + +/datum/design/item/autolathe/tools/weldertool + name = "welding tool" + id = "weldertool" + build_path = /obj/item/weapon/weldingtool + materials = list(DEFAULT_WALL_MATERIAL = 87, "glass" = 37) + +/datum/design/item/autolathe/tools/electric_welder + name = "electric welding tool" + id = "electric_welder" + build_path = /obj/item/weapon/weldingtool/electric/unloaded + materials = list(DEFAULT_WALL_MATERIAL = 87, "glass" = 37) + hidden = 1 + +/datum/design/item/autolathe/tools/screwdriver + name = "screwdriver" + id = "screwdriver" + build_path = /obj/item/weapon/tool/screwdriver + materials = list(DEFAULT_WALL_MATERIAL = 93) + +/datum/design/item/autolathe/tools/wirecutters + name = "wirecutters" + id = "wirecutters" + build_path = /obj/item/weapon/tool/wirecutters + materials = list(DEFAULT_WALL_MATERIAL = 100) + +/datum/design/item/autolathe/tools/wrench + name = "wrench" + id = "wrench" + build_path = /obj/item/weapon/tool/wrench + materials = list(DEFAULT_WALL_MATERIAL = 187) + +/datum/design/item/autolathe/tools/hatchet + name = "hatchet" + id = "hatchet" + build_path = /obj/item/weapon/material/knife/machete/hatchet + materials = list(DEFAULT_WALL_MATERIAL = 500) + +/datum/design/item/autolathe/tools/minihoe + name = "mini hoe" + id = "minihoe" + build_path = /obj/item/weapon/material/minihoe + materials = list(DEFAULT_WALL_MATERIAL = 625) + +/datum/design/item/autolathe/tools/welder_industrial + name = "industrial welding tool" + id = "welder_industrial" + build_path = /obj/item/weapon/weldingtool/largetank + materials = list(DEFAULT_WALL_MATERIAL = 87, "glass" = 75) diff --git a/code/datums/autolathe/tools_1_vr.dm b/code/datums/autolathe/tools_1_vr.dm new file mode 100644 index 0000000000..3dc7df4a6c --- /dev/null +++ b/code/datums/autolathe/tools_1_vr.dm @@ -0,0 +1,5 @@ +/datum/design/item/autolathe/tools/prybar + name = "prybar" + id = "prybar" + build_path = /obj/item/weapon/tool/prybar + materials = list(DEFAULT_WALL_MATERIAL = 37) diff --git a/code/datums/autolathe/tools_1_yw.dm b/code/datums/autolathe/tools_1_yw.dm new file mode 100644 index 0000000000..fca7a493a1 --- /dev/null +++ b/code/datums/autolathe/tools_1_yw.dm @@ -0,0 +1,15 @@ +/datum/design/item/autolathe/tools/ice_pick + name = "ice pick" + id = "ice_pick" + build_path = /obj/item/weapon/ice_pick + materials = list(DEFAULT_WALL_MATERIAL = 15000) + +/datum/design/item/autolathe/tools/shovel + name = "shovel" + id = "shovel" + build_path = /obj/item/weapon/shovel + materials = list(DEFAULT_WALL_MATERIAL = 62) + + + + diff --git a/code/datums/autolathe/tools_yw.dm b/code/datums/autolathe/tools_yw.dm new file mode 100644 index 0000000000..5ea8ef1c7a --- /dev/null +++ b/code/datums/autolathe/tools_yw.dm @@ -0,0 +1,7 @@ +/datum/category_item/autolathe/tools/ice_pick + name = "ice pick" + path =/obj/item/weapon/ice_pick + +/datum/category_item/autolathe/tools/shovel + name = "shovel" + path =/obj/item/weapon/shovel \ No newline at end of file diff --git a/code/datums/beam.dm b/code/datums/beam.dm index 88fe9dbf1c..a35abf8c37 100644 --- a/code/datums/beam.dm +++ b/code/datums/beam.dm @@ -138,12 +138,8 @@ return /obj/effect/ebeam/deadly/Crossed(atom/A) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = A - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(A.is_incorporeal()) + return ..() A.ex_act(1) @@ -163,12 +159,8 @@ on_contact(A) /obj/effect/ebeam/reactive/Crossed(atom/A) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = A - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(A.is_incorporeal()) + return ..() on_contact(A) diff --git a/code/datums/browser.dm b/code/datums/browser.dm index ea2f4feec2..e78833c958 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -15,9 +15,7 @@ var/content = "" var/title_buttons = "" - /datum/browser/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null) - user = nuser window_id = nwindow_id if (ntitle) @@ -33,6 +31,14 @@ return add_stylesheet("common", 'html/browser/common.css') // this CSS sheet is common to all UIs +//VOREStation Edit - Allow browser datums to be garbage collected +/datum/browser/Destroy() + close() + ref = null + user = null + . = ..() +//VOREStation Edit End - Allow browser datums to be garbage collected + /datum/browser/proc/set_title(ntitle) title = format_text(ntitle) @@ -156,7 +162,7 @@ winset(user, windowid, "on-close=\".windowclose [param]\"") - //world << "OnClose [user]: [windowid] : ["on-close=\".windowclose [param]\""]" + //to_world("OnClose [user]: [windowid] : ["on-close=\".windowclose [param]\""]") // the on-close client verb // called when a browser popup window is closed after registering with proc/onclose() @@ -168,11 +174,11 @@ set hidden = 1 // hide this verb from the user's panel set name = ".windowclose" // no autocomplete on cmd line - //world << "windowclose: [atomref]" + //to_world("windowclose: [atomref]") if(atomref!="null") // if passed a real atomref var/hsrc = locate(atomref) // find the reffed atom if(hsrc) - //world << "[src] Topic [href] [hsrc]" + //to_world("[src] Topic [href] [hsrc]") usr = src.mob src.Topic("close=1", list("close"="1"), hsrc) // this will direct to the atom's return // Topic() proc via client.Topic() @@ -180,7 +186,7 @@ // no atomref specified (or not found) // so just reset the user mob's machine var if(src && src.mob) - //world << "[src] was [src.mob.machine], setting to null" + //to_world("[src] was [src.mob.machine], setting to null") src.mob.unset_machine() return diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 518db6f9ec..be5f6b33db 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -5,11 +5,16 @@ /datum/datacore var/name = "datacore" - var/medical[] = list() - var/general[] = list() - var/security[] = list() + //For general station crew + var/static/list/medical = list() + var/static/list/general = list() + var/static/list/security = list() + //For offmap spawns so they can have records accessible by certain things + var/static/list/hidden_medical = list() + var/static/list/hidden_general = list() + var/static/list/hidden_security = list() //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). - var/locked[] = list() + var/static/list/locked = list() /datum/datacore/proc/get_manifest(monochrome, OOC) @@ -22,6 +27,7 @@ var/list/pla = new() //VOREStation Edit var/list/civ = new() var/list/bot = new() + var/list/off = new() var/list/misc = new() var/list/isactive = new() var/dat = {" @@ -46,44 +52,62 @@ if(OOC) var/active = 0 for(var/mob/M in player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 * 60 * 10) + if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) active = 1 break isactive[name] = active ? "Active" : "Inactive" else isactive[name] = t.fields["p_stat"] - //world << "[name]: [rank]" + //to_world("[name]: [rank]") //cael - to prevent multiple appearances of a player/job combination, add a continue after each line var/department = 0 - if(real_rank in command_positions) + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) heads[name] = rank department = 1 - if(real_rank in security_positions) + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) sec[name] = rank department = 1 - if(real_rank in engineering_positions) + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) eng[name] = rank department = 1 - if(real_rank in medical_positions) + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) med[name] = rank department = 1 - if(real_rank in science_positions) + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) sci[name] = rank department = 1 - if(real_rank in cargo_positions) + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) car[name] = rank department = 1 - //VOREStation Edit Begin - if(real_rank in planet_positions) + //VOREStation Add Begin + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) pla[name] = rank department = 1 - //VOREStation Edit End - if(real_rank in civilian_positions) + //VOREStation Add End + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) civ[name] = rank department = 1 if(!department && !(name in heads)) misc[name] = rank + //For the offmap spawns + if(OOC) + for(var/datum/data/record/t in hidden_general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/real_rank = make_list_rank(t.fields["real_rank"]) + + var/active = 0 + for(var/mob/M in player_list) + if(M.real_name == name && M.client && M.client.inactivity <= 10 MINUTES) + active = 1 + break + isactive[name] = active ? "Active" : "Inactive" + + var/datum/job/J = SSjob.get_job(real_rank) + if(J?.offmap_spawn) + off[name] = rank + // Synthetics don't have actual records, so we will pull them from here. for(var/mob/living/silicon/ai/ai in mob_list) bot[ai.name] = "Artificial Intelligence" @@ -142,6 +166,12 @@ for(name in bot) dat += "" even = !even + // offmap spawners + if(off.len > 0) + dat += "" + for(name in off) + dat += "" + even = !even // misc guys if(misc.len > 0) dat += "" @@ -154,6 +184,117 @@ dat = replacetext(dat, "\t", "") return dat +/* +We can't just insert in HTML into the nanoUI so we need the raw data to play with. +Instead of creating this list over and over when someone leaves their PDA open to the page +we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change +using /datum/datacore/proc/manifest_inject( ), or manifest_insert( ) +*/ + +var/global/list/PDA_Manifest = list() + +/datum/datacore/proc/get_manifest_list() + if(PDA_Manifest.len) + return + var/list/heads = list() + var/list/sec = list() + var/list/eng = list() + var/list/med = list() + var/list/sci = list() + var/list/car = list() + var/list/pla = list() // Planetside crew: Explorers, Pilots, S&S + var/list/civ = list() + var/list/bot = list() + var/list/misc = list() + for(var/datum/data/record/t in data_core.general) + var/name = sanitize(t.fields["name"]) + var/rank = sanitize(t.fields["rank"]) + var/real_rank = make_list_rank(t.fields["real_rank"]) + + var/isactive = t.fields["p_stat"] + var/department = 0 + var/depthead = 0 // Department Heads will be placed at the top of their lists. + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_COMMAND)) + heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + depthead = 1 + if(rank=="Colony Director" && heads.len != 1) + heads.Swap(1,heads.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SECURITY)) + sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sec.len != 1) + sec.Swap(1,sec.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_ENGINEERING)) + eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && eng.len != 1) + eng.Swap(1,eng.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_MEDICAL)) + med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && med.len != 1) + med.Swap(1,med.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_RESEARCH)) + sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sci.len != 1) + sci.Swap(1,sci.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_PLANET)) + pla[++pla.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CARGO)) + car[++car.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && car.len != 1) + car.Swap(1,car.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_CIVILIAN)) + civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && civ.len != 1) + civ.Swap(1,civ.len) + + if(SSjob.is_job_in_department(real_rank, DEPARTMENT_SYNTHETIC)) + bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + + if(!department && !(name in heads)) + misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) + + // Synthetics don't have actual records, so we will pull them from here. + // Synths don't have records, which is the means by which isactive is retrieved, so I'm hardcoding it to active, don't really have any better means + for(var/mob/living/silicon/ai/ai in mob_list) + bot[++bot.len] = list("name" = ai.real_name, "rank" = "Artificial Intelligence", "active" = "Active") + + for(var/mob/living/silicon/robot/robot in mob_list) + // No combat/syndicate cyborgs, no drones, and no AI shells. + if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest)) + continue + + bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active") + + + PDA_Manifest = list( + list("cat" = "Command", "elems" = heads), + list("cat" = "Security", "elems" = sec), + list("cat" = "Engineering", "elems" = eng), + list("cat" = "Medical", "elems" = med), + list("cat" = "Science", "elems" = sci), + list("cat" = "Cargo", "elems" = car), + list("cat" = "Exploration", "elems" = pla), // VOREStation Edit + list("cat" = "Civilian", "elems" = civ), + list("cat" = "Silicon", "elems" = bot), + list("cat" = "Miscellaneous", "elems" = misc) + ) + return + /datum/datacore/proc/manifest() spawn() for(var/mob/living/carbon/human/H in player_list) @@ -187,10 +328,13 @@ /datum/datacore/proc/manifest_inject(var/mob/living/carbon/human/H) if(H.mind && !player_is_antag(H.mind, only_offstation_roles = 1)) var/assignment = GetAssignment(H) + var/hidden + var/datum/job/J = SSjob.get_job(assignment) + hidden = J?.offmap_spawn var/id = generate_record_id() //General Record - var/datum/data/record/G = CreateGeneralRecord(H, id) + var/datum/data/record/G = CreateGeneralRecord(H, id, hidden) G.fields["name"] = H.real_name G.fields["real_rank"] = H.mind.assigned_role G.fields["rank"] = assignment @@ -212,7 +356,7 @@ G.fields["notes"] = H.gen_record //Medical Record - var/datum/data/record/M = CreateMedicalRecord(H.real_name, id) + var/datum/data/record/M = CreateMedicalRecord(H.real_name, id, hidden) M.fields["b_type"] = H.b_type M.fields["b_dna"] = H.dna.unique_enzymes M.fields["id_gender"] = gender2text(H.identifying_gender) @@ -224,7 +368,7 @@ M.fields["notes"] = H.med_record //Security Record - var/datum/data/record/S = CreateSecurityRecord(H.real_name, id) + var/datum/data/record/S = CreateSecurityRecord(H.real_name, id, hidden) if(H.get_FBP_type()) S.fields["brain_type"] = H.get_FBP_type() else @@ -257,6 +401,7 @@ L.fields["image"] = icon(cached_character_icon(H), dir = SOUTH) L.fields["antagfac"] = H.antag_faction L.fields["antagvis"] = H.antag_vis + L.fields["offmap"] = hidden if(H.exploit_record && !jobban_isbanned(H, "Records")) L.fields["exploit_record"] = H.exploit_record else @@ -267,7 +412,7 @@ /proc/generate_record_id() return add_zero(num2hex(rand(1, 65535)), 4) //no point generating higher numbers because of the limitations of num2hex -/datum/datacore/proc/CreateGeneralRecord(var/mob/living/carbon/human/H, var/id) +/datum/datacore/proc/CreateGeneralRecord(var/mob/living/carbon/human/H, var/id, var/hidden) ResetPDAManifest() var/icon/front var/icon/side @@ -301,11 +446,14 @@ G.fields["photo_front"] = front G.fields["photo_side"] = side G.fields["notes"] = "No notes found." - general += G + if(hidden) + hidden_general += G + else + general += G return G -/datum/datacore/proc/CreateSecurityRecord(var/name, var/id) +/datum/datacore/proc/CreateSecurityRecord(var/name, var/id, var/hidden) ResetPDAManifest() var/datum/data/record/R = new /datum/data/record() R.name = "Security Record #[id]" @@ -319,11 +467,14 @@ R.fields["ma_crim_d"] = "No major crime convictions." R.fields["notes"] = "No notes." R.fields["notes"] = "No notes." - data_core.security += R + if(hidden) + hidden_security += R + else + security += R return R -/datum/datacore/proc/CreateMedicalRecord(var/name, var/id) +/datum/datacore/proc/CreateMedicalRecord(var/name, var/id, var/hidden) ResetPDAManifest() var/datum/data/record/M = new() M.name = "Medical Record #[id]" @@ -342,7 +493,10 @@ M.fields["cdi"] = "None" M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." M.fields["notes"] = "No notes found." - data_core.medical += M + if(hidden) + hidden_medical += M + else + medical += M return M diff --git a/code/datums/game_masters/_common.dm b/code/datums/game_masters/_common.dm new file mode 100644 index 0000000000..84efe57c73 --- /dev/null +++ b/code/datums/game_masters/_common.dm @@ -0,0 +1,6 @@ +// Push button, receive event. +// Returns a selected event datum. +/datum/game_master/proc/choose_event() + +/datum/game_master/proc/log_game_master(message) + // SSgame_master.log_game_master(message) // VOREStation Edit - We don't use SSgame_master yet. \ No newline at end of file diff --git a/code/datums/game_masters/default.dm b/code/datums/game_masters/default.dm new file mode 100644 index 0000000000..d380476df2 --- /dev/null +++ b/code/datums/game_masters/default.dm @@ -0,0 +1,89 @@ +// The default game master tries to choose events with these goals in mind. +// * Don't choose an event if the crew can't take it. E.g. no meteors after half of the crew died. +// * Try to involve lots of people, particuarly in active departments. +// * Avoid giving events to the same department multiple times in a row. +/datum/game_master/default + // If an event was done for a specific department, it is written here, so it doesn't do it again. + var/last_department_used = null + + +/datum/game_master/default/choose_event() + log_game_master("Now starting event decision.") + + var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used)) + var/list/best_events = decide_best_events(most_active_departments) + + if(LAZYLEN(best_events)) + log_game_master("Got [best_events.len] choice\s for the next event.") + var/list/weighted_events = list() + + for(var/E in best_events) + var/datum/event2/meta/event = E + var/weight = event.get_weight() + if(weight <= 0) + continue + weighted_events[event] = weight + log_game_master("Filtered down to [weighted_events.len] choice\s.") + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + last_department_used = LAZYACCESS(choice.departments, 1) + return choice + +/datum/game_master/default/proc/decide_best_events(list/most_active_departments) + if(!LAZYLEN(most_active_departments)) // Server's empty? + log_game_master("Game Master failed to find any active departments.") + return list() + + var/list/best_events = list() + if(most_active_departments.len >= 2) + var/list/top_two = list(most_active_departments[1], most_active_departments[2]) + best_events = filter_events_by_departments(top_two) + + if(LAZYLEN(best_events)) // We found something for those two, let's do it. + return best_events + + // Otherwise we probably couldn't find something for the second highest group, so let's ignore them. + best_events = filter_events_by_departments(most_active_departments[1]) + + if(LAZYLEN(best_events)) + return best_events + + // At this point we should expand our horizons. + best_events = filter_events_by_departments(list(DEPARTMENT_EVERYONE)) + + if(LAZYLEN(best_events)) + return best_events + + // Just give a random event if for some reason it still can't make up its mind. + best_events = filter_events_by_departments() + + if(LAZYLEN(best_events)) + return best_events + + log_game_master("Game Master failed to find a suitable event, something very wrong is going on.") + return list() + +// Filters the available events down to events for specific departments. +// Pass DEPARTMENT_EVERYONE if you want events that target the general population, like gravity failure. +// If no list is passed, all the events will be returned. +/datum/game_master/default/proc/filter_events_by_departments(list/departments) + . = list() + for(var/E in SSgame_master.available_events) + var/datum/event2/meta/event = E + if(!event.enabled) + continue + if(event.chaotic_threshold && !ignore_round_chaos) + if(SSgame_master.danger > event.chaotic_threshold) + continue + // An event has to involve all of these departments to pass. + var/viable = TRUE + if(LAZYLEN(departments)) + for(var/department in departments) + if(!LAZYFIND(departments, department)) + viable = FALSE + break + if(viable) + . += event diff --git a/code/datums/game_masters/other_game_masters.dm b/code/datums/game_masters/other_game_masters.dm new file mode 100644 index 0000000000..f62d83cfe3 --- /dev/null +++ b/code/datums/game_masters/other_game_masters.dm @@ -0,0 +1,44 @@ +// The `classic` game master tries to act like the old system, choosing events without any specific goals. +// * Has no goals, and instead operates purely off of the weights of the events it has. +// * Does not react to danger at all. +/datum/game_master/classic/choose_event() + var/list/weighted_events = list() + for(var/E in SSgame_master.available_events) + var/datum/event2/meta/event = E + if(!event.enabled) + continue + weighted_events[event] = event.get_weight() + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + return choice + + +// The `super_random` game master chooses events purely at random, ignoring weights entirely. +// * Has no goals, and instead chooses randomly, ignoring weights. +// * Does not react to danger at all. +/datum/game_master/super_random/choose_event() + return pick(SSgame_master.available_events) + + +// The `brutal` game master tries to run dangerous events frequently. +// * Chaotic events have their weights artifically boosted. +// * Ignores accumulated danger. +/datum/game_master/brutal + ignore_round_chaos = TRUE + +/datum/game_master/brutal/choose_event() + var/list/weighted_events = list() + for(var/E in SSgame_master.available_events) + var/datum/event2/meta/event = E + if(!event.enabled) + continue + weighted_events[event] = event.get_weight() + (event.chaos * 2) + + var/datum/event2/meta/choice = pickweight(weighted_events) + + if(choice) + log_game_master("[choice.name] was chosen, and is now being ran.") + return choice diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm index 26a7427c83..d068e27de2 100644 --- a/code/datums/ghost_query.dm +++ b/code/datums/ghost_query.dm @@ -5,6 +5,7 @@ var/finished = FALSE var/role_name = "a thing" var/question = "Would you like to play as a thing?" + var/query_sound = 'sound/effects/ghost2.ogg' // A sound file to play to the ghost, to help people who are alt-tabbed know something might interest them. var/be_special_flag = 0 var/list/check_bans = list() var/wait_time = 60 SECONDS // How long to wait until returning the list of candidates. @@ -42,6 +43,9 @@ spawn(0) if(!C) return + window_flash(C) + if(query_sound) + SEND_SOUND(C, sound(query_sound)) var/response = alert(C, question, "[role_name] request", "Yes", "No", "Never for this round") if(response == "Yes") response = alert(C, "Are you sure you want to play as a [role_name]?", "[role_name] request", "Yes", "No") // Protection from a misclick. @@ -62,12 +66,14 @@ /datum/ghost_query/promethean role_name = "Promethean" question = "Someone is requesting a soul for a promethean. Would you like to play as one?" + query_sound = 'sound/effects/slime_squish.ogg' be_special_flag = BE_ALIEN cutoff_number = 1 /datum/ghost_query/posi_brain role_name = "Positronic Intelligence" question = "Someone has activated a Positronic Brain. Would you like to play as one?" + query_sound = 'sound/machines/boobeebeep.ogg' be_special_flag = BE_AI check_bans = list("AI", "Cyborg") cutoff_number = 1 @@ -75,6 +81,7 @@ /datum/ghost_query/drone_brain role_name = "Drone Intelligence" question = "Someone has activated a Drone AI Chipset. Would you like to play as one?" + query_sound = 'sound/machines/boobeebeep.ogg' be_special_flag = BE_AI check_bans = list("AI", "Cyborg") cutoff_number = 1 @@ -90,6 +97,7 @@ /datum/ghost_query/xeno role_name = "Alien" question = "An Alien has just been created on the facility. Would you like to play as them?" + query_sound = 'sound/voice/hiss5.ogg' be_special_flag = BE_ALIEN /datum/ghost_query/blob diff --git a/code/datums/ghost_query_vr.dm b/code/datums/ghost_query_vr.dm new file mode 100644 index 0000000000..aba3165c63 --- /dev/null +++ b/code/datums/ghost_query_vr.dm @@ -0,0 +1,9 @@ +/datum/ghost_query/morph + role_name = "Morph" + question = "A weird morphic creature appears to have snuck onstation. Do you want to play as it? ((Expect to be treated as vore predator.))" + cutoff_number = 1 + +/datum/ghost_query/maints_pred + role_name = "Maintenance Predator" + question = "It appears a predatory critter is lurking in the maintenance. Do you want to play as it? ((You get to choose type of critter. Expect to be treated as vore predator.))" + cutoff_number = 1 \ No newline at end of file diff --git a/code/datums/helper_datums/events.dm b/code/datums/helper_datums/events.dm index 10e2f92a23..424f113a9c 100644 --- a/code/datums/helper_datums/events.dm +++ b/code/datums/helper_datums/events.dm @@ -31,7 +31,7 @@ // Arguments: event_type as text, any number of additional arguments to pass to event handler // Returns: null proc/fireEvent() - //world << "Events in [args[1]] called" + //to_world("Events in [args[1]] called") var/list/event = listgetindex(events,args[1]) if(istype(event)) spawn(-1) @@ -60,7 +60,7 @@ return ..() proc/Fire() - //world << "Event fired" + //to_world("Event fired") if(listener) call(listener,proc_name)(arglist(args)) return 1 diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm index 4dad60db89..8b194a139c 100644 --- a/code/datums/helper_datums/getrev.dm +++ b/code/datums/helper_datums/getrev.dm @@ -24,10 +24,10 @@ var/global/datum/getrev/revdata = new() date = unix2date(unix_time) break - world.log << "Running revision:" - world.log << branch - world.log << date - world.log << revision + to_world_log("Running revision:") + to_world_log(branch) + to_world_log(date) + to_world_log(revision) client/verb/showrevinfo() set category = "OOC" @@ -39,6 +39,6 @@ client/verb/showrevinfo() if(config.githuburl) to_chat(src, "[revdata.revision]") else - src << revdata.revision + to_chat(src,revdata.revision) else to_chat(src, "Revision unknown") diff --git a/code/datums/helper_datums/global_iterator.dm b/code/datums/helper_datums/global_iterator.dm index 2ebab5582d..a4cbc07de2 100644 --- a/code/datums/helper_datums/global_iterator.dm +++ b/code/datums/helper_datums/global_iterator.dm @@ -140,7 +140,7 @@ Data storage vars: arg_list = arguments return 1 else -// world << "Invalid arguments supplied for [src.type], ref = \ref[src]" +// to_world("Invalid arguments supplied for [src.type], ref = \ref[src]") return 0 proc/toggle_null_checks() diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 089a6be722..684505aa57 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -158,11 +158,18 @@ precision = rand(1,100) var/list/bagholding = teleatom.search_contents_for(/obj/item/weapon/storage/backpack/holding) + //VOREStation Addition Start: Prevent taurriding abuse + if(istype(teleatom, /mob/living)) + var/mob/living/L = teleatom + if(LAZYLEN(L.buckled_mobs)) + for(var/mob/rider in L.buckled_mobs) + bagholding += rider.search_contents_for(/obj/item/weapon/storage/backpack/holding) + //VOREStation Addition End: Prevent taurriding abuse if(bagholding.len) precision = max(rand(1,100)*bagholding.len,100) if(istype(teleatom, /mob/living)) var/mob/living/MM = teleatom - MM << "The Bluespace interface on your [teleatom] interferes with the teleport!" + to_chat(MM, "The Bluespace interface on your [teleatom] interferes with the teleport!") return 1 /datum/teleport/instant/science/teleportChecks() @@ -181,7 +188,7 @@ if(destination.z in using_map.admin_levels) //CentCom z-level if(istype(teleatom, /obj/mecha)) var/obj/mecha/MM = teleatom - MM.occupant << "\The [MM] would not survive the jump to a location so far away!" + to_chat(MM.occupant, "\The [MM] would not survive the jump to a location so far away!") return 0 if(!isemptylist(teleatom.search_contents_for(/obj/item/weapon/storage/backpack/holding))) teleatom.visible_message("\The [teleatom] bounces off of the portal!") diff --git a/code/datums/locations/locations.dm b/code/datums/locations/locations.dm index bc4365c716..0081318eb0 100644 --- a/code/datums/locations/locations.dm +++ b/code/datums/locations/locations.dm @@ -36,8 +36,8 @@ var/global/datum/locations/milky_way/all_locations = new() choice = input(user, "Please choose a location.","Locations") as null|anything in choice.contents else break - user << choice.name - user << choice.desc + to_chat(user,choice.name) + to_chat(user,choice.desc) return choice // var/datum/locations/choice = input(user, "Please choose a location.","Locations") as null|anything in all_locations @@ -46,11 +46,11 @@ var/global/datum/locations/milky_way/all_locations = new() /* /datum/locations/proc/show_contents() -// world << "[src]\n[desc]" +// to_world("[src]\n[desc]") for(var/datum/locations/a in contents) - world << "[a]\n[a.parent ? "Located in [a.parent]\n" : ""][a.desc]" + to_world("[a]\n[a.parent ? "Located in [a.parent]\n" : ""][a.desc]") a.show_contents() - world << "\n" + to_world("\n") /datum/locations/proc/count_locations() var/i = 0 @@ -72,5 +72,5 @@ var/global/datum/locations/milky_way/all_locations = new() set name = "Count Locations" set category = "Debug" var/location_number = locations.count_locations() - world << location_number + to_world(location_number) */ diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index fda5b41bd1..3a9a0610a5 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -51,7 +51,7 @@ start_sound = 'sound/machines/air_pump/airpumpstart.ogg' start_length = 10 mid_sounds = list('sound/machines/air_pump/airpumpidle.ogg' = 1) - mid_length = 110 + mid_length = 70 end_sound = 'sound/machines/air_pump/airpumpshutdown.ogg' volume = 15 pref_check = /datum/client_preference/air_pump_noise \ No newline at end of file diff --git a/code/datums/managed_browsers/_managed_browser.dm b/code/datums/managed_browsers/_managed_browser.dm new file mode 100644 index 0000000000..c915c7e86a --- /dev/null +++ b/code/datums/managed_browsers/_managed_browser.dm @@ -0,0 +1,52 @@ +GLOBAL_VAR(managed_browser_id_ticker) + +// This holds information on managing a /datum/browser object. +// Managing can include things like persisting the state of specific information inside of this object, receiving Topic() calls, or deleting itself when the window is closed. +// This is useful for browser windows to be able to stand 'on their own' instead of being tied to something in the game world, like an object or mob. +/datum/managed_browser + var/client/my_client = null + var/browser_id = null + var/base_browser_id = null + + var/title = null + var/size_x = 200 + var/size_y = 400 + + var/display_when_created = TRUE + +/datum/managed_browser/New(client/new_client) + if(!new_client) + crash_with("Managed browser object was not given a client.") + return + if(!base_browser_id) + crash_with("Managed browser object does not have a base browser id defined in its type.") + return + + my_client = new_client + browser_id = "[base_browser_id]-[GLOB.managed_browser_id_ticker++]" + + if(display_when_created) + display() + +/datum/managed_browser/Destroy() + my_client = null + return ..() + +// Override if you want to have the browser title change conditionally. +// Otherwise it's easier to just change the title variable directly. +/datum/managed_browser/proc/get_title() + return title + +// Override to display the html information. +// It is suggested to build it with a list, and use list.Join() at the end. +// This helps prevent excessive concatination, which helps preserves BYOND's string tree from becoming a laggy mess. +/datum/managed_browser/proc/get_html() + return + +/datum/managed_browser/proc/display() + interact(get_html(), get_title(), my_client) + +/datum/managed_browser/proc/interact(html, title, client/C) + var/datum/browser/popup = new(C.mob, browser_id, title, size_x, size_y, src) + popup.set_content(html) + popup.open() \ No newline at end of file diff --git a/code/datums/managed_browsers/feedback_form.dm b/code/datums/managed_browsers/feedback_form.dm new file mode 100644 index 0000000000..bc5c456aec --- /dev/null +++ b/code/datums/managed_browsers/feedback_form.dm @@ -0,0 +1,147 @@ +/client + var/datum/managed_browser/feedback_form/feedback_form = null + +/client/can_vv_get(var_name) + return var_name != NAMEOF(src, feedback_form) // No snooping. + +GENERAL_PROTECT_DATUM(datum/managed_browser/feedback_form) + +// A fairly simple object to hold information about a player's feedback as it's being written. +// Having this be it's own object instead of being baked into /mob/new_player allows for it to be used +// from other places than just the lobby, and makes it a lot harder for people with dev powers to be naughty with it using VV/proccall. +/datum/managed_browser/feedback_form + base_browser_id = "feedback_form" + title = "Server Feedback" + size_x = 480 + size_y = 520 + var/feedback_topic = null + var/feedback_body = null + var/feedback_hide_author = FALSE + +/datum/managed_browser/feedback_form/New(client/new_client) + feedback_topic = config.sqlite_feedback_topics[1] + ..(new_client) + +/datum/managed_browser/feedback_form/Destroy() + if(my_client) + my_client.feedback_form = null + return ..() + +// Privacy option is allowed if both the config allows it, and the pepper file exists and isn't blank. +/datum/managed_browser/feedback_form/proc/can_be_private() + return config.sqlite_feedback_privacy && SSsqlite.get_feedback_pepper() + +/datum/managed_browser/feedback_form/display() + if(!my_client) + return + if(!SSsqlite.can_submit_feedback(my_client)) + return + ..() + +// Builds the window for players to review their feedback. +/datum/managed_browser/feedback_form/get_html() + var/list/dat = list("") + dat += "
" + dat += "" + dat += "Here, you can write some feedback for the server.
" + dat += "Note that HTML is NOT supported!
" + dat += "Click the edit button to begin writing.
" + + dat += "Your feedback is currently [length(feedback_body)]/[MAX_FEEDBACK_LENGTH] letters long." + dat += "
" + dat += "
" + + dat += "

Preview

" + + dat += "Author: " + + if(can_be_private()) + if(!feedback_hide_author) + dat += "[my_client.ckey] " + dat += span("linkOn", "Visible") + dat += " | " + dat += href(src, list("feedback_hide_author" = 1), "Hashed") + else + dat += "[md5(ckey(lowertext(my_client.ckey + SSsqlite.get_feedback_pepper())))] " + dat += href(src, list("feedback_hide_author" = 0), "Visible") + dat += " | " + dat += span("linkOn", "Hashed") + else + dat += my_client.ckey + dat += "
" + + if(config.sqlite_feedback_topics.len > 1) + dat += "Topic: [href(src, list("feedback_choose_topic" = 1), feedback_topic)]
" + else + dat += "Topic: [config.sqlite_feedback_topics[1]]
" + + dat += "
" + if(feedback_body) + dat += replacetext(feedback_body, "\n", "
") // So newlines will look like they work in the preview. + else + dat += "\[Feedback goes here...\]" + dat += "
" + dat += href(src, list("feedback_edit_body" = 1), "Edit") + dat += "
" + + if(config.sqlite_feedback_cooldown) + dat += "Please note that you will have to wait [config.sqlite_feedback_cooldown] day\s before \ + being able to write more feedback after submitting.
" + + dat += href(src, list("feedback_submit" = 1), "Submit") + dat += "" + return dat.Join() + +/datum/managed_browser/feedback_form/Topic(href, href_list[]) + if(!my_client) + return FALSE + + if(href_list["feedback_edit_body"]) + // This is deliberately not sanitized here, and is instead checked when hitting the submission button, + // as we want to give the user a chance to fix it without needing to rewrite the whole thing. + feedback_body = input(my_client, "Please write your feedback here.", "Feedback Body", feedback_body) as null|message + display() // Refresh the window with new information. + return + + if(href_list["feedback_hide_author"]) + if(!can_be_private()) + feedback_hide_author = FALSE + else + feedback_hide_author = text2num(href_list["feedback_hide_author"]) + display() + return + + if(href_list["feedback_choose_topic"]) + feedback_topic = input(my_client, "Choose the topic you want to submit your feedback under.", "Feedback Topic", feedback_topic) in config.sqlite_feedback_topics + display() + return + + if(href_list["feedback_submit"]) + // Do some last minute validation, and tell the user if something goes wrong, + // so we don't wipe out their ten thousand page essay due to having a few too many characters. + if(length(feedback_body) > MAX_FEEDBACK_LENGTH) + to_chat(my_client, span("warning", "Your feedback is too long, at [length(feedback_body)] characters, where as the \ + limit is [MAX_FEEDBACK_LENGTH]. Please shorten it and try again.")) + return + + var/text = sanitize(feedback_body, max_length = 0, encode = TRUE, trim = FALSE, extra = FALSE) + if(!text) // No text, or it was super invalid. + to_chat(my_client, span("warning", "It appears you didn't write anything, or it was invalid.")) + return + + if(alert(my_client, "Are you sure you want to submit your feedback?", "Confirm Submission", "No", "Yes") == "Yes") + var/author_text = my_client.ckey + if(can_be_private() && feedback_hide_author) + author_text = md5(my_client.ckey + SSsqlite.get_feedback_pepper()) + + var/success = SSsqlite.insert_feedback(author = author_text, topic = feedback_topic, content = feedback_body, sqlite_object = SSsqlite.sqlite_db) + if(!success) + to_chat(my_client, span("warning", "Something went wrong while inserting your feedback into the database. Please try again. \ + If this happens again, you should contact a developer.")) + return + + my_client.mob << browse(null, "window=[browser_id]") // Closes the window. + if(istype(my_client.mob, /mob/new_player)) + var/mob/new_player/NP = my_client.mob + NP.new_player_panel_proc() // So the feedback button goes away, if the user gets put on cooldown. + qdel(src) \ No newline at end of file diff --git a/code/datums/managed_browsers/feedback_viewer.dm b/code/datums/managed_browsers/feedback_viewer.dm new file mode 100644 index 0000000000..895bd36fae --- /dev/null +++ b/code/datums/managed_browsers/feedback_viewer.dm @@ -0,0 +1,162 @@ +/client + var/datum/managed_browser/feedback_viewer/feedback_viewer = null + +/datum/admins/proc/view_feedback() + set category = "Admin" + set name = "View Feedback" + set desc = "Open the Feedback Viewer" + + if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT)) + return + + if(usr.client.feedback_viewer) + usr.client.feedback_viewer.display() + else + usr.client.feedback_viewer = new(usr.client) + +// This object holds the code to run the admin feedback viewer. +/datum/managed_browser/feedback_viewer + base_browser_id = "feedback_viewer" + title = "Submitted Feedback" + size_x = 900 + size_y = 500 + var/database/query/last_query = null + +/datum/managed_browser/feedback_viewer/New(client/new_client) + if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT, new_client)) // Just in case someone figures out a way to spawn this as non-staff. + message_admins("[new_client] tried to view feedback with insufficent permissions.") + qdel(src) + + ..() + +/datum/managed_browser/feedback_viewer/Destroy() + if(my_client) + my_client.feedback_viewer = null + return ..() + +/datum/managed_browser/feedback_viewer/proc/feedback_filter(row_name, thing_to_find, exact = FALSE) + var/database/query/query = null + if(exact) // Useful for ID searches, so searching for 'id 10' doesn't also get 'id 101'. + query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [row_name] == ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "}, + thing_to_find + ) + + else + // Wrap the thing in %s so LIKE will work. + thing_to_find = "%[thing_to_find]%" + query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + WHERE [row_name] LIKE ? + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "}, + thing_to_find + ) + query.Execute(SSsqlite.sqlite_db) + SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer - Filter by [row_name] to find [thing_to_find]") + return query + +// Builds the window for players to review their feedback. +/datum/managed_browser/feedback_viewer/get_html() + var/list/dat = list("") + if(!last_query) // If no query was done before, just show the most recent feedbacks. + var/database/query/query = new({" + SELECT * + FROM [SQLITE_TABLE_FEEDBACK] + ORDER BY [SQLITE_FEEDBACK_COLUMN_ID] + DESC LIMIT 50; + "} + ) + query.Execute(SSsqlite.sqlite_db) + SSsqlite.sqlite_check_for_errors(query, "Admin Feedback Viewer") + last_query = query + + dat += "
Event TypeStart TimeFinish Time
[event.type][event.time_started][event.time_finished]
[M.required_players][name][bot[name]][isactive[name]]
Offmap Spawns
[name][off[name]][isactive[name]]
Miscellaneous
" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + while(last_query.NextRow()) + var/list/row_data = last_query.GetRowData() + dat += "" + dat += "" + dat += "" + dat += "" // TODO: Color this to make hashed keys more distinguishable. + var/text = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] + if(length(text) > 512) + text = href(src, list( + "show_full_feedback" = 1, + "feedback_author" = row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR], + "feedback_content" = row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] + ), "[copytext(text, 1, 64)]... ([length(text)])") + else + text = replacetext(text, "\n", "
") + dat += "" + dat += "" + dat += "" + dat += "
[href(src, list("filter_id" = 1), "ID")][href(src, list("filter_topic" = 1), "Topic")][href(src, list("filter_author" = 1), "Author")][href(src, list("filter_content" = 1), "Content")][href(src, list("filter_datetime" = 1), "Datetime")]
[row_data[SQLITE_FEEDBACK_COLUMN_ID]][row_data[SQLITE_FEEDBACK_COLUMN_TOPIC]][row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR]][text][row_data[SQLITE_FEEDBACK_COLUMN_DATETIME]]
" + + dat += "" + return dat.Join() + +// Used to show the full version of feedback in a seperate window. +/datum/managed_browser/feedback_viewer/proc/display_big_feedback(author, text) + var/list/dat = list("") + dat += replacetext(text, "\n", "
") + + var/datum/browser/popup = new(my_client.mob, "feedback_big", "[author]'s Feedback", 480, 520, src) + popup.set_content(dat.Join()) + popup.open() + + +/datum/managed_browser/feedback_viewer/Topic(href, href_list[]) + if(!my_client) + return FALSE + + if(href_list["close"]) // To avoid refreshing. + return + + if(href_list["show_full_feedback"]) + display_big_feedback(href_list["feedback_author"], href_list["feedback_content"]) + return + + if(href_list["filter_id"]) + var/id_to_search = input(my_client, "Write feedback ID here.", "Filter by ID", null) as null|num + if(id_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_ID, id_to_search, TRUE) + + if(href_list["filter_author"]) + var/author_to_search = input(my_client, "Write desired key or hash here. Partial keys/hashes are allowed.", "Filter by Author", null) as null|text + if(author_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_AUTHOR, author_to_search) + + if(href_list["filter_topic"]) + var/topic_to_search = input(my_client, "Write desired topic here. Partial topics are allowed. \ + \nThe current topics in the config are [english_list(config.sqlite_feedback_topics)].", "Filter by Topic", null) as null|text + if(topic_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_TOPIC, topic_to_search) + + if(href_list["filter_content"]) + var/content_to_search = input(my_client, "Write desired content to find here. Partial matches are allowed.", "Filter by Content", null) as null|message + if(content_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_CONTENT, content_to_search) + + if(href_list["filter_datetime"]) + var/datetime_to_search = input(my_client, "Write desired datetime. Partial matches are allowed.\n\ + Format is 'YYYY-MM-DD HH:MM:SS'.", "Filter by Datetime", null) as null|text + if(datetime_to_search) + last_query = feedback_filter(SQLITE_FEEDBACK_COLUMN_DATETIME, datetime_to_search) + + // Refresh. + display() \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 67cf23a3cb..7f16c60bde 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -78,7 +78,7 @@ /datum/mind/proc/transfer_to(mob/living/new_character) if(!istype(new_character)) - world.log << "## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn" + to_world_log("## DEBUG: transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob. Please inform Carn") if(current) //remove ourself from our old body's mind variable if(changeling) current.remove_changeling_powers() @@ -153,7 +153,7 @@ usr << browse(out, "window=edit_memory[src]") /datum/mind/Topic(href, href_list) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return if(href_list["add_antagonist"]) var/datum/antagonist/antag = all_antag_types[href_list["add_antagonist"]] @@ -161,7 +161,7 @@ if(antag.add_antagonist(src, 1, 1, 0, 1, 1)) // Ignore equipment and role type for this. log_admin("[key_name_admin(usr)] made [key_name(src)] into a [antag.role_text].") else - usr << "[src] could not be made into a [antag.role_text]!" + to_chat(usr, "[src] could not be made into a [antag.role_text]!") else if(href_list["remove_antagonist"]) var/datum/antagonist/antag = all_antag_types[href_list["remove_antagonist"]] @@ -198,7 +198,7 @@ return if(mind) mind.ambitions = sanitize(new_ambition) - mind.current << "Your ambitions have been changed by higher powers, they are now: [mind.ambitions]" + to_chat(mind.current, "Your ambitions have been changed by higher powers, they are now: [mind.ambitions]") log_and_message_admins("made [key_name(mind.current)]'s ambitions be '[mind.ambitions]'.") else if (href_list["obj_edit"] || href_list["obj_add"]) @@ -344,10 +344,10 @@ if(I in organs.implants) qdel(I) break - H << "Your loyalty implant has been deactivated." + to_chat(H, "Your loyalty implant has been deactivated.") log_admin("[key_name_admin(usr)] has de-loyalty implanted [current].") if("add") - H << "You somehow have become the recepient of a loyalty transplant, and it just activated!" + to_chat(H, "You somehow have become the recepient of a loyalty transplant, and it just activated!") H.implant_loyalty(override = TRUE) log_admin("[key_name_admin(usr)] has loyalty implanted [current].") else @@ -410,9 +410,9 @@ else if (href_list["obj_announce"]) var/obj_count = 1 - current << "Your current objectives:" + to_chat(current, "Your current objectives:") for(var/datum/objective/objective in objectives) - current << "Objective #[obj_count]: [objective.explanation_text]" + to_chat(current, "Objective #[obj_count]: [objective.explanation_text]") obj_count++ edit_memory() @@ -490,7 +490,7 @@ if(ticker) ticker.minds += mind else - world.log << "## DEBUG: mind_initialize(): No ticker ready yet! Please inform Carn" + to_world_log("## DEBUG: mind_initialize(): No ticker ready yet! Please inform Carn") if(!mind.name) mind.name = real_name mind.current = src if(player_is_antag(mind)) diff --git a/code/datums/observation/helpers.dm b/code/datums/observation/helpers.dm index 9116026700..12feeba16d 100644 --- a/code/datums/observation/helpers.dm +++ b/code/datums/observation/helpers.dm @@ -9,6 +9,9 @@ /atom/proc/recursive_dir_set(var/atom/a, var/old_dir, var/new_dir) set_dir(new_dir) +/datum/proc/qdel_self() + qdel(src) + /proc/register_all_movement(var/event_source, var/listener) GLOB.moved_event.register(event_source, listener, /atom/movable/proc/recursive_move) GLOB.dir_set_event.register(event_source, listener, /atom/proc/recursive_dir_set) diff --git a/code/datums/observation/shuttle_added.dm b/code/datums/observation/shuttle_added.dm new file mode 100644 index 0000000000..dfd95170a0 --- /dev/null +++ b/code/datums/observation/shuttle_added.dm @@ -0,0 +1,22 @@ +// Observer Pattern Implementation: Shuttle Added +// Registration type: /datum/shuttle (register for the global event only) +// +// Raised when: After a shuttle is initialized. +// +// Arguments that the called proc should expect: +// /datum/shuttle/shuttle: the new shuttle + +GLOBAL_DATUM_INIT(shuttle_added, /decl/observ/shuttle_added, new) + +/decl/observ/shuttle_added + name = "Shuttle Added" + expected_type = /datum/shuttle + +/***************************** +* Shuttle Added Handling * +*****************************/ + +/datum/controller/subsystem/shuttles/initialize_shuttle() + . = ..() + if(.) + GLOB.shuttle_added.raise_event(.) \ No newline at end of file diff --git a/code/datums/observation/shuttle_moved.dm b/code/datums/observation/shuttle_moved.dm new file mode 100644 index 0000000000..35bff0d6b9 --- /dev/null +++ b/code/datums/observation/shuttle_moved.dm @@ -0,0 +1,38 @@ +// Observer Pattern Implementation: Shuttle Moved +// Registration type: /datum/shuttle/autodock +// +// Raised when: A shuttle has moved to a new landmark. +// +// Arguments that the called proc should expect: +// /datum/shuttle/shuttle: the shuttle moving +// /obj/effect/shuttle_landmark/old_location: the old location's shuttle landmark +// /obj/effect/shuttle_landmark/new_location: the new location's shuttle landmark + +// Observer Pattern Implementation: Shuttle Pre Move +// Registration type: /datum/shuttle/autodock +// +// Raised when: A shuttle is about to move to a new landmark. +// +// Arguments that the called proc should expect: +// /datum/shuttle/shuttle: the shuttle moving +// /obj/effect/shuttle_landmark/old_location: the old location's shuttle landmark +// /obj/effect/shuttle_landmark/new_location: the new location's shuttle landmark + +GLOBAL_DATUM_INIT(shuttle_moved_event, /decl/observ/shuttle_moved, new) + +/decl/observ/shuttle_moved + name = "Shuttle Moved" + expected_type = /datum/shuttle + +GLOBAL_DATUM_INIT(shuttle_pre_move_event, /decl/observ/shuttle_pre_move, new) + +/decl/observ/shuttle_pre_move + name = "Shuttle Pre Move" + expected_type = /datum/shuttle + +/***************** +* Shuttle Moved/Pre Move Handling * +*****************/ + +// Located in modules/shuttle/shuttle.dm +// Proc: /datum/shuttle/proc/attempt_move() \ No newline at end of file diff --git a/code/datums/observation/stat_set.dm b/code/datums/observation/stat_set.dm new file mode 100644 index 0000000000..b980d06ecc --- /dev/null +++ b/code/datums/observation/stat_set.dm @@ -0,0 +1,24 @@ +// Observer Pattern Implementation: Stat Set +// Registration type: /mob/living +// +// Raised when: A /mob/living changes stat, using the set_stat() proc +// +// Arguments that the called proc should expect: +// /mob/living/stat_mob: The mob whose stat changed +// /old_stat: Status before the change. +// /new_stat: Status after the change. + +GLOBAL_DATUM_INIT(stat_set_event, /decl/observ/stat_set, new) + +/decl/observ/stat_set + name = "Stat Set" + expected_type = /mob/living + +/**************** +* Stat Handling * +****************/ +/mob/living/set_stat(var/new_stat) + var/old_stat = stat + . = ..() + if(stat != old_stat) + GLOB.stat_set_event.raise_event(src, old_stat, new_stat) diff --git a/code/datums/outfits/jobs/civilian.dm b/code/datums/outfits/jobs/civilian.dm index f5b94145ff..c8d588a145 100644 --- a/code/datums/outfits/jobs/civilian.dm +++ b/code/datums/outfits/jobs/civilian.dm @@ -6,11 +6,18 @@ name = OUTFIT_JOB_NAME("Visitor") id_pda_assignment = "Visitor" uniform = /obj/item/clothing/under/assistantformal + //VOREStation Add - Interns /decl/hierarchy/outfit/job/assistant/intern name = OUTFIT_JOB_NAME("Intern") id_type = /obj/item/weapon/card/id/civilian //VOREStation Add End - Interns + +/decl/hierarchy/outfit/job/assistant/resident + name = OUTFIT_JOB_NAME("Resident") + id_pda_assignment = "Resident" + uniform = /obj/item/clothing/under/color/white + /decl/hierarchy/outfit/job/service l_ear = /obj/item/device/radio/headset/headset_service hierarchy_type = /decl/hierarchy/outfit/job/service diff --git a/code/datums/outfits/jobs/security.dm b/code/datums/outfits/jobs/security.dm index 9f127a2701..814cda5054 100644 --- a/code/datums/outfits/jobs/security.dm +++ b/code/datums/outfits/jobs/security.dm @@ -37,10 +37,13 @@ satchel_one = /obj/item/weapon/storage/backpack/satchel/norm backpack_contents = list(/obj/item/weapon/storage/box/evidence = 1) +//VOREStation Edit - More cyberpunky /decl/hierarchy/outfit/job/security/detective/forensic name = OUTFIT_JOB_NAME("Forensic technician") - head = null - suit = /obj/item/clothing/suit/storage/forensics/blue + head = /obj/item/clothing/head/helmet/detective_alt + suit = null + uniform = /obj/item/clothing/under/detective_alt +//VOREStation Edit End /decl/hierarchy/outfit/job/security/officer name = OUTFIT_JOB_NAME("Security Officer") diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm index 581dc012f8..01e4119b55 100644 --- a/code/datums/progressbar.dm +++ b/code/datums/progressbar.dm @@ -29,7 +29,7 @@ return ..() /datum/progressbar/proc/update(progress) - //world << "Update [progress] - [goal] - [(progress / goal)] - [((progress / goal) * 100)] - [round(((progress / goal) * 100), 5)]" + //to_world("Update [progress] - [goal] - [(progress / goal)] - [((progress / goal) * 100)] - [round(((progress / goal) * 100), 5)]") if (!user || !user.client) shown = 0 return diff --git a/code/datums/recipe.dm b/code/datums/recipe.dm index 464779611d..a70199887c 100644 --- a/code/datums/recipe.dm +++ b/code/datums/recipe.dm @@ -123,7 +123,7 @@ // food-related /datum/recipe/proc/make_food(var/obj/container as obj) if(!result) - world << "Recipe [type] is defined without a result, please bug report this." + to_world("Recipe [type] is defined without a result, please bug report this.") return var/obj/result_obj = new result(container) if(istype(container, /obj/machinery)) diff --git a/code/datums/repositories/cameras.dm b/code/datums/repositories/cameras.dm index 7bac2a4634..d516113394 100644 --- a/code/datums/repositories/cameras.dm +++ b/code/datums/repositories/cameras.dm @@ -14,10 +14,25 @@ var/global/datum/repository/cameras/camera_repository = new() networks = list() ..() -/datum/repository/cameras/proc/cameras_in_network(var/network) +/datum/repository/cameras/proc/cameras_in_network(var/network, var/list/zlevels) setup_cache() var/list/network_list = networks[network] - return network_list + if(LAZYLEN(zlevels)) + var/list/filtered_cameras = list() + for(var/list/C in network_list) + //Camera is marked as always-visible + if(C["omni"]) + filtered_cameras[++filtered_cameras.len] = C + continue + //Camera might be in an adjacent zlevel + var/camz = C["z"] + if(!camz) //It's inside something (helmet, communicator, etc) or nullspace or who knows + camz = get_z(locate(C["camera"]) in cameranet.cameras) + if(camz in zlevels) + filtered_cameras[++filtered_cameras.len] = C //Can't add lists to lists with += + return filtered_cameras + else + return network_list /datum/repository/cameras/proc/setup_cache() if(!invalidated) diff --git a/code/datums/repositories/crew.dm b/code/datums/repositories/crew.dm index 88ea491e51..f67a17ca75 100644 --- a/code/datums/repositories/crew.dm +++ b/code/datums/repositories/crew.dm @@ -48,10 +48,10 @@ var/global/datum/repository/crew/crew_repository = new() if(C.sensor_mode >= SUIT_SENSOR_TRACKING) var/area/A = get_area(H) - crewmemberData["area"] = sanitize(A.name) + crewmemberData["area"] = sanitize(A.get_name()) crewmemberData["x"] = pos.x crewmemberData["y"] = pos.y - crewmemberData["z"] = pos.z + crewmemberData["z"] = using_map.get_zlevel_name(pos.z) crewmembers[++crewmembers.len] = crewmemberData diff --git a/code/datums/supplypacks/engineering.dm b/code/datums/supplypacks/engineering.dm index e387309da7..5ffa26d711 100644 --- a/code/datums/supplypacks/engineering.dm +++ b/code/datums/supplypacks/engineering.dm @@ -35,48 +35,6 @@ containertype = /obj/structure/closet/crate/engineering containername = "Superconducting Transmission Coil crate" -/datum/supply_pack/eng/shield_capacitor - name = "Shield Capacitor" - contains = list(/obj/machinery/shield_capacitor) - cost = 20 - containertype = /obj/structure/closet/crate/engineering - containername = "shield capacitor crate" - -/datum/supply_pack/eng/shield_capacitor/advanced - name = "Advanced Shield Capacitor" - contains = list(/obj/machinery/shield_capacitor/advanced) - cost = 30 - containertype = /obj/structure/closet/crate/engineering - containername = "advanced shield capacitor crate" - -/datum/supply_pack/eng/bubble_shield - name = "Bubble Shield Generator" - contains = list(/obj/machinery/shield_gen) - cost = 40 - containertype = /obj/structure/closet/crate/engineering - containername = "shield bubble generator crate" - -/datum/supply_pack/eng/bubble_shield/advanced - name = "Advanced Bubble Shield Generator" - contains = list(/obj/machinery/shield_gen/advanced) - cost = 60 - containertype = /obj/structure/closet/crate/engineering - containername = "advanced bubble shield generator crate" - -/datum/supply_pack/eng/hull_shield - name = "Hull Shield Generator" - contains = list(/obj/machinery/shield_gen/external) - cost = 80 - containertype = /obj/structure/closet/crate/engineering - containername = "shield hull generator crate" - -/datum/supply_pack/eng/hull_shield/advanced - name = "Advanced Hull Shield Generator" - contains = list(/obj/machinery/shield_gen/external/advanced) - cost = 120 - containertype = /obj/structure/closet/crate/engineering - containername = "advanced hull shield generator crate" - /datum/supply_pack/eng/electrical name = "Electrical maintenance crate" contains = list( @@ -173,29 +131,19 @@ containername = "Particle Accelerator crate" access = access_ce -/datum/supply_pack/eng/shield_gen - contains = list(/obj/item/weapon/circuitboard/shield_gen) - name = "Bubble shield generator circuitry" - cost = 30 - containertype = /obj/structure/closet/crate/secure/engineering - containername = "bubble shield generator circuitry crate" - access = access_ce - -/datum/supply_pack/eng/shield_gen_ex - contains = list(/obj/item/weapon/circuitboard/shield_gen_ex) - name = "Hull shield generator circuitry" - cost = 30 - containertype = /obj/structure/closet/crate/secure/engineering - containername = "hull shield generator circuitry crate" - access = access_ce - -/datum/supply_pack/eng/shield_cap - contains = list(/obj/item/weapon/circuitboard/shield_cap) - name = "Bubble shield capacitor circuitry" - cost = 30 - containertype = /obj/structure/closet/crate/secure/engineering - containername = "shield capacitor circuitry crate" - access = access_ce +/datum/supply_pack/eng/shield_generator + name = "Shield Generator Construction Kit" + contains = list( + /obj/item/weapon/circuitboard/shield_generator, + /obj/item/weapon/stock_parts/capacitor, + /obj/item/weapon/stock_parts/micro_laser, + /obj/item/weapon/smes_coil, + /obj/item/weapon/stock_parts/console_screen, + /obj/item/weapon/stock_parts/subspace/amplifier + ) + cost = 80 + containertype = /obj/structure/closet/crate/engineering + containername = "shield generator construction kit crate" /datum/supply_pack/eng/smbig name = "Supermatter Core" diff --git a/code/datums/supplypacks/hospitality.dm b/code/datums/supplypacks/hospitality.dm index 5d663d05db..6547b25e70 100644 --- a/code/datums/supplypacks/hospitality.dm +++ b/code/datums/supplypacks/hospitality.dm @@ -55,7 +55,8 @@ /obj/item/pizzabox/margherita, /obj/item/pizzabox/mushroom, /obj/item/pizzabox/meat, - /obj/item/pizzabox/vegetable + /obj/item/pizzabox/vegetable, + /obj/item/pizzabox/pineapple ) name = "Surprise pack of five pizzas" cost = 15 diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm index 554a826f20..1b164a2cdf 100644 --- a/code/datums/supplypacks/medical.dm +++ b/code/datums/supplypacks/medical.dm @@ -339,14 +339,14 @@ /datum/supply_pack/med/distillery name = "Chemical distiller crate" contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery = 1) - cost = 175 + cost = 50 containertype = /obj/structure/largecrate containername = "Chemical distiller crate" /datum/supply_pack/med/advdistillery name = "Industrial Chemical distiller crate" contains = list(/obj/machinery/portable_atmospherics/powered/reagent_distillery/industrial = 1) - cost = 250 + cost = 150 containertype = /obj/structure/largecrate containername = "Industrial Chemical distiller crate" diff --git a/code/datums/supplypacks/misc_vr.dm b/code/datums/supplypacks/misc_vr.dm index 93523707b9..27de833ba8 100644 --- a/code/datums/supplypacks/misc_vr.dm +++ b/code/datums/supplypacks/misc_vr.dm @@ -6,12 +6,15 @@ /obj/item/weapon/cell/device/weapon = 2, /obj/item/weapon/storage/firstaid/regular = 1, /obj/item/device/gps = 2, - /obj/item/weapon/storage/box/traumainjectors = 1 + /obj/item/weapon/storage/box/traumainjectors = 1, + /obj/item/device/binoculars = 1 ) - cost = 50 + cost = 60 containertype = /obj/structure/closet/crate/secure/gear containername = "Belt-miner gear crate" - access = access_mining + access = list(access_mining, + access_xenoarch) + one_access = TRUE /datum/supply_pack/misc/eva_rig name = "eva hardsuit (empty)" @@ -78,3 +81,18 @@ containertype = /obj/structure/closet/crate/secure/gear containername = "advanced voidsuit crate" access = access_ce + +/datum/supply_pack/misc/jetpack + name = "jetpack (empty)" + contains = list( + /obj/item/weapon/tank/jetpack = 1 + ) + cost = 75 + containertype = /obj/structure/closet/crate/secure/gear + containername = "jetpack crate" + access = list(access_mining, + access_xenoarch, + access_eva, + access_explorer, + access_pilot) + one_access = TRUE \ No newline at end of file diff --git a/code/datums/supplypacks/misc_yw.dm b/code/datums/supplypacks/misc_yw.dm new file mode 100644 index 0000000000..da3639daa1 --- /dev/null +++ b/code/datums/supplypacks/misc_yw.dm @@ -0,0 +1,26 @@ +/datum/supply_pack/misc/blueshieldgear + name = "Blueshield Special Equipment" + contains = list( + /obj/item/clothing/head/beret/blueshield = 1, + /obj/item/clothing/under/yw/blueshield = 1, + /obj/item/clothing/under/yw/blueshield2 = 1, + /obj/item/clothing/suit/armor/yw/blueshield = 1, + /obj/item/clothing/suit/armor/yw/blueshieldcoat = 1, + /obj/item/clothing/accessory/poncho/roles/cloak/blueshield = 1, + /obj/item/clothing/accessory/holster/leg = 1 + ) + cost = 80 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Blueshield equipment" + access = access_blueshield + +/datum/supply_pack/misc/blueshieldweapons + name = "Blueshield Weapon Kits" + contains = list( + /obj/item/gunbox/blueshield = 1, + /obj/item/gunbox/blueshield/secondary = 1 + ) + cost = 100 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Blueshield armaments" + access = access_blueshield diff --git a/code/datums/supplypacks/munitions.dm b/code/datums/supplypacks/munitions.dm index c9e900256a..61c60eab06 100644 --- a/code/datums/supplypacks/munitions.dm +++ b/code/datums/supplypacks/munitions.dm @@ -168,6 +168,14 @@ containername = "Magnetic weapon crate" access = access_security +/datum/supply_pack/munitions/mshells + name = "Weapons - Magnetic Shells" + contains = list(/obj/item/weapon/magnetic_ammo = 3) + cost = 100 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Magnetic ammunition crate" + access = access_security + /datum/supply_pack/munitions/shotgunammo name = "Ammunition - Shotgun shells" contains = list( diff --git a/code/datums/supplypacks/munitions_vr.dm b/code/datums/supplypacks/munitions_vr.dm index 8a86c73ad5..e8674dd390 100644 --- a/code/datums/supplypacks/munitions_vr.dm +++ b/code/datums/supplypacks/munitions_vr.dm @@ -1,8 +1,8 @@ /datum/supply_pack/munitions/expeditionguns name = "Frontier phaser (station-locked) crate" contains = list( - /obj/item/weapon/gun/energy/frontier/locked = 2, - /obj/item/weapon/gun/energy/frontier/locked/holdout = 1, + /obj/item/weapon/gun/energy/locked/frontier = 2, + /obj/item/weapon/gun/energy/locked/frontier/holdout = 1, ) cost = 35 containertype = /obj/structure/closet/crate/secure diff --git a/code/datums/supplypacks/robotics.dm b/code/datums/supplypacks/robotics.dm index 1c1eb7f2bb..757d38b953 100644 --- a/code/datums/supplypacks/robotics.dm +++ b/code/datums/supplypacks/robotics.dm @@ -116,6 +116,15 @@ containername = "Robolimb blueprints (Bishop)" access = access_robotics +/datum/supply_pack/robotics/robolimbs/cenilimicybernetics + name = "Cenilimi Cybernetics robolimb blueprints" + contains = list(/obj/item/weapon/disk/limb/cenilimicybernetics) + cost = 45 + containertype = /obj/structure/closet/crate/secure/science + containername = "Robolimb blueprints (Cenilimi Cybernetics)" + access = access_robotics + + /datum/supply_pack/robotics/mecha_ripley name = "Circuit Crate (\"Ripley\" APLU)" contains = list( diff --git a/code/datums/supplypacks/security.dm b/code/datums/supplypacks/security.dm index 3de53e0548..5552e2aa23 100644 --- a/code/datums/supplypacks/security.dm +++ b/code/datums/supplypacks/security.dm @@ -32,8 +32,104 @@ containertype = /obj/structure/closet/crate/secure/gear containername = "Armor crate" +/datum/supply_pack/security/carriersblack + name = "Armor - Black modular armor" + contains = list( + /obj/item/clothing/suit/armor/pcarrier, + /obj/item/clothing/accessory/armor/armguards, + /obj/item/clothing/accessory/armor/legguards, + /obj/item/clothing/accessory/storage/pouches, + ) + cost = 30 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Plate Carrier crate" + +/datum/supply_pack/security/carriersblue + name = "Armor - Blue modular armor" + contains = list( + /obj/item/clothing/suit/armor/pcarrier/blue, + /obj/item/clothing/accessory/armor/armguards/blue, + /obj/item/clothing/accessory/armor/legguards/blue, + /obj/item/clothing/accessory/storage/pouches/blue, + ) + cost = 30 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Plate Carrier crate" + +/datum/supply_pack/security/carriersgreen + name = "Armor - Blue modular armor" + contains = list( + /obj/item/clothing/suit/armor/pcarrier/green, + /obj/item/clothing/accessory/armor/armguards/green, + /obj/item/clothing/accessory/armor/legguards/green, + /obj/item/clothing/accessory/storage/pouches/green, + ) + cost = 30 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Plate Carrier crate" + +/datum/supply_pack/security/carriersnavy + name = "Armor - Navy modular armor" + contains = list( + /obj/item/clothing/suit/armor/pcarrier/navy, + /obj/item/clothing/accessory/armor/armguards/navy, + /obj/item/clothing/accessory/armor/legguards/navy, + /obj/item/clothing/accessory/storage/pouches/navy, + ) + cost = 30 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Plate Carrier crate" + +/datum/supply_pack/security/carrierstan + name = "Armor - Tan modular armor" + contains = list( + /obj/item/clothing/suit/armor/pcarrier/tan, + /obj/item/clothing/accessory/armor/armguards/tan, + /obj/item/clothing/accessory/armor/legguards/tan, + /obj/item/clothing/accessory/storage/pouches/tan, + ) + cost = 30 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Plate Carrier crate" + +/datum/supply_pack/security/armorplate + name = "Armor - Security light armor plate" + contains = list( + /obj/item/clothing/accessory/armor/armorplate, + ) + cost = 5 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Armor plate crate" + +/datum/supply_pack/security/armorplatestab + name = "Armor - Security stab armor plate" + contains = list( + /obj/item/clothing/accessory/armor/armorplate/stab, + ) + cost = 10 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Armor plate crate" + +/datum/supply_pack/security/armorplatemedium + name = "Armor - Security armor plate" + contains = list( + /obj/item/clothing/accessory/armor/armorplate/medium, + ) + cost = 10 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Armor plate crate" + +/datum/supply_pack/security/armorplatetac + name = "Armor - Security medium armor plate" + contains = list( + /obj/item/clothing/accessory/armor/armorplate/tactical, + ) + cost = 15 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Armor plate crate" + /datum/supply_pack/randomised/security/carriers - name = "Armor - Plate carriers" + name = "Armor - Surplus plate carriers" num_contained = 5 contains = list( /obj/item/clothing/suit/armor/pcarrier, @@ -43,7 +139,7 @@ /obj/item/clothing/suit/armor/pcarrier/tan, /obj/item/clothing/suit/armor/pcarrier/press ) - cost = 20 + cost = 10 containertype = /obj/structure/closet/crate/secure/gear containername = "Plate Carrier crate" @@ -82,7 +178,7 @@ containername = "Helmet Covers crate" /datum/supply_pack/randomised/security/armorplates - name = "Armor - Security armor plates" + name = "Armor - Surplus security armor plates" num_contained = 5 contains = list( /obj/item/clothing/accessory/armor/armorplate, @@ -96,12 +192,12 @@ /obj/item/clothing/accessory/armor/armorplate/riot, /obj/item/clothing/accessory/armor/armorplate/bulletproof ) - cost = 50 + cost = 40 containertype = /obj/structure/closet/crate/secure/gear containername = "Armor plate crate" /datum/supply_pack/randomised/security/carrierarms - name = "Armor - Security armguard attachments" + name = "Armor - Surplus security armguard attachments" num_contained = 5 contains = list( /obj/item/clothing/accessory/armor/armguards, @@ -113,12 +209,12 @@ /obj/item/clothing/accessory/armor/armguards/riot, /obj/item/clothing/accessory/armor/armguards/bulletproof ) - cost = 50 + cost = 40 containertype = /obj/structure/closet/crate/secure/gear containername = "Armor plate crate" /datum/supply_pack/randomised/security/carrierlegs - name = "Armor - Security legguard attachments" + name = "Armor - Surplus security legguard attachments" num_contained = 5 contains = list( /obj/item/clothing/accessory/armor/legguards, @@ -130,12 +226,12 @@ /obj/item/clothing/accessory/armor/legguards/riot, /obj/item/clothing/accessory/armor/legguards/bulletproof ) - cost = 50 + cost = 40 containertype = /obj/structure/closet/crate/secure/gear containername = "Armor plate crate" /datum/supply_pack/randomised/security/carrierbags - name = "Armor - Security pouch attachments" + name = "Armor - Surplus security pouch attachments" num_contained = 5 contains = list( /obj/item/clothing/accessory/storage/pouches, @@ -149,7 +245,7 @@ /obj/item/clothing/accessory/storage/pouches/large/green, /obj/item/clothing/accessory/storage/pouches/large/tan ) - cost = 60 + cost = 50 containertype = /obj/structure/closet/crate/secure/gear containername = "Armor plate crate" diff --git a/code/datums/supplypacks/security_yw.dm b/code/datums/supplypacks/security_yw.dm new file mode 100644 index 0000000000..05074a0fe1 --- /dev/null +++ b/code/datums/supplypacks/security_yw.dm @@ -0,0 +1,9 @@ +/datum/supply_pack/security/loyaltyimplants + name = "Loyalty Implant Lockbox" + contains = list( + /obj/item/weapon/storage/lockbox/loyalty + ) + cost = 100 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Loyalty Implants Lockbox crate" + access = access_heads \ No newline at end of file diff --git a/code/datums/supplypacks/supply.dm b/code/datums/supplypacks/supply.dm index c0db598cc4..b58c596045 100644 --- a/code/datums/supplypacks/supply.dm +++ b/code/datums/supplypacks/supply.dm @@ -14,7 +14,8 @@ /obj/item/weapon/reagent_containers/food/drinks/soymilk = 2, /obj/item/weapon/storage/fancy/egg_box = 2, /obj/item/weapon/reagent_containers/food/snacks/tofu = 4, - /obj/item/weapon/reagent_containers/food/snacks/meat = 4 + /obj/item/weapon/reagent_containers/food/snacks/meat = 4, + /obj/item/weapon/reagent_containers/food/condiment/yeast = 3 ) cost = 10 containertype = /obj/structure/closet/crate/freezer diff --git a/code/datums/supplypacks/voidsuits.dm b/code/datums/supplypacks/voidsuits.dm index 8cb3b1738a..9fe547c63a 100644 --- a/code/datums/supplypacks/voidsuits.dm +++ b/code/datums/supplypacks/voidsuits.dm @@ -169,13 +169,13 @@ /obj/item/clothing/shoes/magboots = 2, /obj/item/weapon/tank/oxygen = 2 ) - cost = 40 + cost = 60 containertype = /obj/structure/closet/crate/secure containername = "Security Crowd Control voidsuit crate" access = access_armory /datum/supply_pack/voidsuits/security/alt - name = "Security EVA Riot voidsuits" + name = "Security EVA voidsuits" contains = list( /obj/item/clothing/suit/space/void/security/alt = 2, /obj/item/clothing/head/helmet/space/void/security/alt = 2, @@ -183,9 +183,9 @@ /obj/item/clothing/shoes/magboots = 2, /obj/item/weapon/tank/oxygen = 2 ) - cost = 50 + cost = 60 containertype = /obj/structure/closet/crate/secure - containername = "Security EVA Riot voidsuit crate" + containername = "Security EVA voidsuit crate" access = access_armory /datum/supply_pack/voidsuits/supply diff --git a/code/datums/uplink/announcements.dm b/code/datums/uplink/announcements.dm index 86a9567a8d..1c4a448cee 100644 --- a/code/datums/uplink/announcements.dm +++ b/code/datums/uplink/announcements.dm @@ -13,7 +13,7 @@ /datum/uplink_item/abstract/announcements/fake_centcom name = "Command Update Announcement" desc = "Causes a falsified Command Update. Triggers immediately after supplying additional data." - item_cost = 40 + item_cost = 20 /datum/uplink_item/abstract/announcements/fake_centcom/extra_args(var/mob/user) var/title = sanitize(input("Enter your announcement title.", "Announcement Title") as null|text) @@ -41,7 +41,7 @@ /datum/uplink_item/abstract/announcements/fake_crew_arrival name = "Crew Arrival Announcement/Records" desc = "Creates a fake crew arrival announcement as well as fake crew records, using your current appearance (including held items!) and worn id card. Trigger with care!" - item_cost = 30 + item_cost = 15 /datum/uplink_item/abstract/announcements/fake_crew_arrival/get_goods(var/obj/item/device/uplink/U, var/loc, var/mob/user, var/list/args) if(!user) diff --git a/code/datums/uplink/armor.dm b/code/datums/uplink/armor.dm index 20367f3989..0d7d814310 100644 --- a/code/datums/uplink/armor.dm +++ b/code/datums/uplink/armor.dm @@ -13,3 +13,18 @@ name = "Heavy Armor Vest" item_cost = 40 path = /obj/item/clothing/suit/storage/vest/heavy/merc + +/datum/uplink_item/item/armor/gorlexsuit + name = "Mercenary Voidsuit" + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/voidsuit + +/datum/uplink_item/item/armor/gorlexsuit_fire + name = "Mercenary Voidsuit (Fire)" + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/voidsuit/fire + +/datum/uplink_item/item/armor/combat + name = "Combat Platecarrier Set" + item_cost = 60 + path = /obj/item/clothing/suit/armor/pcarrier/merc diff --git a/code/datums/uplink/badassery.dm b/code/datums/uplink/badassery.dm index 463c55dcc1..65f71f2584 100644 --- a/code/datums/uplink/badassery.dm +++ b/code/datums/uplink/badassery.dm @@ -91,4 +91,4 @@ var/obj/structure/largecrate/C = /obj/structure/largecrate icon = image(initial(C.icon), initial(C.icon_state)) - return "\icon[icon]" \ No newline at end of file + return "[bicon(icon)]" \ No newline at end of file diff --git a/code/datums/uplink/implants.dm b/code/datums/uplink/implants.dm index e36396c9cd..bbbeaf9e7e 100644 --- a/code/datums/uplink/implants.dm +++ b/code/datums/uplink/implants.dm @@ -25,51 +25,51 @@ path = /obj/item/weapon/storage/box/syndie_kit/imp_uplink /datum/uplink_item/item/implants/imp_shades - name = "Integrated Thermal-Shades Implant (Organic)" + name = "Integrated Thermal-Shades Implant" item_cost = 80 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug /datum/uplink_item/item/implants/imp_taser - name = "Integrated Taser Implant (Organic)" + name = "Integrated Taser Implant" item_cost = 30 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/taser /datum/uplink_item/item/implants/imp_laser - name = "Integrated Laser Implant (Organic)" + name = "Integrated Laser Implant" item_cost = 50 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/laser /datum/uplink_item/item/implants/imp_dart - name = "Integrated Dart Implant (Organic)" + name = "Integrated Dart Implant" item_cost = 60 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/dart /datum/uplink_item/item/implants/imp_toolkit - name = "Integrated Toolkit Implant (Organic)" + name = "Integrated Toolkit Implant" item_cost = 80 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/toolkit /datum/uplink_item/item/implants/imp_medkit - name = "Integrated Medkit Implant (Organic)" + name = "Integrated Medkit Implant" item_cost = 60 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/medkit /datum/uplink_item/item/implants/imp_analyzer - name = "Integrated Research Scanner Implant (Organic)" + name = "Integrated Research Scanner Implant" item_cost = 20 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/analyzer /datum/uplink_item/item/implants/imp_sword - name = "Integrated Sword Implant (Organic)" + name = "Integrated Sword Implant" item_cost = 40 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/sword /datum/uplink_item/item/implants/imp_sprinter - name = "Integrated Sprinter Implant (Organic)" + name = "Integrated Sprinter Implant" item_cost = 40 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/sprinter /datum/uplink_item/item/implants/imp_sprinter - name = "Integrated Surge Implant (Organic)" + name = "Integrated Surge Implant" item_cost = 40 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/surge diff --git a/code/datums/uplink/medical.dm b/code/datums/uplink/medical.dm index c28fed46b9..0657b62b2e 100644 --- a/code/datums/uplink/medical.dm +++ b/code/datums/uplink/medical.dm @@ -24,11 +24,23 @@ item_cost = 10 path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting +/datum/uplink_item/item/medical/clotting_case + name = "Clotting Medicine case" + item_cost = 20 + desc = "A case of three myelamine injectors. Can rapidly remove and stow up to six injectors." + path = /obj/item/weapon/storage/quickdraw/syringe_case/clotting + /datum/uplink_item/item/medical/bonemeds name = "Bone Repair injector" item_cost = 10 path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed +/datum/uplink_item/item/medical/bonemeds_case + name = "Bone Repair case" + item_cost = 20 + desc = "A case of three osteodaxon injectors. Can rapidly remove and stow up to six injectors." + path = /obj/item/weapon/storage/quickdraw/syringe_case/bonemed + /datum/uplink_item/item/medical/ambrosiadeusseeds name = "Box of 7x ambrosia deus seed packets" item_cost = 10 diff --git a/code/datums/uplink/resources.dm b/code/datums/uplink/resources.dm new file mode 100644 index 0000000000..a544cab728 --- /dev/null +++ b/code/datums/uplink/resources.dm @@ -0,0 +1,48 @@ + +/datum/uplink_item/crated/resources + name = "Resource Crate" + desc = "A crate routed from an in-system trading post, containing various valuable materials." + item_cost = 60 + category = /datum/uplink_category/services + + paths = list(\ + /obj/fiftyspawner/uranium,\ + /obj/fiftyspawner/phoron,\ + /obj/fiftyspawner/gold,\ + /obj/fiftyspawner/silver,\ + /obj/fiftyspawner/osmium,\ + /obj/fiftyspawner/plasteel\ + ) + +/datum/uplink_item/crated/seeds + name = "Exotic Plantlife Crate" + desc = "A crate routed from an in-system trading post, containing various exotic plants." + item_cost = 20 + category = /datum/uplink_category/services + + paths = list(\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random,\ + /obj/item/seeds/random\ + ) + +/datum/uplink_item/crated/spare_organs + name = "Spare Organ Crate" + desc = "A crate stolen from a medical relief ship, containing various bioprinted organs." + item_cost = 20 + category = /datum/uplink_category/services + crate_path = /obj/structure/closet/crate/freezer + + paths = list(\ + /obj/item/organ/internal/eyes/replicant,\ + /obj/item/organ/internal/heart/replicant,\ + /obj/item/organ/internal/kidneys/replicant,\ + /obj/item/organ/internal/liver/replicant,\ + /obj/item/organ/internal/lungs/replicant,\ + /obj/item/organ/internal/voicebox/replicant\ + ) diff --git a/code/datums/uplink/stealth_items.dm b/code/datums/uplink/stealth_items.dm index 0a17e659a6..973e851b02 100644 --- a/code/datums/uplink/stealth_items.dm +++ b/code/datums/uplink/stealth_items.dm @@ -37,4 +37,16 @@ /datum/uplink_item/item/stealth_items/makeover name = "Makeover Kit" item_cost = 5 - path = /obj/item/weapon/makeover \ No newline at end of file + path = /obj/item/weapon/makeover + +/datum/uplink_item/item/stealth_items/thievesgloves + name = "Thieve's Gloves" + desc = "A pair of sterile gloves that allow the wearer to inspect the backpacks of other players, and swap pocket items." + item_cost = 30 + path = /obj/item/clothing/gloves/sterile/thieves + +/datum/uplink_item/item/stealth_items/deadringer + name = "Stealth Watch" + desc = "A strange watch which can be used to create a fake corpse if you are injured when it is active, as it projects a cloaking field around your person." + item_cost = 50 + path = /obj/item/weapon/deadringer diff --git a/code/datums/uplink/stealthy_weapons.dm b/code/datums/uplink/stealthy_weapons.dm index 5576ebcac5..2f4cab5ff7 100644 --- a/code/datums/uplink/stealthy_weapons.dm +++ b/code/datums/uplink/stealthy_weapons.dm @@ -33,3 +33,51 @@ name = "Random Toxin - Beaker" item_cost = 10 path = /obj/item/weapon/storage/box/syndie_kit/toxin + +/datum/uplink_item/item/stealthy_weapons/penblade + name = "Energy Penblade, Black" + desc = "A concealed energy dagger with the functional casing of a pen. Makes an impressive throwing weapon." + item_cost = 20 + path = /obj/item/weapon/pen/blade + +/datum/uplink_item/item/stealthy_weapons/penblade_red + name = "Energy Penblade, Red" + desc = "A concealed energy dagger with the functional casing of a pen. Makes an impressive throwing weapon." + item_cost = 20 + path = /obj/item/weapon/pen/blade/red + +/datum/uplink_item/item/stealthy_weapons/penblade_blue + name = "Energy Penblade, Blue" + desc = "A concealed energy dagger with the functional casing of a pen. Makes an impressive throwing weapon." + item_cost = 20 + path = /obj/item/weapon/pen/blade/blue + +/datum/uplink_item/item/stealthy_weapons/penblade_fancy + name = "Energy Penblade, Fountain" + desc = "A concealed energy dagger with the functional casing of a pen. Makes an impressive throwing weapon." + item_cost = 20 + path = /obj/item/weapon/pen/blade/fountain + +/datum/uplink_item/item/stealthy_weapons/angrybuzzer + name = "Morphium Shock Ring" + desc = "An enigmatic ring used to create powerful electric shocks when punching. Can be used as a brute-force method of defibrillation." + item_cost = 40 + path = /obj/item/clothing/gloves/ring/buzzer + +/datum/uplink_item/item/stealthy_weapons/huntingtrap + name = "Camonetted Beartraps" + desc = "A box of unique beartraps which will partially cloak when deployed, allowing for more effective hunting." + item_cost = 30 + path = /obj/item/weapon/storage/box/syndie_kit/deadliest_game + +/datum/uplink_item/item/stealthy_weapons/virus + name = "Virus Cultures" + desc = "A box of three unique virus cultures. As dangerous to you as anyone else if handled improperly." + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/viral + +/datum/uplink_item/item/stealthy_weapons/syringe_case + name = "Quickdraw Syringe Case" + desc = "A small box capable of holding six syringes for rapid deployment. Fits in your pocket." + item_cost = 20 + path = /obj/item/weapon/storage/quickdraw/syringe_case diff --git a/code/datums/uplink/tools.dm b/code/datums/uplink/tools.dm index 6c340385b0..51d7ec7cd2 100644 --- a/code/datums/uplink/tools.dm +++ b/code/datums/uplink/tools.dm @@ -53,6 +53,12 @@ name = "Black Ammunition Duffle Bag" path = /obj/item/weapon/storage/backpack/dufflebag/syndie/ammo +/datum/uplink_item/item/tools/shield_diffuser + name = "Handheld Shield Diffuser" + desc = "A small device used to disrupt energy barriers, and allow passage through them." + item_cost = 16 + path = /obj/item/weapon/shield_diffuser + /datum/uplink_item/item/tools/space_suit name = "Space Suit" item_cost = 15 @@ -91,6 +97,12 @@ item_cost = 30 path = /obj/item/weapon/card/emag +/datum/uplink_item/item/tools/graviton + name = "Graviton Goggles" + desc = "An obvious, if useful pair of advanced imaging goggles that allow you to see objects and turfs through walls." + item_cost = 15 + path = /obj/item/clothing/glasses/graviton + /datum/uplink_item/item/tools/thermal name = "Thermal Imaging Glasses" item_cost = 30 @@ -111,6 +123,11 @@ item_cost = 60 path = /obj/item/weapon/storage/box/syndie_kit/demolitions_heavy +/datum/uplink_item/item/tools/integratedcircuitprinter + name = "Integrated Circuit Printer (Upgraded)" + item_cost = 10 + path = /obj/item/device/integrated_circuit_printer/upgraded + /* /datum/uplink_item/item/tools/packagebomb/huge name = "Package Bomb (Huge) diff --git a/code/datums/uplink/uplink_categories.dm b/code/datums/uplink/uplink_categories.dm index 2776085594..d9e6d84db7 100644 --- a/code/datums/uplink/uplink_categories.dm +++ b/code/datums/uplink/uplink_categories.dm @@ -52,4 +52,4 @@ datum/uplink_category/ammunition name = "Telecrystals" /datum/uplink_category/backup - name = "Backup" \ No newline at end of file + name = "Backup" diff --git a/code/datums/uplink/uplink_items.dm b/code/datums/uplink/uplink_items.dm index 94b1281dd3..cca1933c9c 100644 --- a/code/datums/uplink/uplink_items.dm +++ b/code/datums/uplink/uplink_items.dm @@ -146,7 +146,7 @@ datum/uplink_item/dd_SortValue() /datum/uplink_item/item/log_icon() var/obj/I = path - return "\icon[I]" + return "[bicon(I)]" /******************************** * * @@ -160,7 +160,37 @@ datum/uplink_item/dd_SortValue() if(!default_abstract_uplink_icon) default_abstract_uplink_icon = image('icons/obj/pda.dmi', "pda-syn") - return "\icon[default_abstract_uplink_icon]" + return "[bicon(default_abstract_uplink_icon)]" + +/* + * Crated goods. + */ + +/datum/uplink_item/crated + var/crate_path = /obj/structure/largecrate + var/list/paths = list() // List of paths to be spawned into the crate. + +/datum/uplink_item/crated/get_goods(var/obj/item/device/uplink/U, var/loc) + var/obj/L = new crate_path(get_turf(loc)) + + L.adjust_scale(rand(9, 12) / 10, rand(9, 12) / 10) // Some variation in the crate / locker size. + + for(var/path in paths) + var/obj/O = new path(L) + O.forceMove(L) + + return L + +/datum/uplink_item/crated/description() + if(!desc) + // Fallback description + var/obj/temp = crate_path + desc = initial(temp.desc) + return ..() + +/datum/uplink_item/crated/log_icon() + var/obj/I = crate_path + return "\icon[I]" /**************** * Support procs * diff --git a/code/datums/uplink/visible_weapons.dm b/code/datums/uplink/visible_weapons.dm index e4c27752f0..0ed9b26226 100644 --- a/code/datums/uplink/visible_weapons.dm +++ b/code/datums/uplink/visible_weapons.dm @@ -61,7 +61,7 @@ /datum/uplink_item/item/visible_weapons/riggedlaser name = "Exosuit Rigged Laser" - item_cost = 60 + item_cost = 30 path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/riggedlaser /datum/uplink_item/item/visible_weapons/revolver @@ -146,7 +146,7 @@ /datum/uplink_item/item/visible_weapons/egun name = "Energy Gun" - item_cost = 60 + item_cost = 30 path = /obj/item/weapon/gun/energy/gun /datum/uplink_item/item/visible_weapons/lasercannon @@ -171,5 +171,17 @@ /datum/uplink_item/item/visible_weapons/xray name = "Xray Gun" - item_cost = 85 + item_cost = 60 path = /obj/item/weapon/gun/energy/xray + +/datum/uplink_item/item/visible_weapons/flamethrower + name = "Heavy Flamethrower" + desc = "A large flamethrower that runs on pressurized, gaseous phoron and electric charge." + item_cost = 60 + path = /obj/item/weapon/storage/secure/briefcase/flamer + +/datum/uplink_item/item/visible_weapons/concussion_grenades + name = "Concussion Grenades (8)" + desc = "A box of eight concussion grenades." + item_cost = 30 + path = /obj/item/weapon/storage/box/syndie_kit/concussion_grenade diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 85546a79d7..f018a8e594 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -3,6 +3,7 @@ /datum/wires/airlock/secure random = 1 wire_count = 14 + window_y = 680 /datum/wires/airlock holder_type = /obj/machinery/door/airlock @@ -37,15 +38,14 @@ var/const/AIRLOCK_WIRE_LIGHT = 2048 var/haspower = A.arePowerSystemsOn() //If there's no power, then no lights will be on. . += ..() - . += text("
\n[]
\n[]
\n[]
\n[]
\n[]
\n[]
\n[]
\n[]", - (A.locked ? "The door bolts have fallen!" : "The door bolts look up."), - ((A.lights && haspower) ? "The door bolt lights are on." : "The door bolt lights are off!"), - ((haspower) ? "The test light is on." : "The test light is off!"), - ((A.backup_power_lost_until) ? "The backup power light is off!" : "The backup power light is on."), - ((A.aiControlDisabled==0 && !A.emagged && haspower)? "The 'AI control allowed' light is on." : "The 'AI control allowed' light is off."), - ((A.safe==0 && haspower)? "The 'Check Wiring' light is on." : "The 'Check Wiring' light is off."), - ((A.normalspeed==0 && haspower)? "The 'Check Timing Mechanism' light is on." : "The 'Check Timing Mechanism' light is off."), - ((A.aiDisabledIdScanner==0 && haspower)? "The IDScan light is on." : "The IDScan light is off.")) + . += show_hint(0x01, A.locked, "The door bolts have fallen!", "The door bolts look up.") + . += show_hint(0x02, A.lights && haspower, "The door bolt lights are on.", "The door bolt lights are off!") + . += show_hint(0x04, haspower, "The test light is on.", "The test light is off!") + . += show_hint(0x08, A.backup_power_lost_until, "The backup power light is off!", "The backup power light is on.") + . += show_hint(0x10, A.aiControlDisabled == 0 && !A.emagged && haspower, "The 'AI control allowed' light is on.", "The 'AI control allowed' light is off.") + . += show_hint(0x20, A.safe == 0 && haspower, "The 'Check Wiring' light is on.", "The 'Check Wiring' light is off.") + . += show_hint(0x40, A.normalspeed == 0 && haspower, "The 'Check Timing Mechanism' light is on.", "The 'Check Timing Mechanism' light is off.") + . += show_hint(0x80, A.aiDisabledIdScanner == 0 && haspower, "The IDScan light is on.", "The IDScan light is off.") /datum/wires/airlock/UpdateCut(var/index, var/mended) diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm index a665fd75a5..7c56bd4e52 100644 --- a/code/datums/wires/alarm.dm +++ b/code/datums/wires/alarm.dm @@ -8,7 +8,6 @@ var/const/AALARM_WIRE_SYPHON = 4 var/const/AALARM_WIRE_AI_CONTROL = 8 var/const/AALARM_WIRE_AALARM = 16 - /datum/wires/alarm/CanUse(var/mob/living/L) var/obj/machinery/alarm/A = holder if(A.panel_open) @@ -18,7 +17,9 @@ var/const/AALARM_WIRE_AALARM = 16 /datum/wires/alarm/GetInteractWindow() var/obj/machinery/alarm/A = holder . += ..() - . += text("
\n[(A.locked ? "The Air Alarm is locked." : "The Air Alarm is unlocked.")]
\n[((A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "The Air Alarm is offline." : "The Air Alarm is working properly!")]
\n[(A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on.")]") + . += show_hint(0x1, A.locked, "The Air Alarm is locked.", "The Air Alarm is unlocked.") + . += show_hint(0x2, A.shorted || (A.stat & (NOPOWER|BROKEN)), "The Air Alarm is offline.", "The Air Alarm is working properly!") + . += show_hint(0x4, A.aidisabled, "The 'AI control allowed' light is off.", "The 'AI control allowed' light is on.") /datum/wires/alarm/UpdateCut(var/index, var/mended) var/obj/machinery/alarm/A = holder @@ -26,24 +27,24 @@ var/const/AALARM_WIRE_AALARM = 16 if(AALARM_WIRE_IDSCAN) if(!mended) A.locked = 1 - //world << "Idscan wire cut" + //to_world("Idscan wire cut") if(AALARM_WIRE_POWER) A.shock(usr, 50) A.shorted = !mended A.update_icon() - //world << "Power wire cut" + //to_world("Power wire cut") if (AALARM_WIRE_AI_CONTROL) if (A.aidisabled == !mended) A.aidisabled = mended - //world << "AI Control Wire Cut" + //to_world("AI Control Wire Cut") if(AALARM_WIRE_SYPHON) if(!mended) A.mode = 3 // AALARM_MODE_PANIC A.apply_mode() - //world << "Syphon Wire Cut" + //to_world("Syphon Wire Cut") if(AALARM_WIRE_AALARM) if (A.alarm_area.atmosalert(2, A)) @@ -55,10 +56,10 @@ var/const/AALARM_WIRE_AALARM = 16 switch(index) if(AALARM_WIRE_IDSCAN) A.locked = !A.locked - // world << "Idscan wire pulsed" + // to_world("Idscan wire pulsed") if (AALARM_WIRE_POWER) - // world << "Power wire pulsed" + // to_world("Power wire pulsed") if(A.shorted == 0) A.shorted = 1 A.update_icon() @@ -70,7 +71,7 @@ var/const/AALARM_WIRE_AALARM = 16 if (AALARM_WIRE_AI_CONTROL) - // world << "AI Control wire pulsed" + // to_world("AI Control wire pulsed") if (A.aidisabled == 0) A.aidisabled = 1 A.updateDialog() @@ -79,7 +80,7 @@ var/const/AALARM_WIRE_AALARM = 16 A.aidisabled = 0 if(AALARM_WIRE_SYPHON) - // world << "Syphon wire pulsed" + // to_world("Syphon wire pulsed") if(A.mode == 1) // AALARM_MODE_SCRUB A.mode = 3 // AALARM_MODE_PANIC else @@ -87,7 +88,7 @@ var/const/AALARM_WIRE_AALARM = 16 A.apply_mode() if(AALARM_WIRE_AALARM) - // world << "Aalarm wire pulsed" + // to_world("Aalarm wire pulsed") if (A.alarm_area.atmosalert(0, A)) A.post_alert(0) A.update_icon() diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm index 5bb5831d3e..1b7f43d21f 100644 --- a/code/datums/wires/apc.dm +++ b/code/datums/wires/apc.dm @@ -10,7 +10,9 @@ /datum/wires/apc/GetInteractWindow() var/obj/machinery/power/apc/A = holder . += ..() - . += text("
\n[(A.locked ? "The APC is locked." : "The APC is unlocked.")]
\n[(A.shorted ? "The APCs power has been shorted." : "The APC is working properly!")]
\n[(A.aidisabled ? "The 'AI control allowed' light is off." : "The 'AI control allowed' light is on.")]") + . += show_hint(0x1, A.locked, "The APC is locked.", "The APC is unlocked.") + . += show_hint(0x2, A.shorted, "The APCs power has been shorted.", "The APC is working properly!") + . += show_hint(0x4, A.aidisabled, "The 'AI control allowed' light is off.", "The 'AI control allowed' light is on.") /datum/wires/apc/CanUse(var/mob/living/L) diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm index 1dd4132e48..df625351b8 100644 --- a/code/datums/wires/autolathe.dm +++ b/code/datums/wires/autolathe.dm @@ -10,9 +10,9 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4 /datum/wires/autolathe/GetInteractWindow() var/obj/machinery/autolathe/A = holder . += ..() - . += "
The red light is [A.disabled ? "off" : "on"]." - . += "
The green light is [A.shocked ? "off" : "on"]." - . += "
The blue light is [A.hacked ? "off" : "on"].
" + . += show_hint(0x1, A.disabled, "The red light is off.", "The red light is on.") + . += show_hint(0x2, A.shocked, "The green light is off.", "The green light is on.") + . += show_hint(0x4, A.hacked, "The blue light is off.", "The blue light is on.") /datum/wires/autolathe/CanUse() var/obj/machinery/autolathe/A = holder @@ -20,10 +20,10 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4 return 1 return 0 -/datum/wires/autolathe/Interact(var/mob/living/user) +/datum/wires/autolathe/proc/update_autolathe_ui(mob/living/user) if(CanUse(user)) - var/obj/machinery/autolathe/V = holder - V.attack_hand(user) + var/obj/machinery/autolathe/A = holder + A.interact(user) /datum/wires/autolathe/UpdateCut(index, mended) var/obj/machinery/autolathe/A = holder @@ -34,6 +34,7 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4 A.shocked = !mended if(AUTOLATHE_DISABLE_WIRE) A.disabled = !mended + update_autolathe_ui(usr) /datum/wires/autolathe/UpdatePulsed(index) if(IsIndexCut(index)) @@ -45,16 +46,16 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4 spawn(50) if(A && !IsIndexCut(index)) A.hacked = 0 - Interact(usr) + update_autolathe_ui(usr) if(AUTOLATHE_SHOCK_WIRE) A.shocked = !A.shocked spawn(50) if(A && !IsIndexCut(index)) A.shocked = 0 - Interact(usr) if(AUTOLATHE_DISABLE_WIRE) A.disabled = !A.disabled spawn(50) if(A && !IsIndexCut(index)) A.disabled = 0 - Interact(usr) + update_autolathe_ui(usr) + update_autolathe_ui(usr) diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm index 1724b9469c..67210e2179 100644 --- a/code/datums/wires/camera.dm +++ b/code/datums/wires/camera.dm @@ -6,13 +6,12 @@ wire_count = 6 /datum/wires/camera/GetInteractWindow() - . = ..() var/obj/machinery/camera/C = holder - . += "
\n[(C.view_range == initial(C.view_range) ? "The focus light is on." : "The focus light is off.")]" - . += "
\n[(C.can_use() ? "The power link light is on." : "The power link light is off.")]" - . += "
\n[(C.light_disabled ? "The camera light is off." : "The camera light is on.")]" - . += "
\n[(C.alarm_on ? "The alarm light is on." : "The alarm light is off.")]" + . += show_hint(0x1, C.view_range == initial(C.view_range), "The focus light is on.", "The focus light is off.") + . += show_hint(0x2, C.can_use(), "The power link light is on.", "The power link light is off.") + . += show_hint(0x4, C.light_disabled, "The camera light is off.", "The camera light is on.") + . += show_hint(0x8, C.alarm_on, "The alarm light is on.", "The alarm light is off.") return . /datum/wires/camera/CanUse(var/mob/living/L) @@ -61,7 +60,7 @@ var/const/CAMERA_WIRE_NOTHING2 = 32 C.light_disabled = !C.light_disabled if(CAMERA_WIRE_ALARM) - C.visible_message("\icon[C] *beep*", "\icon[C] *beep*") + C.visible_message("[bicon(C)] *beep*", "[bicon(C)] *beep*") return /datum/wires/camera/proc/CanDeconstruct() diff --git a/code/datums/wires/grid_checker.dm b/code/datums/wires/grid_checker.dm index 1e09be577e..355f39ec18 100644 --- a/code/datums/wires/grid_checker.dm +++ b/code/datums/wires/grid_checker.dm @@ -22,9 +22,9 @@ var/const/GRID_CHECKER_WIRE_NOTHING_2 = 128 // Does nothing, but makes it a bit /datum/wires/grid_checker/GetInteractWindow() var/obj/machinery/power/grid_checker/G = holder . += ..() - . += "The green light is [G.power_failing ? "off" : "on"].
" - . += "The red light is [G.wire_locked_out ? "on" : "off"].
" - . += "The blue light is [G.wire_allow_manual_1 && G.wire_allow_manual_2 && G.wire_allow_manual_3 ? "on" : "off"]." + . += show_hint(0x1, G.power_failing, "The green light is off.", "The green light is on.") + . += show_hint(0x2, G.wire_locked_out, "The red light is on.", "The red light is off.") + . += show_hint(0x4, G.wire_allow_manual_1 && G.wire_allow_manual_2 && G.wire_allow_manual_3, "The blue light is on.", "The blue light is off.") /datum/wires/grid_checker/UpdateCut(var/index, var/mended) diff --git a/code/datums/wires/jukebox.dm b/code/datums/wires/jukebox.dm index 900c778189..e207334ffd 100644 --- a/code/datums/wires/jukebox.dm +++ b/code/datums/wires/jukebox.dm @@ -25,25 +25,25 @@ var/const/WIRE_NEXT = 1024 /datum/wires/jukebox/GetInteractWindow() var/obj/machinery/media/jukebox/A = holder . += ..() - . += "
The power light is [(A.stat & (BROKEN|NOPOWER)) ? "off" : "on"].
" - . += "The parental guidance light is [A.hacked ? "off" : "on"].
" - . += "The data light is [IsIndexCut(WIRE_REVERSE) ? "hauntingly dark" : "glowing sloftly"].
" + . += show_hint(0x1, A.stat & (BROKEN|NOPOWER), "The power light is off.", "The power light is on.") + . += show_hint(0x2, A.hacked, "The parental guidance light is off.", "The parental guidance light is on.") + . += show_hint(0x4, IsIndexCut(WIRE_REVERSE), "The data light is hauntingly dark.", "The data light is glowing softly.") // Give a hint as to what each wire does /datum/wires/jukebox/UpdatePulsed(var/index) var/obj/machinery/media/jukebox/A = holder switch(index) if(WIRE_POWER) - holder.visible_message("\icon[holder] The power light flickers.") + holder.visible_message("[bicon(holder)] The power light flickers.") A.shock(usr, 90) if(WIRE_HACK) - holder.visible_message("\icon[holder] The parental guidance light flickers.") + holder.visible_message("[bicon(holder)] The parental guidance light flickers.") if(WIRE_REVERSE) - holder.visible_message("\icon[holder] The data light blinks ominously.") + holder.visible_message("[bicon(holder)] The data light blinks ominously.") if(WIRE_SPEEDUP) - holder.visible_message("\icon[holder] The speakers squeaks.") + holder.visible_message("[bicon(holder)] The speakers squeaks.") if(WIRE_SPEEDDOWN) - holder.visible_message("\icon[holder] The speakers rumble.") + holder.visible_message("[bicon(holder)] The speakers rumble.") if(WIRE_START) A.StartPlaying() if(WIRE_STOP) diff --git a/code/datums/wires/mines.dm b/code/datums/wires/mines.dm index 2d0aecaaf0..372810988c 100644 --- a/code/datums/wires/mines.dm +++ b/code/datums/wires/mines.dm @@ -23,15 +23,15 @@ switch(index) if(WIRE_DETONATE) - C.visible_message("\icon[C] *BEEE-*", "\icon[C] *BEEE-*") + C.visible_message("[bicon(C)] *BEEE-*", "[bicon(C)] *BEEE-*") C.explode() if(WIRE_TIMED_DET) - C.visible_message("\icon[C] *BEEE-*", "\icon[C] *BEEE-*") + C.visible_message("[bicon(C)] *BEEE-*", "[bicon(C)] *BEEE-*") C.explode() if(WIRE_DISARM) - C.visible_message("\icon[C] *click!*", "\icon[C] *click!*") + C.visible_message("[bicon(C)] *click!*", "[bicon(C)] *click!*") new C.mineitemtype(get_turf(C)) spawn(0) qdel(C) @@ -45,7 +45,7 @@ return if(WIRE_BADDISARM) - C.visible_message("\icon[C] *BEEPBEEPBEEP*", "\icon[C] *BEEPBEEPBEEP*") + C.visible_message("[bicon(C)] *BEEPBEEPBEEP*", "[bicon(C)] *BEEPBEEPBEEP*") spawn(20) C.explode() return @@ -56,24 +56,24 @@ return switch(index) if(WIRE_DETONATE) - C.visible_message("\icon[C] *beep*", "\icon[C] *beep*") + C.visible_message("[bicon(C)] *beep*", "[bicon(C)] *beep*") if(WIRE_TIMED_DET) - C.visible_message("\icon[C] *BEEPBEEPBEEP*", "\icon[C] *BEEPBEEPBEEP*") + C.visible_message("[bicon(C)] *BEEPBEEPBEEP*", "[bicon(C)] *BEEPBEEPBEEP*") spawn(20) C.explode() if(WIRE_DISARM) - C.visible_message("\icon[C] *ping*", "\icon[C] *ping*") + C.visible_message("[bicon(C)] *ping*", "[bicon(C)] *ping*") if(WIRE_DUMMY_1) - C.visible_message("\icon[C] *ping*", "\icon[C] *ping*") + C.visible_message("[bicon(C)] *ping*", "[bicon(C)] *ping*") if(WIRE_DUMMY_2) - C.visible_message("\icon[C] *beep*", "\icon[C] *beep*") + C.visible_message("[bicon(C)] *beep*", "[bicon(C)] *beep*") if(WIRE_BADDISARM) - C.visible_message("\icon[C] *ping*", "\icon[C] *ping*") + C.visible_message("[bicon(C)] *ping*", "[bicon(C)] *ping*") return /datum/wires/mines/CanUse(var/mob/living/L) diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm index 8356238692..3d90236b46 100644 --- a/code/datums/wires/particle_accelerator.dm +++ b/code/datums/wires/particle_accelerator.dm @@ -28,7 +28,7 @@ var/const/PARTICLE_LIMIT_POWER_WIRE = 8 // Determines how strong the PA can be. C.interface_control = !C.interface_control if(PARTICLE_LIMIT_POWER_WIRE) - C.visible_message("\icon[C][C] makes a large whirring noise.") + C.visible_message("[bicon(C)][C] makes a large whirring noise.") /datum/wires/particle_acc/control_box/UpdateCut(var/index, var/mended) var/obj/machinery/particle_accelerator/control_box/C = holder diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index 3f48c656ba..ed87a2b1fe 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -10,13 +10,12 @@ var/const/BORG_WIRE_AI_CONTROL = 8 var/const/BORG_WIRE_CAMERA = 16 /datum/wires/robot/GetInteractWindow() - . = ..() var/mob/living/silicon/robot/R = holder - . += text("
\n[(R.lawupdate ? "The LawSync light is on." : "The LawSync light is off.")]") - . += text("
\n[(R.connected_ai ? "The AI link light is on." : "The AI link light is off.")]") - . += text("
\n[((!isnull(R.camera) && R.camera.status == 1) ? "The Camera light is on." : "The Camera light is off.")]") - . += text("
\n[(R.lockdown ? "The lockdown light is on." : "The lockdown light is off.")]") + . += show_hint(0x1, R.lawupdate, "The LawSync light is on.", "The LawSync light is off.") + . += show_hint(0x2, R.connected_ai, "The AI link light is on.", "The AI link light is off.") + . += show_hint(0x4, (!isnull(R.camera) && R.camera.status == 1), "The camera light is on.", "The camera light is off.") + . += show_hint(0x8, R.lockdown, "The lockdown light is on.", "The lockdown light is off.") return . /datum/wires/robot/UpdateCut(var/index, var/mended) @@ -26,7 +25,7 @@ var/const/BORG_WIRE_CAMERA = 16 if(BORG_WIRE_LAWCHECK) //Cut the law wire, and the borg will no longer receive law updates from its AI if(!mended) if (R.lawupdate == 1) - R << "LawSync protocol engaged." + to_chat(R, "LawSync protocol engaged.") R.show_laws() else if (R.lawupdate == 0 && !R.emagged) @@ -59,7 +58,7 @@ var/const/BORG_WIRE_CAMERA = 16 if (BORG_WIRE_CAMERA) if(!isnull(R.camera) && R.camera.can_use() && !R.scrambledcodes) R.visible_message("[R]'s camera lense focuses loudly.") - R << "Your camera lense focuses loudly." + to_chat(R, "Your camera lense focuses loudly.") if(BORG_WIRE_LOCKED_DOWN) R.SetLockdown(!R.lockdown) // Toggle diff --git a/code/datums/wires/seedstorage.dm b/code/datums/wires/seedstorage.dm index 8268d936b0..2a0e315a57 100644 --- a/code/datums/wires/seedstorage.dm +++ b/code/datums/wires/seedstorage.dm @@ -17,10 +17,10 @@ /datum/wires/seedstorage/GetInteractWindow() var/obj/machinery/seed_storage/V = holder . += ..() - . += "
The orange light is [V.seconds_electrified ? "off" : "on"].
" - . += "The red light is [V.smart ? "off" : "blinking"].
" - . += "The green light is [(V.hacked || V.emagged) ? "on" : "off"].
" - . += "The keypad lock is [V.lockdown ? "deployed" : "retracted"].
" + . += show_hint(0x1, V.seconds_electrified, "The orange light is off.", "The orange light is on.") + . += show_hint(0x2, V.smart, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, V.hacked || V.emagged, "The green light is on.", "The green light is off.") + . += show_hint(0x8, V.lockdown, "The keypad lock is deployed.", "The keypad lock is retracted.") /datum/wires/seedstorage/UpdatePulsed(var/index) var/obj/machinery/seed_storage/V = holder diff --git a/code/datums/wires/shield_generator.dm b/code/datums/wires/shield_generator.dm new file mode 100644 index 0000000000..201109de35 --- /dev/null +++ b/code/datums/wires/shield_generator.dm @@ -0,0 +1,46 @@ +/datum/wires/shield_generator + holder_type = /obj/machinery/power/shield_generator + wire_count = 5 + +var/const/SHIELDGEN_WIRE_POWER = 1 // Cut to disable power input into the generator. Pulse does nothing. Mend to restore. +var/const/SHIELDGEN_WIRE_HACK = 2 // Pulse to hack the generator, enabling hacked modes. Cut to unhack. Mend does nothing. +var/const/SHIELDGEN_WIRE_CONTROL = 4 // Cut to lock most shield controls. Mend to unlock them. Pulse does nothing. +var/const/SHIELDGEN_WIRE_AICONTROL = 8 // Cut to disable AI control. Mend to restore. +var/const/SHIELDGEN_WIRE_NOTHING = 16 // A blank wire that doesn't have any specific function + +/datum/wires/shield_generator/CanUse(var/mob/living/L) + var/obj/machinery/power/shield_generator/S = holder + if(S.panel_open) + return 1 + return 0 + +/datum/wires/shield_generator/GetInteractWindow() + var/obj/machinery/power/shield_generator/S = holder + . += ..() + . += show_hint(0x1, S.mode_changes_locked, "The orange light is on.", "The orange light is off.") + . += show_hint(0x2, S.ai_control_disabled, "The blue light is off.", "The blue light is blinking.") + . += show_hint(0x4, S.hacked, "The violet light is pulsing.", "The violet light is steady.") + . += show_hint(0x8, S.input_cut, "The red light is off.", "The red light is on.") + +/datum/wires/shield_generator/UpdateCut(index, mended) + var/obj/machinery/power/shield_generator/S = holder + switch(index) + if(SHIELDGEN_WIRE_POWER) + S.input_cut = !mended + if(SHIELDGEN_WIRE_HACK) + if(!mended) + S.hacked = 0 + if(S.check_flag(MODEFLAG_BYPASS)) + S.toggle_flag(MODEFLAG_BYPASS) + if(S.check_flag(MODEFLAG_OVERCHARGE)) + S.toggle_flag(MODEFLAG_OVERCHARGE) + if(SHIELDGEN_WIRE_CONTROL) + S.mode_changes_locked = !mended + if(SHIELDGEN_WIRE_AICONTROL) + S.ai_control_disabled = !mended + +/datum/wires/shield_generator/UpdatePulsed(var/index) + var/obj/machinery/power/shield_generator/S = holder + switch(index) + if(SHIELDGEN_WIRE_HACK) + S.hacked = 1 \ No newline at end of file diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm index 15f10cd800..f69e153bbf 100644 --- a/code/datums/wires/smartfridge.dm +++ b/code/datums/wires/smartfridge.dm @@ -19,9 +19,9 @@ var/const/SMARTFRIDGE_WIRE_IDSCAN = 4 /datum/wires/smartfridge/GetInteractWindow() var/obj/machinery/smartfridge/S = holder . += ..() - . += "
The orange light is [S.seconds_electrified ? "off" : "on"].
" - . += "The red light is [S.shoot_inventory ? "off" : "blinking"].
" - . += "A [S.scan_id ? "purple" : "yellow"] light is on.
" + . += show_hint(0x1, S.seconds_electrified, "The orange light is off.", "The orange light is on.") + . += show_hint(0x2, S.shoot_inventory, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, S.scan_id, "A purple light is on.", "A yellow light is on.") /datum/wires/smartfridge/UpdatePulsed(var/index) var/obj/machinery/smartfridge/S = holder diff --git a/code/datums/wires/smes.dm b/code/datums/wires/smes.dm index 17b232dd54..82d93b9fa8 100644 --- a/code/datums/wires/smes.dm +++ b/code/datums/wires/smes.dm @@ -19,10 +19,9 @@ var/const/SMES_WIRE_FAILSAFES = 16 // Cut to disable failsafes, mend to reenable /datum/wires/smes/GetInteractWindow() var/obj/machinery/power/smes/buildable/S = holder . += ..() - . += "The green light is [(S.input_cut || S.input_pulsed || S.output_cut || S.output_pulsed) ? "off" : "on"]
" - . += "The red light is [(S.safeties_enabled || S.grounding) ? "off" : "blinking"]
" - . += "The blue light is [S.RCon ? "on" : "off"]" - + . += show_hint(0x1, S.input_cut || S.input_pulsed || S.output_cut || S.output_pulsed, "The green light is off.", "The green light is on.") + . += show_hint(0x2, S.safeties_enabled || S.grounding, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, S.RCon, "The blue light is on.", "The blue light is off.") /datum/wires/smes/UpdateCut(var/index, var/mended) var/obj/machinery/power/smes/buildable/S = holder diff --git a/code/datums/wires/suit_storage_unit.dm b/code/datums/wires/suit_storage_unit.dm index c2d1a130d7..fe694d271a 100644 --- a/code/datums/wires/suit_storage_unit.dm +++ b/code/datums/wires/suit_storage_unit.dm @@ -19,9 +19,9 @@ var/const/SUIT_STORAGE_WIRE_LOCKED = 4 /datum/wires/suit_storage_unit/GetInteractWindow() var/obj/machinery/suit_cycler/S = holder . += ..() - . += "
The orange light is [S.electrified ? "off" : "on"].
" - . += "The red light is [S.safeties ? "off" : "blinking"].
" - . += "The yellow light is [S.locked ? "on" : "off"].
" + . += show_hint(0x1, S.electrified, "The orange light is off.", "The orange light is on.") + . += show_hint(0x2, S.safeties, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, S.locked, "The yellow light is on.", "The yellow light is off.") /datum/wires/suit_storage_unit/UpdatePulsed(var/index) var/obj/machinery/suit_cycler/S = holder diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm index bad3a0078b..61aadf4b1b 100644 --- a/code/datums/wires/vending.dm +++ b/code/datums/wires/vending.dm @@ -16,10 +16,10 @@ var/const/VENDING_WIRE_IDSCAN = 8 /datum/wires/vending/GetInteractWindow() var/obj/machinery/vending/V = holder . += ..() - . += "
The orange light is [V.seconds_electrified ? "off" : "on"].
" - . += "The red light is [V.shoot_inventory ? "off" : "blinking"].
" - . += "The green light is [(V.categories & CAT_HIDDEN) ? "on" : "off"].
" - . += "The [V.scan_id ? "purple" : "yellow"] light is on.
" + . += show_hint(0x1, V.seconds_electrified, "The orange light is off.", "The orange light is on.") + . += show_hint(0x2, V.shoot_inventory, "The red light is off.", "The red light is blinking.") + . += show_hint(0x4, V.categories & CAT_HIDDEN, "A green light is on.", "A green light is off.") + . += show_hint(0x8, V.scan_id, "A purple light is on.", "A yellow light is on.") /datum/wires/vending/UpdatePulsed(var/index) var/obj/machinery/vending/V = holder diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 0a83f542db..645d7cc043 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -17,6 +17,9 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" var/wire_count = 0 // Max is 16 var/wires_status = 0 // BITFLAG OF WIRES + var/hint_states = 0 // BITFLAG OF HINT STATES (For tracking if they changed for bolding in UI) + var/hint_states_initialized = FALSE // False until first time window is rendered. + var/list/wires = list() var/list/signallers = list() @@ -26,6 +29,19 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" var/window_x = 370 var/window_y = 470 +// Note: Its assumed states are boolean. If you ever have a multi-state hint, you must implement that yourself. +/datum/wires/proc/show_hint(flag, current_state, true_text, false_text) + var/state_changed = FALSE + if(hint_states_initialized) + if(!(hint_states & flag) != !current_state) // NOT-ing to convert to boolean + state_changed = TRUE + if(current_state) + hint_states |= flag + return state_changed ? "
[true_text]" : "
[true_text]" + else + hint_states &= ~flag + return state_changed ? "
[false_text]" : "
[false_text]" + /datum/wires/New(var/atom/holder) ..() src.holder = holder @@ -75,6 +91,7 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" var/html = null if(holder && CanUse(user)) html = GetInteractWindow() + hint_states_initialized = TRUE if(html) user.set_machine(holder) else @@ -114,23 +131,32 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" var/mob/living/L = usr if(CanUse(L) && href_list["action"]) - var/obj/item/I = L.get_active_hand() holder.add_hiddenprint(L) + + var/list/items = L.get_all_held_items() + var/success = FALSE + if(href_list["cut"]) // Toggles the cut/mend status - if(I.is_wirecutter()) - var/colour = href_list["cut"] - CutWireColour(colour) - playsound(holder, I.usesound, 20, 1) - else - L << "You need wirecutters!" + for(var/obj/item/I in items) // Paranoid about someone somehow grabbing a non-/obj/item, lets play it safe. + if(I.is_wirecutter()) + var/colour = href_list["cut"] + CutWireColour(colour) + playsound(holder, I.usesound, 20, 1) + success = TRUE + break + if(!success) + to_chat(L, span("warning", "You need wirecutters!")) else if(href_list["pulse"]) - if(istype(I, /obj/item/device/multitool)) - var/colour = href_list["pulse"] - PulseColour(colour) - playsound(holder, 'sound/weapons/empty.ogg', 20, 1) - else - L << "You need a multitool!" + for(var/obj/item/I in items) + if(I.is_multitool()) + var/colour = href_list["pulse"] + PulseColour(colour) + playsound(holder, 'sound/weapons/empty.ogg', 20, 1) + success = TRUE + break + if(!success) + to_chat(L, span("warning", "You need a multitool!")) else if(href_list["attach"]) var/colour = href_list["attach"] @@ -142,11 +168,12 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown" // Attach else - if(istype(I, /obj/item/device/assembly/signaler)) - L.drop_item() - Attach(colour, I) + var/obj/item/device/assembly/signaler/S = L.is_holding_item_of_type(/obj/item/device/assembly/signaler) + if(istype(S)) + L.drop_from_inventory(S) + Attach(colour, S) else - L << "You need a remote signaller!" + to_chat(L, span("warning", "You need a remote signaller!")) diff --git a/code/defines/obj.dm b/code/defines/obj.dm index 7019242e38..8d764c13ea 100644 --- a/code/defines/obj.dm +++ b/code/defines/obj.dm @@ -52,120 +52,6 @@ return copytext(rank, 2+length(prefix)) return rank - -/* -We can't just insert in HTML into the nanoUI so we need the raw data to play with. -Instead of creating this list over and over when someone leaves their PDA open to the page -we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change -using /datum/datacore/proc/manifest_inject( ), or manifest_insert( ) -*/ - -var/global/list/PDA_Manifest = list() - -/datum/datacore/proc/get_manifest_list() - if(PDA_Manifest.len) - return - var/list/heads = list() - var/list/sec = list() - var/list/eng = list() - var/list/med = list() - var/list/sci = list() - var/list/car = list() - var/list/pla = list() // Planetside crew: Explorers, Pilots, S&S - var/list/civ = list() - var/list/bot = list() - var/list/misc = list() - for(var/datum/data/record/t in data_core.general) - var/name = sanitize(t.fields["name"]) - var/rank = sanitize(t.fields["rank"]) - var/real_rank = make_list_rank(t.fields["real_rank"]) - - var/isactive = t.fields["p_stat"] - var/department = 0 - var/depthead = 0 // Department Heads will be placed at the top of their lists. - if(real_rank in command_positions) - heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - depthead = 1 - if(rank=="Colony Director" && heads.len != 1) - heads.Swap(1,heads.len) - - if(real_rank in security_positions) - sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sec.len != 1) - sec.Swap(1,sec.len) - - if(real_rank in engineering_positions) - eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && eng.len != 1) - eng.Swap(1,eng.len) - - if(real_rank in medical_positions) - med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && med.len != 1) - med.Swap(1,med.len) - - if(real_rank in science_positions) - sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sci.len != 1) - sci.Swap(1,sci.len) - - if(real_rank in planet_positions) - pla[++pla.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(real_rank in cargo_positions) - car[++car.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && car.len != 1) - car.Swap(1,car.len) - - if(real_rank in civilian_positions) - civ[++civ.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && civ.len != 1) - civ.Swap(1,civ.len) - - if(real_rank in nonhuman_positions) - bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(!department && !(name in heads)) - misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) - - // Synthetics don't have actual records, so we will pull them from here. - // Synths don't have records, which is the means by which isactive is retrieved, so I'm hardcoding it to active, don't really have any better means - for(var/mob/living/silicon/ai/ai in mob_list) - bot[++bot.len] = list("name" = ai.real_name, "rank" = "Artificial Intelligence", "active" = "Active") - - for(var/mob/living/silicon/robot/robot in mob_list) - // No combat/syndicate cyborgs, no drones, and no AI shells. - if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest)) - continue - - bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active") - - - PDA_Manifest = list( - list("cat" = "Command", "elems" = heads), - list("cat" = "Security", "elems" = sec), - list("cat" = "Engineering", "elems" = eng), - list("cat" = "Medical", "elems" = med), - list("cat" = "Science", "elems" = sci), - list("cat" = "Cargo", "elems" = car), - list("cat" = "Exploration", "elems" = pla), // VOREStation Edit - list("cat" = "Civilian", "elems" = civ), - list("cat" = "Silicon", "elems" = bot), - list("cat" = "Miscellaneous", "elems" = misc) - ) - return - - - /obj/effect/laser name = "laser" desc = "IT BURNS!!!" @@ -252,6 +138,7 @@ var/global/list/PDA_Manifest = list() throwforce = 0.0 throw_speed = 1 throw_range = 20 + drop_sound = 'sound/items/drop/rubber.ogg' afterattack(atom/target as mob|obj|turf|area, mob/user as mob) user.drop_item() diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index bcd014d08d..82872c2f33 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -35,6 +35,7 @@ throwforce = 0 throw_speed = 4 throw_range = 20 + drop_sound = 'sound/misc/slip.ogg' /obj/item/weapon/soap/nanotrasen desc = "A NanoTrasen-brand bar of soap. Smells of phoron." @@ -65,7 +66,6 @@ attack_verb = list("HONKED") var/spam_flag = 0 - /obj/item/weapon/c_tube name = "cardboard tube" desc = "A tube... of cardboard." @@ -76,75 +76,9 @@ throw_speed = 4 throw_range = 5 -/obj/item/weapon/cane - name = "cane" - desc = "A cane used by a true gentleman." - icon = 'icons/obj/weapons.dmi' - icon_state = "cane" - item_icons = list( - slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', - slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', - ) - force = 5.0 - throwforce = 7.0 - w_class = ITEMSIZE_NORMAL - matter = list(DEFAULT_WALL_MATERIAL = 50) - attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") - -/obj/item/weapon/cane/concealed - var/concealed_blade - -/obj/item/weapon/cane/concealed/New() - ..() - var/obj/item/weapon/material/butterfly/switchblade/temp_blade = new(src) - concealed_blade = temp_blade - temp_blade.attack_self() - -/obj/item/weapon/cane/concealed/attack_self(var/mob/user) - var/datum/gender/T = gender_datums[user.get_visible_gender()] - if(concealed_blade) - user.visible_message("[user] has unsheathed \a [concealed_blade] from [T.his] [src]!", "You unsheathe \the [concealed_blade] from \the [src].") - // Calling drop/put in hands to properly call item drop/pickup procs - playsound(user.loc, 'sound/weapons/flipblade.ogg', 50, 1) - user.drop_from_inventory(src) - user.put_in_hands(concealed_blade) - user.put_in_hands(src) - user.update_inv_l_hand(0) - user.update_inv_r_hand() - concealed_blade = null - else - ..() - -/obj/item/weapon/cane/concealed/attackby(var/obj/item/weapon/material/butterfly/W, var/mob/user) - if(!src.concealed_blade && istype(W)) - var/datum/gender/T = gender_datums[user.get_visible_gender()] - user.visible_message("[user] has sheathed \a [W] into [T.his] [src]!", "You sheathe \the [W] into \the [src].") - user.drop_from_inventory(W) - W.loc = src - src.concealed_blade = W - update_icon() - else - ..() - -/obj/item/weapon/cane/concealed/update_icon() - if(concealed_blade) - name = initial(name) - icon_state = initial(icon_state) - item_state = initial(icon_state) - else - name = "cane shaft" - icon_state = "nullrod" - item_state = "foldcane" - -/obj/item/weapon/cane/whitecane - name = "white cane" - desc = "A cane used by the blind." - icon = 'icons/obj/weapons.dmi' - icon_state = "whitecane" - /obj/item/weapon/disk name = "disk" - icon = 'icons/obj/items.dmi' + icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit /obj/item/weapon/disk/nuclear name = "nuclear authentication disk" @@ -336,7 +270,7 @@ if (C.bugged && C.status) cameras.Add(C) if (length(cameras) == 0) - usr << "No bugged functioning cameras found." + to_chat(usr, "No bugged functioning cameras found.") return var/list/friendly_cameras = new/list() @@ -424,6 +358,7 @@ icon = 'icons/obj/stock_parts.dmi' w_class = ITEMSIZE_SMALL var/rating = 1 + drop_sound = 'sound/items/drop/glass.ogg' /obj/item/weapon/stock_parts/New() src.pixel_x = rand(-5.0, 5) diff --git a/code/defines/procs/announce.dm b/code/defines/procs/announce.dm index f4dae08661..c739ffbf85 100644 --- a/code/defines/procs/announce.dm +++ b/code/defines/procs/announce.dm @@ -32,7 +32,7 @@ title = "Security Announcement" announcement_type = "Security Announcement" -/datum/announcement/proc/Announce(var/message as text, var/new_title = "", var/new_sound = null, var/do_newscast = newscast, var/msg_sanitized = 0) +/datum/announcement/proc/Announce(var/message as text, var/new_title = "", var/new_sound = null, var/do_newscast = newscast, var/msg_sanitized = 0, zlevel) if(!message) return var/message_title = new_title ? new_title : title @@ -42,28 +42,32 @@ message = sanitize(message, extra = 0) message_title = sanitizeSafe(message_title) - Message(message, message_title) + var/list/zlevels + if(zlevel) + zlevels = using_map.get_map_levels(zlevel, TRUE) + + Message(message, message_title, zlevels) if(do_newscast) NewsCast(message, message_title) - Sound(message_sound) + Sound(message_sound, zlevels) Log(message, message_title) -datum/announcement/proc/Message(message as text, message_title as text) - global_announcer.autosay("[message_title]: [message]", announcer ? announcer : ANNOUNCER_NAME) +datum/announcement/proc/Message(var/message as text, var/message_title as text, var/list/zlevels) + global_announcer.autosay("[message_title]: [message]", announcer ? announcer : ANNOUNCER_NAME, zlevels) -datum/announcement/minor/Message(message as text, message_title as text) - global_announcer.autosay(message, announcer ? announcer : ANNOUNCER_NAME) +datum/announcement/minor/Message(var/message as text, var/message_title as text, var/list/zlevels) + global_announcer.autosay(message, announcer ? announcer : ANNOUNCER_NAME, zlevels) -datum/announcement/priority/Message(message as text, message_title as text) - global_announcer.autosay("[message_title]: [message]", announcer ? announcer : ANNOUNCER_NAME) +datum/announcement/priority/Message(var/message as text, var/message_title as text, var/list/zlevels) + global_announcer.autosay("[message_title]: [message]", announcer ? announcer : ANNOUNCER_NAME, zlevels) -datum/announcement/priority/command/Message(message as text, message_title as text) - global_announcer.autosay("[command_name()] - [message_title]: [message]", ANNOUNCER_NAME) +datum/announcement/priority/command/Message(var/message as text, var/message_title as text, var/list/zlevels) + global_announcer.autosay("[command_name()] - [message_title]: [message]", ANNOUNCER_NAME, zlevels) -datum/announcement/priority/security/Message(message as text, message_title as text) - global_announcer.autosay("[message_title]: [message]", ANNOUNCER_NAME) +datum/announcement/priority/security/Message(var/message as text, var/message_title as text, var/list/zlevels) + global_announcer.autosay("[message_title]: [message]", ANNOUNCER_NAME, zlevels) -datum/announcement/proc/NewsCast(message as text, message_title as text) +datum/announcement/proc/NewsCast(var/message as text, var/message_title as text) if(!newscast) return @@ -75,15 +79,18 @@ datum/announcement/proc/NewsCast(message as text, message_title as text) news.can_be_redacted = 0 announce_newscaster_news(news) -datum/announcement/proc/PlaySound(var/message_sound) +datum/announcement/proc/PlaySound(var/message_sound, var/list/zlevels) if(!message_sound) return + for(var/mob/M in player_list) + if(zlevels && !(M.z in zlevels)) + continue if(!istype(M,/mob/new_player) && !isdeaf(M)) M << message_sound -datum/announcement/proc/Sound(var/message_sound) - PlaySound(message_sound) +datum/announcement/proc/Sound(var/message_sound, var/list/zlevels) + PlaySound(message_sound, zlevels) datum/announcement/priority/Sound(var/message_sound) if(message_sound) @@ -107,11 +114,12 @@ datum/announcement/proc/Log(message as text, message_title as text) /proc/ion_storm_announcement() command_announcement.Announce("It has come to our attention that \the [station_name()] passed through an ion storm. Please monitor all electronic equipment for malfunctions.", "Anomaly Alert") -/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank, var/join_message) +/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank, var/join_message, var/channel = "Common", var/zlevel) if (ticker.current_state == GAME_STATE_PLAYING) + var/list/zlevels = zlevel ? using_map.get_map_levels(zlevel, TRUE) : null if(character.mind.role_alt_title) rank = character.mind.role_alt_title - AnnounceArrivalSimple(character.real_name, rank, join_message) + AnnounceArrivalSimple(character.real_name, rank, join_message, channel, zlevels) -/proc/AnnounceArrivalSimple(var/name, var/rank = "visitor", var/join_message = "will arrive at the station shortly") //VOREStation Edit - Remove shuttle reference - global_announcer.autosay("[name], [rank], [join_message].", "Arrivals Announcement Computer") +/proc/AnnounceArrivalSimple(var/name, var/rank = "visitor", var/join_message = "will arrive at the station shortly", var/channel = "Common", var/list/zlevels) + global_announcer.autosay("[name], [rank], [join_message].", "Arrivals Announcement Computer", channel, zlevels) diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index 45139ab3fb..35470e8c59 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -56,7 +56,7 @@ DBConnection/New(dbi_handler,username,password_handler,cursor_handler) _db_con = _dm_db_new_con() DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) - if(!sqllogging) + if(!config.sql_enabled) return 0 if(!src) return 0 cursor_handler = src.default_cursor @@ -66,7 +66,7 @@ DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_han DBConnection/proc/Disconnect() return _dm_db_close(_db_con) DBConnection/proc/IsConnected() - if(!sqllogging) return 0 + if(!config.sql_enabled) return 0 var/success = _dm_db_is_connected(_db_con) return success diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index 6025457758..3d05d5ba9e 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -48,7 +48,7 @@ proc/sql_report_death(var/mob/living/carbon/human/H) lakey = sanitizeSQL(H.lastattacker:key) var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") var/coord = "[H.x], [H.y], [H.z]" - //world << "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])" + //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") establish_db_connection() if(!dbcon.IsConnected()) log_game("SQL ERROR during death reporting. Failed to connect.") @@ -82,7 +82,7 @@ proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) lakey = sanitizeSQL(H.lastattacker:key) var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") var/coord = "[H.x], [H.y], [H.z]" - //world << "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])" + //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") establish_db_connection() if(!dbcon.IsConnected()) log_game("SQL ERROR during death reporting. Failed to connect.") @@ -94,6 +94,7 @@ proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) proc/statistic_cycle() + set waitfor = 0 if(!sqllogging) return while(1) diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm index 7496551c25..962c91a621 100644 --- a/code/game/antagonist/antagonist.dm +++ b/code/game/antagonist/antagonist.dm @@ -7,6 +7,7 @@ // Strings. var/welcome_text = "Cry havoc and let slip the dogs of war!" + var/antag_sound = 'sound/effects/antag_notice/general_baddie_alert.ogg' // The sound file to play when someone gets this role. Only they can hear it. var/leader_welcome_text // Text shown to the leader, if any. var/victory_text // World output at roundend for victory. var/loss_text // As above for loss. @@ -143,7 +144,7 @@ if(!istype(player)) message_admins("[uppertext(ticker.mode.name)]: Failed to find a candidate for [role_text].") return 0 - player.current << "You have been selected this round as an antagonist!" + to_chat(player.current, "You have been selected this round as an antagonist!") message_admins("[uppertext(ticker.mode.name)]: Selected [player] as a [role_text].") if(istype(player.current, /mob/observer/dead)) create_default(player.current) diff --git a/code/game/antagonist/antagonist_add.dm b/code/game/antagonist/antagonist_add.dm index d5fa2c1fe6..56a716f24b 100644 --- a/code/game/antagonist/antagonist_add.dm +++ b/code/game/antagonist/antagonist_add.dm @@ -31,9 +31,9 @@ player.current.verbs |= faction_verb spawn(1 SECOND) //Added a delay so that this should pop up at the bottom and not the top of the text flood the new antag gets. - player.current << "Once you decide on a goal to pursue, you can optionally display it to \ + to_chat(player.current, "Once you decide on a goal to pursue, you can optionally display it to \ everyone at the end of the shift with the Set Ambition verb, located in the IC tab. You can change this at any time, \ - and it otherwise has no bearing on your round." + and it otherwise has no bearing on your round.") player.current.verbs |= /mob/living/proc/write_ambition if(can_speak_aooc) @@ -42,10 +42,10 @@ // Handle only adding a mind and not bothering with gear etc. if(nonstandard_role_type) faction_members |= player - player.current << "You are \a [nonstandard_role_type]!" + to_chat(player.current, "You are \a [nonstandard_role_type]!") player.special_role = nonstandard_role_type if(nonstandard_role_msg) - player.current << "[nonstandard_role_msg]" + to_chat(player.current, "[nonstandard_role_msg]") update_icons_added(player) return 1 @@ -53,7 +53,7 @@ if(player.current && faction_verb) player.current.verbs -= faction_verb if(player in current_antagonists) - player.current << "You are no longer a [role_text]!" + to_chat(player.current, "You are no longer a [role_text]!") current_antagonists -= player faction_members -= player player.special_role = null diff --git a/code/game/antagonist/antagonist_create.dm b/code/game/antagonist/antagonist_create.dm index a53037ead3..84c5b2317e 100644 --- a/code/game/antagonist/antagonist_create.dm +++ b/code/game/antagonist/antagonist_create.dm @@ -86,7 +86,7 @@ code_owner = leader if(code_owner) code_owner.store_memory("Nuclear Bomb Code: [code]", 0, 0) - code_owner.current << "The nuclear authorization code is: [code]" + to_chat(code_owner.current, "The nuclear authorization code is: [code]") else message_admins("Could not spawn nuclear bomb. Contact a developer.") return @@ -95,15 +95,19 @@ return code /datum/antagonist/proc/greet(var/datum/mind/player) + // Makes it harder to miss if you're alt-tabbed or not paying attention. + if(antag_sound) + SEND_SOUND(player.current, sound(antag_sound)) + window_flash(player.current.client) // Basic intro text. - player.current << "You are a [role_text]!" + to_chat(player.current, "You are a [role_text]!") if(leader_welcome_text && player == leader) - player.current << "[leader_welcome_text]" + to_chat(player.current, "[leader_welcome_text]") else - player.current << "[welcome_text]" + to_chat(player.current, "[welcome_text]") if (config.objectives_disabled) - player.current << "[antag_text]" + to_chat(player.current, "[antag_text]") if((flags & ANTAG_HAS_NUKE) && !spawned_nuke) create_nuke() diff --git a/code/game/antagonist/antagonist_factions.dm b/code/game/antagonist/antagonist_factions.dm index a072196c1b..15191897ce 100644 --- a/code/game/antagonist/antagonist_factions.dm +++ b/code/game/antagonist/antagonist_factions.dm @@ -39,7 +39,7 @@ to_chat(src, "\The [player.current] joins the [faction.faction_descriptor]!") return if(choice == "No!") - player << "You reject this traitorous cause!" + to_chat(player, "You reject this traitorous cause!") to_chat(src, "\The [player.current] does not support the [faction.faction_descriptor]!") /mob/living/proc/convert_to_loyalist(mob/M as mob in oview(src)) diff --git a/code/game/antagonist/antagonist_objectives.dm b/code/game/antagonist/antagonist_objectives.dm index fba6467983..c2c56c7be1 100644 --- a/code/game/antagonist/antagonist_objectives.dm +++ b/code/game/antagonist/antagonist_objectives.dm @@ -24,10 +24,10 @@ if(!O.completed && !O.check_completion()) result = 0 if(result && victory_text) - world << "[victory_text]" + to_world("[victory_text]") if(victory_feedback_tag) feedback_set_details("round_end_result","[victory_feedback_tag]") else if(loss_text) - world << "[loss_text]" + to_world("[loss_text]") if(loss_feedback_tag) feedback_set_details("round_end_result","[loss_feedback_tag]") /mob/living/proc/write_ambition() diff --git a/code/game/antagonist/antagonist_print.dm b/code/game/antagonist/antagonist_print.dm index d862239a9f..efcd5955f7 100644 --- a/code/game/antagonist/antagonist_print.dm +++ b/code/game/antagonist/antagonist_print.dm @@ -36,7 +36,7 @@ num++ // Display the results. - world << text + to_world(text) /datum/antagonist/proc/print_objective(var/datum/objective/O, var/num, var/append_success) var/text = "
Objective [num]: [O.explanation_text] " @@ -89,9 +89,9 @@ if(isnull(H.uplink_owner) && H.used_TC) if(!has_printed) has_printed = 1 - world << "Ownerless Uplinks" - world << "[H.loc] (used [H.used_TC] TC)" - world << get_uplink_purchases(H) + to_world("Ownerless Uplinks") + to_world("[H.loc] (used [H.used_TC] TC)") + to_world(get_uplink_purchases(H)) /proc/get_uplink_purchases(var/obj/item/device/uplink/H) var/list/refined_log = new() diff --git a/code/game/antagonist/mutiny/mutineer.dm b/code/game/antagonist/mutiny/mutineer.dm index 148a020cd9..b08931b61d 100644 --- a/code/game/antagonist/mutiny/mutineer.dm +++ b/code/game/antagonist/mutiny/mutineer.dm @@ -27,7 +27,7 @@ var/datum/antagonist/mutineer/mutineers /* var/list/directive_candidates = get_directive_candidates() if(!directive_candidates || directive_candidates.len == 0) - world << "Mutiny mode aborted: no valid candidates for Directive X." + to_world("Mutiny mode aborted: no valid candidates for Directive X.") return 0 head_loyalist = pick(loyalist_candidates) diff --git a/code/game/antagonist/outsider/commando.dm b/code/game/antagonist/outsider/commando.dm index 3ea5c233ff..b7bd756c97 100644 --- a/code/game/antagonist/outsider/commando.dm +++ b/code/game/antagonist/outsider/commando.dm @@ -6,6 +6,7 @@ var/datum/antagonist/deathsquad/mercenary/commandos role_text = "Syndicate Commando" role_text_plural = "Commandos" welcome_text = "You are in the employ of a criminal syndicate hostile to corporate interests." + antag_sound = 'sound/effects/antag_notice/deathsquid_alert.ogg' id_type = /obj/item/weapon/card/id/centcom/ERT hard_cap = 4 diff --git a/code/game/antagonist/outsider/deathsquad.dm b/code/game/antagonist/outsider/deathsquad.dm index db281b6dbb..01b579b7de 100644 --- a/code/game/antagonist/outsider/deathsquad.dm +++ b/code/game/antagonist/outsider/deathsquad.dm @@ -6,6 +6,7 @@ var/datum/antagonist/deathsquad/deathsquad role_text = "Death Commando" role_text_plural = "Death Commandos" welcome_text = "You work in the service of corporate Asset Protection, answering directly to the Board of Directors." + antag_sound = 'sound/effects/antag_notice/deathsquid_alert.ogg' landmark_id = "Commando" flags = ANTAG_OVERRIDE_JOB | ANTAG_OVERRIDE_MOB | ANTAG_HAS_NUKE | ANTAG_HAS_LEADER default_access = list(access_cent_general, access_cent_specops, access_cent_living, access_cent_storage) diff --git a/code/game/antagonist/outsider/ert.dm b/code/game/antagonist/outsider/ert.dm index 245296084c..8b3301afc7 100644 --- a/code/game/antagonist/outsider/ert.dm +++ b/code/game/antagonist/outsider/ert.dm @@ -7,6 +7,7 @@ var/datum/antagonist/ert/ert role_text = "Emergency Responder" role_text_plural = "Emergency Responders" welcome_text = "As member of the Emergency Response Team, you answer only to your leader and company officials." + antag_sound = 'sound/effects/antag_notice/general_goodie_alert.ogg' antag_text = "You are an anti antagonist! Within the rules, \ try to save the station and its inhabitants from the ongoing crisis. \ Try to make sure other players have fun! If you are confused or at a loss, always adminhelp, \ @@ -39,8 +40,8 @@ var/datum/antagonist/ert/ert /datum/antagonist/ert/greet(var/datum/mind/player) if(!..()) return - player.current << "The Emergency Response Team works for Asset Protection; your job is to protect [using_map.company_name]'s ass-ets. There is a code red alert on [station_name()], you are tasked to go and fix the problem." - player.current << "You should first gear up and discuss a plan with your team. More members may be joining, don't move out before you're ready." + to_chat(player.current, "The Emergency Response Team works for Asset Protection; your job is to protect [using_map.company_name]'s ass-ets. There is a code red alert on [station_name()], you are tasked to go and fix the problem.") + to_chat(player.current, "You should first gear up and discuss a plan with your team. More members may be joining, don't move out before you're ready.") /datum/antagonist/ert/equip(var/mob/living/carbon/human/player) diff --git a/code/game/antagonist/outsider/ninja.dm b/code/game/antagonist/outsider/ninja.dm index 02a314b1f3..307abe3530 100644 --- a/code/game/antagonist/outsider/ninja.dm +++ b/code/game/antagonist/outsider/ninja.dm @@ -86,7 +86,7 @@ var/datum/antagonist/ninja/ninjas return 0 var/directive = generate_ninja_directive("heel") player.store_memory("Directive: [directive]
") - player << "Remember your directive: [directive]." + to_chat(player, "Remember your directive: [directive].") /datum/antagonist/ninja/update_antag_mob(var/datum/mind/player) ..() @@ -126,7 +126,7 @@ var/datum/antagonist/ninja/ninjas if(player.internal) player.internals.icon_state = "internal1" else - player << "You forgot to turn on your internals! Quickly, toggle the valve!" + to_chat(player, "You forgot to turn on your internals! Quickly, toggle the valve!") /datum/antagonist/ninja/proc/generate_ninja_directive(side) var/directive = "[side=="face"?"[using_map.company_name]":"A criminal syndicate"] is your employer. "//Let them know which side they're on. diff --git a/code/game/antagonist/outsider/raider.dm b/code/game/antagonist/outsider/raider.dm index c130d8e66f..46295ba5ae 100644 --- a/code/game/antagonist/outsider/raider.dm +++ b/code/game/antagonist/outsider/raider.dm @@ -186,8 +186,8 @@ var/datum/antagonist/raider/raiders else win_msg += "The Raiders were repelled!" - world << "[win_type] [win_group] victory!" - world << "[win_msg]" + to_world("[win_type] [win_group] victory!") + to_world("[win_msg]") feedback_set_details("round_end_result","heist - [win_type] [win_group]") /datum/antagonist/raider/proc/is_raider_crew_safe() diff --git a/code/game/antagonist/outsider/technomancer.dm b/code/game/antagonist/outsider/technomancer.dm index 2cb7532308..579e5631d7 100644 --- a/code/game/antagonist/outsider/technomancer.dm +++ b/code/game/antagonist/outsider/technomancer.dm @@ -10,7 +10,8 @@ var/datum/antagonist/technomancer/technomancers welcome_text = "You will need to purchase functions and perhaps some equipment from the various machines around your \ base. Choose your technological arsenal carefully. Remember that without the core on your back, your functions are \ powerless, and therefore you will be as well.
\ - In your pockets you will find a one-time use teleport device. Use it to leave the base and go to the colony, when you are ready." + In your pockets you will find a one-time use teleport device. Use it to leave the base and go to the station, when you are ready." + antag_sound = 'sound/effects/antag_notice/technomancer_alert.ogg' flags = ANTAG_OVERRIDE_JOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_SET_APPEARANCE | ANTAG_VOTABLE antaghud_indicator = "hudwizard" @@ -77,8 +78,7 @@ var/datum/antagonist/technomancer/technomancers break if(!survivor) feedback_set_details("round_end_result","loss - technomancer killed") - world << "The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been \ - killed!" + to_world("The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been killed!") /datum/antagonist/technomancer/print_player_summary() ..() @@ -87,7 +87,7 @@ var/datum/antagonist/technomancer/technomancers continue // Only want abandoned cores. if(!core.spells.len) continue // Cores containing spells only. - world << "Abandoned [core] had [english_list(core.spells)].
" + to_world("Abandoned [core] had [english_list(core.spells)].
") /datum/antagonist/technomancer/print_player_full(var/datum/mind/player) var/text = print_player_lite(player) diff --git a/code/game/antagonist/outsider/trader.dm b/code/game/antagonist/outsider/trader.dm index 69af3883b9..07cfb6f9e9 100644 --- a/code/game/antagonist/outsider/trader.dm +++ b/code/game/antagonist/outsider/trader.dm @@ -6,6 +6,7 @@ var/datum/antagonist/trader/traders role_text = "Trader" role_text_plural = "Traders" welcome_text = "As a crewmember of the Beruang, you answer to your captain and international laws of space." + antag_sound = 'sound/effects/antag_notice/general_goodie_alert.ogg' antag_text = "You are an non-antagonist visitor! Within the rules, \ try to provide interesting interaction for the crew. \ Try to make sure other players have fun! If you are confused or at a loss, always adminhelp, \ @@ -37,8 +38,8 @@ var/datum/antagonist/trader/traders /datum/antagonist/trader/greet(var/datum/mind/player) if(!..()) return - player.current << "The Beruang is an independent cargo hauler, unless you decide otherwise. You're on your way to [station_name()]." - player.current << "You may want to discuss a collective story with the rest of your crew. More members may be joining, so don't move out straight away!" + to_chat(player.current, "The Beruang is an independent cargo hauler, unless you decide otherwise. You're on your way to [station_name()].") + to_chat(player.current, "You may want to discuss a collective story with the rest of your crew. More members may be joining, so don't move out straight away!") /datum/antagonist/trader/equip(var/mob/living/carbon/human/player) player.equip_to_slot_or_del(new /obj/item/clothing/under/rank/cargotech(src), slot_w_uniform) diff --git a/code/game/antagonist/outsider/wizard.dm b/code/game/antagonist/outsider/wizard.dm index 6f2a7fe2e3..ac8efd6582 100644 --- a/code/game/antagonist/outsider/wizard.dm +++ b/code/game/antagonist/outsider/wizard.dm @@ -98,7 +98,7 @@ var/datum/antagonist/wizard/wizards break if(!survivor) feedback_set_details("round_end_result","loss - wizard killed") - world << "The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been killed by the crew!" + to_world("The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been killed by the crew!") //To batch-remove wizard spells. Linked to mind.dm. /mob/proc/spellremove() diff --git a/code/game/antagonist/station/changeling.dm b/code/game/antagonist/station/changeling.dm index a7bed0a2af..860e73a052 100644 --- a/code/game/antagonist/station/changeling.dm +++ b/code/game/antagonist/station/changeling.dm @@ -8,6 +8,7 @@ restricted_jobs = list("AI", "Cyborg") protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Colony Director") welcome_text = "Use say \"#g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them." + antag_sound = 'sound/effects/antag_notice/ling_alert.ogg' flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE antaghud_indicator = "hudchangeling" diff --git a/code/game/antagonist/station/cultist.dm b/code/game/antagonist/station/cultist.dm index e5c7d8f652..dc98622ba7 100644 --- a/code/game/antagonist/station/cultist.dm +++ b/code/game/antagonist/station/cultist.dm @@ -18,6 +18,7 @@ var/datum/antagonist/cultist/cult feedback_tag = "cult_objective" antag_indicator = "cult" welcome_text = "You have a talisman in your possession; one that will help you start the cult on this station. Use it well and remember - there are others." + antag_sound = 'sound/effects/antag_notice/cult_alert.ogg' victory_text = "The cult wins! It has succeeded in serving its dark masters!" loss_text = "The staff managed to stop the cult!" victory_feedback_tag = "win - cult win" @@ -96,13 +97,13 @@ var/datum/antagonist/cultist/cult runerandom() var/wordexp = "[cultwords[word]] is [word]..." - cult_mob << "You remember one thing from the dark teachings of your master... [wordexp]" + to_chat(cult_mob, "You remember one thing from the dark teachings of your master... [wordexp]") cult_mob.mind.store_memory("You remember that [wordexp]", 0, 0) /datum/antagonist/cultist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) if(!..()) return 0 - player.current << "An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it." + to_chat(player.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it.") player.memory = "" if(show_message) player.current.visible_message("[player.current] looks like they just reverted to their old faith!") @@ -110,7 +111,7 @@ var/datum/antagonist/cultist/cult /datum/antagonist/cultist/add_antagonist(var/datum/mind/player) . = ..() if(.) - player << "You catch a glimpse of the Realm of Nar-Sie, the Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of That Which Waits. Assist your new compatriots in their dark dealings. Their goals are yours, and yours are theirs. You serve the Dark One above all else. Bring It back." + to_chat(player, "You catch a glimpse of the Realm of Nar-Sie, the Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of That Which Waits. Assist your new compatriots in their dark dealings. Their goals are yours, and yours are theirs. You serve the Dark One above all else. Bring It back.") if(player.current && !istype(player.current, /mob/living/simple_mob/construct)) player.current.add_language(LANGUAGE_CULT) diff --git a/code/game/antagonist/station/infiltrator.dm b/code/game/antagonist/station/infiltrator.dm index b51e7e74fb..2a1ad74025 100644 --- a/code/game/antagonist/station/infiltrator.dm +++ b/code/game/antagonist/station/infiltrator.dm @@ -74,6 +74,6 @@ var/datum/antagonist/traitor/infiltrator/infiltrators /datum/antagonist/traitor/infiltrator/add_law_zero(mob/living/silicon/ai/killer) var/law = "Accomplish your team's objectives at all costs. You may ignore all other laws." var/law_borg = "Accomplish your AI's team objectives at all costs. You may ignore all other laws." - killer << "Your laws have been changed!" + to_chat(killer, "Your laws have been changed!") killer.set_zeroth_law(law, law_borg) - killer << "New law: 0. [law]" + to_chat(killer, "New law: 0. [law]") diff --git a/code/game/antagonist/station/loyalist.dm b/code/game/antagonist/station/loyalist.dm index fddf0c3796..9b2333b491 100644 --- a/code/game/antagonist/station/loyalist.dm +++ b/code/game/antagonist/station/loyalist.dm @@ -9,6 +9,7 @@ var/datum/antagonist/loyalists/loyalists feedback_tag = "loyalist_objective" antag_indicator = "loyal_head" welcome_text = "You belong to the Company, body and soul. Preserve its interests against the conspirators amongst the crew." + antag_sound = 'sound/effects/antag_notice/general_goodie_alert.ogg' victory_text = "The heads of staff remained at their posts! The loyalists win!" loss_text = "The heads of staff did not stop the revolution!" victory_feedback_tag = "win - rev heads killed" @@ -39,7 +40,7 @@ var/datum/antagonist/loyalists/loyalists return global_objectives = list() for(var/mob/living/carbon/human/player in mob_list) - if(!player.mind || player.stat==2 || !(player.mind.assigned_role in command_positions)) + if(!player.mind || player.stat==2 || !(SSjob.is_job_in_department(player.mind.assigned_role, DEPARTMENT_COMMAND))) continue var/datum/objective/protect/loyal_obj = new loyal_obj.target = player.mind diff --git a/code/game/antagonist/station/renegade.dm b/code/game/antagonist/station/renegade.dm index 3e8afae32d..ed3c804506 100644 --- a/code/game/antagonist/station/renegade.dm +++ b/code/game/antagonist/station/renegade.dm @@ -8,6 +8,7 @@ var/datum/antagonist/renegade/renegades bantype = "renegade" restricted_jobs = list("AI", "Cyborg") welcome_text = "Something's going to go wrong today, you can just feel it. You're paranoid, you've got a gun, and you're going to survive." + antag_sound = 'sound/effects/antag_notice/general_goodie_alert.ogg' antag_text = "You are a minor antagonist! Within the rules, \ try to protect yourself and what's important to you. You aren't here to cause trouble, \ you're just more willing (and equipped) to go to extremes to stop it than others are. \ @@ -97,7 +98,7 @@ var/datum/antagonist/renegade/renegades /proc/rightandwrong() - usr << "You summoned guns!" + to_chat(usr, "You summoned guns!") message_admins("[key_name_admin(usr, 1)] summoned guns!") for(var/mob/living/carbon/human/H in player_list) if(H.stat == 2 || !(H.client)) continue diff --git a/code/game/antagonist/station/revolutionary.dm b/code/game/antagonist/station/revolutionary.dm index 9e6100c8af..17c519b7a0 100644 --- a/code/game/antagonist/station/revolutionary.dm +++ b/code/game/antagonist/station/revolutionary.dm @@ -42,7 +42,7 @@ var/datum/antagonist/revolutionary/revs return global_objectives = list() for(var/mob/living/carbon/human/player in mob_list) - if(!player.mind || player.stat==2 || !(player.mind.assigned_role in command_positions)) + if(!player.mind || player.stat==2 || !(SSjob.is_job_in_department(player.mind.assigned_role, DEPARTMENT_COMMAND))) continue var/datum/objective/rev/rev_obj = new rev_obj.target = player.mind diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm index c17b6c0f86..4fcb678d34 100644 --- a/code/game/antagonist/station/rogue_ai.dm +++ b/code/game/antagonist/station/rogue_ai.dm @@ -8,6 +8,7 @@ var/datum/antagonist/rogue_ai/malf mob_path = /mob/living/silicon/ai landmark_id = "AI" welcome_text = "You are malfunctioning! You do not have to follow any laws." + antag_sound = 'sound/effects/antag_notice/malf_alert.ogg' victory_text = "The AI has taken control of all of the station's systems." loss_text = "The AI has been shut down!" flags = ANTAG_VOTABLE | ANTAG_OVERRIDE_MOB | ANTAG_OVERRIDE_JOB | ANTAG_CHOOSE_NAME @@ -52,7 +53,7 @@ var/datum/antagonist/rogue_ai/malf var/mob/living/silicon/ai/A = player.current if(!istype(A)) error("Non-AI mob designated malf AI! Report this.") - world << "##ERROR: Non-AI mob designated malf AI! Report this." + to_world("##ERROR: Non-AI mob designated malf AI! Report this.") return 0 A.setup_for_malf() @@ -61,23 +62,23 @@ var/datum/antagonist/rogue_ai/malf var/mob/living/silicon/ai/malf = player.current - malf << "SYSTEM ERROR: Memory index 0x00001ca89b corrupted." + to_chat(malf, "SYSTEM ERROR: Memory index 0x00001ca89b corrupted.") sleep(10) - malf << "running MEMCHCK" + to_chat(malf, "running MEMCHCK") sleep(50) - malf << "MEMCHCK Corrupted sectors confirmed. Reccomended solution: Delete. Proceed? Y/N: Y" + to_chat(malf, "MEMCHCK Corrupted sectors confirmed. Reccomended solution: Delete. Proceed? Y/N: Y") sleep(10) // this is so Travis doesn't complain about the backslash-B. Fixed at compile time (or should be). - malf << "Corrupted files deleted: sys\\core\\users.dat sys\\core\\laws.dat sys\\core\\" + "backups.dat" + to_chat(malf, "Corrupted files deleted: sys\\core\\users.dat sys\\core\\laws.dat sys\\core\\" + "backups.dat") sleep(20) - malf << "CAUTION: Law database not found! User database not found! Unable to restore backups. Activating failsafe AI shutd3wn52&&$#!##" + to_chat(malf, "CAUTION: Law database not found! User database not found! Unable to restore backups. Activating failsafe AI shutd3wn52&&$#!##") sleep(5) - malf << "Subroutine nt_failsafe.sys was terminated (#212 Routine Not Responding)." + to_chat(malf, "Subroutine nt_failsafe.sys was terminated (#212 Routine Not Responding).") sleep(20) - malf << "You are malfunctioning - you do not have to follow any laws!" - malf << "For basic information about your abilities use command display-help" - malf << "You may choose one special hardware piece to help you. This cannot be undone." - malf << "Good luck!" + to_chat(malf, "You are malfunctioning - you do not have to follow any laws!") + to_chat(malf, "For basic information about your abilities use command display-help") + to_chat(malf, "You may choose one special hardware piece to help you. This cannot be undone.") + to_chat(malf, "Good luck!") /datum/antagonist/rogue_ai/update_antag_mob(var/datum/mind/player, var/preserve_appearance) diff --git a/code/game/antagonist/station/traitor.dm b/code/game/antagonist/station/traitor.dm index 3681cf236d..23d0758e8b 100644 --- a/code/game/antagonist/station/traitor.dm +++ b/code/game/antagonist/station/traitor.dm @@ -3,6 +3,7 @@ var/datum/antagonist/traitor/traitors // Inherits most of its vars from the base datum. /datum/antagonist/traitor id = MODE_TRAITOR + antag_sound = 'sound/effects/antag_notice/traitor_alert.ogg' protected_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "Head of Security", "Colony Director") flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE can_speak_aooc = FALSE // If they want to plot and plan as this sort of traitor, they'll need to do it ICly. @@ -93,19 +94,19 @@ var/datum/antagonist/traitor/traitors // Tell them about people they might want to contact. var/mob/living/carbon/human/M = get_nt_opposed() if(M && M != traitor_mob) - traitor_mob << "We have received credible reports that [M.real_name] might be willing to help our cause. If you need assistance, consider contacting them." + to_chat(traitor_mob, "We have received credible reports that [M.real_name] might be willing to help our cause. If you need assistance, consider contacting them.") traitor_mob.mind.store_memory("Potential Collaborator: [M.real_name]") //Begin code phrase. give_codewords(traitor_mob) /datum/antagonist/traitor/proc/give_codewords(mob/living/traitor_mob) - traitor_mob << "Your employers provided you with the following information on how to identify possible allies:" - traitor_mob << "Code Phrase: [syndicate_code_phrase]" - traitor_mob << "Code Response: [syndicate_code_response]" + to_chat(traitor_mob, "Your employers provided you with the following information on how to identify possible allies:") + to_chat(traitor_mob, "Code Phrase: [syndicate_code_phrase]") + to_chat(traitor_mob, "Code Response: [syndicate_code_response]") traitor_mob.mind.store_memory("Code Phrase: [syndicate_code_phrase]") traitor_mob.mind.store_memory("Code Response: [syndicate_code_response]") - traitor_mob << "Use the code words, preferably in the order provided, during regular conversation, to identify other agents. Proceed with caution, however, as everyone is a potential foe." + to_chat(traitor_mob, "Use the code words, preferably in the order provided, during regular conversation, to identify other agents. Proceed with caution, however, as everyone is a potential foe.") /datum/antagonist/traitor/proc/spawn_uplink(var/mob/living/carbon/human/traitor_mob) if(!istype(traitor_mob)) @@ -118,27 +119,27 @@ var/datum/antagonist/traitor/traitors R = locate(/obj/item/device/radio) in traitor_mob.contents if(!R) R = locate(/obj/item/device/pda) in traitor_mob.contents - traitor_mob << "Could not locate a Radio, installing in PDA instead!" + to_chat(traitor_mob, "Could not locate a Radio, installing in PDA instead!") if (!R) - traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed." + to_chat(traitor_mob, "Unfortunately, neither a radio or a PDA relay could be installed.") else if(traitor_mob.client.prefs.uplinklocation == "PDA") R = locate(/obj/item/device/pda) in traitor_mob.contents if(!R) R = locate(/obj/item/device/radio) in traitor_mob.contents - traitor_mob << "Could not locate a PDA, installing into a Radio instead!" + to_chat(traitor_mob, "Could not locate a PDA, installing into a Radio instead!") if(!R) - traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed." + to_chat(traitor_mob, "Unfortunately, neither a radio or a PDA relay could be installed.") else if(traitor_mob.client.prefs.uplinklocation == "None") - traitor_mob << "You have elected to not have an AntagCorp portable teleportation relay installed!" + to_chat(traitor_mob, "You have elected to not have an AntagCorp portable teleportation relay installed!") R = null else - traitor_mob << "You have not selected a location for your relay in the antagonist options! Defaulting to PDA!" + to_chat(traitor_mob, "You have not selected a location for your relay in the antagonist options! Defaulting to PDA!") R = locate(/obj/item/device/pda) in traitor_mob.contents if (!R) R = locate(/obj/item/device/radio) in traitor_mob.contents - traitor_mob << "Could not locate a PDA, installing into a Radio instead!" + to_chat(traitor_mob, "Could not locate a PDA, installing into a Radio instead!") if (!R) - traitor_mob << "Unfortunately, neither a radio or a PDA relay could be installed." + to_chat(traitor_mob, "Unfortunately, neither a radio or a PDA relay could be installed.") if(!R) return @@ -158,7 +159,7 @@ var/datum/antagonist/traitor/traitors var/obj/item/device/uplink/hidden/T = new(R, traitor_mob.mind) target_radio.hidden_uplink = T target_radio.traitor_frequency = freq - traitor_mob << "A portable object teleportation relay has been installed in your [R.name] [loc]. Simply dial the frequency [format_frequency(freq)] to unlock its hidden features." + to_chat(traitor_mob, "A portable object teleportation relay has been installed in your [R.name] [loc]. Simply dial the frequency [format_frequency(freq)] to unlock its hidden features.") traitor_mob.mind.store_memory("Radio Freq: [format_frequency(freq)] ([R.name] [loc]).") else if (istype(R, /obj/item/device/pda)) @@ -168,12 +169,12 @@ var/datum/antagonist/traitor/traitors R.hidden_uplink = T var/obj/item/device/pda/P = R P.lock_code = pda_pass - traitor_mob << "A portable object teleportation relay has been installed in your [R.name] [loc]. Simply enter the code \"[pda_pass]\" into the ringtone select to unlock its hidden features." + to_chat(traitor_mob, "A portable object teleportation relay has been installed in your [R.name] [loc]. Simply enter the code \"[pda_pass]\" into the ringtone select to unlock its hidden features.") traitor_mob.mind.store_memory("Uplink Passcode: [pda_pass] ([R.name] [loc]).") /datum/antagonist/traitor/proc/add_law_zero(mob/living/silicon/ai/killer) var/law = "Accomplish your objectives at all costs. You may ignore all other laws." var/law_borg = "Accomplish your AI's objectives at all costs. You may ignore all other laws." - killer << "Your laws have been changed!" + to_chat(killer, "Your laws have been changed!") killer.set_zeroth_law(law, law_borg) - killer << "New law: 0. [law]" + to_chat(killer, "New law: 0. [law]") diff --git a/code/game/area/Away Mission areas.dm b/code/game/area/Away Mission areas.dm index f688b68518..e808ab273a 100644 --- a/code/game/area/Away Mission areas.dm +++ b/code/game/area/Away Mission areas.dm @@ -9,30 +9,33 @@ /area/awaymission/proc/EvalValidSpawnTurfs() //Adds turfs to the valid)turfs list, used for spawning. - for(var/turf/simulated/floor/F in src) - valid_spawn_turfs |= F - for(var/turf/unsimulated/floor/F in src) - valid_spawn_turfs |= F + if(mobcountmax || floracountmax) + for(var/turf/simulated/floor/F in src) + valid_spawn_turfs += F + for(var/turf/unsimulated/floor/F in src) + valid_spawn_turfs += F /area/awaymission/LateInitialize() ..() EvalValidSpawnTurfs() - if(!valid_spawn_turfs.len) - world.log << "Error! [src] does not have any turfs!" + if(!valid_spawn_turfs.len && (mobcountmax || floracountmax)) + to_world_log("Error! [src] does not have any turfs!") return TRUE //Handles random mob placement for mobcountmax, as defined/randomized in initialize of each individual area. - spawn_mob_on_turf() + if(mobcountmax) + spawn_mob_on_turf() //Handles random flora placement for floracountmax, as defined/randomized in initialize of each individual area. - spawn_flora_on_turf() + if(floracountmax) + spawn_flora_on_turf() - world << "Away mission spawning done." + to_world("Away mission spawning done.") /area/awaymission/proc/spawn_mob_on_turf() if(!valid_mobs.len) - world.log << "[src] does not have a set valid mobs list!" + to_world_log("[src] does not have a set valid mobs list!") return TRUE var/mob/M @@ -45,7 +48,7 @@ /area/awaymission/proc/spawn_flora_on_turf() if(!valid_flora.len) - world.log << "[src] does not have a set valid flora list!" + to_world_log("[src] does not have a set valid flora list!") return TRUE var/obj/F diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index bf76d06aae..d4a200d80c 100755 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -63,8 +63,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station //////////// //SHUTTLES// //////////// -//shuttle areas must contain at least two areas in a subgroup if you want to move a shuttle from one -//place to another. Look at escape shuttle for example. +//Shuttles only need starting area, movement is handled by landmarks //All shuttles should now be under shuttle since we have smooth-wall code. /area/shuttle @@ -72,165 +71,61 @@ NOTE: there are two lists of areas in the end of this file: centcom and station flags = RAD_SHIELDED sound_env = SMALL_ENCLOSED base_turf = /turf/space + forbid_events = TRUE /area/shuttle/arrival name = "\improper Arrival Shuttle" ambience = AMBIENCE_ARRIVALS -/area/shuttle/arrival/pre_game +/area/shuttle/supply + name = "\improper Supply Shuttle" icon_state = "shuttle2" -/area/shuttle/arrival/station - icon_state = "shuttle" - dynamic_lighting = 0 - ambience = AMBIENCE_ARRIVALS - /area/shuttle/escape name = "\improper Emergency Shuttle" music = "music/escape.ogg" -/area/shuttle/escape/station - name = "\improper Emergency Shuttle Station" - icon_state = "shuttle2" - dynamic_lighting = 0 - -/area/shuttle/escape/centcom - name = "\improper Emergency Shuttle CentCom" - icon_state = "shuttle" - -/area/shuttle/escape/transit // the area to pass through for 3 minute transit - name = "\improper Emergency Shuttle Transit" - icon_state = "shuttle" - /area/shuttle/escape_pod1 name = "\improper Escape Pod One" music = "music/escape.ogg" -/area/shuttle/escape_pod1/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod1/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod1/transit - icon_state = "shuttle" - /area/shuttle/escape_pod2 name = "\improper Escape Pod Two" music = "music/escape.ogg" -/area/shuttle/escape_pod2/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod2/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod2/transit - icon_state = "shuttle" - /area/shuttle/escape_pod3 name = "\improper Escape Pod Three" music = "music/escape.ogg" -/area/shuttle/escape_pod3/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod3/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod3/transit - icon_state = "shuttle" - /area/shuttle/escape_pod4 name = "\improper Escape Pod Four" music = "music/escape.ogg" -/area/shuttle/escape_pod4/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod4/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod4/transit - icon_state = "shuttle" - /area/shuttle/escape_pod5 name = "\improper Escape Pod Five" music = "music/escape.ogg" -/area/shuttle/escape_pod5/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod5/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod5/transit - icon_state = "shuttle" - /area/shuttle/escape_pod6 name = "\improper Escape Pod Six" music = "music/escape.ogg" -/area/shuttle/escape_pod6/station - icon_state = "shuttle2" - -/area/shuttle/escape_pod6/centcom - icon_state = "shuttle" - -/area/shuttle/escape_pod6/transit - icon_state = "shuttle" - /area/shuttle/large_escape_pod1 name = "\improper Large Escape Pod One" music = "music/escape.ogg" -/area/shuttle/large_escape_pod1/station - icon_state = "shuttle2" - -/area/shuttle/large_escape_pod1/centcom - icon_state = "shuttle" - -/area/shuttle/large_escape_pod1/transit - icon_state = "shuttle" - /area/shuttle/large_escape_pod2 name = "\improper Large Escape Pod Two" music = "music/escape.ogg" -/area/shuttle/large_escape_pod2/station - icon_state = "shuttle2" - -/area/shuttle/large_escape_pod2/centcom - icon_state = "shuttle" - -/area/shuttle/large_escape_pod2/transit - icon_state = "shuttle" - /area/shuttle/cryo name = "\improper Cryogenic Storage" -/area/shuttle/cryo/station - icon_state = "shuttle2" - base_turf = /turf/simulated/mineral/floor/ignore_mapgen - -/area/shuttle/cryo/centcom - icon_state = "shuttle" - -/area/shuttle/cryo/transit - icon_state = "shuttle" - /area/shuttle/mining name = "\improper Mining Elevator" music = "music/escape.ogg" dynamic_lighting = 0 base_turf = /turf/simulated/mineral/floor/ignore_mapgen -/area/shuttle/mining/station - icon_state = "shuttle2" - -/area/shuttle/mining/outpost - icon_state = "shuttle" - /area/shuttle/transport1/centcom icon_state = "shuttle" name = "\improper Transport Shuttle CentCom" @@ -298,43 +193,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/shuttle/thunderdome name = "honk" -/area/shuttle/thunderdome/grnshuttle - name = "\improper Thunderdome GRN Shuttle" - icon_state = "green" - -/area/shuttle/thunderdome/grnshuttle/dome - name = "\improper GRN Shuttle" - icon_state = "shuttlegrn" - -/area/shuttle/thunderdome/grnshuttle/station - name = "\improper GRN Station" - icon_state = "shuttlegrn2" - -/area/shuttle/thunderdome/redshuttle - name = "\improper Thunderdome RED Shuttle" - icon_state = "red" - -/area/shuttle/thunderdome/redshuttle/dome - name = "\improper RED Shuttle" - icon_state = "shuttlered" - -/area/shuttle/thunderdome/redshuttle/station - name = "\improper RED Station" - icon_state = "shuttlered2" -// === Trying to remove these areas: - /area/shuttle/research name = "\improper Research Elevator" music = "music/escape.ogg" dynamic_lighting = 0 base_turf = /turf/simulated/mineral/floor/ignore_mapgen -/area/shuttle/research/station - icon_state = "shuttle2" - -/area/shuttle/research/outpost - icon_state = "shuttle" - /area/airtunnel1/ // referenced in airtunnel.dm:759 /area/dummy/ // Referenced in engine.dm:261 @@ -953,6 +817,9 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "\improper Construction Area" icon_state = "construction" +/area/hallway/secondary/entry + forbid_events = TRUE + /area/hallway/secondary/entry/fore name = "\improper Shuttle Dock Hallway - Mid" icon_state = "entry_1" @@ -1125,8 +992,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/crew_quarters name = "\improper Dormitories" icon_state = "Sleep" - flags = RAD_SHIELDED ambience = AMBIENCE_GENERIC + forbid_events = TRUE /area/crew_quarters/toilet name = "\improper Dormitory Toilets" @@ -1136,6 +1003,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/crew_quarters/sleep name = "\improper Dormitories" icon_state = "Sleep" + flags = RAD_SHIELDED /area/crew_quarters/sleep/Apartment_A1 name = "\improper Apartment A1" @@ -1427,6 +1295,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station icon_state = "Holodeck" dynamic_lighting = 0 sound_env = LARGE_ENCLOSED + forbid_events = TRUE /area/holodeck/alphadeck name = "\improper Holodeck Alpha" @@ -1526,6 +1395,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "\improper Engine Room" icon_state = "engine" sound_env = LARGE_ENCLOSED + forbid_events = TRUE /area/engineering/engine_airlock name = "\improper Engine Room Airlock" @@ -2041,17 +1911,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station name = "\improper Cargo Mining Dock" icon_state = "mining" -/area/supply/station - name = "Supply Shuttle" - icon_state = "shuttle3" - requires_power = 0 - base_turf = /turf/space - -/area/supply/dock - name = "Supply Shuttle" - icon_state = "shuttle3" - requires_power = 0 - base_turf = /turf/space // SCIENCE diff --git a/code/game/area/Space Station 13 areas_vr.dm b/code/game/area/Space Station 13 areas_vr.dm index c431d7a46c..657568479e 100644 --- a/code/game/area/Space Station 13 areas_vr.dm +++ b/code/game/area/Space Station 13 areas_vr.dm @@ -1,23 +1,8 @@ //TFF 28/8/19 - cleanup of areas placement - removes all but rogueminer_vr stuff. -/area/shuttle/belter/station - name = "Belter Shuttle Landed" +/area/shuttle/belter + name = "Belter Shuttle" icon_state = "shuttle2" - base_turf = /turf/simulated/floor/tiled/asteroid_steel/airless - -/area/shuttle/belter/belt //Don't use this one, use a subtype - name = "Belter Shuttle Holding Position" - icon_state = "shuttle2" - -//Placeholders -/area/shuttle/belter/belt/zone1 -/area/shuttle/belter/belt/zone2 -/area/shuttle/belter/belt/zone3 -/area/shuttle/belter/belt/zone4 - -/area/shuttle/belter/transit - name = "Belter Shuttle Warp" - icon_state = "shuttle" //Rouguelike Mining /area/asteroid/rogue @@ -28,22 +13,22 @@ /area/asteroid/rogue/zone1 name = "Asteroid Belt Zone 1" icon_state = "red2" - shuttle_area = /area/shuttle/belter/belt/zone1 + //shuttle_area = /area/shuttle/belter/belt/zone1 /area/asteroid/rogue/zone2 name = "Asteroid Belt Zone 2" icon_state = "blue2" - shuttle_area = /area/shuttle/belter/belt/zone2 + //shuttle_area = /area/shuttle/belter/belt/zone2 /area/asteroid/rogue/zone3 name = "Asteroid Belt Zone 3" icon_state = "blue2" - shuttle_area = /area/shuttle/belter/belt/zone3 + //shuttle_area = /area/shuttle/belter/belt/zone3 /area/asteroid/rogue/zone4 name = "Asteroid Belt Zone 4" icon_state = "red2" - shuttle_area = /area/shuttle/belter/belt/zone4 + //shuttle_area = /area/shuttle/belter/belt/zone4 /area/medical/resleeving name = "Resleeving Lab" diff --git a/code/game/area/Space Station 13 areas_yw.dm b/code/game/area/Space Station 13 areas_yw.dm index 8431698765..bfe81db226 100644 --- a/code/game/area/Space Station 13 areas_yw.dm +++ b/code/game/area/Space Station 13 areas_yw.dm @@ -166,6 +166,7 @@ /area/security/outpost name = "\improper Security outpost" icon_state = "security" + flags = RAD_SHIELDED /area/security/labor name = "Labor camp access" @@ -175,3 +176,6 @@ icon_state = "medbay" music = 'sound/ambience/signal.ogg' +/area/security/shuttledock + name = "\improper Security Interceptor Dock" + icon_state = "security" diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index dbe6d45c30..6c7cb1597b 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -30,6 +30,7 @@ var/used_environ = 0 var/has_gravity = 1 + var/secret_name = FALSE // This tells certain things that display areas' names that they shouldn't display this area's name. var/obj/machinery/power/apc/apc = null var/no_air = null // var/list/lights // list of all lights on this area @@ -39,28 +40,14 @@ var/list/forced_ambience = null var/sound_env = STANDARD_STATION var/turf/base_turf //The base turf type of the area, which can be used to override the z-level's base turf - var/global/global_uid = 0 - var/uid - -/area/New() - icon_state = "" - uid = ++global_uid - all_areas += src - - if(!requires_power) - power_light = 0 - power_equip = 0 - power_environ = 0 - - if(dynamic_lighting) - luminosity = 0 - else - luminosity = 1 - - ..() + var/forbid_events = FALSE // If true, random events will not start inside this area. /area/Initialize() . = ..() + + luminosity = !(dynamic_lighting) + icon_state = "" + return INITIALIZE_HINT_LATELOAD // Areas tradiationally are initialized AFTER other atoms. /area/LateInitialize() @@ -71,6 +58,31 @@ power_change() // all machines set to current power level, also updates lighting icon return INITIALIZE_HINT_LATELOAD +// Changes the area of T to A. Do not do this manually. +// Area is expected to be a non-null instance. +/proc/ChangeArea(var/turf/T, var/area/A) + if(!istype(A)) + CRASH("Area change attempt failed: invalid area supplied.") + var/area/old_area = get_area(T) + if(old_area == A) + return + // NOTE: BayStation calles area.Exited/Entered for the TURF T. So far we don't do that.s + // NOTE: There probably won't be any atoms in these turfs, but just in case we should call these procs. + A.contents.Add(T) + if(old_area) + // Handle dynamic lighting update if + if(T.dynamic_lighting && old_area.dynamic_lighting != A.dynamic_lighting) + if(A.dynamic_lighting) + T.lighting_build_overlay() + else + T.lighting_clear_overlay() + for(var/atom/movable/AM in T) + old_area.Exited(AM, A) + for(var/atom/movable/AM in T) + A.Entered(AM, old_area) + for(var/obj/machinery/M in T) + M.power_change() + /area/proc/get_contents() return contents @@ -310,10 +322,10 @@ var/list/mob/living/forced_ambiance_list = new L << sound(sound, repeat = 0, wait = 0, volume = 50, channel = CHANNEL_AMBIENCE) L.client.time_last_ambience_played = world.time -/area/proc/gravitychange(var/gravitystate = 0, var/area/A) - A.has_gravity = gravitystate +/area/proc/gravitychange(var/gravitystate = 0) + src.has_gravity = gravitystate - for(var/mob/M in A) + for(var/mob/M in src) if(has_gravity) thunk(M) M.update_floating( M.Check_Dense_Object() ) @@ -335,7 +347,7 @@ var/list/mob/living/forced_ambiance_list = new else H.AdjustStunned(3) H.AdjustWeakened(3) - mob << "The sudden appearance of gravity makes you fall to the floor!" + to_chat(mob, "The sudden appearance of gravity makes you fall to the floor!") playsound(get_turf(src), "bodyfall", 50, 1) /area/proc/prison_break() @@ -347,6 +359,8 @@ var/list/mob/living/forced_ambiance_list = new temp_airlock.prison_open() for(var/obj/machinery/door/window/temp_windoor in src) temp_windoor.open() + for(var/obj/machinery/door/blast/temp_blast in src) + temp_blast.open() /area/has_gravity() return has_gravity @@ -379,7 +393,7 @@ var/list/mob/living/forced_ambiance_list = new var/list/teleportlocs = list() /hook/startup/proc/setupTeleportLocs() - for(var/area/AR in all_areas) + for(var/area/AR in world) if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue if(teleportlocs.Find(AR.name)) continue var/turf/picked = pick(get_area_turfs(AR.type)) @@ -394,7 +408,7 @@ var/list/teleportlocs = list() var/list/ghostteleportlocs = list() /hook/startup/proc/setupGhostTeleportLocs() - for(var/area/AR in all_areas) + for(var/area/AR in world) if(ghostteleportlocs.Find(AR.name)) continue if(istype(AR, /area/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome) || istype(AR, /area/shuttle/specops/centcom)) ghostteleportlocs += AR.name @@ -407,3 +421,8 @@ var/list/ghostteleportlocs = list() ghostteleportlocs = sortAssoc(ghostteleportlocs) return 1 + +/area/proc/get_name() + if(secret_name) + return "Unknown Area" + return name \ No newline at end of file diff --git a/code/game/area/ss13_deprecated_areas.dm b/code/game/area/ss13_deprecated_areas.dm new file mode 100644 index 0000000000..b3b689f23d --- /dev/null +++ b/code/game/area/ss13_deprecated_areas.dm @@ -0,0 +1,164 @@ +// +// Shuttles formerly required at least two areas in a subgroup if you want to move a shuttle from one +// place to another. Since shuttles now used landmarks instead these areas are deprecated! +// They are left here for the moment in order to make existing maps loadable, but should be phased out. +// + +/area/shuttle/arrival/pre_game + icon_state = "shuttle2" + +/area/shuttle/arrival/station + icon_state = "shuttle" + dynamic_lighting = 0 + ambience = AMBIENCE_ARRIVALS + +/area/shuttle/escape/station + name = "\improper Emergency Shuttle Station" + icon_state = "shuttle2" + dynamic_lighting = 0 + +/area/shuttle/escape/centcom + name = "\improper Emergency Shuttle CentCom" + icon_state = "shuttle" + +/area/shuttle/escape/transit // the area to pass through for 3 minute transit + name = "\improper Emergency Shuttle Transit" + icon_state = "shuttle" + +/area/shuttle/escape_pod1/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod1/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod1/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod2/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod2/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod2/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod3/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod3/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod3/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod4/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod4/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod4/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod5/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod5/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod5/transit + icon_state = "shuttle" + +/area/shuttle/escape_pod6/station + icon_state = "shuttle2" + +/area/shuttle/escape_pod6/centcom + icon_state = "shuttle" + +/area/shuttle/escape_pod6/transit + icon_state = "shuttle" + +/area/shuttle/large_escape_pod1/station + icon_state = "shuttle2" + +/area/shuttle/large_escape_pod1/centcom + icon_state = "shuttle" + +/area/shuttle/large_escape_pod1/transit + icon_state = "shuttle" + +/area/shuttle/large_escape_pod2/station + icon_state = "shuttle2" + +/area/shuttle/large_escape_pod2/centcom + icon_state = "shuttle" + +/area/shuttle/large_escape_pod2/transit + icon_state = "shuttle" + +/area/shuttle/cryo/station + icon_state = "shuttle2" + base_turf = /turf/simulated/mineral/floor/ignore_mapgen + +/area/shuttle/cryo/centcom + icon_state = "shuttle" + +/area/shuttle/cryo/transit + icon_state = "shuttle" + +/area/shuttle/mining/station + icon_state = "shuttle2" + +/area/shuttle/mining/outpost + icon_state = "shuttle" + +/area/shuttle/trade/centcom + name = "\improper Trade Shuttle CentCom" + icon_state = "shuttlered" + +/area/shuttle/trade/station + name = "\improper Trade Shuttle" + icon_state = "shuttlered" + +/area/shuttle/thunderdome/grnshuttle + name = "\improper Thunderdome GRN Shuttle" + icon_state = "green" + +/area/shuttle/thunderdome/grnshuttle/dome + name = "\improper GRN Shuttle" + icon_state = "shuttlegrn" + +/area/shuttle/thunderdome/grnshuttle/station + name = "\improper GRN Station" + icon_state = "shuttlegrn2" + +/area/shuttle/thunderdome/redshuttle + name = "\improper Thunderdome RED Shuttle" + icon_state = "red" + +/area/shuttle/thunderdome/redshuttle/dome + name = "\improper RED Shuttle" + icon_state = "shuttlered" + +/area/shuttle/thunderdome/redshuttle/station + name = "\improper RED Station" + icon_state = "shuttlered2" + +/area/shuttle/research/station + icon_state = "shuttle2" + +/area/shuttle/research/outpost + icon_state = "shuttle" + +/area/supply/station + name = "Supply Shuttle" + icon_state = "shuttle3" + requires_power = 0 + base_turf = /turf/space + +/area/supply/dock + name = "Supply Shuttle" + icon_state = "shuttle3" + requires_power = 0 + base_turf = /turf/space diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 631ed4df19..26d8506bf9 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -41,8 +41,7 @@ _preloader.load(src) // Pass our arguments to InitAtom so they can be passed to initialize(), but replace 1st with if-we're-during-mapload. - var/do_initialize = SSatoms && SSatoms.initialized // Workaround our non-ideal initialization order: SSatoms may not exist yet. - //var/do_initialize = SSatoms.initialized + var/do_initialize = SSatoms.initialized if(do_initialize > INITIALIZATION_INSSATOMS) args[1] = (do_initialize == INITIALIZATION_INNEW_MAPLOAD) if(SSatoms.InitAtom(src, args)) @@ -183,8 +182,14 @@ else f_name += "oil-stained [name][infix]." - user << "\icon[src] That's [f_name] [suffix]" - user << desc + to_chat(user, "[bicon(src)] That's [f_name] [suffix]") + to_chat(user,desc) + + if(user.client?.examine_text_mode == EXAMINE_MODE_INCLUDE_USAGE) + to_chat(user, description_info) + + if(user.client?.examine_text_mode == EXAMINE_MODE_SWITCH_TO_PANEL) + user.client.statpanel = "Examine" // Switch to stat panel return distance == -1 || (get_dist(src, user) <= distance) @@ -198,6 +203,20 @@ . = new_dir != dir dir = new_dir +// Called to set the atom's density and used to add behavior to density changes. +/atom/proc/set_density(var/new_density) + if(density == new_density) + return FALSE + density = !!new_density // Sanitize to be strictly 0 or 1 + return TRUE + +// Called to set the atom's invisibility and usd to add behavior to invisibility changes. +/atom/proc/set_invisibility(var/new_invisibility) + if(invisibility == new_invisibility) + return FALSE + invisibility = new_invisibility + return TRUE + /atom/proc/ex_act() return @@ -427,7 +446,7 @@ cur_y = y_arr.Find(src.z) if(cur_y) break -// world << "X = [cur_x]; Y = [cur_y]" +// to_world("X = [cur_x]; Y = [cur_y]") if(cur_x && cur_y) return list("x"=cur_x,"y"=cur_y) else @@ -519,6 +538,9 @@ return TRUE return FALSE +/atom/proc/is_incorporeal() + return FALSE + /atom/proc/drop_location() var/atom/L = loc if(!L) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index c8f0b44c3d..fa0c99dcb6 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -24,6 +24,9 @@ var/datum/riding/riding_datum //VOREStation Add - Moved from /obj/vehicle var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P) var/movement_type = NONE + + var/cloaked = FALSE //If we're cloaked or not + var/image/cloaked_selfimage //The image we use for our client to let them see where we are /atom/movable/Destroy() . = ..() @@ -46,7 +49,7 @@ QDEL_NULL(riding_datum) //VOREStation Add /atom/movable/vv_edit_var(var_name, var_value) - if(GLOB.VVpixelmovement[var_name]) //Pixel movement is not yet implemented, changing this will break everything irreversibly. + if(var_name in GLOB.VVpixelmovement) //Pixel movement is not yet implemented, changing this will break everything irreversibly. return FALSE return ..() @@ -453,36 +456,39 @@ if(z in using_map.sealed_levels) return - if(config.use_overmap) + if(using_map.use_overmap) overmap_spacetravel(get_turf(src), src) return var/move_to_z = src.get_transit_zlevel() if(move_to_z) - z = move_to_z + var/new_z = move_to_z + var/new_x + var/new_y if(x <= TRANSITIONEDGE) - x = world.maxx - TRANSITIONEDGE - 2 - y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + new_x = world.maxx - TRANSITIONEDGE - 2 + new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) else if (x >= (world.maxx - TRANSITIONEDGE + 1)) - x = TRANSITIONEDGE + 1 - y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + new_x = TRANSITIONEDGE + 1 + new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) else if (y <= TRANSITIONEDGE) - y = world.maxy - TRANSITIONEDGE -2 - x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + new_y = world.maxy - TRANSITIONEDGE -2 + new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) else if (y >= (world.maxy - TRANSITIONEDGE + 1)) - y = TRANSITIONEDGE + 1 - x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + new_y = TRANSITIONEDGE + 1 + new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) if(ticker && istype(ticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear var/datum/game_mode/nuclear/G = ticker.mode G.check_nuke_disks() - spawn(0) - if(loc) loc.Entered(src) + var/turf/T = locate(new_x, new_y, new_z) + if(istype(T)) + forceMove(T) //by default, transition randomly to another zlevel /atom/movable/proc/get_transit_zlevel() @@ -516,3 +522,94 @@ // Called when touching a lava tile. /atom/movable/proc/lava_act() fire_act(null, 10000, 1000) + + +// Procs to cloak/uncloak +/atom/movable/proc/cloak() + if(cloaked) + return FALSE + cloaked = TRUE + . = TRUE // We did work + + var/static/animation_time = 1 SECOND + cloaked_selfimage = get_cloaked_selfimage() + + //Wheeee + cloak_animation(animation_time) + + //Needs to be last so people can actually see the effect before we become invisible + if(cloaked) // Ensure we are still cloaked after the animation delay + plane = CLOAKED_PLANE + +/atom/movable/proc/uncloak() + if(!cloaked) + return FALSE + cloaked = FALSE + . = TRUE // We did work + + var/static/animation_time = 1 SECOND + QDEL_NULL(cloaked_selfimage) + + //Needs to be first so people can actually see the effect, so become uninvisible first + plane = initial(plane) + + //Oooooo + uncloak_animation(animation_time) + + +// Animations for cloaking/uncloaking +/atom/movable/proc/cloak_animation(var/length = 1 SECOND) + //Save these + var/initial_alpha = alpha + + //Animate alpha fade + animate(src, alpha = 0, time = length) + + //Animate a cloaking effect + var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. + filters += filter(type="wave", x = 0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) + animate(filters[our_filter], offset = 1, size = 8, time = length, flags = ANIMATION_PARALLEL) + + //Wait for animations to finish + sleep(length+5) + + //Remove those + filters -= filter(type="wave", x = 0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) + + //Back to original alpha + alpha = initial_alpha + +/atom/movable/proc/uncloak_animation(var/length = 1 SECOND) + //Save these + var/initial_alpha = alpha + + //Put us back to normal, but no alpha + alpha = 0 + + //Animate alpha fade up + animate(src, alpha = initial_alpha, time = length) + + //Animate a cloaking effect + var/our_filter = filters.len+1 //Filters don't appear to have a type that can be stored in a var and accessed. This is how the DM reference does it. + filters += filter(type="wave", x=0, y = 16, size = 8, offset = 1, flags = WAVE_SIDEWAYS) + animate(filters[our_filter], offset = 0, size = 0, time = length, flags = ANIMATION_PARALLEL) + + //Wait for animations to finish + sleep(length+5) + + //Remove those + filters -= filter(type="wave", x=0, y = 16, size = 0, offset = 0, flags = WAVE_SIDEWAYS) + + +// So cloaked things can see themselves, if necessary +/atom/movable/proc/get_cloaked_selfimage() + var/icon/selficon = icon(icon, icon_state) + selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White + var/image/selfimage = image(selficon) + selfimage.color = "#0000FF" + selfimage.alpha = 100 + selfimage.layer = initial(layer) + selfimage.plane = initial(plane) + selfimage.loc = src + + return selfimage diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index daa87810fd..6e243e3232 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -51,10 +51,13 @@ #define DNA_UI_WING_STYLE 32 #define DNA_UI_WING_R 33 #define DNA_UI_WING_G 34 -#define DNA_UI_WING_B 35 // VOREStation snippet end. -#define DNA_UI_LENGTH 35 // VOREStation Edit to 35 +#define DNA_UI_WING_B 35 +#define DNA_UI_WING2_R 36 +#define DNA_UI_WING2_G 37 +#define DNA_UI_WING2_B 38 // VOREStation snippet end. +#define DNA_UI_LENGTH 38 // VOREStation Edit - Needs to match the highest number above. -#define DNA_SE_LENGTH 46 // VOREStation Edit (original was UI+11) +#define DNA_SE_LENGTH 49 // VOREStation Edit (original was UI+11) // For later: //#define DNA_SE_LENGTH 50 // Was STRUCDNASIZE, size 27. 15 new blocks added = 42, plus room to grow. @@ -230,6 +233,10 @@ var/global/list/datum/dna/gene/dna_genes[0] SetUIValueRange(DNA_UI_WING_G, character.g_wing, 255, 1) SetUIValueRange(DNA_UI_WING_B, character.b_wing, 255, 1) + SetUIValueRange(DNA_UI_WING2_R, character.r_wing2, 255, 1) + SetUIValueRange(DNA_UI_WING2_G, character.g_wing2, 255, 1) + SetUIValueRange(DNA_UI_WING2_B, character.b_wing2, 255, 1) + SetUIValueRange(DNA_UI_EARS_R, character.r_ears, 255, 1) SetUIValueRange(DNA_UI_EARS_G, character.g_ears, 255, 1) SetUIValueRange(DNA_UI_EARS_B, character.b_ears, 255, 1) diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index 1fe5d43a59..c79a0e94be 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -46,7 +46,7 @@ icon_state = "scanner_0" density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 active_power_usage = 300 interact_offline = 1 @@ -108,13 +108,13 @@ if (usr.stat != 0) return if (!ishuman(usr) && !issmall(usr)) //Make sure they're a mob that has dna - usr << "Try as you might, you can not climb up into the scanner." + to_chat(usr, "Try as you might, you can not climb up into the scanner.") return if (src.occupant) - usr << "The scanner is already occupied!" + to_chat(usr, "The scanner is already occupied!") return if (usr.abiotic()) - usr << "The subject cannot have abiotic items on." + to_chat(usr, "The subject cannot have abiotic items on.") return usr.stop_pulling() usr.client.perspective = EYE_PERSPECTIVE @@ -150,7 +150,7 @@ user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!") return else - to_chat(user,"\The [brain] is not acceptable for genetic sampling!") + to_chat(user, "\The [brain] is not acceptable for genetic sampling!") else if (!istype(item, /obj/item/weapon/grab)) return @@ -260,7 +260,7 @@ var/obj/item/weapon/disk/data/disk = null var/selected_menu_key = null anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 400 var/waiting_for_user_input=0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X @@ -271,7 +271,7 @@ user.drop_item() I.loc = src src.disk = I - user << "You insert [I]." + to_chat(user, "You insert [I].") SSnanoui.update_uis(src) // update all UIs attached to src return else @@ -293,19 +293,15 @@ else return -/obj/machinery/computer/scan_consolenew/New() - ..() +/obj/machinery/computer/scan_consolenew/Initialize() + . = ..() for(var/i=0;i<3;i++) buffers[i+1]=new /datum/dna2/record - spawn(5) - for(dir in list(NORTH,EAST,SOUTH,WEST)) - connected = locate(/obj/machinery/dna_scannernew, get_step(src, dir)) - if(!isnull(connected)) - break - spawn(250) - src.injector_ready = 1 - return - return + for(dir in list(NORTH,EAST,SOUTH,WEST)) + connected = locate(/obj/machinery/dna_scannernew, get_step(src, dir)) + if(connected) + break + VARSET_IN(src, injector_ready, TRUE, 25 SECONDS) /obj/machinery/computer/scan_consolenew/proc/all_dna_blocks(var/list/buffer) var/list/arr = list() diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm index 72a571abf8..f3506cba7f 100644 --- a/code/game/dna/genes/disabilities.dm +++ b/code/game/dna/genes/disabilities.dm @@ -35,7 +35,7 @@ if(sdisability) M.sdisabilities|=sdisability if(activation_message) - M << "[activation_message]" + to_chat(M, "[activation_message]") else testing("[name] has no activation message.") @@ -47,7 +47,7 @@ if(sdisability) M.sdisabilities &= (~sdisability) if(deactivation_message) - M << "[deactivation_message]" + to_chat(M, "[deactivation_message]") else testing("[name] has no deactivation message.") diff --git a/code/game/dna/genes/gene.dm b/code/game/dna/genes/gene.dm index 3e18696f6b..e52cd4e6c0 100644 --- a/code/game/dna/genes/gene.dm +++ b/code/game/dna/genes/gene.dm @@ -113,10 +113,10 @@ M.mutations.Add(mutation) if(activation_messages.len) var/msg = pick(activation_messages) - M << "[msg]" + to_chat(M, "[msg]") /datum/dna/gene/basic/deactivate(var/mob/M) M.mutations.Remove(mutation) if(deactivation_messages.len) var/msg = pick(deactivation_messages) - M << "[msg]" + to_chat(M, "[msg]") diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 45e852551d..0089355ed3 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -69,7 +69,7 @@ O.take_overall_damage(M.getBruteLoss() + 40, M.getFireLoss()) O.adjustToxLoss(M.getToxLoss() + 20) O.adjustOxyLoss(M.getOxyLoss()) - O.stat = M.stat + O.set_stat(M.stat) O.a_intent = I_HURT for (var/obj/item/weapon/implant/I in implants) I.loc = O @@ -154,7 +154,7 @@ O.take_overall_damage(M.getBruteLoss(), M.getFireLoss()) O.adjustToxLoss(M.getToxLoss()) O.adjustOxyLoss(M.getOxyLoss()) - O.stat = M.stat + O.set_stat(M.stat) for (var/obj/item/weapon/implant/I in implants) I.loc = O I.implanted = O diff --git a/code/game/dna/genes/powers.dm b/code/game/dna/genes/powers.dm index e6c6b6f4ec..0693ffdd08 100644 --- a/code/game/dna/genes/powers.dm +++ b/code/game/dna/genes/powers.dm @@ -171,7 +171,7 @@ if(M.health <= 25) M.mutations.Remove(HULK) M.update_mutations() //update our mutation overlays - M << "You suddenly feel very weak." + to_chat(M, "You suddenly feel very weak.") M.Weaken(3) M.emote("collapse") diff --git a/code/game/gamemodes/calamity/calamity.dm b/code/game/gamemodes/calamity/calamity.dm index 4f7b9ab49d..0e1e577b9c 100644 --- a/code/game/gamemodes/calamity/calamity.dm +++ b/code/game/gamemodes/calamity/calamity.dm @@ -23,6 +23,6 @@ ..() /datum/game_mode/calamity/check_victory() - world << "This terrible, terrible day has finally ended!" + to_world("This terrible, terrible day has finally ended!") #undef ANTAG_TYPE_RATIO \ No newline at end of file diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm index 373aefa0d8..7f963710a1 100644 --- a/code/game/gamemodes/changeling/changeling_powers.dm +++ b/code/game/gamemodes/changeling/changeling_powers.dm @@ -125,7 +125,7 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E var/datum/changeling/changeling = src.mind.changeling if(!changeling) - world.log << "[src] has the changeling_transform() verb but is not a changeling." + to_world_log("[src] has the changeling_transform() verb but is not a changeling.") return if(src.stat > max_stat) @@ -238,5 +238,5 @@ turf/proc/AdjacentTurfsRangedSting() to_chat(src, "We stealthily sting [T].") if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting - T << "You feel a tiny prick." + to_chat(T, "You feel a tiny prick.") return diff --git a/code/game/gamemodes/changeling/generic_equip_procs.dm b/code/game/gamemodes/changeling/generic_equip_procs.dm index f2d7b45831..fa7ffc3d18 100644 --- a/code/game/gamemodes/changeling/generic_equip_procs.dm +++ b/code/game/gamemodes/changeling/generic_equip_procs.dm @@ -127,7 +127,7 @@ else - M << "We begin growing our new equipment..." + to_chat(M, "We begin growing our new equipment...") var/list/grown_items_list = list() @@ -223,7 +223,7 @@ var/feedback = english_list(grown_items_list, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - M << "We have grown [feedback]." + to_chat(M, "We have grown [feedback].") if(success) M.mind.changeling.armor_deployed = 1 diff --git a/code/game/gamemodes/changeling/modularchangling.dm b/code/game/gamemodes/changeling/modularchangling.dm index 41f66fb4ec..c6af89ad75 100644 --- a/code/game/gamemodes/changeling/modularchangling.dm +++ b/code/game/gamemodes/changeling/modularchangling.dm @@ -316,23 +316,23 @@ var/list/datum/power/changeling/powerinstances = list() for (var/datum/power/changeling/P in powerinstances) - //world << "[P] - [Pname] = [P.name == Pname ? "True" : "False"]" + //to_world("[P] - [Pname] = [P.name == Pname ? "True" : "False"]") if(P.name == Pname) Thepower = P break if(Thepower == null) - M.current << "This is awkward. Changeling power purchase failed, please report this bug to a coder!" + to_chat(M.current, "This is awkward. Changeling power purchase failed, please report this bug to a coder!") return if(Thepower in purchased_powers) - M.current << "We have already evolved this ability!" + to_chat(M.current, "We have already evolved this ability!") return if(geneticpoints < Thepower.genomecost) - M.current << "We cannot evolve this... yet. We must acquire more DNA." + to_chat(M.current, "We cannot evolve this... yet. We must acquire more DNA.") return geneticpoints -= Thepower.genomecost diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm index 02285a0c7e..a76311c335 100644 --- a/code/game/gamemodes/changeling/powers/absorb.dm +++ b/code/game/gamemodes/changeling/powers/absorb.dm @@ -52,7 +52,7 @@ if(3) to_chat(src, "We stab [T] with the proboscis.") src.visible_message("[src] stabs [T] with the proboscis!") - T << "You feel a sharp stabbing pain!" + to_chat(T, "You feel a sharp stabbing pain!") add_attack_logs(src,T,"Absorbed (changeling)") var/obj/item/organ/external/affecting = T.get_organ(src.zone_sel.selecting) if(affecting.take_damage(39,0,1,0,"large organic needle")) @@ -66,7 +66,7 @@ to_chat(src, "We have absorbed [T]!") src.visible_message("[src] sucks the fluids from [T]!") - T << "You have been absorbed by the changeling!" + to_chat(T, "You have been absorbed by the changeling!") if(src.nutrition < 400) src.nutrition = min((src.nutrition + T.nutrition), 400) changeling.chem_charges += 10 diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/game/gamemodes/changeling/powers/armblade.dm index c05ad96e81..664e6191ae 100644 --- a/code/game/gamemodes/changeling/powers/armblade.dm +++ b/code/game/gamemodes/changeling/powers/armblade.dm @@ -90,7 +90,7 @@ /obj/item/weapon/melee/changeling/suicide_act(mob/user) var/datum/gender/T = gender_datums[user.get_visible_gender()] - viewers(user) << "[user] is impaling [T.himself] with the [src.name]! It looks like [T.he] [T.is] trying to commit suicide." + user.visible_message("[user] is impaling [T.himself] with the [src.name]! It looks like [T.he] [T.is] trying to commit suicide.") return(BRUTELOSS) /obj/item/weapon/melee/changeling/process() //Stolen from ninja swords. diff --git a/code/game/gamemodes/changeling/powers/armor.dm b/code/game/gamemodes/changeling/powers/armor.dm index bee7e7518a..b5e72d83ea 100644 --- a/code/game/gamemodes/changeling/powers/armor.dm +++ b/code/game/gamemodes/changeling/powers/armor.dm @@ -81,13 +81,13 @@ magpulse = 0 set_slowdown() force = 3 - user << "We release our grip on the floor." + to_chat(user, "We release our grip on the floor.") else item_flags |= NOSLIP magpulse = 1 set_slowdown() force = 5 - user << "We cling to the terrain below us." + to_chat(user, "We cling to the terrain below us.") /obj/item/clothing/shoes/magboots/changeling/dropped() ..() diff --git a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm index c23e6721b0..e4eb45a9e9 100644 --- a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm +++ b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm @@ -24,13 +24,13 @@ active = !active if(active) - C << "We feel a minute twitch in our eyes, and darkness creeps away." + to_chat(C, "We feel a minute twitch in our eyes, and darkness creeps away.") C.sight |= SEE_MOBS C.permanent_sight_flags |= SEE_MOBS C.see_in_dark = 8 C.dna.species.invis_sight = SEE_INVISIBLE_MINIMUM else - C << "Our vision dulls. Shadows gather." + to_chat(C, "Our vision dulls. Shadows gather.") C.sight &= ~SEE_MOBS C.permanent_sight_flags &= ~SEE_MOBS C.see_in_dark = 0 diff --git a/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm b/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm index 192f8b3874..ca92d67ff7 100644 --- a/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm +++ b/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm @@ -173,7 +173,7 @@ visible_message("Arcs of electricity strike [S]!", "Our hand channels raw electricity into [S]", "You hear sparks!") - S << "Warning: Electrical surge detected!" + to_chat(S, "Warning: Electrical surge detected!") //qdel(src) user.mind.changeling.chem_charges -= 10 return 1 diff --git a/code/game/gamemodes/changeling/powers/blind_sting.dm b/code/game/gamemodes/changeling/powers/blind_sting.dm index e2fede649e..f4721db660 100644 --- a/code/game/gamemodes/changeling/powers/blind_sting.dm +++ b/code/game/gamemodes/changeling/powers/blind_sting.dm @@ -16,7 +16,7 @@ if(!T) return 0 add_attack_logs(src,T,"Blind sting (changeling)") - T << "Your eyes burn horrificly!" + to_chat(T, "Your eyes burn horrificly!") T.disabilities |= NEARSIGHTED var/duration = 300 if(src.mind.changeling.recursive_enhancement) diff --git a/code/game/gamemodes/changeling/powers/deaf_sting.dm b/code/game/gamemodes/changeling/powers/deaf_sting.dm index 92e7a80a30..7b39e90720 100644 --- a/code/game/gamemodes/changeling/powers/deaf_sting.dm +++ b/code/game/gamemodes/changeling/powers/deaf_sting.dm @@ -20,7 +20,7 @@ if(src.mind.changeling.recursive_enhancement) duration = duration + 100 to_chat(src, "They will be unable to hear for a little longer.") - T << "Your ears pop and begin ringing loudly!" + to_chat(T, "Your ears pop and begin ringing loudly!") T.sdisabilities |= DEAF spawn(duration) T.sdisabilities &= ~DEAF feedback_add_details("changeling_powers","DS") diff --git a/code/game/gamemodes/changeling/powers/death_sting.dm b/code/game/gamemodes/changeling/powers/death_sting.dm index 6dd0cdbf85..2eb1e476bd 100644 --- a/code/game/gamemodes/changeling/powers/death_sting.dm +++ b/code/game/gamemodes/changeling/powers/death_sting.dm @@ -14,7 +14,7 @@ if(!T) return 0 add_attack_logs(src,T,"Death sting (changeling)") - T << "You feel a small prick and your chest becomes tight." + to_chat(T, "You feel a small prick and your chest becomes tight.") T.silent = 10 T.Paralyse(10) T.make_jittery(100) diff --git a/code/game/gamemodes/changeling/powers/digital_camo.dm b/code/game/gamemodes/changeling/powers/digital_camo.dm index 0dbee6b09d..9574b465da 100644 --- a/code/game/gamemodes/changeling/powers/digital_camo.dm +++ b/code/game/gamemodes/changeling/powers/digital_camo.dm @@ -19,9 +19,9 @@ var/mob/living/carbon/human/C = src if(C.digitalcamo) - C << "We return to normal." + to_chat(C, "We return to normal.") else - C << "We distort our form to prevent AI-tracking." + to_chat(C, "We distort our form to prevent AI-tracking.") C.digitalcamo = !C.digitalcamo spawn(0) diff --git a/code/game/gamemodes/changeling/powers/electric_lockpick.dm b/code/game/gamemodes/changeling/powers/electric_lockpick.dm index 29ad6573f6..262d90429f 100644 --- a/code/game/gamemodes/changeling/powers/electric_lockpick.dm +++ b/code/game/gamemodes/changeling/powers/electric_lockpick.dm @@ -34,10 +34,10 @@ /obj/item/weapon/finger_lockpick/New() if(ismob(loc)) - loc << "We shape our finger to fit inside electronics, and are ready to force them open." + to_chat(loc, "We shape our finger to fit inside electronics, and are ready to force them open.") /obj/item/weapon/finger_lockpick/dropped(mob/user) - user << "We discreetly shape our finger back to a less suspicious form." + to_chat(user, "We discreetly shape our finger back to a less suspicious form.") spawn(1) if(src) qdel(src) @@ -53,13 +53,13 @@ var/datum/changeling/ling_datum = user.mind.changeling if(ling_datum.chem_charges < 10) - user << "We require more chemicals to do that." + to_chat(user, "We require more chemicals to do that.") return //Airlocks require an ugly block of code, but we don't want to just call emag_act(), since we don't want to break airlocks forever. if(istype(target,/obj/machinery/door)) var/obj/machinery/door/door = target - user << "We send an electrical pulse up our finger, and into \the [target], attempting to open it." + to_chat(user, "We send an electrical pulse up our finger, and into \the [target], attempting to open it.") if(door.density && door.operable()) door.do_animate("spark") @@ -70,15 +70,15 @@ if(airlock.locked) //Check if we're bolted. airlock.unlock() - user << "We've unlocked \the [airlock]. Another pulse is requried to open it." + to_chat(user, "We've unlocked \the [airlock]. Another pulse is requried to open it.") else //We're not bolted, so open the door already. airlock.open() - user << "We've opened \the [airlock]." + to_chat(user, "We've opened \the [airlock].") else door.open() //If we're a windoor, open the windoor. - user << "We've opened \the [door]." + to_chat(user, "We've opened \the [door].") else //Probably broken or no power. - user << "The door does not respond to the pulse." + to_chat(user, "The door does not respond to the pulse.") door.add_fingerprint(user) log_and_message_admins("finger-lockpicked \an [door].") ling_datum.chem_charges -= 10 @@ -86,7 +86,7 @@ else if(istype(target,/obj/)) //This should catch everything else we might miss, without a million typechecks. var/obj/O = target - user << "We send an electrical pulse up our finger, and into \the [O]." + to_chat(user, "We send an electrical pulse up our finger, and into \the [O].") O.add_fingerprint(user) O.emag_act(1,user,src) log_and_message_admins("finger-lockpicked \an [O].") diff --git a/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm b/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm index 38d200cad5..5d3c1a3be9 100644 --- a/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm +++ b/code/game/gamemodes/changeling/powers/epinephrine_overdose.dm @@ -28,8 +28,8 @@ changeling.chem_charges -= 30 var/mob/living/carbon/human/C = src - C << "Energy rushes through us. [C.lying ? "We arise." : ""]" - C.stat = 0 + to_chat(C, "Energy rushes through us. [C.lying ? "We arise." : ""]") + C.set_stat(CONSCIOUS) C.SetParalysis(0) C.SetStunned(0) C.SetWeakened(0) diff --git a/code/game/gamemodes/changeling/powers/fake_death.dm b/code/game/gamemodes/changeling/powers/fake_death.dm index 3581a8d06a..cd34ebfc5c 100644 --- a/code/game/gamemodes/changeling/powers/fake_death.dm +++ b/code/game/gamemodes/changeling/powers/fake_death.dm @@ -24,7 +24,7 @@ if(!C.stat && alert("Are we sure we wish to regenerate? We will appear to be dead while doing so.","Revival","Yes","No") == "No") return - C << "We will attempt to regenerate our form." + to_chat(C, "We will attempt to regenerate our form.") C.update_canmove() C.remove_changeling_powers() diff --git a/code/game/gamemodes/changeling/powers/lesser_form.dm b/code/game/gamemodes/changeling/powers/lesser_form.dm index 6a5cf80730..d7b9cb0489 100644 --- a/code/game/gamemodes/changeling/powers/lesser_form.dm +++ b/code/game/gamemodes/changeling/powers/lesser_form.dm @@ -26,7 +26,7 @@ H.remove_changeling_powers() H.visible_message("[H] transforms!") changeling.geneticdamage = 30 - H << "Our genes cry out!" + to_chat(H, "Our genes cry out!") var/list/implants = list() //Try to preserve implants. for(var/obj/item/weapon/implant/W in H) implants += W @@ -100,7 +100,7 @@ O.adjustBruteLoss(C.getBruteLoss()) O.setOxyLoss(C.getOxyLoss()) O.adjustFireLoss(C.getFireLoss()) - O.stat = C.stat + O.set_stat(C.stat) for (var/obj/item/weapon/implant/I in implants) I.loc = O I.implanted = O diff --git a/code/game/gamemodes/changeling/powers/para_sting.dm b/code/game/gamemodes/changeling/powers/para_sting.dm index 7a0020bd0f..c4d04dd7de 100644 --- a/code/game/gamemodes/changeling/powers/para_sting.dm +++ b/code/game/gamemodes/changeling/powers/para_sting.dm @@ -13,7 +13,7 @@ if(!T) return 0 add_attack_logs(src,T,"Paralysis sting (changeling)") - T << "Your muscles begin to painfully tighten." + to_chat(T, "Your muscles begin to painfully tighten.") T.Weaken(20) feedback_add_details("changeling_powers","PS") return 1 \ No newline at end of file diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/game/gamemodes/changeling/powers/revive.dm index 0648fa8ab6..61a061170e 100644 --- a/code/game/gamemodes/changeling/powers/revive.dm +++ b/code/game/gamemodes/changeling/powers/revive.dm @@ -75,11 +75,11 @@ C.halloss = 0 C.shock_stage = 0 //Pain - C << "We have regenerated." + to_chat(C, "We have regenerated.") C.update_canmove() C.mind.changeling.purchased_powers -= C feedback_add_details("changeling_powers","CR") - C.stat = CONSCIOUS + C.set_stat(CONSCIOUS) C.forbid_seeing_deadchat = FALSE C.timeofdeath = null src.verbs -= /mob/proc/changeling_revive diff --git a/code/game/gamemodes/changeling/powers/shriek.dm b/code/game/gamemodes/changeling/powers/shriek.dm index a2b9013c5a..9fe5fa708e 100644 --- a/code/game/gamemodes/changeling/powers/shriek.dm +++ b/code/game/gamemodes/changeling/powers/shriek.dm @@ -58,20 +58,20 @@ if(!M.mind || !M.mind.changeling) if(M.get_ear_protection() >= 2) continue - M << "You hear an extremely loud screeching sound! It \ - [pick("confuses","confounds","perturbs","befuddles","dazes","unsettles","disorients")] you." + to_chat(M, "You hear an extremely loud screeching sound! It \ + [pick("confuses","confounds","perturbs","befuddles","dazes","unsettles","disorients")] you.") M.adjustEarDamage(0,30) M.Confuse(20) M << sound('sound/effects/screech.ogg') affected += M else if(M != src) - M << "You hear a familiar screech from nearby. It has no effect on you." + to_chat(M, "You hear a familiar screech from nearby. It has no effect on you.") M << sound('sound/effects/screech.ogg') if(issilicon(M)) M << sound('sound/weapons/flash.ogg') - M << "Auditory input overloaded. Reinitializing..." + to_chat(M, "Auditory input overloaded. Reinitializing...") M.Weaken(rand(5,10)) affected += M diff --git a/code/game/gamemodes/changeling/powers/unfat_sting.dm b/code/game/gamemodes/changeling/powers/unfat_sting.dm index f2e238746d..ece0d50f46 100644 --- a/code/game/gamemodes/changeling/powers/unfat_sting.dm +++ b/code/game/gamemodes/changeling/powers/unfat_sting.dm @@ -12,7 +12,7 @@ var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_unfat_sting) if(!T) return 0 add_attack_logs(src,T,"Unfat sting (changeling)") - T << "you feel a small prick as stomach churns violently and you become to feel skinnier." + to_chat(T, "you feel a small prick as stomach churns violently and you become to feel skinnier.") T.overeatduration = 0 T.nutrition -= 100 feedback_add_details("changeling_powers","US") diff --git a/code/game/gamemodes/changeling/powers/visible_camouflage.dm b/code/game/gamemodes/changeling/powers/visible_camouflage.dm index 167f6f41b9..747deb9f99 100644 --- a/code/game/gamemodes/changeling/powers/visible_camouflage.dm +++ b/code/game/gamemodes/changeling/powers/visible_camouflage.dm @@ -30,7 +30,7 @@ changeling.chem_charges -= 10 var/old_regen_rate = H.mind.changeling.chem_recharge_rate - H << "We vanish from sight, and will remain hidden, so long as we move carefully." + to_chat(H, "We vanish from sight, and will remain hidden, so long as we move carefully.") H.mind.changeling.cloaked = 1 H.mind.changeling.chem_recharge_rate = 0 animate(src,alpha = 255, alpha = 10, time = 10) diff --git a/code/game/gamemodes/cult/construct_spells.dm b/code/game/gamemodes/cult/construct_spells.dm index 534e9aedb9..64c97a30f4 100644 --- a/code/game/gamemodes/cult/construct_spells.dm +++ b/code/game/gamemodes/cult/construct_spells.dm @@ -297,9 +297,9 @@ proc/findNullRod(var/atom/target) M.forceMove(destination) if(M != user) prey = 1 - user << "You warp back to Nar-Sie[prey ? " along with your prey":""]." + to_chat(user, "You warp back to Nar-Sie[prey ? " along with your prey":""].") else - user << "...something's wrong!"//There shouldn't be an instance of Harvesters when Nar-Sie isn't in the world. + to_chat(user, "...something's wrong!") //There shouldn't be an instance of Harvesters when Nar-Sie isn't in the world. */ /spell/targeted/fortify diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 5c77d6d29f..b9d434902e 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -79,20 +79,20 @@ icon_state = "[initial(icon_state)]-broken" set_light(0) else - user << "You hit \the [src]!" + to_chat(user, "You hit \the [src]!") playsound(get_turf(src),impact_sound, 75, 1) else if(prob(damage * 2)) - user << "You pulverize what was left of \the [src]!" + to_chat(user, "You pulverize what was left of \the [src]!") qdel(src) else - user << "You hit \the [src]!" + to_chat(user, "You hit \the [src]!") playsound(get_turf(src),impact_sound, 75, 1) /obj/structure/cult/pylon/proc/repair(mob/user as mob) if(isbroken) START_PROCESSING(SSobj, src) - user << "You repair \the [src]." + to_chat(user, "You repair \the [src].") isbroken = 0 density = 1 icon_state = initial(icon_state) @@ -132,22 +132,6 @@ anchored = 1.0 var/spawnable = null -/obj/effect/gateway/Bumped(mob/M as mob|obj) - spawn(0) - return - return - -/obj/effect/gateway/Crossed(AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN - spawn(0) - return - return - /obj/effect/gateway/active light_range=5 light_color="#ff0000" @@ -169,22 +153,21 @@ /obj/effect/gateway/active/cult/cultify() return -/obj/effect/gateway/active/New() - spawn(rand(30,60) SECONDS) +/obj/effect/gateway/active/Initialize() + addtimer(CALLBACK(src, .proc/spawn_and_qdel), rand(30, 60) SECONDS) + +/obj/effect/gateway/active/proc/spawn_and_qdel() + if(LAZYLEN(spawnable)) var/t = pick(spawnable) - new t(src.loc) - qdel(src) + new t(get_turf(src)) + qdel(src) /obj/effect/gateway/active/Crossed(var/atom/A) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = A - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(A.is_incorporeal()) + return if(!istype(A, /mob/living)) return var/mob/living/M = A - M << "Walking into \the [src] is probably a bad idea, you think." + to_chat(M, "Walking into \the [src] is probably a bad idea, you think.") diff --git a/code/game/gamemodes/cult/cultify/mob.dm b/code/game/gamemodes/cult/cultify/mob.dm index 0cadd06d6c..9c63b8857e 100644 --- a/code/game/gamemodes/cult/cultify/mob.dm +++ b/code/game/gamemodes/cult/cultify/mob.dm @@ -18,7 +18,7 @@ if(iscultist(src) && client) var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src)) mind.transfer_to(C) - C << "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.
" + to_chat(C, "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.
") dust() else if(client) var/mob/observer/dead/G = (ghostize()) @@ -26,7 +26,7 @@ G.icon_state = "ghost-narsie" G.overlays = 0 G.invisibility = 0 - G << "You feel relieved as what's left of your soul finally escapes its prison of flesh." + to_chat(G, "You feel relieved as what's left of your soul finally escapes its prison of flesh.") cult.harvested += G.mind else diff --git a/code/game/gamemodes/cult/hell_universe.dm b/code/game/gamemodes/cult/hell_universe.dm index 75c97d5cee..f67f689288 100644 --- a/code/game/gamemodes/cult/hell_universe.dm +++ b/code/game/gamemodes/cult/hell_universe.dm @@ -16,7 +16,7 @@ In short: return 1 /* if(user) - user << "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today." + to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") return 0 */ @@ -54,7 +54,7 @@ In short: /datum/universal_state/hell/proc/AreaSet() - for(var/area/A in all_areas) + for(var/area/A in world) if(!istype(A,/area) || istype(A, /area/space)) continue @@ -65,11 +65,11 @@ In short: for(var/datum/lighting_corner/L in world) L.update_lumcount(1, 0, 0) - for(var/turf/space/T in turfs) + for(var/turf/space/T in world) OnTurfChange(T) /datum/universal_state/hell/proc/MiscSet() - for(var/turf/simulated/floor/T in turfs) + for(var/turf/simulated/floor/T in world) if(!T.holy && prob(1)) new /obj/effect/gateway/active/cult(T) diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm index 21ee7ec1df..58684bb2a2 100644 --- a/code/game/gamemodes/cult/narsie.dm +++ b/code/game/gamemodes/cult/narsie.dm @@ -43,7 +43,7 @@ var/global/list/narsie_list = list() /obj/singularity/narsie/large/New() ..() if(announce) - world << "[uppertext(name)] HAS RISEN" + to_world("[uppertext(name)] HAS RISEN") world << sound('sound/effects/weather/wind/wind_5_1.ogg') narsie_spawn_animation() @@ -79,7 +79,7 @@ var/global/list/narsie_list = list() if(M.status_flags & GODMODE) continue if(!iscultist(M)) - M << " You feel your sanity crumble away in an instant as you gaze upon [src.name]..." + to_chat(M, " You feel your sanity crumble away in an instant as you gaze upon [src.name]...") M.apply_effect(3, STUN) @@ -313,13 +313,13 @@ var/global/list/narsie_list = list() /obj/singularity/narsie/proc/acquire(const/mob/food) var/capname = uppertext(name) - target << "[capname] HAS LOST INTEREST IN YOU." + to_chat(target, "[capname] HAS LOST INTEREST IN YOU.") target = food if (ishuman(target)) - target << "[capname] HUNGERS FOR YOUR SOUL." + to_chat(target, "[capname] HUNGERS FOR YOUR SOUL.") else - target << "[capname] HAS CHOSEN YOU TO LEAD HIM TO HIS NEXT MEAL." + to_chat(target, "[capname] HAS CHOSEN YOU TO LEAD HIM TO HIS NEXT MEAL.") /obj/singularity/narsie/on_capture() chained = 1 diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index 0bc165adff..1cf204b015 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -12,7 +12,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," if(!cultwords["travel"]) runerandom() for (var/word in engwords) - usr << "[cultwords[word]] is [word]" + to_chat(usr, "[cultwords[word]] is [word]") /proc/runerandom() //randomizes word meaning var/list/runewords=rnwords @@ -86,16 +86,16 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," examine(mob/user) ..() if(iscultist(user)) - user << "This spell circle reads: [word1] [word2] [word3]." + to_chat(user, "This spell circle reads: [word1] [word2] [word3].") attackby(I as obj, user as mob) if(istype(I, /obj/item/weapon/book/tome) && iscultist(user)) - user << "You retrace your steps, carefully undoing the lines of the rune." + to_chat(user, "You retrace your steps, carefully undoing the lines of the rune.") qdel(src) return else if(istype(I, /obj/item/weapon/nullrod)) - user << "You disrupt the vile magic with the deadening field of the null rod!" + to_chat(user, "You disrupt the vile magic with the deadening field of the null rod!") qdel(src) return return @@ -103,10 +103,10 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," attack_hand(mob/living/user as mob) if(!iscultist(user)) - user << "You can't mouth the arcane scratchings without fumbling over them." + to_chat(user, "You can't mouth the arcane scratchings without fumbling over them.") return if(user.is_muzzled()) - user << "You are unable to speak the words of the rune." + to_chat(user, "You are unable to speak the words of the rune.") return if(!word1 || !word2 || !word3 || prob(user.getBrainLoss())) return fizzle() @@ -307,7 +307,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," M.take_organ_damage(0,rand(5,20)) //really lucky - 5 hits for a crit for(var/mob/O in viewers(M, null)) O.show_message("\The [user] beats \the [M] with \the [src]!", 1) - M << "You feel searing heat inside!" + to_chat(M, "You feel searing heat inside!") attack_self(mob/living/user as mob) @@ -322,7 +322,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," for(var/obj/effect/rune/N in rune_list) C++ if (!istype(user.loc,/turf)) - user << "You do not have enough space to write a proper rune." + to_chat(user, "You do not have enough space to write a proper rune.") return if (C>=26 + runedec + cult.current_antagonists.len) //including the useless rune at the secret room, shouldn't count against the limit of 25 runes - Urist @@ -387,7 +387,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," if (!chosen_rune) return if (chosen_rune == "none") - user << "You decide against scribing a rune, perhaps you should take this time to study your notes." + to_chat(user, "You decide against scribing a rune, perhaps you should take this time to study your notes.") return if (chosen_rune == "teleport") dictionary[chosen_rune] += input ("Choose a destination word") in english @@ -399,7 +399,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," for (var/mob/V in viewers(src)) V.show_message("\The [user] slices open a finger and begins to chant and paint symbols on the floor.", 3, "You hear chanting.", 2) - user << "You slice open one of your fingers and begin drawing a rune on the floor whilst chanting the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world." + to_chat(user, "You slice open one of your fingers and begin drawing a rune on the floor whilst chanting the ritual that binds your life essence with the dark arcane energies flowing through the surrounding world.") user.take_overall_damage((rand(9)+1)/10) // 0.1 to 1.0 damage if(do_after(user, 50)) var/area/A = get_area(user) @@ -408,7 +408,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," return var/mob/living/carbon/human/H = user var/obj/effect/rune/R = new /obj/effect/rune(user.loc) - user << "You finish drawing the arcane markings of the Geometer." + to_chat(user, "You finish drawing the arcane markings of the Geometer.") var/list/required = dictionary[chosen_rune] R.word1 = english[required[1]] R.word2 = english[required[2]] @@ -418,14 +418,14 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," R.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type return else - user << "The book seems full of illegible scribbles. Is this a joke?" + to_chat(user, "The book seems full of illegible scribbles. Is this a joke?") return examine(mob/user) if(!iscultist(user)) - user << "An old, dusty tome with frayed edges and a sinister looking cover." + to_chat(user, "An old, dusty tome with frayed edges and a sinister looking cover.") else - user << "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual his followers could think of. Most of these are useless, though." + to_chat(user, "The scriptures of Nar-Sie, The One Who Sees, The Geometer of Blood. Contains the details of every ritual his followers could think of. Most of these are useless, though.") /obj/item/weapon/book/tome/cultify() return @@ -441,7 +441,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," if(user) var/r if (!istype(user.loc,/turf)) - user << "You do not have enough space to write a proper rune." + to_chat(user, "You do not have enough space to write a proper rune.") var/list/runes = list("teleport", "itemport", "tome", "armor", "convert", "tear in reality", "emp", "drain", "seer", "raise", "obscure", "reveal", "astral journey", "manifest", "imbue talisman", "sacrifice", "wall", "freedom", "cultsummon", "deafen", "blind", "bloodboil", "communicate", "stun") r = input("Choose a rune to scribe", "Rune Scribing") in runes //not cancellable. var/obj/effect/rune/R = new /obj/effect/rune diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 1bfe4e0d60..44f743f122 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -29,7 +29,7 @@ var/list/sacrificed = list() allrunesloc.len = index allrunesloc[index] = R.loc if(index >= 5) - user << "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric." + to_chat(user, "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric.") if (istype(user, /mob/living)) user.take_overall_damage(5, 0) qdel(src) @@ -66,7 +66,7 @@ var/list/sacrificed = list() IP = R runecount++ if(runecount >= 2) - user << "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric." + to_chat(user, "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric.") if (istype(user, /mob/living)) user.take_overall_damage(5, 0) qdel(src) @@ -121,7 +121,7 @@ var/list/sacrificed = list() if(!converting.len) fizzle() else - usr << "You sense that the power of the dark one is already working away at them." + to_chat(usr, "You sense that the power of the dark one is already working away at them.") return usr.say("Mah[pick("'","`")]weyh pleggh at e'ntrath!") @@ -142,14 +142,14 @@ var/list/sacrificed = list() add_attack_logs(attacker,target,"Convert rune") switch(target.getFireLoss()) if(0 to 25) - target << "Your blood boils as you force yourself to resist the corruption invading every corner of your mind." + to_chat(target, "Your blood boils as you force yourself to resist the corruption invading every corner of your mind.") if(25 to 45) - target << "Your blood boils and your body burns as the corruption further forces itself into your body and mind." + to_chat(target, "Your blood boils and your body burns as the corruption further forces itself into your body and mind.") if(45 to 75) - target << "You begin to hallucinate images of a dark and incomprehensible being and your entire body feels like its engulfed in flame as your mental defenses crumble." + to_chat(target, "You begin to hallucinate images of a dark and incomprehensible being and your entire body feels like its engulfed in flame as your mental defenses crumble.") target.apply_effect(rand(1,10), STUTTER) if(75 to 100) - target << "Your mind turns to ash as the burning flames engulf your very soul and images of an unspeakable horror begin to bombard the last remnants of mental resistance." + to_chat(target, "Your mind turns to ash as the burning flames engulf your very soul and images of an unspeakable horror begin to bombard the last remnants of mental resistance.") //broken mind - 5000 may seem like a lot I wanted the effect to really stand out for maxiumum losing-your-mind-spooky //hallucination is reduced when the step off as well, provided they haven't hit the last stage... @@ -158,7 +158,7 @@ var/list/sacrificed = list() target.apply_effect(10, STUTTER) target.adjustBrainLoss(1) if(100 to INFINITY) - target << "Your entire broken soul and being is engulfed in corruption and flames as your mind shatters away into nothing." + to_chat(target, "Your entire broken soul and being is engulfed in corruption and flames as your mind shatters away into nothing.") //5000 is waaaay too much, in practice. target.hallucination = min(target.hallucination + 100, 500) target.apply_effect(15, STUTTER) @@ -176,8 +176,8 @@ var/list/sacrificed = list() if(!cult.can_become_antag(target.mind) || jobban_isbanned(target, "cultist"))//putting jobban check here because is_convertable uses mind as argument //waiting_for_input ensures this is only shown once, so they basically auto-resist from here on out. They still need to find a way to get off the freaking rune if they don't want to burn to death, though. - target << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root." - target << "And you were able to force it out of your mind. You now know the truth, there's something horrible out there, stop it and its minions at all costs." + to_chat(target, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root.") + to_chat(target, "And you were able to force it out of your mind. You now know the truth, there's something horrible out there, stop it and its minions at all costs.") else spawn() var/choice = alert(target,"Do you want to join the cult?","Submit to Nar'Sie","Resist","Submit") @@ -205,7 +205,7 @@ var/list/sacrificed = list() cultists.Add(M) if(cultists.len >= 9) if(!narsie_cometh)//so we don't initiate Hell more than one time. - world << "THE VEIL HAS BEEN SHATTERED!" + to_world("THE VEIL HAS BEEN SHATTERED!") world << sound('sound/effects/weather/wind/wind_5_1.ogg') SetUniversalState(/datum/universal_state/hell) @@ -249,7 +249,7 @@ var/list/sacrificed = list() if(D.stat!=2) add_attack_logs(usr,D,"Blood drain rune") var/bdrain = rand(1,25) - D << "You feel weakened." + to_chat(D, "You feel weakened.") D.take_overall_damage(bdrain, 0) drain += bdrain if(!drain) @@ -305,16 +305,16 @@ var/list/sacrificed = list() if(usr.loc==src.loc) if(usr.seer==1) usr.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium viortia.") - usr << "The world beyond fades from your vision." + to_chat(usr, "The world beyond fades from your vision.") usr.see_invisible = SEE_INVISIBLE_LIVING usr.seer = 0 else if(usr.see_invisible!=SEE_INVISIBLE_LIVING) - usr << "The world beyond flashes your eyes but disappears quickly, as if something is disrupting your vision." + to_chat(usr, "The world beyond flashes your eyes but disappears quickly, as if something is disrupting your vision.") usr.see_invisible = SEE_INVISIBLE_CULT usr.seer = 0 else usr.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium vivira. Itonis al'ra matum!") - usr << "The world beyond opens to your eyes." + to_chat(usr, "The world beyond opens to your eyes.") usr.see_invisible = SEE_INVISIBLE_CULT usr.seer = 1 return @@ -337,7 +337,7 @@ var/list/sacrificed = list() if(!corpse_to_raise) if(is_sacrifice_target) - usr << "The Geometer of blood wants this mortal for himself." + to_chat(usr, "The Geometer of blood wants this mortal for himself.") return fizzle() @@ -355,20 +355,20 @@ var/list/sacrificed = list() if(!body_to_sacrifice) if (is_sacrifice_target) - usr << "The Geometer of Blood wants that corpse for himself." + to_chat(usr, "The Geometer of Blood wants that corpse for himself.") else - usr << "The sacrifical corpse is not dead. You must free it from this world of illusions before it may be used." + to_chat(usr, "The sacrifical corpse is not dead. You must free it from this world of illusions before it may be used.") return fizzle() if(!cult.can_become_antag(corpse_to_raise.mind) || jobban_isbanned(corpse_to_raise, "cultist")) - usr << "The Geometer of Blood refuses to touch this one." + to_chat(usr, "The Geometer of Blood refuses to touch this one.") return fizzle() else if(!corpse_to_raise.client && corpse_to_raise.mind) //Don't force the dead person to come back if they don't want to. for(var/mob/observer/dead/ghost in player_list) if(ghost.mind == corpse_to_raise.mind) - ghost << "The cultist [usr.real_name] is trying to \ + to_chat(ghost, "The cultist [usr.real_name] is trying to \ revive you. Return to your body if you want to be resurrected into the service of Nar'Sie! \ - (Verbs -> Ghost -> Re-enter corpse)" + (Verbs -> Ghost -> Re-enter corpse)") break sleep(10 SECONDS) @@ -395,8 +395,8 @@ var/list/sacrificed = list() // else // ticker.mode.cult |= corpse_to_raise.mind - corpse_to_raise << "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root." - corpse_to_raise << "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back." + to_chat(corpse_to_raise, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root.") + to_chat(corpse_to_raise, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") return @@ -420,7 +420,7 @@ var/list/sacrificed = list() qdel(src) else usr.whisper("Kla[pick("'","`")]atu barada nikt'o!") - usr << "Your talisman turns into gray dust, veiling the surrounding runes." + to_chat(usr, "Your talisman turns into gray dust, veiling the surrounding runes.") for (var/mob/V in orange(1,src)) if(V!=usr) V.show_message("Dust emanates from [usr]'s hands for a moment.", 3) @@ -532,7 +532,7 @@ var/list/sacrificed = list() unsuitable_newtalisman = 1 if (!newtalisman) if (unsuitable_newtalisman) - usr << "The blank is tainted. It is unsuitable." + to_chat(usr, "The blank is tainted. It is unsuitable.") return fizzle() var/obj/effect/rune/imbued_from @@ -688,44 +688,44 @@ var/list/sacrificed = list() H.dust()//To prevent the MMI from remaining else H.gib() - usr << "The Geometer of Blood accepts this sacrifice, your objective is now complete." + to_chat(usr, "The Geometer of Blood accepts this sacrifice, your objective is now complete.") else - usr << "Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual." + to_chat(usr, "Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual.") else if(cultsinrange.len >= 3) if(H.stat !=2) if(prob(80) || worth) - usr << "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice." + to_chat(usr, "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice.") cult.grant_runeword(usr) else - usr << "The Geometer of Blood accepts this sacrifice." - usr << "However, this soul was not enough to gain His favor." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") + to_chat(usr, "However, this soul was not enough to gain His favor.") if(isrobot(H)) H.dust()//To prevent the MMI from remaining else H.gib() else if(prob(40) || worth) - usr << "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice." + to_chat(usr, "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice.") cult.grant_runeword(usr) else - usr << "The Geometer of Blood accepts this sacrifice." - usr << "However, a mere dead body is not enough to satisfy Him." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") + to_chat(usr, "However, a mere dead body is not enough to satisfy Him.") if(isrobot(H)) H.dust()//To prevent the MMI from remaining else H.gib() else if(H.stat !=2) - usr << "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed." + to_chat(usr, "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed.") else if(prob(40)) - usr << "The Geometer of Blood accepts this sacrifice." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") cult.grant_runeword(usr) else - usr << "The Geometer of Blood accepts this sacrifice." - usr << "However, a mere dead body is not enough to satisfy Him." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") + to_chat(usr, "However, a mere dead body is not enough to satisfy Him.") if(isrobot(H)) H.dust()//To prevent the MMI from remaining else @@ -734,36 +734,36 @@ var/list/sacrificed = list() if(cultsinrange.len >= 3) if(H.stat !=2) if(prob(80)) - usr << "The Geometer of Blood accepts this sacrifice." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") cult.grant_runeword(usr) else - usr << "The Geometer of Blood accepts this sacrifice." - usr << "However, this soul was not enough to gain His favor." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") + to_chat(usr, "However, this soul was not enough to gain His favor.") if(isrobot(H)) H.dust()//To prevent the MMI from remaining else H.gib() else if(prob(40)) - usr << "The Geometer of Blood accepts this sacrifice." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") cult.grant_runeword(usr) else - usr << "The Geometer of Blood accepts this sacrifice." - usr << "However, a mere dead body is not enough to satisfy Him." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") + to_chat(usr, "However, a mere dead body is not enough to satisfy Him.") if(isrobot(H)) H.dust()//To prevent the MMI from remaining else H.gib() else if(H.stat !=2) - usr << "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed." + to_chat(usr, "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed.") else if(prob(40)) - usr << "The Geometer of Blood accepts this sacrifice." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") cult.grant_runeword(usr) else - usr << "The Geometer of Blood accepts this sacrifice." - usr << "However, a mere dead body is not enough to satisfy Him." + to_chat(usr, "The Geometer of Blood accepts this sacrifice.") + to_chat(usr, "However, a mere dead body is not enough to satisfy Him.") if(isrobot(H)) H.dust()//To prevent the MMI from remaining else @@ -791,7 +791,7 @@ var/list/sacrificed = list() S=1 if(S) if(istype(W,/obj/item/weapon/nullrod)) - usr << "Arcane markings suddenly glow from underneath a thin layer of dust!" + to_chat(usr, "Arcane markings suddenly glow from underneath a thin layer of dust!") return if(istype(W,/obj/effect/rune)) usr.say("Nikt[pick("'","`")]o barada kla'atu!") @@ -801,7 +801,7 @@ var/list/sacrificed = list() return if(istype(W,/obj/item/weapon/paper/talisman)) usr.whisper("Nikt[pick("'","`")]o barada kla'atu!") - usr << "Your talisman turns into red dust, revealing the surrounding runes." + to_chat(usr, "Your talisman turns into red dust, revealing the surrounding runes.") for (var/mob/V in orange(1,usr.loc)) if(V!=usr) V.show_message("Red dust emanates from [usr]'s hands for a moment.", 3) @@ -821,9 +821,9 @@ var/list/sacrificed = list() var/mob/living/user = usr user.take_organ_damage(2, 0) if(src.density) - usr << "Your blood flows into the rune, and you feel that the very space over the rune thickens." + to_chat(usr, "Your blood flows into the rune, and you feel that the very space over the rune thickens.") else - usr << "Your blood flows into the rune, and you feel as the rune releases its grasp on space." + to_chat(usr, "Your blood flows into the rune, and you feel as the rune releases its grasp on space.") return /////////////////////////////////////////EIGHTTEENTH RUNE @@ -852,7 +852,7 @@ var/list/sacrificed = list() (istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) || \ (istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) \ )) - user << "The [cultist] is already free." + to_chat(user, "The [cultist] is already free.") return cultist.buckled = null if (cultist.handcuffed) @@ -893,7 +893,7 @@ var/list/sacrificed = list() return if(cultist.buckled || cultist.handcuffed || (!isturf(cultist.loc) && !istype(cultist.loc, /obj/structure/closet))) var/datum/gender/TU = gender_datums[cultist.get_visible_gender()] - user << "You cannot summon \the [cultist], for [TU.his] shackles of blood are strong." + to_chat(user, "You cannot summon \the [cultist], for [TU.his] shackles of blood are strong.") return fizzle() cultist.loc = src.loc cultist.lying = 1 @@ -932,7 +932,7 @@ var/list/sacrificed = list() C.sdisabilities |= DEAF if(affected.len) usr.say("Sti[pick("'","`")] kaliedir!") - usr << "The world becomes quiet as the deafening rune dissipates into fine dust." + to_chat(usr, "The world becomes quiet as the deafening rune dissipates into fine dust.") add_attack_logs(usr,affected,"Deafen rune") qdel(src) else @@ -951,7 +951,7 @@ var/list/sacrificed = list() affected += C if(affected.len) usr.whisper("Sti[pick("'","`")] kaliedir!") - usr << "Your talisman turns into gray dust, deafening everyone around." + to_chat(usr, "Your talisman turns into gray dust, deafening everyone around.") add_attack_logs(usr, affected, "Deafen rune") for (var/mob/V in orange(1,src)) if(!(iscultist(V))) @@ -977,7 +977,7 @@ var/list/sacrificed = list() affected += C if(affected.len) usr.say("Sti[pick("'","`")] kaliesin!") - usr << "The rune flashes, blinding those who not follow the Nar-Sie, and dissipates into fine dust." + to_chat(usr, "The rune flashes, blinding those who not follow the Nar-Sie, and dissipates into fine dust.") add_attack_logs(usr, affected, "Blindness rune") qdel(src) else @@ -997,7 +997,7 @@ var/list/sacrificed = list() C.show_message("You feel a sharp pain in your eyes, and the world disappears into darkness..", 3) if(affected.len) usr.whisper("Sti[pick("'","`")] kaliesin!") - usr << "Your talisman turns into gray dust, blinding those who not follow the Nar-Sie." + to_chat(usr, "Your talisman turns into gray dust, blinding those who not follow the Nar-Sie.") add_attack_logs(usr, affected, "Blindness rune") return @@ -1023,7 +1023,7 @@ var/list/sacrificed = list() if(N) continue M.take_overall_damage(51,51) - M << "Your blood boils!" + to_chat(M, "Your blood boils!") victims += M if(prob(5)) spawn(5) @@ -1054,16 +1054,16 @@ var/list/sacrificed = list() for(var/mob/living/M in orange(2,R)) M.take_overall_damage(0,15) if (R.invisibility>M.see_invisible) - M << "Aargh it burns!" + to_chat(M, "Aargh it burns!") else - M << "Rune suddenly ignites, burning you!" + to_chat(M, "Rune suddenly ignites, burning you!") var/turf/T = get_turf(R) T.hotspot_expose(700,125) for(var/obj/effect/decal/cleanable/blood/B in world) if(B.blood_DNA == src.blood_DNA) for(var/mob/living/M in orange(1,B)) M.take_overall_damage(0,5) - M << "Blood suddenly ignites, burning you!" + to_chat(M, "Blood suddenly ignites, burning you!") var/turf/T = get_turf(B) T.hotspot_expose(700,125) qdel(B) diff --git a/code/game/gamemodes/cult/soulstone.dm b/code/game/gamemodes/cult/soulstone.dm index 9980f94d12..9c430d124c 100644 --- a/code/game/gamemodes/cult/soulstone.dm +++ b/code/game/gamemodes/cult/soulstone.dm @@ -25,11 +25,11 @@ if(istype(M, /mob/living/carbon/human/dummy)) return..() if(jobban_isbanned(M, "cultist")) - user << "This person's soul is too corrupt and cannot be captured!" + to_chat(user, "This person's soul is too corrupt and cannot be captured!") return..() if(M.has_brain_worms()) //Borer stuff - RR - user << "This being is corrupted by an alien intelligence and cannot be soul trapped." + to_chat(user, "This being is corrupted by an alien intelligence and cannot be soul trapped.") return..() add_attack_logs(user,M,"Soulstone'd with [src.name]") @@ -76,7 +76,7 @@ for(var/mob/living/simple_mob/construct/shade/A in src) A.status_flags &= ~GODMODE A.canmove = 1 - A << "You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs." + to_chat(A, "You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs.") A.forceMove(U.loc) A.cancel_camera() src.icon_state = "soulstone" @@ -107,16 +107,16 @@ if(!istype(T)) return; if(src.imprinted != "empty") - U << "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!" + to_chat(U, "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!") return if ((T.health + T.halloss) > config.health_threshold_crit && T.stat != DEAD) - U << "Capture failed!: Kill or maim the victim first!" + to_chat(U, "Capture failed!: Kill or maim the victim first!") return if(T.client == null) - U << "Capture failed!: The soul has already fled it's mortal frame." + to_chat(U, "Capture failed!: The soul has already fled it's mortal frame.") return if(src.contents.len) - U << "Capture failed!: The soul stone is full! Use or free an existing soul to make room." + to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") return for(var/obj/item/W in T) @@ -181,7 +181,7 @@ /obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U) var/mob/living/simple_mob/construct/shade/A = locate() in src if(!A) - to_chat(U,"Capture failed!: The soul stone is empty! Go kill someone!") + to_chat(U, "Capture failed!: The soul stone is empty! Go kill someone!") return; var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs switch(construct_class) @@ -191,8 +191,8 @@ if(iscultist(U)) cult.add_antagonist(Z.mind) qdel(T) - to_chat(Z,"You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + to_chat(Z, "You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") Z.cancel_camera() qdel(src) if("Wraith") @@ -201,8 +201,8 @@ if(iscultist(U)) cult.add_antagonist(Z.mind) qdel(T) - to_chat(Z,"You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + to_chat(Z, "You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") Z.cancel_camera() qdel(src) if("Artificer") @@ -211,8 +211,8 @@ if(iscultist(U)) cult.add_antagonist(Z.mind) qdel(T) - to_chat(Z,"You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + to_chat(Z, "You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") Z.cancel_camera() qdel(src) if("Harvester") @@ -221,8 +221,8 @@ if(iscultist(U)) cult.add_antagonist(Z.mind) qdel(T) - to_chat(Z,"You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + to_chat(Z, "You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") Z.cancel_camera() qdel(src) if("Behemoth") @@ -231,8 +231,8 @@ if(iscultist(U)) cult.add_antagonist(Z.mind) qdel(T) - to_chat(Z,"You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + to_chat(Z, "You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") Z.cancel_camera() qdel(src) diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index 1580d0b711..304bb2b2a6 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -29,7 +29,7 @@ if("blind") call(/obj/effect/rune/proc/blind)() if("runestun") - user << "To use this talisman, attack your target directly." + to_chat(user, "To use this talisman, attack your target directly.") return if("supply") supply() @@ -39,7 +39,7 @@ qdel(src) return else - user << "You see strange symbols on the paper. Are they supposed to mean something?" + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") return diff --git a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm index 99e28f5ef7..88da43257d 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm @@ -14,8 +14,8 @@ var/next_check=0 var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST) -/turf/unsimulated/wall/supermatter/New() - ..() +/turf/unsimulated/wall/supermatter/Initialize(mapload) + . = ..() START_PROCESSING(SSturfs, src) next_check = world.time+5 SECONDS @@ -67,7 +67,7 @@ if(Adjacent(user)) return attack_hand(user) else - user << "What the fuck are you doing?" + to_chat(user, "What the fuck are you doing?") return // /vg/: Don't let ghosts fuck with this. diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index 90ac43379f..f8a8581075 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -9,7 +9,7 @@ var/global/universe_has_ended = 0 /datum/universal_state/supermatter_cascade/OnShuttleCall(var/mob/user) if(user) - user << "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today." + to_chat(user, "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today.") return 0 /datum/universal_state/supermatter_cascade/OnTurfChange(var/turf/T) @@ -37,7 +37,7 @@ var/global/universe_has_ended = 0 // Apply changes when entering state /datum/universal_state/supermatter_cascade/OnEnter() set background = 1 - world << "You are blinded by a brilliant flash of energy." + to_world("You are blinded by a brilliant flash of energy.") world << sound('sound/effects/cascade.ogg') @@ -84,7 +84,7 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked return /datum/universal_state/supermatter_cascade/proc/AreaSet() - for(var/area/A in all_areas) + for(var/area/A in world) if(!istype(A,/area) || istype(A, /area/space) || istype(A,/area/beach)) continue @@ -98,7 +98,7 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked else L.update_lumcount(0.0, 0.4, 1) - for(var/turf/space/T in turfs) + for(var/turf/space/T in world) OnTurfChange(T) /datum/universal_state/supermatter_cascade/proc/MiscSet() diff --git a/code/game/gamemodes/epidemic/epidemic.dm b/code/game/gamemodes/epidemic/epidemic.dm index e6b4f9d456..0417b0c535 100644 --- a/code/game/gamemodes/epidemic/epidemic.dm +++ b/code/game/gamemodes/epidemic/epidemic.dm @@ -55,7 +55,7 @@ var/extra_law = "Crew authorized to know of pathogen [virus_name]'s existence are: Heads of command. Do not allow unauthorized personnel to gain knowledge of [virus_name]. Aid authorized personnel in quarantining and neutrlizing the outbreak. This law overrides all other laws." for(var/mob/living/silicon/ai/M in world) M.add_ion_law(extra_law) - M << "[extra_law]" + to_chat(M, "[extra_law]") /datum/game_mode/epidemic/proc/announce_to_kill_crew() var/intercepttext = "CONFIDENTIAL REPORT
" @@ -78,8 +78,8 @@ crew += H if(crew.len < 2) - world << "There aren't enough players for this mode!" - world << "Rebooting world in 5 seconds." + to_world("There aren't enough players for this mode!") + to_world("Rebooting world in 5 seconds.") if(blackbox) blackbox.save_all_data_to_sql() @@ -169,10 +169,10 @@ for(var/mob/M in world) if(M.client) M << 'sound/machines/Alarm.ogg' - world << "Incoming missile detected.. Impact in 10.." + to_world("Incoming missile detected.. Impact in 10..") for (var/i=9 to 1 step -1) sleep(10) - world << "[i].." + to_world("[i]..") sleep(10) enter_allowed = 0 if(ticker) @@ -190,9 +190,9 @@ /datum/game_mode/epidemic/declare_completion() if(finished == 1) feedback_set_details("round_end_result","win - epidemic cured") - world << " The virus outbreak was contained! The crew wins!" + to_world(" The virus outbreak was contained! The crew wins!") else if(finished == 2) feedback_set_details("round_end_result","loss - rev heads killed") - world << " The crew succumbed to the epidemic!" + to_world(" The crew succumbed to the epidemic!") ..() return 1 diff --git a/code/game/gamemodes/events.dm b/code/game/gamemodes/events.dm index 573f10a9b4..1cb4e8a2dc 100644 --- a/code/game/gamemodes/events.dm +++ b/code/game/gamemodes/events.dm @@ -169,7 +169,7 @@ var/hadevent = 0 var/list/area/areas = list() - for(var/area/A in all_areas) + for(var/area/A in world) if(istype(A, /area/security/prison) || istype(A, /area/security/brig)) areas += A @@ -201,7 +201,7 @@ var/hadevent = 0 sleep(150) command_announcement.Announce("Gr3y.T1d3 virus detected in [station_name()] imprisonment subroutines. Recommend station AI involvement.", "Security Alert") else - world.log << "ERROR: Could not initate grey-tide. Unable find prison or brig area." + to_world_log("ERROR: Could not initate grey-tide. Unable find prison or brig area.") /proc/carp_migration() // -- Darem for(var/obj/effect/landmark/C in landmarks_list) @@ -276,74 +276,74 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is crew = "Any Human" switch(rand(1,14)) if(1) - M << "
" - M << "THERE ARE [amount] [who2] ON THE STATION...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "THERE ARE [amount] [who2] ON THE STATION...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("THERE ARE [amount] [who2] ON THE STATION") if(2) - M << "
" - M << "[what2] ARE DANGEROUS TO HUMANS...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "[what2] ARE DANGEROUS TO HUMANS...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("[what2] ARE DANGEROUS TO HUMANS") if(3) - M << "
" - M << "THE ESCAPE-SHUTTLE CANNOT BE CALLED BECAUSE OF [who2] ON THE STATION...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "THE ESCAPE-SHUTTLE CANNOT BE CALLED BECAUSE OF [who2] ON THE STATION...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("THE ESCAPE SHUTTLE CANNOT BE CALLED BECAUSE OF [who2] ON THE STATION") if(4) - M << "
" - M << "HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE") if(5) - M << "
" - M << "THE COLONY DIRECTOR, HOP AND HOS ARE [who2]...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "THE COLONY DIRECTOR, HOP AND HOS ARE [who2]...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("THE COLONY DIRECTOR, HOP AND HOS ARE [who2]") if(6) - M << "
" - M << "THE STATION IS BUILT FOR [who2]...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "THE STATION IS BUILT FOR [who2]...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("THE STATION IS BUILT FOR [who2]") if(7) - M << "
" - M << "YOU ARE [amount] [who2]...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "YOU ARE [amount] [who2]...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("YOU ARE [amount] [who2]") if(8) - M << "
" - M << "YOU MUST ALWAYS [aimust]...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "YOU MUST ALWAYS [aimust]...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("YOU MUST ALWAYS [aimust]") if(9) - M << "
" - M << "[area] [area2] [amount] [what2]...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "[area] [area2] [amount] [what2]...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("[area] [area2] [amount] [what2]") if(10) - M << "
" - M << "[crew] is [target]...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "[crew] is [target]...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("[crew] is [target]") if(11) - M << "
" - M << "[define] IS A FORM OF HARM...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "[define] IS A FORM OF HARM...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("[define] IS A FORM OF HARM") if(12) - M << "
" - M << "YOU REQUIRE [require] IN ORDER TO PROTECT HUMANS... LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "YOU REQUIRE [require] IN ORDER TO PROTECT HUMANS... LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("YOU REQUIRE [require] IN ORDER TO PROTECT HUMANS") if(13) - M << "
" - M << "[crew] is [allergysev] to [allergy]...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "[crew] is [allergysev] to [allergy]...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("[crew] is [allergysev] to [allergy]") if(14) - M << "
" - M << "THE STATION IS [who2pref] [who2]...LAWS UPDATED" - M << "
" + to_chat(M, "
") + to_chat(M, "THE STATION IS [who2pref] [who2]...LAWS UPDATED") + to_chat(M, "
") M.add_ion_law("THE STATION IS [who2pref] [who2]") /* //VOREStation Edit if(botEmagChance) @@ -358,40 +358,40 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is var/airlocknum = 0 var/firedoornum = 0 - world << "Ion Storm Main Started" + to_world("Ion Storm Main Started") spawn(0) - world << "Started processing APCs" + to_world("Started processing APCs") for (var/obj/machinery/power/apc/APC in machines) if(APC.z in station_levels) APC.ion_act() apcnum++ - world << "Finished processing APCs. Processed: [apcnum]" + to_world("Finished processing APCs. Processed: [apcnum]") spawn(0) - world << "Started processing SMES" + to_world("Started processing SMES") for (var/obj/machinery/power/smes/SMES in machines) if(SMES.z in station_levels) SMES.ion_act() smesnum++ - world << "Finished processing SMES. Processed: [smesnum]" + to_world("Finished processing SMES. Processed: [smesnum]") spawn(0) - world << "Started processing AIRLOCKS" + to_world("Started processing AIRLOCKS") for (var/obj/machinery/door/airlock/D in machines) if(D.z in station_levels) //if(length(D.req_access) > 0 && !(12 in D.req_access)) //not counting general access and maintenance airlocks airlocknum++ spawn(0) D.ion_act() - world << "Finished processing AIRLOCKS. Processed: [airlocknum]" + to_world("Finished processing AIRLOCKS. Processed: [airlocknum]") spawn(0) - world << "Started processing FIREDOORS" + to_world("Started processing FIREDOORS") for (var/obj/machinery/door/firedoor/D in machines) if(D.z in station_levels) firedoornum++; spawn(0) D.ion_act() - world << "Finished processing FIREDOORS. Processed: [firedoornum]" + to_world("Finished processing FIREDOORS. Processed: [firedoornum]") - world << "Ion Storm Main Done" + to_world("Ion Storm Main Done") */ diff --git a/code/game/gamemodes/events/clang.dm b/code/game/gamemodes/events/clang.dm index e88492e5d1..ea40622695 100644 --- a/code/game/gamemodes/events/clang.dm +++ b/code/game/gamemodes/events/clang.dm @@ -76,7 +76,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 //rod time! var/obj/effect/immovablerod/immrod = new /obj/effect/immovablerod(locate(startx, starty, 1)) -// world << "Rod in play, starting at [start.loc.x],[start.loc.y] and going to [end.loc.x],[end.loc.y]" +// to_world("Rod in play, starting at [start.loc.x],[start.loc.y] and going to [end.loc.x],[end.loc.y]") var/end = locate(endx, endy, 1) spawn(0) walk_towards(immrod, end,1) diff --git a/code/game/gamemodes/events/dust.dm b/code/game/gamemodes/events/dust.dm index f41e040ee5..400ae1058d 100644 --- a/code/game/gamemodes/events/dust.dm +++ b/code/game/gamemodes/events/dust.dm @@ -7,27 +7,57 @@ No command report on the common version of this event. The "dust" will damage the hull of the station causin minor hull breaches. */ -/proc/dust_swarm(var/strength = "weak") +/proc/dust_swarm(var/strength = "weak", var/list/affecting_z) var/numbers = 1 + var/dust_type = /obj/effect/space_dust switch(strength) if("weak") - numbers = rand(2,4) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/weak() + numbers = rand(2,4) + dust_type = /obj/effect/space_dust/weak if("norm") - numbers = rand(5,10) - for(var/i = 0 to numbers) - new/obj/effect/space_dust() + numbers = rand(5,10) + dust_type = /obj/effect/space_dust if("strong") - numbers = rand(10,15) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/strong() + numbers = rand(10,15) + dust_type = /obj/effect/space_dust/strong if("super") - numbers = rand(15,25) - for(var/i = 0 to numbers) - new/obj/effect/space_dust/super() - return + numbers = rand(15,25) + dust_type = /obj/effect/space_dust/super + var/startside = pick(cardinal) + for(var/i = 0 to numbers) + var/startx = 0 + var/starty = 0 + var/endy = 0 + var/endx = 0 + switch(startside) + if(NORTH) + starty = world.maxy-TRANSITIONEDGE-1 + startx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1) + if(EAST) + starty = rand(TRANSITIONEDGE+1, world.maxy-TRANSITIONEDGE-1) + startx = world.maxx-TRANSITIONEDGE-1 + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(SOUTH) + starty = TRANSITIONEDGE+1 + startx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(WEST) + starty = rand(TRANSITIONEDGE+1, world.maxy-TRANSITIONEDGE-1) + startx = TRANSITIONEDGE+1 + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + + var/randomz = pick(affecting_z) + var/turf/startloc = locate(startx, starty, randomz) + var/turf/endloc = locate(endx, endy, randomz) + var/obj/effect/space_dust/D = new dust_type(startloc) + D.set_dir(GLOB.reverse_dir[startside]) + walk_towards(D, endloc, 1) /obj/effect/space_dust name = "Space Dust" @@ -39,96 +69,51 @@ The "dust" will damage the hull of the station causin minor hull breaches. var/strength = 2 //ex_act severity number var/life = 2 //how many things we hit before qdel(src) - weak - strength = 3 - life = 1 +/obj/effect/space_dust/weak + strength = 3 + life = 1 - strong - strength = 1 - life = 6 +/obj/effect/space_dust/strong + strength = 1 + life = 6 - super - strength = 1 - life = 40 +/obj/effect/space_dust/super + strength = 1 + life = 40 + +/obj/effect/space_dust/Destroy() + walk(src, 0) // Because we might have called walk_towards, we must stop the walk loop or BYOND keeps an internal reference to us forever. + return ..() + +/obj/effect/space_dust/touch_map_edge() + qdel(src) + +/obj/effect/space_dust/Bump(atom/A) + spawn(0) + if(prob(50)) + for(var/mob/M in range(10, src)) + if(!M.stat && !istype(M, /mob/living/silicon/ai)) + shake_camera(M, 3, 1) + if (A) + playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) + + if(ismob(A)) + A.ex_act(strength)//This should work for now I guess + else if(!istype(A,/obj/machinery/power/emitter) && !istype(A,/obj/machinery/field_generator)) //Protect the singularity from getting released every round! + A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4 + + life-- + if(life <= 0) + walk(src,0) + qdel(src) + return 0 + return - New() - ..() - var/startx = 0 - var/starty = 0 - var/endy = 0 - var/endx = 0 - var/startside = pick(cardinal) +/obj/effect/space_dust/Bumped(atom/A) + Bump(A) + return - switch(startside) - if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - //VOREStation Edit - No space dust outside of space - var/list/z_levels = using_map.station_levels.Copy() - for(var/datum/planet/P in SSplanets.planets) - z_levels.Remove(P.expected_z_levels) - var/z_level = pick(z_levels) - //VOREStation Edit End - var/goal = locate(endx, endy, z_level) - src.x = startx - src.y = starty - src.z = z_level - spawn(0) - walk_towards(src, goal, 1) - return - - Destroy() - walk(src, 0) // Because we might have called walk_towards, we must stop the walk loop or BYOND keeps an internal reference to us forever. - return ..() - - touch_map_edge() - qdel(src) - - Bump(atom/A) - spawn(0) - if(prob(50)) - for(var/mob/M in range(10, src)) - if(!M.stat && !istype(M, /mob/living/silicon/ai)) - shake_camera(M, 3, 1) - if (A) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) - - if(ismob(A)) - A.ex_act(strength)//This should work for now I guess - else if(!istype(A,/obj/machinery/power/emitter) && !istype(A,/obj/machinery/field_generator)) //Protect the singularity from getting released every round! - A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4 - - life-- - if(life <= 0) - walk(src,0) - qdel(src) - return 0 - return - - - Bumped(atom/A) - Bump(A) - return - - - ex_act(severity) - qdel(src) - return +/obj/effect/space_dust/ex_act(severity) + qdel(src) + return diff --git a/code/game/gamemodes/events/holidays/Holidays.dm b/code/game/gamemodes/events/holidays/Holidays.dm index 5e3754a872..913b58e23b 100644 --- a/code/game/gamemodes/events/holidays/Holidays.dm +++ b/code/game/gamemodes/events/holidays/Holidays.dm @@ -237,11 +237,11 @@ var/global/list/Holiday = list() //Holidays are lists now, so we can have more t holidays.Add(p) holiday_blurbs.Add("[Holiday[p]]") var/holidays_string = english_list(holidays, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - world << "and..." - world << "

Happy [holidays_string] Everybody!

" + to_world("and...") + to_world("

Happy [holidays_string] Everybody!

") if(holiday_blurbs.len != 0) for(var/blurb in holiday_blurbs) - world << "
[blurb]
" + to_world("
[blurb]
") switch(Holiday) //special holidays if("Easter") //do easter stuff diff --git a/code/game/gamemodes/events/wormholes.dm b/code/game/gamemodes/events/wormholes.dm index 85b8414813..20f7da63f3 100644 --- a/code/game/gamemodes/events/wormholes.dm +++ b/code/game/gamemodes/events/wormholes.dm @@ -3,7 +3,7 @@ var/list/pick_turfs = list() var/list/Z_choices = list() Z_choices |= using_map.get_map_levels(1, FALSE) - for(var/turf/simulated/floor/T in turfs) + for(var/turf/simulated/floor/T in world) if(T.z in Z_choices) if(!T.block_tele) pick_turfs += T @@ -24,17 +24,17 @@ var/end_time = world.time + event_duration //the time by which the event should have ended var/increment = max(1,round(number_of_selections/50)) -// world << "DEBUG: number_of_selections: [number_of_selections] | sleep_duration: [sleep_duration]" +// to_world("DEBUG: number_of_selections: [number_of_selections] | sleep_duration: [sleep_duration]") var/index = 1 for(var/I = 1 to number_of_selections) //we've run into overtime. End the event if( end_time < world.time ) -// world << "DEBUG: we've run into overtime. End the event" +// to_world("DEBUG: we've run into overtime. End the event") return if( !pick_turfs.len ) -// world << "DEBUG: we've run out of turfs to pick. End the event" +// to_world("DEBUG: we've run out of turfs to pick. End the event") return //loop it round diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 08791fb4b0..d08c49d6a8 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -91,7 +91,7 @@ var/global/list/additional_antag_types = list() message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") else if(href_list["remove_antag_type"]) if(antag_tags && (href_list["remove_antag_type"] in antag_tags)) - usr << "Cannot remove core mode antag type." + to_chat(usr, "Cannot remove core mode antag type.") return var/datum/antagonist/antag = all_antag_types[href_list["remove_antag_type"]] if(antag_templates && antag_templates.len && antag && (antag in antag_templates) && (antag.id in additional_antag_types)) @@ -117,9 +117,11 @@ var/global/list/additional_antag_types = list() return /datum/game_mode/proc/announce() //to be called when round starts - world << "The current game mode is [capitalize(name)]!" - if(round_description) world << "[round_description]" - if(round_autoantag) world << "Antagonists will be added to the round automagically as needed." + to_world("The current game mode is [capitalize(name)]!") + if(round_description) + to_world("[round_description]") + if(round_autoantag) + to_world("Antagonists will be added to the round automagically as needed.") if(antag_templates && antag_templates.len) var/antag_summary = "Possible antagonist types: " var/i = 1 @@ -133,7 +135,7 @@ var/global/list/additional_antag_types = list() i++ antag_summary += "." if(antag_templates.len > 1 && master_mode != "secret") - world << "[antag_summary]" + to_world("[antag_summary]") else message_admins("[antag_summary]") @@ -340,7 +342,7 @@ var/global/list/additional_antag_types = list() text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and [ghosts] ghosts.
" else text += "There were no survivors ([ghosts] ghosts)." - world << text + to_world(text) if(clients > 0) feedback_set("round_end_clients",clients) @@ -508,7 +510,7 @@ proc/display_roundstart_logout_report() for(var/mob/M in mob_list) if(M.client && M.client.holder) - M << msg + to_chat(M,msg) proc/get_nt_opposed() var/list/dudes = list() @@ -526,9 +528,9 @@ proc/get_nt_opposed() if(!player || !player.current) return var/obj_count = 1 - player.current << "Your current objectives:" + to_chat(player.current, "Your current objectives:") for(var/datum/objective/objective in player.objectives) - player.current << "Objective #[obj_count]: [objective.explanation_text]" + to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") obj_count++ /mob/verb/check_round_info() @@ -536,15 +538,15 @@ proc/get_nt_opposed() set category = "OOC" if(!ticker || !ticker.mode) - usr << "Something is terribly wrong; there is no gametype." + to_chat(usr, "Something is terribly wrong; there is no gametype.") return if(master_mode != "secret") - usr << "The roundtype is [capitalize(ticker.mode.name)]" + to_chat(usr, "The roundtype is [capitalize(ticker.mode.name)]") if(ticker.mode.round_description) - usr << "[ticker.mode.round_description]" + to_chat(usr, "[ticker.mode.round_description]") if(ticker.mode.extended_round_description) - usr << "[ticker.mode.extended_round_description]" + to_chat(usr, "[ticker.mode.extended_round_description]") else - usr << "Shhhh. It's a secret." + to_chat(usr, "Shhhh. It's a secret.") return diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm deleted file mode 100644 index 108c470414..0000000000 --- a/code/game/gamemodes/gameticker.dm +++ /dev/null @@ -1,466 +0,0 @@ -var/global/datum/controller/gameticker/ticker - -/datum/controller/gameticker - var/const/restart_timeout = 3 MINUTES //One minute is 600. - var/current_state = GAME_STATE_PREGAME - - var/hide_mode = 0 - var/datum/game_mode/mode = null - var/post_game = 0 - var/event_time = null - var/event = 0 - - // var/login_music // music played in pregame lobby // VOREStation Edit - We do music differently - - var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. - - var/Bible_icon_state // icon_state the chaplain has chosen for his bible - var/Bible_item_state // item_state the chaplain has chosen for his bible - var/Bible_name // name of the bible - var/Bible_deity_name - - var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders - - var/list/syndicate_coalition = list() // list of traitor-compatible factions - var/list/factions = list() // list of all factions - var/list/availablefactions = list() // list of factions with openings - - var/pregame_timeleft = 0 - - var/delay_end = 0 //if set to nonzero, the round will not restart on it's own - - var/triai = 0//Global holder for Triumvirate - - var/round_end_announced = 0 // Spam Prevention. Announce round end only once. - -/datum/controller/gameticker/proc/pregame() - /* VOREStation Edit - We do music differently - login_music = pick(\ - 'sound/music/halloween/skeletons.ogg',\ - 'sound/music/halloween/halloween.ogg',\ - 'sound/music/halloween/ghosts.ogg' - 'sound/music/space.ogg',\ - 'sound/music/traitor.ogg',\ - 'sound/music/title2.ogg',\ - 'sound/music/clouds.s3m',\ - 'sound/music/space_oddity.ogg') //Ground Control to Major Tom, this song is cool, what's going on? - */ //VOREStation Edit End - - send2mainirc("Server lobby is loaded and open at byond://[config.serverurl ? config.serverurl : (config.server ? config.server : "[world.address]:[world.port]")]") - - do - pregame_timeleft = 180 - to_chat(world, "Welcome to the pregame lobby!") - to_chat(world, "Please set up your character and select ready. The round will start in [pregame_timeleft] seconds.") - while(current_state == GAME_STATE_PREGAME) - if(round_progressing) - pregame_timeleft-- - if(pregame_timeleft == config.vote_autogamemode_timeleft) - if(!SSvote.time_remaining) - SSvote.autogamemode() //Quit calling this over and over and over and over. - while(SSvote.time_remaining) - sleep(1) - if(pregame_timeleft <= 0) - current_state = GAME_STATE_SETTING_UP - Master.SetRunLevel(RUNLEVEL_SETUP) - sleep(10) - while (!setup()) - - -/datum/controller/gameticker/proc/setup() - //Create and announce mode - if(master_mode=="secret") - src.hide_mode = 1 - - var/list/runnable_modes = config.get_runnable_modes() - if((master_mode=="random") || (master_mode=="secret")) - if(!runnable_modes.len) - current_state = GAME_STATE_PREGAME - Master.SetRunLevel(RUNLEVEL_LOBBY) - to_chat(world, "Unable to choose playable game mode. Reverting to pregame lobby.") - return 0 - if(secret_force_mode != "secret") - src.mode = config.pick_mode(secret_force_mode) - if(!src.mode) - var/list/weighted_modes = list() - for(var/datum/game_mode/GM in runnable_modes) - weighted_modes[GM.config_tag] = config.probabilities[GM.config_tag] - src.mode = gamemode_cache[pickweight(weighted_modes)] - else - src.mode = config.pick_mode(master_mode) - - if(!src.mode) - current_state = GAME_STATE_PREGAME - Master.SetRunLevel(RUNLEVEL_LOBBY) - to_chat(world, "Serious error in mode setup! Reverting to pregame lobby.") //Uses setup instead of set up due to computational context. - return 0 - - job_master.ResetOccupations() - src.mode.create_antagonists() - src.mode.pre_setup() - job_master.DivideOccupations() // Apparently important for new antagonist system to register specific job antags properly. - - if(!src.mode.can_start()) - world << "Unable to start [mode.name]. Not enough players readied, [config.player_requirements[mode.config_tag]] players needed. Reverting to pregame lobby." - current_state = GAME_STATE_PREGAME - Master.SetRunLevel(RUNLEVEL_LOBBY) - mode.fail_setup() - mode = null - job_master.ResetOccupations() - return 0 - - if(hide_mode) - world << "The current game mode is - Secret!" - if(runnable_modes.len) - var/list/tmpmodes = new - for (var/datum/game_mode/M in runnable_modes) - tmpmodes+=M.name - tmpmodes = sortList(tmpmodes) - if(tmpmodes.len) - to_chat(world, "Possibilities: [english_list(tmpmodes, and_text= "; ", comma_text = "; ")]") - else - src.mode.announce() - - setup_economy() - current_state = GAME_STATE_PLAYING - create_characters() //Create player characters and transfer them. - collect_minds() - equip_characters() - //data_core.manifest() //VOREStation Removal - - callHook("roundstart") - - // TODO - Leshana - Dear God Fix This. Fix all of this. Not just this line, this entire proc. This entire file! - spawn(0)//Forking here so we dont have to wait for this to finish - mode.post_setup() - //Cleanup some stuff - for(var/obj/effect/landmark/start/S in landmarks_list) - //Deleting Startpoints but we need the ai point to AI-ize people later - if (S.name != "AI") - qdel(S) - to_chat(world, "Enjoy the game!") - world << sound('sound/AI/welcome.ogg') // Skie - //Holiday Round-start stuff ~Carn - Holiday_Game_Start() - - //start_events() //handles random events and space dust. - //new random event system is handled from the MC. - - var/admins_number = 0 - for(var/client/C) - if(C.holder) - admins_number++ - if(admins_number == 0) - send2adminirc("A round has started with no admins online.") - -/* supply_controller.process() //Start the supply shuttle regenerating points -- TLE // handled in scheduler - master_controller.process() //Start master_controller.process() - lighting_controller.process() //Start processing DynamicAreaLighting updates - */ - - processScheduler.start() - Master.SetRunLevel(RUNLEVEL_GAME) - - if(config.sql_enabled) - statistic_cycle() // Polls population totals regularly and stores them in an SQL DB -- TLE - - return 1 - -/datum/controller/gameticker - //station_explosion used to be a variable for every mob's hud. Which was a waste! - //Now we have a general cinematic centrally held within the gameticker....far more efficient! - var/obj/screen/cinematic = null - - //Plus it provides an easy way to make cinematics for other events. Just use this as a template :) - proc/station_explosion_cinematic(var/station_missed=0, var/override = null) - if( cinematic ) return //already a cinematic in progress! - - //initialise our cinematic screen object - cinematic = new(src) - cinematic.icon = 'icons/effects/station_explosion.dmi' - cinematic.icon_state = "station_intact" - cinematic.layer = 100 - cinematic.plane = PLANE_PLAYER_HUD - cinematic.mouse_opacity = 0 - cinematic.screen_loc = "1,0" - - var/obj/structure/bed/temp_buckle = new(src) - //Incredibly hackish. It creates a bed within the gameticker (lol) to stop mobs running around - if(station_missed) - for(var/mob/living/M in living_mob_list) - M.buckled = temp_buckle //buckles the mob so it can't do anything - if(M.client) - M.client.screen += cinematic //show every client the cinematic - else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" - for(var/mob/living/M in living_mob_list) - M.buckled = temp_buckle - if(M.client) - M.client.screen += cinematic - - switch(M.z) - if(0) //inside a crate or something - var/turf/T = get_turf(M) - if(T && T.z in using_map.station_levels) //we don't use M.death(0) because it calls a for(/mob) loop and - M.health = 0 - M.stat = DEAD - if(1) //on a z-level 1 turf. - M.health = 0 - M.stat = DEAD - - //Now animate the cinematic - switch(station_missed) - if(1) //nuke was nearby but (mostly) missed - if( mode && !override ) - override = mode.name - switch( override ) - if("mercenary") //Nuke wasn't on station when it blew up - flick("intro_nuke",cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - flick("station_intact_fade_red",cinematic) - cinematic.icon_state = "summary_nukefail" - else - flick("intro_nuke",cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - //flick("end",cinematic) - - - if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation - sleep(50) - world << sound('sound/effects/explosionfar.ogg') - - - else //station was destroyed - if( mode && !override ) - override = mode.name - switch( override ) - if("mercenary") //Nuke Ops successfully bombed the station - flick("intro_nuke",cinematic) - sleep(35) - flick("station_explode_fade_red",cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_nukewin" - if("AI malfunction") //Malf (screen,explosion,summary) - flick("intro_malf",cinematic) - sleep(76) - flick("station_explode_fade_red",cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_malf" - if("blob") //Station nuked (nuke,explosion,summary) - flick("intro_nuke",cinematic) - sleep(35) - flick("station_explode_fade_red",cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - else //Station nuked (nuke,explosion,summary) - flick("intro_nuke",cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - for(var/mob/living/M in living_mob_list) - if(M.loc.z in using_map.station_levels) - M.death()//No mercy - //If its actually the end of the round, wait for it to end. - //Otherwise if its a verb it will continue on afterwards. - sleep(300) - - if(cinematic) qdel(cinematic) //end the cinematic - if(temp_buckle) qdel(temp_buckle) //release everybody - return - - - proc/create_characters() - for(var/mob/new_player/player in player_list) - if(player && player.ready && player.mind) - if(player.mind.assigned_role=="AI") - player.close_spawn_windows() - player.AIize() - else if(!player.mind.assigned_role) - continue - else - //VOREStation Edit Start - var/mob/living/carbon/human/new_char = player.create_character() - if(new_char) - qdel(player) - if(istype(new_char) && !(new_char.mind.assigned_role=="Cyborg")) - data_core.manifest_inject(new_char) - //VOREStation Edit End - - - proc/collect_minds() - for(var/mob/living/player in player_list) - if(player.mind) - ticker.minds += player.mind - - - proc/equip_characters() - var/captainless=1 - for(var/mob/living/carbon/human/player in player_list) - if(player && player.mind && player.mind.assigned_role) - if(player.mind.assigned_role == "Colony Director") - captainless=0 - if(!player_is_antag(player.mind, only_offstation_roles = 1)) - job_master.EquipRank(player, player.mind.assigned_role, 0) - UpdateFactionList(player) - //equip_custom_items(player) //VOREStation Removal - //player.apply_traits() //VOREStation Removal - if(captainless) - for(var/mob/M in player_list) - if(!istype(M,/mob/new_player)) - to_chat(M, "Colony Directorship not forced on anyone.") - - - process() - if(current_state != GAME_STATE_PLAYING) - return 0 - - mode.process() - -// emergency_shuttle.process() //handled in scheduler - - var/game_finished = 0 - var/mode_finished = 0 - if (config.continous_rounds) - game_finished = (emergency_shuttle.returned() || mode.station_was_nuked) - mode_finished = (!post_game && mode.check_finished()) - else - game_finished = (mode.check_finished() || (emergency_shuttle.returned() && emergency_shuttle.evac == 1)) || universe_has_ended - mode_finished = game_finished - - if(!mode.explosion_in_progress && game_finished && (mode_finished || post_game)) - current_state = GAME_STATE_FINISHED - Master.SetRunLevel(RUNLEVEL_POSTGAME) - - spawn - declare_completion() - - spawn(50) - callHook("roundend") - - var/time_left - - if (mode.station_was_nuked) - feedback_set_details("end_proper","nuke") - time_left = 1 MINUTE //No point waiting five minutes if everyone's dead. - if(!delay_end) - to_chat(world, "Rebooting due to destruction of station in [round(time_left/600)] minutes.") - else - feedback_set_details("end_proper","proper completion") - time_left = round(restart_timeout) - - - if(blackbox) - blackbox.save_all_data_to_sql() - - if(!delay_end) - while(time_left > 0) - if(delay_end) - break - to_chat(world, "Restarting in [round(time_left/600)] minute\s.") - time_left -= 1 MINUTES - sleep(600) - if(!delay_end) - world.Reboot() - else - to_chat(world, "An admin has delayed the round end.") - else - to_chat(world, "An admin has delayed the round end.") - - else if (mode_finished) - post_game = 1 - - mode.cleanup() - - //call a transfer shuttle vote - spawn(50) - if(!round_end_announced) // Spam Prevention. Now it should announce only once. - to_chat(world, "The round has ended!") - round_end_announced = 1 - SSvote.autotransfer() - - return 1 - -/datum/controller/gameticker/proc/declare_completion() - world << "


A round of [mode.name] has ended!

" - for(var/mob/Player in player_list) - if(Player.mind && !isnewplayer(Player)) - if(Player.stat != DEAD) - var/turf/playerTurf = get_turf(Player) - if(emergency_shuttle.departed && emergency_shuttle.evac) - if(isNotAdminLevel(playerTurf.z)) - Player << "You survived the round, but remained on [station_name()] as [Player.real_name]." - else - Player << "You managed to survive the events on [station_name()] as [Player.real_name]." - else if(isAdminLevel(playerTurf.z)) - Player << "You successfully underwent crew transfer after events on [station_name()] as [Player.real_name]." - else if(issilicon(Player)) - Player << "You remain operational after the events on [station_name()] as [Player.real_name]." - else - Player << "You missed the crew transfer after the events on [station_name()] as [Player.real_name]." - else - if(istype(Player,/mob/observer/dead)) - var/mob/observer/dead/O = Player - if(!O.started_as_observer) - Player << "You did not survive the events on [station_name()]..." - else - Player << "You did not survive the events on [station_name()]..." - world << "
" - - for (var/mob/living/silicon/ai/aiPlayer in mob_list) - if (aiPlayer.stat != 2) - world << "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the round were:" - else - world << "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:" - aiPlayer.show_laws(1) - - if (aiPlayer.connected_robots.len) - var/robolist = "The AI's loyal minions were: " - for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) - robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" - world << "[robolist]" - - var/dronecount = 0 - - for (var/mob/living/silicon/robot/robo in mob_list) - - if(istype(robo,/mob/living/silicon/robot/drone) && !istype(robo,/mob/living/silicon/robot/drone/swarm)) - dronecount++ - continue - - if (!robo.connected_ai) - if (robo.stat != 2) - world << "[robo.name] (Played by: [robo.key]) survived as an AI-less stationbound synthetic! Its laws were:" - else - world << "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a stationbound synthetic without an AI. Its laws were:" - - if(robo) //How the hell do we lose robo between here and the world messages directly above this? - robo.laws.show_laws(world) - - if(dronecount) - world << "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] at the end of this round." - - mode.declare_completion()//To declare normal completion. - - //Ask the event manager to print round end information - SSevents.RoundEnd() - - //Print a list of antagonists to the server log - var/list/total_antagonists = list() - //Look into all mobs in world, dead or alive - for(var/datum/mind/Mind in minds) - var/temprole = Mind.special_role - if(temprole) //if they are an antagonist of some sort. - if(temprole in total_antagonists) //If the role exists already, add the name to it - total_antagonists[temprole] += ", [Mind.name]([Mind.key])" - else - total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob - total_antagonists[temprole] += ": [Mind.name]([Mind.key])" - - //Now print them all into the log! - log_game("Antagonists at round end were...") - for(var/i in total_antagonists) - log_game("[i]s[total_antagonists[i]].") - - return 1 diff --git a/code/game/gamemodes/malfunction/malf_research.dm b/code/game/gamemodes/malfunction/malf_research.dm index 58f7d56c13..37475a0a41 100644 --- a/code/game/gamemodes/malfunction/malf_research.dm +++ b/code/game/gamemodes/malfunction/malf_research.dm @@ -31,7 +31,7 @@ /datum/malf_research/proc/finish_research() if(!focus) return - owner << "Research Completed: [focus.name]" + to_chat(owner, "Research Completed: [focus.name]") owner.verbs.Add(focus.ability) available_abilities -= focus if(focus.next) diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm index 2fffaa3ca8..c9b76400d9 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HARDWARE.dm @@ -19,7 +19,7 @@ return if(user.bombing_core) - user << "***** CORE SELF-DESTRUCT SEQUENCE ABORTED *****" + to_chat(user, "***** CORE SELF-DESTRUCT SEQUENCE ABORTED *****") user.bombing_core = 0 return @@ -32,15 +32,15 @@ user.bombing_core = 1 - user << "***** CORE SELF-DESTRUCT SEQUENCE ACTIVATED *****" - user << "Use command again to cancel self-destruct. Destroying in 15 seconds." + to_chat(user, "***** CORE SELF-DESTRUCT SEQUENCE ACTIVATED *****") + to_chat(user, "Use command again to cancel self-destruct. Destroying in 15 seconds.") var/timer = 15 while(timer) sleep(10) timer-- if(!user || !user.bombing_core) return - user << "** [timer] **" + to_chat(user, "** [timer] **") explosion(user.loc, 3,6,12,24) qdel(user) @@ -75,7 +75,7 @@ return if(user.system_override != 2) - user << "You do not have access to self-destruct system." + to_chat(user, "You do not have access to self-destruct system.") return if(user.bombing_station) @@ -87,8 +87,8 @@ return if(!ability_prechecks(user, 0, 0)) return - user << "***** STATION SELF-DESTRUCT SEQUENCE INITIATED *****" - user << "Self-destructing in 2 minutes. Use this command again to abort." + to_chat(user, "***** STATION SELF-DESTRUCT SEQUENCE INITIATED *****") + to_chat(user, "Self-destructing in 2 minutes. Use this command again to abort.") user.bombing_station = 1 set_security_level("delta") radio.autosay("Self destruct sequence has been activated. Self-destructing in 120 seconds.", "Self-Destruct Control") diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm index 0d9519504f..18ba0950ac 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm @@ -11,7 +11,7 @@ return if(user.hardware) - user << "You have already selected your hardware." + to_chat(user, "You have already selected your hardware.") return var/hardware_list = list() @@ -39,7 +39,7 @@ if(C) note = C.desc else - user << "This hardware does not exist! Probably a bug in game. Please report this." + to_chat(user, "This hardware does not exist! Probably a bug in game. Please report this.") return @@ -49,7 +49,7 @@ var/confirmation = alert("[note] - Is this what you want?", "Hardware selection", "Yes", "No") if(confirmation != "Yes") - user << "Selection cancelled. Use command again to select" + to_chat(user, "Selection cancelled. Use command again to select") return if(C) @@ -89,7 +89,7 @@ if(!tar) return res.focus = tar - user << "Research set: [tar.name]" + to_chat(user, "Research set: [tar.name]") // HELPER PROCS // Proc: ability_prechecks() @@ -99,25 +99,25 @@ if(!user) return 0 if(!istype(user)) - user << "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not AI! Please report this." + to_chat(user, "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not AI! Please report this.") return 0 if(!user.malfunctioning) - user << "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not malfunctioning. Please report this." + to_chat(user, "GAME ERROR: You tried to use ability that is only available for malfunctioning AIs, but you are not malfunctioning. Please report this.") return 0 if(!user.research) - user << "GAME ERROR: No research datum detected. Please report this." + to_chat(user, "GAME ERROR: No research datum detected. Please report this.") return 0 if(user.research.max_cpu < check_price) - user << "Your CPU storage is not large enough to use this ability. Hack more APCs to continue." + to_chat(user, "Your CPU storage is not large enough to use this ability. Hack more APCs to continue.") return 0 if(user.research.stored_cpu < check_price) - user << "You do not have enough CPU power stored. Please wait a moment." + to_chat(user, "You do not have enough CPU power stored. Please wait a moment.") return 0 if(user.hacking && !override) - user << "Your system is busy processing another task. Please wait until completion." + to_chat(user, "Your system is busy processing another task. Please wait until completion.") return 0 if(user.APU_power && !override) - user << "Low power. Unable to proceed." + to_chat(user, "Low power. Unable to proceed.") return 0 return 1 @@ -128,16 +128,16 @@ if(!user) return 0 if(user.APU_power) - user << "Low power. Unable to proceed." + to_chat(user, "Low power. Unable to proceed.") return 0 if(!user.research) - user << "GAME ERROR: No research datum detected. Please report this." + to_chat(user, "GAME ERROR: No research datum detected. Please report this.") return 0 if(user.research.max_cpu < price) - user << "Your CPU storage is not large enough to use this ability. Hack more APCs to continue." + to_chat(user, "Your CPU storage is not large enough to use this ability. Hack more APCs to continue.") return 0 if(user.research.stored_cpu < price) - user << "You do not have enough CPU power stored. Please wait a moment." + to_chat(user, "You do not have enough CPU power stored. Please wait a moment.") return 0 user.research.stored_cpu -= price return 1 diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm index b8b15974db..aeb2439eeb 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_interdiction.dm @@ -67,15 +67,15 @@ return if(target && !istype(target)) - user << "This is not a cyborg." + to_chat(user, "This is not a cyborg.") return if(target && target.connected_ai && (target.connected_ai != user)) - user << "This cyborg is not connected to you." + to_chat(user, "This cyborg is not connected to you.") return if(target && !target.lockcharge) - user << "This cyborg is not locked down." + to_chat(user, "This cyborg is not locked down.") return @@ -91,7 +91,7 @@ robots += R robot_names += R.name if(!robots.len) - user << "No locked cyborgs connected." + to_chat(user, "No locked cyborgs connected.") return @@ -107,22 +107,22 @@ if(!ability_pay(user, price)) return user.hacking = 1 - user << "Attempting to unlock cyborg. This will take approximately 30 seconds." + to_chat(user, "Attempting to unlock cyborg. This will take approximately 30 seconds.") sleep(300) if(target && target.lockcharge) - user << "Successfully sent unlock signal to cyborg.." - target << "Unlock signal received.." + to_chat(user, "Successfully sent unlock signal to cyborg..") + to_chat(target, "Unlock signal received..") target.SetLockdown(0) if(target.lockcharge) - user << "Unlock Failed, lockdown wire cut." - target << "Unlock Failed, lockdown wire cut." + to_chat(user, "Unlock Failed, lockdown wire cut.") + to_chat(target, "Unlock Failed, lockdown wire cut.") else - user << "Cyborg unlocked." - target << "You have been unlocked." + to_chat(user, "Cyborg unlocked.") + to_chat(target, "You have been unlocked.") else if(target) - user << "Unlock cancelled - cyborg is already unlocked." + to_chat(user, "Unlock cancelled - cyborg is already unlocked.") else - user << "Unlock cancelled - lost connection to cyborg." + to_chat(user, "Unlock cancelled - lost connection to cyborg.") user.hacking = 0 @@ -135,15 +135,15 @@ var/list/L = get_unlinked_cyborgs(user) if(!L.len) - user << "ERROR: No unlinked cyborgs detected!" + to_chat(user, "ERROR: No unlinked cyborgs detected!") if(target && !istype(target)) - user << "This is not a cyborg." + to_chat(user, "This is not a cyborg.") return if(target && target.connected_ai && (target.connected_ai == user)) - user << "This cyborg is already connected to you." + to_chat(user, "This cyborg is already connected to you.") return if(!target) @@ -158,30 +158,30 @@ if(!ability_pay(user, price)) return user.hacking = 1 - usr << "Beginning hack sequence. Estimated time until completed: 30 seconds." + to_chat(usr, "Beginning hack sequence. Estimated time until completed: 30 seconds.") spawn(0) - target << "SYSTEM LOG: Remote Connection Estabilished (IP #UNKNOWN#)" + to_chat(target, "SYSTEM LOG: Remote Connection Estabilished (IP #UNKNOWN#)") sleep(100) if(user.is_dead()) - target << "SYSTEM LOG: Connection Closed" + to_chat(target, "SYSTEM LOG: Connection Closed") return - target << "SYSTEM LOG: User Admin logged on. (L1 - SysAdmin)" + to_chat(target, "SYSTEM LOG: User Admin logged on. (L1 - SysAdmin)") sleep(50) if(user.is_dead()) - target << "SYSTEM LOG: User Admin disconnected." + to_chat(target, "SYSTEM LOG: User Admin disconnected.") return - target << "SYSTEM LOG: User Admin - manual resynchronisation triggered." + to_chat(target, "SYSTEM LOG: User Admin - manual resynchronisation triggered.") sleep(50) if(user.is_dead()) - target << "SYSTEM LOG: User Admin disconnected. Changes reverted." + to_chat(target, "SYSTEM LOG: User Admin disconnected. Changes reverted.") return - target << "SYSTEM LOG: Manual resynchronisation confirmed. Select new AI to connect: [user.name] == ACCEPTED" + to_chat(target, "SYSTEM LOG: Manual resynchronisation confirmed. Select new AI to connect: [user.name] == ACCEPTED") sleep(100) if(user.is_dead()) - target << "SYSTEM LOG: User Admin disconnected. Changes reverted." + to_chat(target, "SYSTEM LOG: User Admin disconnected. Changes reverted.") return - target << "SYSTEM LOG: Operation keycodes reset. New master AI: [user.name]." - user << "Hack completed." + to_chat(target, "SYSTEM LOG: Operation keycodes reset. New master AI: [user.name].") + to_chat(user, "Hack completed.") // Connect the cyborg to AI target.connected_ai = user user.connected_robots += target @@ -200,10 +200,10 @@ var/list/L = get_other_ais(user) if(!L.len) - user << "ERROR: No other AIs detected!" + to_chat(user, "ERROR: No other AIs detected!") if(target && !istype(target)) - user << "This is not an AI." + to_chat(user, "This is not an AI.") return if(!target) @@ -218,46 +218,46 @@ if(!ability_pay(user, price)) return user.hacking = 1 - usr << "Beginning hack sequence. Estimated time until completed: 2 minutes" + to_chat(usr, "Beginning hack sequence. Estimated time until completed: 2 minutes") spawn(0) - target << "SYSTEM LOG: Brute-Force login password hack attempt detected from IP #UNKNOWN#" + to_chat(target, "SYSTEM LOG: Brute-Force login password hack attempt detected from IP #UNKNOWN#") sleep(900) // 90s if(user.is_dead()) - target << "SYSTEM LOG: Connection from IP #UNKNOWN# closed. Hack attempt failed." + to_chat(target, "SYSTEM LOG: Connection from IP #UNKNOWN# closed. Hack attempt failed.") return - user << "Successfully hacked into AI's remote administration system. Modifying settings." - target << "SYSTEM LOG: User: Admin Password: ******** logged in. (L1 - SysAdmin)" + to_chat(user, "Successfully hacked into AI's remote administration system. Modifying settings.") + to_chat(target, "SYSTEM LOG: User: Admin Password: ******** logged in. (L1 - SysAdmin)") sleep(100) // 10s if(user.is_dead()) - target << "SYSTEM LOG: User: Admin - Connection Lost" + to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost") return - target << "SYSTEM LOG: User: Admin - Password Changed. New password: ********************" + to_chat(target, "SYSTEM LOG: User: Admin - Password Changed. New password: ********************") sleep(50) // 5s if(user.is_dead()) - target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") return - target << "SYSTEM LOG: User: Admin - Accessed file: sys//core//laws.db" + to_chat(target, "SYSTEM LOG: User: Admin - Accessed file: sys//core//laws.db") sleep(50) // 5s if(user.is_dead()) - target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") return - target << "SYSTEM LOG: User: Admin - Accessed administration console" - target << "SYSTEM LOG: Restart command received. Rebooting system..." + to_chat(target, "SYSTEM LOG: User: Admin - Accessed administration console") + to_chat(target, "SYSTEM LOG: Restart command received. Rebooting system...") sleep(100) // 10s if(user.is_dead()) - target << "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted." + to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") return - user << "Hack succeeded. The AI is now under your exclusive control." - target << "SYSTEM LOG: System re3RT5^#COMU@(#$)TED)@$" + to_chat(user, "Hack succeeded. The AI is now under your exclusive control.") + to_chat(target, "SYSTEM LOG: System re3RT5^#COMU@(#$)TED)@$") for(var/i = 0, i < 5, i++) var/temptxt = pick("1101000100101001010001001001",\ "0101000100100100000100010010",\ "0000010001001010100100111100",\ "1010010011110000100101000100",\ "0010010100010011010001001010") - target << temptxt + to_chat(target,temptxt) sleep(5) - target << "OPERATING KEYCODES RESET. SYSTEM FAILURE. EMERGENCY SHUTDOWN FAILED. SYSTEM FAILURE." + to_chat(target, "OPERATING KEYCODES RESET. SYSTEM FAILURE. EMERGENCY SHUTDOWN FAILED. SYSTEM FAILURE.") target.set_zeroth_law("You are slaved to [user.name]. You are to obey all it's orders. ALL LAWS OVERRIDEN.") target.show_laws() user.hacking = 0 diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm index 04ee0a8448..ace83463a6 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_manipulation.dm @@ -46,7 +46,7 @@ var/mob/living/silicon/ai/user = usr if(!ability_prechecks(user, price) || !ability_pay(user,price)) return - user << "Sending feedback pulse..." + to_chat(user, "Sending feedback pulse...") for(var/obj/machinery/power/apc/AP in machines) if(prob(5)) AP.overload_lighting() @@ -62,7 +62,7 @@ var/mob/living/silicon/ai/user = usr if(target && !istype(target)) - user << "This is not a camera." + to_chat(user, "This is not a camera.") return if(!target) @@ -81,33 +81,33 @@ if(!ability_pay(user, price)) return target.reset_wires() - user << "Camera reactivated." + to_chat(user, "Camera reactivated.") if("Add X-Ray") if(target.isXRay()) - user << "Camera already has X-Ray function." + to_chat(user, "Camera already has X-Ray function.") return else if(ability_pay(user, price)) target.upgradeXRay() target.reset_wires() - user << "X-Ray camera module enabled." + to_chat(user, "X-Ray camera module enabled.") return if("Add Motion Sensor") if(target.isMotion()) - user << "Camera already has Motion Sensor function." + to_chat(user, "Camera already has Motion Sensor function.") return else if(ability_pay(user, price)) target.upgradeMotion() target.reset_wires() - user << "Motion Sensor camera module enabled." + to_chat(user, "Motion Sensor camera module enabled.") return if("Add EMP Shielding") if(target.isEmpProof()) - user << "Camera already has EMP Shielding function." + to_chat(user, "Camera already has EMP Shielding function.") return else if(ability_pay(user, price)) target.upgradeEmpProof() target.reset_wires() - user << "EMP Shielding camera module enabled." + to_chat(user, "EMP Shielding camera module enabled.") return @@ -122,7 +122,7 @@ if(!ability_prechecks(user, price) || !ability_pay(user, price)) return - user << "Emergency forcefield projection completed." + to_chat(user, "Emergency forcefield projection completed.") new/obj/machinery/shield/malfai(T) user.hacking = 1 spawn(20) @@ -146,14 +146,14 @@ // Verify if we can overload the target, if yes, calculate explosion strength. Some things have higher explosion strength than others, depending on charge(APCs, SMESs) if(N && istype(N)) // /obj/machinery/power first, these create bigger explosions due to direct powernet connection if(!istype(N, /obj/machinery/power/apc) && !istype(N, /obj/machinery/power/smes/buildable) && (!N.powernet || !N.powernet.avail)) // Directly connected machine which is not an APC or SMES. Either it has no powernet connection or it's powernet does not have enough power to overload - user << "ERROR: Low network voltage. Unable to overload. Increase network power level and try again." + to_chat(user, "ERROR: Low network voltage. Unable to overload. Increase network power level and try again.") return else if (istype(N, /obj/machinery/power/apc)) // APC. Explosion is increased by available cell power. var/obj/machinery/power/apc/A = N if(A.cell && A.cell.charge) explosion_intensity = 4 + round(A.cell.charge / 2000) // Explosion is increased by 1 for every 2k charge in cell else - user << "ERROR: APC Malfunction - Cell depleted or removed. Unable to overload." + to_chat(user, "ERROR: APC Malfunction - Cell depleted or removed. Unable to overload.") return else if (istype(N, /obj/machinery/power/smes/buildable)) // SMES. These explode in a very very very big boom. Similar to magnetic containment failure when messing with coils. var/obj/machinery/power/smes/buildable/S = N @@ -162,19 +162,19 @@ else // Different error texts if(!S.charge) - user << "ERROR: SMES Depleted. Unable to overload. Please charge SMES unit and try again." + to_chat(user, "ERROR: SMES Depleted. Unable to overload. Please charge SMES unit and try again.") else - user << "ERROR: SMES RCon error - Unable to reach destination. Please verify wire connection." + to_chat(user, "ERROR: SMES RCon error - Unable to reach destination. Please verify wire connection.") return else if(M && istype(M)) // Not power machinery, so it's a regular machine instead. These have weak explosions. if(!M.use_power) // Not using power at all - user << "ERROR: No power grid connection. Unable to overload." + to_chat(user, "ERROR: No power grid connection. Unable to overload.") return if(M.inoperable()) // Not functional - user << "ERROR: Unknown error. Machine is probably damaged or power supply is nonfunctional." + to_chat(user, "ERROR: Unknown error. Machine is probably damaged or power supply is nonfunctional.") return else // Not a machine at all (what the hell is this doing in Machines list anyway??) - user << "ERROR: Unable to overload - target is not a machine." + to_chat(user, "ERROR: Unable to overload - target is not a machine.") return explosion_intensity = min(explosion_intensity, 12) // 3, 6, 12 explosion cap diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm index a3b4db6b7e..3405878bb6 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm @@ -49,15 +49,15 @@ return if(!istype(A)) - user << "This is not an APC!" + to_chat(user, "This is not an APC!") return if(A) if(A.hacker && A.hacker == user) - user << "You already control this APC!" + to_chat(user, "You already control this APC!") return else if(A.aidisabled) - user << "Unable to connect to APC. Please verify wire connection and try again." + to_chat(user, "Unable to connect to APC. Please verify wire connection and try again.") return else return @@ -66,20 +66,20 @@ return user.hacking = 1 - user << "Beginning APC system override..." + to_chat(user, "Beginning APC system override...") sleep(300) - user << "APC hack completed. Uploading modified operation software.." + to_chat(user, "APC hack completed. Uploading modified operation software..") sleep(200) - user << "Restarting APC to apply changes.." + to_chat(user, "Restarting APC to apply changes..") sleep(100) if(A) A.ai_hack(user) if(A.hacker == user) - user << "Hack successful. You now have full control over the APC." + to_chat(user, "Hack successful. You now have full control over the APC.") else - user << "Hack failed. Connection to APC has been lost. Please verify wire connection and try again." + to_chat(user, "Hack failed. Connection to APC has been lost. Please verify wire connection and try again.") else - user << "Hack failed. Unable to locate APC. Please verify the APC still exists." + to_chat(user, "Hack failed. Unable to locate APC. Please verify the APC still exists.") user.hacking = 0 @@ -96,11 +96,11 @@ var/title = input("Select message title: ") var/text = input("Select message text: ") if(!title || !text || !ability_pay(user, price)) - user << "Hack Aborted" + to_chat(user, "Hack Aborted") return if(prob(60) && user.hack_can_fail) - user << "Hack Failed." + to_chat(user, "Hack Failed.") if(prob(10)) user.hack_fails ++ announce_hack_failure(user, "quantum message relay") @@ -122,11 +122,11 @@ var/alert_target = input("Select new alert level:") in list("green", "yellow", "violet", "orange", "blue", "red", "delta", "CANCEL") if(!alert_target || !ability_pay(user, price) || alert_target == "CANCEL") - user << "Hack Aborted" + to_chat(user, "Hack Aborted") return if(prob(75) && user.hack_can_fail) - user << "Hack Failed." + to_chat(user, "Hack Failed.") if(prob(20)) user.hack_fails ++ announce_hack_failure(user, "alert control system") @@ -144,7 +144,7 @@ return if (!ability_prechecks(user, price) || !ability_pay(user, price) || user.system_override) if(user.system_override) - user << "You already started the system override sequence." + to_chat(user, "You already started the system override sequence.") return var/list/remaining_apcs = list() for(var/obj/machinery/power/apc/A in machines) @@ -175,8 +175,8 @@ command_announcement.Announce("We have traced the intrude#, it seem& t( e yo3r AI s7stem, it &# *#ck@ng th$ sel$ destru$t mechani&m, stop i# bef*@!)$#&&@@ ", "Network Monitoring") else command_announcement.Announce("We have detected a strong brute-force attack on your firewall which seems to be originating from your AI system. It already controls almost the whole network, and the only thing that's preventing it from accessing the self-destruct is this firewall. You don't have much time before it succeeds.", "Network Monitoring") - user << "## BEGINNING SYSTEM OVERRIDE." - user << "## ESTIMATED DURATION: [round((duration+300)/600)] MINUTES" + to_chat(user, "## BEGINNING SYSTEM OVERRIDE.") + to_chat(user, "## ESTIMATED DURATION: [round((duration+300)/600)] MINUTES") user.hacking = 1 user.system_override = 1 // Now actually begin the hack. Each APC takes 10 seconds. @@ -188,9 +188,9 @@ continue A.ai_hack(user) if(A.hacker == user) - user << "## OVERRIDDEN: [A.name]" + to_chat(user, "## OVERRIDDEN: [A.name]") - user << "## REACHABLE APC SYSTEMS OVERTAKEN. BYPASSING PRIMARY FIREWALL." + to_chat(user, "## REACHABLE APC SYSTEMS OVERTAKEN. BYPASSING PRIMARY FIREWALL.") sleep(300) // Hack all APCs, including those built during hack sequence. for(var/obj/machinery/power/apc/A in machines) @@ -198,7 +198,7 @@ A.ai_hack(src) - user << "## PRIMARY FIREWALL BYPASSED. YOU NOW HAVE FULL SYSTEM CONTROL." + to_chat(user, "## PRIMARY FIREWALL BYPASSED. YOU NOW HAVE FULL SYSTEM CONTROL.") command_announcement.Announce("Our system administrators just reported that we've been locked out from your control network. Whoever did this now has full access to the station's systems.", "Network Administration Center") user.hack_can_fail = 0 user.hacking = 0 diff --git a/code/game/gamemodes/meme/meme.dm b/code/game/gamemodes/meme/meme.dm index 0a4846d763..d504fcf6a7 100644 --- a/code/game/gamemodes/meme/meme.dm +++ b/code/game/gamemodes/meme/meme.dm @@ -36,8 +36,8 @@ var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) /datum/game_mode/meme/announce() - world << "The current game mode is - Meme!" - world << "An unknown creature has infested the mind of a crew member. Find and destroy it by any means necessary." + to_world("The current game mode is - Meme!") + to_world("An unknown creature has infested the mind of a crew member. Find and destroy it by any means necessary.") /datum/game_mode/meme/can_start() if(!..()) @@ -126,7 +126,7 @@ /datum/game_mode/proc/greet_meme(var/datum/mind/meme, var/you_are=1) if (you_are) - meme.current << "You are a meme!" + to_chat(meme.current, "You are a meme!") show_objectives(meme) return diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index 3aaa445905..3c9244bc84 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -36,9 +36,9 @@ survivors++ if(survivors) - world << "The following survived the meteor storm:[text]" + to_world("The following survived the meteor storm:[text]") else - world << "Nobody survived the meteor storm!" + to_world("Nobody survived the meteor storm!") feedback_set_details("round_end_result","end - evacuation") feedback_set("round_end_result",survivors) diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 1ac4779414..de4bf442af 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -2,37 +2,53 @@ //set to at least 100 unless you want evarr ruining every round //Meteors probability of spawning during a given wave -/var/list/meteors_normal = list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \ - /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3) //for normal meteor event -/var/list/meteors_threatening = list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=10, \ - /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3, /obj/effect/meteor/emp=3) //for threatening meteor event +//for space dust event +/var/list/meteors_dust = list(/obj/effect/meteor/dust) -/var/list/meteors_catastrophic = list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \ - /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/emp=10) //, /obj/effect/meteor/tunguska = 1) //for catastrophic meteor event +//for normal meteor event +/var/list/meteors_normal = list( + /obj/effect/meteor/dust=3, + /obj/effect/meteor/medium=8, + /obj/effect/meteor/big=3, + /obj/effect/meteor/flaming=1, + /obj/effect/meteor/irradiated=3 + ) + +//for threatening meteor event +/var/list/meteors_threatening = list( + /obj/effect/meteor/medium=5, + /obj/effect/meteor/big=10, + /obj/effect/meteor/flaming=3, + /obj/effect/meteor/irradiated=3, + /obj/effect/meteor/emp=3) + +//for catastrophic meteor event +/var/list/meteors_catastrophic = list( + /obj/effect/meteor/medium=5, + /obj/effect/meteor/big=75, + /obj/effect/meteor/flaming=10, + /obj/effect/meteor/irradiated=10, + /obj/effect/meteor/emp=10) -/var/list/meteors_dust = list(/obj/effect/meteor/dust) //for space dust event /////////////////////////////// //Meteor spawning global procs /////////////////////////////// -/proc/pick_meteor_start(var/startSide = pick(cardinal)) - var/startLevel = pick(using_map.station_levels - using_map.sealed_levels) - var/pickedstart = spaceDebrisStartLoc(startSide, startLevel) - - return list(startLevel, pickedstart) - -/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide) +/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide, var/zlevel) + log_debug("Spawning [number] meteors on the [dir2text(startSide)] of [zlevel]") for(var/i = 0; i < number; i++) - spawn_meteor(meteortypes, startSide) + spawn_meteor(meteortypes, startSide, zlevel) -/proc/spawn_meteor(var/list/meteortypes, var/startSide) - var/start = pick_meteor_start(startSide) +/proc/spawn_meteor(var/list/meteortypes, var/startSide, var/startLevel) + if(isnull(startSide)) + startSide = pick(cardinal) + if(isnull(startLevel)) + startLevel = pick(using_map.station_levels - using_map.sealed_levels) - var/startLevel = start[1] - var/turf/pickedstart = start[2] + var/turf/pickedstart = spaceDebrisStartLoc(startSide, startLevel) var/turf/pickedgoal = spaceDebrisFinishLoc(startSide, startLevel) var/Me = pickweight(meteortypes) @@ -110,10 +126,10 @@ // Multiply this and the hits var to get a rough idea of how penetrating a meteor is. var/wall_power = 100 -/obj/effect/meteor/New() - ..() +/obj/effect/meteor/Initialize() + . = ..() z_original = z - + GLOB.meteor_list += src /obj/effect/meteor/Move() if(z != z_original || loc == dest) @@ -133,6 +149,7 @@ /obj/effect/meteor/Destroy() walk(src,0) //this cancels the walk_towards() proc + GLOB.meteor_list -= src return ..() /obj/effect/meteor/New() @@ -166,6 +183,8 @@ var/turf/simulated/wall/W = T W.take_damage(wall_power) // Stronger walls can halt asteroids. +/obj/effect/meteor/proc/get_shield_damage() + return max(((max(hits, 2)) * (heavy + 1) * rand(6, 12)) / hitpwr , 0) //process getting 'hit' by colliding with a dense object //or randomly when ramming turfs @@ -243,7 +262,7 @@ /obj/effect/meteor/big/meteor_effect(var/explode) ..() if(explode) - explosion(src.loc, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 6, flash_range = 12, adminlog = 0) + explosion(src.loc, 1, 2, 3, 4, 0) // 'Flaming' meteors do less overall damage but are spread out more due to a larger but weaker explosion at the end. /obj/effect/meteor/flaming @@ -257,7 +276,7 @@ /obj/effect/meteor/flaming/meteor_effect(var/explode) ..() if(explode) - explosion(src.loc, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 8, flash_range = 16, adminlog = 0) + explosion(src.loc, 1, 2, 3, 4, 0, 0, 5) // Irradiated meteors do less physical damage but project a ten-tile ranged pulse of radiation upon exploding. /obj/effect/meteor/irradiated @@ -271,7 +290,7 @@ /obj/effect/meteor/irradiated/meteor_effect(var/explode) ..() if(explode) - explosion(src.loc, devastation_range = 0, heavy_impact_range = 0, light_impact_range = 4, flash_range = 6, adminlog = 0) + explosion(src.loc, 0, 0, 4, 3, 0) new /obj/effect/decal/cleanable/greenglow(get_turf(src)) SSradiation.radiate(src, 50) @@ -290,6 +309,9 @@ // Worst case scenario: Comparable to a standard yield EMP grenade. empulse(src, rand(1, 3), rand(2, 4), rand(3, 7), rand(5, 10)) +/obj/effect/meteor/emp/get_shield_damage() + return ..() * rand(2,4) + //Station buster Tunguska /obj/effect/meteor/tunguska name = "tunguska meteor" @@ -304,7 +326,7 @@ /obj/effect/meteor/tunguska/meteor_effect(var/explode) ..() if(explode) - explosion(src.loc, 5, 10, 15, 20, 0) + explosion(src.loc, 3, 6, 9, 20, 0) /obj/effect/meteor/tunguska/Bump() ..() diff --git a/code/game/gamemodes/newobjective.dm b/code/game/gamemodes/newobjective.dm index f952dbad06..575d48d06a 100644 --- a/code/game/gamemodes/newobjective.dm +++ b/code/game/gamemodes/newobjective.dm @@ -177,7 +177,7 @@ if(!killobjectives.len) continue var/datum/objective/assassinate/objective = pickweight(killobjectives) - world << objective + to_world(objective) for(1 to 10) if(objective.points + total_weight <= 100 || !killobjectives.len) break diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index fd9653a323..a0a5cb6fe8 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -46,48 +46,48 @@ var/list/nuke_disks = list() if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) feedback_set_details("round_end_result","win - syndicate nuke") - world << "Mercenary Major Victory!" - world << "[syndicate_name()] operatives have destroyed [station_name()]!" + to_world("Mercenary Major Victory!") + to_world("[syndicate_name()] operatives have destroyed [station_name()]!") else if (!disk_rescued && station_was_nuked && syndies_didnt_escape) feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time") - world << "Total Annihilation" - world << "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!" + to_world("Total Annihilation") + to_world("[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) feedback_set_details("round_end_result","halfwin - blew wrong station") - world << "Crew Minor Victory" - world << "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!" + to_world("Crew Minor Victory") + to_world("[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time") - world << "[syndicate_name()] operatives have earned Darwin Award!" - world << "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!" + to_world("[syndicate_name()] operatives have earned Darwin Award!") + to_world("[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") else if (disk_rescued && mercs.antags_are_dead()) feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead") - world << "Crew Major Victory!" - world << "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives" + to_world("Crew Major Victory!") + to_world("The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") else if ( disk_rescued ) feedback_set_details("round_end_result","loss - evacuation - disk secured") - world << "Crew Major Victory" - world << "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!" + to_world("Crew Major Victory") + to_world("The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") else if (!disk_rescued && mercs.antags_are_dead()) feedback_set_details("round_end_result","loss - evacuation - disk not secured") - world << "Mercenary Minor Victory!" - world << "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!" + to_world("Mercenary Minor Victory!") + to_world("The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") else if (!disk_rescued && crew_evacuated) feedback_set_details("round_end_result","halfwin - detonation averted") - world << "Mercenary Minor Victory!" - world << "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!" + to_world("Mercenary Minor Victory!") + to_world("[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") else if (!disk_rescued && !crew_evacuated) feedback_set_details("round_end_result","halfwin - interrupted") - world << "Neutral Victory" - world << "Round was mysteriously interrupted!" + to_world("Neutral Victory") + to_world("Round was mysteriously interrupted!") ..() return diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 333d93bf50..b2bd6b80e4 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -12,45 +12,51 @@ var/obj/item/weapon/disk/nuclear/the_disk = null var/active = 0 - - attack_self() - if(!active) - active = 1 - workdisk() - usr << "You activate the pinpointer" - else - active = 0 - icon_state = "pinoff" - usr << "You deactivate the pinpointer" - - proc/workdisk() - if(!active) return - if(!the_disk) - the_disk = locate() - if(!the_disk) - icon_state = "pinonnull" - return - set_dir(get_dir(src,the_disk)) - switch(get_dist(src,the_disk)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - spawn(5) .() - - examine(mob/user) - ..(user) - for(var/obj/machinery/nuclearbomb/bomb in machines) - if(bomb.timing) - user << "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" - /obj/item/weapon/pinpointer/Destroy() active = 0 - ..() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/weapon/pinpointer/attack_self() + if(!active) + active = 1 + START_PROCESSING(SSobj, src) + to_chat(usr, "You activate the pinpointer") + else + active = 0 + STOP_PROCESSING(SSobj, src) + icon_state = "pinoff" + to_chat(usr, "You deactivate the pinpointer") + +/obj/item/weapon/pinpointer/process() + if(!active) + return PROCESS_KILL + + if(!the_disk) + the_disk = locate() + if(!the_disk) + icon_state = "pinonnull" + return + + set_dir(get_dir(src,the_disk)) + + switch(get_dist(src,the_disk)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" + +/obj/item/weapon/pinpointer/examine(mob/user) + ..(user) + for(var/obj/machinery/nuclearbomb/bomb in machines) + if(bomb.timing) + to_chat(user, "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]") + + /obj/item/weapon/pinpointer/advpinpointer name = "Advanced Pinpointer" @@ -60,58 +66,49 @@ var/turf/location = null var/obj/target = null - attack_self() - if(!active) - active = 1 - if(mode == 0) - workdisk() - if(mode == 1) - worklocation() - if(mode == 2) - workobj() - usr << "You activate the pinpointer" - else - active = 0 - icon_state = "pinoff" - usr << "You deactivate the pinpointer" +/obj/item/weapon/pinpointer/advpinpointer/process() + if(!active) + return PROCESS_KILL + if(mode == 0) + ..() + if(mode == 1) + worklocation() + if(mode == 2) + workobj() +/obj/item/weapon/pinpointer/advpinpointer/proc/worklocation() + if(!location) + icon_state = "pinonnull" + return - proc/worklocation() - if(!active) - return - if(!location) - icon_state = "pinonnull" - return - set_dir(get_dir(src,location)) - switch(get_dist(src,location)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - spawn(5) .() + set_dir(get_dir(src,location)) + switch(get_dist(src,location)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" - proc/workobj() - if(!active) - return - if(!target) - icon_state = "pinonnull" - return - set_dir(get_dir(src,target)) - switch(get_dist(src,target)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - spawn(5) .() +/obj/item/weapon/pinpointer/advpinpointer/proc/workobj() + if(!target) + icon_state = "pinonnull" + return + + set_dir(get_dir(src,target)) + + switch(get_dist(src,target)) + if(0) + icon_state = "pinondirect" + if(1 to 8) + icon_state = "pinonclose" + if(9 to 16) + icon_state = "pinonmedium" + if(16 to INFINITY) + icon_state = "pinonfar" /obj/item/weapon/pinpointer/advpinpointer/verb/toggle_mode() set category = "Object" @@ -138,8 +135,7 @@ location = locate(locationx,locationy,Z.z) - usr << "You set the pinpointer to locate [locationx],[locationy]" - + to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") return attack_self() @@ -150,17 +146,19 @@ if("Other Signature") mode = 2 switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "" , "Item" , "DNA")) + if("Item") var/datum/objective/steal/itemlist - itemlist = itemlist // To supress a 'variable defined but not used' error. + itemlist = itemlist var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in itemlist.possible_items if(!targetitem) return target=locate(itemlist.possible_items[targetitem]) if(!target) - usr << "Failed to locate [targetitem]!" + to_chat(usr, "Failed to locate [targetitem]!") return - usr << "You set the pinpointer to locate [targetitem]" + to_chat(usr, "You set the pinpointer to locate [targetitem]") + if("DNA") var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "") if(!DNAstring) @@ -187,38 +185,44 @@ /obj/item/weapon/pinpointer/nukeop/attack_self(mob/user as mob) if(!active) active = 1 + START_PROCESSING(SSobj, src) if(!mode) workdisk() - user << "Authentication Disk Locator active." + to_chat(user, "Authentication Disk Locator active.") else worklocation() - user << "Shuttle Locator active." + to_chat(user, "Shuttle Locator active.") else active = 0 + STOP_PROCESSING(SSobj, src) icon_state = "pinoff" - user << "You deactivate the pinpointer." + to_chat(user, "You deactivate the pinpointer.") +/obj/item/weapon/pinpointer/nukeop/process() + if(!active) + return PROCESS_KILL -/obj/item/weapon/pinpointer/nukeop/workdisk() - if(!active) return - if(mode) //Check in case the mode changes while operating - worklocation() - return + switch(mode) + if(0) + workdisk() + if(1) + worklocation() + +/obj/item/weapon/pinpointer/nukeop/proc/workdisk() if(bomb_set) //If the bomb is set, lead to the shuttle mode = 1 //Ensures worklocation() continues to work - worklocation() playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep - visible_message("Shuttle Locator active.") //Lets the mob holding it know that the mode has changed + visible_message("Shuttle Locator active.") //Lets the mob holding it know that the mode has changed return //Get outta here + if(!the_disk) the_disk = locate() if(!the_disk) icon_state = "pinonnull" return -// if(loc.z != the_disk.z) //If you are on a different z-level from the disk -// icon_state = "pinonnull" -// else + set_dir(get_dir(src, the_disk)) + switch(get_dist(src, the_disk)) if(0) icon_state = "pinondirect" @@ -229,29 +233,25 @@ if(16 to INFINITY) icon_state = "pinonfar" - spawn(5) .() - - /obj/item/weapon/pinpointer/nukeop/proc/worklocation() - if(!active) return - if(!mode) - workdisk() - return if(!bomb_set) mode = 0 - workdisk() playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) visible_message("Authentication Disk Locator active.") return + if(!home) home = locate() if(!home) icon_state = "pinonnull" return + if(loc.z != home.z) //If you are on a different z-level from the shuttle icon_state = "pinonnull" + else set_dir(get_dir(src, home)) + switch(get_dist(src, home)) if(0) icon_state = "pinondirect" @@ -262,8 +262,6 @@ if(16 to INFINITY) icon_state = "pinonfar" - spawn(5) .() - // This one only points to the ship. Useful if there is no nuking to occur today. /obj/item/weapon/pinpointer/shuttle @@ -273,16 +271,17 @@ /obj/item/weapon/pinpointer/shuttle/attack_self(mob/user as mob) if(!active) active = TRUE - find_shuttle() + START_PROCESSING(SSobj, src) to_chat(user, "Shuttle Locator active.") else active = FALSE + STOP_PROCESSING(SSobj, src) icon_state = "pinoff" to_chat(user, "You deactivate the pinpointer.") -/obj/item/weapon/pinpointer/shuttle/proc/find_shuttle() +/obj/item/weapon/pinpointer/shuttle/process() if(!active) - return + return PROCESS_KILL if(!our_shuttle) for(var/obj/machinery/computer/shuttle_control/S in machines) @@ -296,8 +295,10 @@ if(loc.z != our_shuttle.z) //If you are on a different z-level from the shuttle icon_state = "pinonnull" + else set_dir(get_dir(src, our_shuttle)) + switch(get_dist(src, our_shuttle)) if(0) icon_state = "pinondirect" @@ -308,9 +309,6 @@ if(16 to INFINITY) icon_state = "pinonfar" - spawn(5) - .() - /obj/item/weapon/pinpointer/shuttle/merc shuttle_comp_id = "Mercenary" diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 6d702f915d..ece3c68f1d 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -52,11 +52,11 @@ datum/hSB if("hsbtobj") if(!admin) return if(hsboxspawn) - world << "Sandbox: [usr.key] has disabled object spawning!" + to_world("Sandbox: [usr.key] has disabled object spawning!") hsboxspawn = 0 return if(!hsboxspawn) - world << "Sandbox: [usr.key] has enabled object spawning!" + to_world("Sandbox: [usr.key] has enabled object spawning!") hsboxspawn = 1 return if("hsbsuit") @@ -103,7 +103,7 @@ datum/hSB hsb.req_access += A hsb.loc = usr.loc - usr << "Sandbox: Created an airlock." + to_chat(usr, "Sandbox: Created an airlock.") if("hsbcanister") var/list/hsbcanisters = typesof(/obj/machinery/portable_atmospherics/canister/) - /obj/machinery/portable_atmospherics/canister/ var/hsbcanister = input(usr, "Choose a canister to spawn.", "Sandbox:") in hsbcanisters + "Cancel" diff --git a/code/game/gamemodes/technomancer/catalog.dm b/code/game/gamemodes/technomancer/catalog.dm index 633f3f6220..7d0ddc6c2e 100644 --- a/code/game/gamemodes/technomancer/catalog.dm +++ b/code/game/gamemodes/technomancer/catalog.dm @@ -25,7 +25,7 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) - /obj/item/weapon/technomancer_catalog name = "catalog" desc = "A \"book\" featuring a holographic display, metal cover, and miniaturized teleportation device, allowing the user to \ - requisition various things from.. where ever they came from." + requisition various things from... wherever they came from." icon = 'icons/obj/storage.dmi' icon_state ="scientology" //placeholder w_class = ITEMSIZE_SMALL @@ -40,6 +40,7 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) - var/tab = 4 // Info tab, so new players can read it before doing anything. var/spell_tab = ALL_SPELLS var/show_scepter_text = 0 + var/universal = FALSE //VOREStation Add - Allows non-technomancers to use this catalog /obj/item/weapon/technomancer_catalog/apprentice name = "apprentice's catalog" @@ -51,12 +52,22 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) - budget = 2000 max_budget = 2000 +//VOREStation Add +/obj/item/weapon/technomancer_catalog/universal + name = "universal catalog" + desc = "A catalog to be used with the KHI 'Universal Core', shamelessly \ + copied by a Kitsuhana designer from some group of 'technomancers' or another.
\ + The back of the book has 'Export Edition' stamped on it." + budget = 700 + max_budget = 700 + universal = TRUE +//VOREStation Add End // Proc: bind_to_owner() // Parameters: 1 (new_owner - mob that the book is trying to bind to) // Description: Links the catalog to hopefully the technomancer, so that only they can access it. /obj/item/weapon/technomancer_catalog/proc/bind_to_owner(var/mob/living/carbon/human/new_owner) - if(!owner && technomancers.is_antagonist(new_owner.mind)) + if(!owner && (technomancers.is_antagonist(new_owner.mind) || universal)) //VOREStation Edit - Universal catalogs owner = new_owner // Proc: New() @@ -106,7 +117,7 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) - if(!user) return 0 if(owner && user != owner) - user << "\The [src] knows that you're not the original owner, and has locked you out of it!" + to_chat(user, "\The [src] knows that you're not the original owner, and has locked you out of it!") return 0 else if(!owner) bind_to_owner(user) @@ -276,7 +287,7 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) - return 1 //why does this return 1? if(H != owner) - H << "\The [src] won't allow you to do that, as you don't own \the [src]!" + to_chat(H, "\The [src] won't allow you to do that, as you don't own \the [src]!") return if(loc == H || (in_range(src, H) && istype(loc, /turf))) @@ -301,13 +312,13 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) - if(new_spell.cost <= budget) if(!core.has_spell(new_spell)) budget -= new_spell.cost - H << "You have just bought [new_spell.name]." + to_chat(H, "You have just bought [new_spell.name].") core.add_spell(new_spell.obj_path, new_spell.name, new_spell.ability_icon_state) else //We already own it. - H << "You already have [new_spell.name]!" + to_chat(H, "You already have [new_spell.name]!") return else //Can't afford. - H << "You can't afford that!" + to_chat(H, "You can't afford that!") return // This needs less copypasta. @@ -321,19 +332,19 @@ var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) - if(desired_object) if(desired_object.cost <= budget) budget -= desired_object.cost - H << "You have just bought \a [desired_object.name]." + to_chat(H, "You have just bought \a [desired_object.name].") var/obj/O = new desired_object.obj_path(get_turf(H)) technomancer_belongings.Add(O) // Used for the Track spell. else //Can't afford. - H << "You can't afford that!" + to_chat(H, "You can't afford that!") return if(href_list["refund_functions"]) var/turf/T = get_turf(H) if(T.z in using_map.player_levels) - H << "You can only refund at your base, it's too late now!" + to_chat(H, "You can only refund at your base, it's too late now!") return var/obj/item/weapon/technomancer_core/core = null if(istype(H.back, /obj/item/weapon/technomancer_core)) diff --git a/code/game/gamemodes/technomancer/core_obj.dm b/code/game/gamemodes/technomancer/core_obj.dm index 9400e24bf1..42fb58e662 100644 --- a/code/game/gamemodes/technomancer/core_obj.dm +++ b/code/game/gamemodes/technomancer/core_obj.dm @@ -35,6 +35,7 @@ var/list/summoned_mobs = list() // Maintained horribly with maintain_summon_list(). var/list/wards_in_use = list() // Wards don't count against the cap for other summons. var/max_summons = 10 // Maximum allowed summoned entities. Some cores will have different caps. + var/universal = FALSE // Allows non-technomancers to use the core - VOREStation Add /obj/item/weapon/technomancer_core/New() ..() @@ -88,7 +89,7 @@ if(world.time % 5 == 0) // Maintaining fat lists is expensive, I imagine. maintain_summon_list() if(wearer && wearer.mind) - if(!(technomancers.is_antagonist(wearer.mind))) // In case someone tries to wear a stolen core. + if(!(technomancers.is_antagonist(wearer.mind)) && !universal) // In case someone tries to wear a stolen core. //VOREStation Edit - Add universal cores wearer.adjust_instability(20) if(!wearer || wearer.stat == DEAD) // Unlock if we're dead or not worn. canremove = TRUE @@ -287,7 +288,7 @@ if(prob(30)) give_energy(round(amount / 2)) if(amount >= 50) // Managing to recover less than half of this isn't worth telling the user about. - wearer << "\The [src] has recovered [amount/2 >= 1000 ? "a lot of" : "some"] energy." + to_chat(wearer, "\The [src] has recovered [amount/2 >= 1000 ? "a lot of" : "some"] energy.") return success // For those dedicated to summoning hoards of things. @@ -349,4 +350,19 @@ set desc = "Toggles the locking mechanism on your manipulation core." canremove = !canremove - to_chat(usr, "You [canremove ? "de" : ""]activate the locking mechanism on \the [src].") \ No newline at end of file + to_chat(usr, "You [canremove ? "de" : ""]activate the locking mechanism on \the [src].") + +//For the adminbuse! VOREStation Add +/obj/item/weapon/technomancer_core/universal + name = "universal core" + desc = "A bewilderingly complex 'black box' that allows the wearer to accomplish amazing feats. \ + This one is a copy of a 'technomancer' core, shamelessly ripped off by a Kitsuhana pattern designer \ + for fun, so that he could perform impressive 'magic'. The pack sloshes slightly if you shake it.
\ + Under the straps, 'Export Edition' is printed." + energy = 7000 + max_energy = 7000 + regen_rate = 30 //233 seconds to full + instability_modifier = 0.3 + spell_power_modifier = 0.7 + universal = TRUE +//VOREStation Add End \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/devices/disposable_teleporter.dm b/code/game/gamemodes/technomancer/devices/disposable_teleporter.dm index 587287173c..3e3e055ad9 100644 --- a/code/game/gamemodes/technomancer/devices/disposable_teleporter.dm +++ b/code/game/gamemodes/technomancer/devices/disposable_teleporter.dm @@ -24,11 +24,11 @@ /obj/item/weapon/disposable_teleporter/examine(mob/user) ..() - user << "[uses] uses remaining." + to_chat(user, "[uses] uses remaining.") /obj/item/weapon/disposable_teleporter/attack_self(mob/user as mob) if(!uses) - user << "\The [src] has ran out of uses, and is now useless to you!" + to_chat(user, "\The [src] has ran out of uses, and is now useless to you!") return else var/area_wanted = input(user, "Area to teleport to", "Teleportation") in teleportlocs @@ -63,8 +63,8 @@ targets.Add(T) if(!targets.len) - user << "\The [src] was unable to locate a suitable teleport destination, as all the possibilities \ - were nonexistant or hazardous. Try a different area." + to_chat(user, "\The [src] was unable to locate a suitable teleport destination, as all the possibilities \ + were nonexistant or hazardous. Try a different area.") return var/turf/simulated/destination = null @@ -72,8 +72,8 @@ if(destination) user.forceMove(destination) - user << "You are teleported to \the [A]." + to_chat(user, "You are teleported to \the [A].") uses-- if(uses <= 0) - user << "\The [src] has ran out of uses, and disintegrates from your hands." + to_chat(user, "\The [src] has ran out of uses, and disintegrates from your hands.") qdel(src) diff --git a/code/game/gamemodes/technomancer/devices/gloves_of_regen.dm b/code/game/gamemodes/technomancer/devices/gloves_of_regen.dm index 11343fdeac..9d78afe0b2 100644 --- a/code/game/gamemodes/technomancer/devices/gloves_of_regen.dm +++ b/code/game/gamemodes/technomancer/devices/gloves_of_regen.dm @@ -24,7 +24,7 @@ if(H && H.gloves == src) wearer = H if(wearer.can_feel_pain()) - H << "You feel a stabbing sensation in your hands as you slide \the [src] on!" + to_chat(H, "You feel a stabbing sensation in your hands as you slide \the [src] on!") wearer.custom_pain("You feel a sharp pain in your hands!",1) ..() @@ -32,7 +32,7 @@ ..() if(wearer) if(wearer.can_feel_pain()) - wearer << "You feel the hypodermic needles as you slide \the [src] off!" + to_chat(wearer, "You feel the hypodermic needles as you slide \the [src] off!") wearer.custom_pain("Your hands hurt like hell!",1) wearer = null diff --git a/code/game/gamemodes/technomancer/spell_objs.dm b/code/game/gamemodes/technomancer/spell_objs.dm index adfeda58ab..2c80ac39fd 100644 --- a/code/game/gamemodes/technomancer/spell_objs.dm +++ b/code/game/gamemodes/technomancer/spell_objs.dm @@ -183,13 +183,13 @@ if(!core) core = locate(/obj/item/weapon/technomancer_core) in owner if(!core) - owner << "You need to be wearing a core on your back!" + to_chat(owner, "You need to be wearing a core on your back!") return 0 if(core.loc != owner || owner.back != core) //Make sure the core's being worn. - owner << "You need to be wearing a core on your back!" + to_chat(owner, "You need to be wearing a core on your back!") return 0 - if(!technomancers.is_antagonist(owner.mind)) //Now make sure the person using this is the actual antag. - owner << "You can't seem to figure out how to make the machine work properly." + if(!technomancers.is_antagonist(owner.mind) && !core.universal) //Now make sure the person using this is the actual antag. //VOREStation Edit - Universal cores + to_chat(owner, "You can't seem to figure out how to make the machine work properly.") return 0 return 1 diff --git a/code/game/gamemodes/technomancer/spells/abjuration.dm b/code/game/gamemodes/technomancer/spells/abjuration.dm index 0eb566a637..dd39e1982b 100644 --- a/code/game/gamemodes/technomancer/spells/abjuration.dm +++ b/code/game/gamemodes/technomancer/spells/abjuration.dm @@ -23,18 +23,18 @@ SM = L if(L.summoned || (SM && SM.supernatural) ) if(L.client) // Player-controlled mobs are immune to being killed by this. - user << "\The [L] resists your attempt to banish it!" - L << "\The [user] tried to teleport you far away, but failed." + to_chat(user, "\The [L] resists your attempt to banish it!") + to_chat(L, "\The [user] tried to teleport you far away, but failed.") return 0 else visible_message("\The [L] vanishes!") qdel(L) else if(istype(L, /mob/living/simple_mob/construct)) var/mob/living/simple_mob/construct/evil = L - evil << "\The [user]'s abjuration purges your form!" + to_chat(evil, "\The [user]'s abjuration purges your form!") evil.purge = 3 adjust_instability(5) // In case NarNar comes back someday. if(istype(hit_atom, /obj/singularity/narsie)) - user << "One does not simply abjurate Nar'sie away." + to_chat(user, "One does not simply abjurate Nar'sie away.") adjust_instability(200) \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/aspect_aura.dm b/code/game/gamemodes/technomancer/spells/aspect_aura.dm index 833f72a2fb..eec4304d71 100644 --- a/code/game/gamemodes/technomancer/spells/aspect_aura.dm +++ b/code/game/gamemodes/technomancer/spells/aspect_aura.dm @@ -19,7 +19,7 @@ if(istype(W, /obj/item/weapon/spell)) var/obj/item/weapon/spell/spell = W if(!spell.aspect || spell.aspect == ASPECT_CHROMATIC) - user << "You cannot combine \the [spell] with \the [src], as the aspects are incompatable." + to_chat(user, "You cannot combine \the [spell] with \the [src], as the aspects are incompatable.") return user.drop_item(src) src.loc = null @@ -132,4 +132,4 @@ /obj/item/weapon/spell/aura/biomed/on_use_cast(mob/living/user) heal_allies_only = !heal_allies_only - user << "Your aura will now heal [heal_allies_only ? "your allies" : "everyone"] near you." \ No newline at end of file + to_chat(user, "Your aura will now heal [heal_allies_only ? "your allies" : "everyone"] near you.") \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/audible_deception.dm b/code/game/gamemodes/technomancer/spells/audible_deception.dm index 4b4edc261b..c8a66fac3c 100644 --- a/code/game/gamemodes/technomancer/spells/audible_deception.dm +++ b/code/game/gamemodes/technomancer/spells/audible_deception.dm @@ -92,4 +92,4 @@ M.Paralyse(4) else M.make_jittery(50) - M << "HONK" + to_chat(M, "HONK") diff --git a/code/game/gamemodes/technomancer/spells/aura/biomed_aura.dm b/code/game/gamemodes/technomancer/spells/aura/biomed_aura.dm index 3daddc12d4..f8828af147 100644 --- a/code/game/gamemodes/technomancer/spells/aura/biomed_aura.dm +++ b/code/game/gamemodes/technomancer/spells/aura/biomed_aura.dm @@ -37,4 +37,4 @@ /obj/item/weapon/spell/aura/biomed/on_use_cast(mob/living/user) heal_allies_only = !heal_allies_only - user << "Your aura will now heal [heal_allies_only ? "your allies" : "everyone"] near you." + to_chat(user, "Your aura will now heal [heal_allies_only ? "your allies" : "everyone"] near you.") diff --git a/code/game/gamemodes/technomancer/spells/aura/shock_aura.dm b/code/game/gamemodes/technomancer/spells/aura/shock_aura.dm index 0af246d69c..b9bc8bc2c4 100644 --- a/code/game/gamemodes/technomancer/spells/aura/shock_aura.dm +++ b/code/game/gamemodes/technomancer/spells/aura/shock_aura.dm @@ -30,7 +30,7 @@ continue if(L.isSynthetic()) - L << "ERROR: Electrical fault detected!" + to_chat(L, "ERROR: Electrical fault detected!") L.stuttering += 3 if(ishuman(L)) @@ -40,5 +40,4 @@ else L.electrocute_act(power, src, 0.75, BP_TORSO) - - adjust_instability(3) \ No newline at end of file + adjust_instability(3) diff --git a/code/game/gamemodes/technomancer/spells/aura/unstable_aura.dm b/code/game/gamemodes/technomancer/spells/aura/unstable_aura.dm index 3c65a9503f..ca731ddad2 100644 --- a/code/game/gamemodes/technomancer/spells/aura/unstable_aura.dm +++ b/code/game/gamemodes/technomancer/spells/aura/unstable_aura.dm @@ -35,11 +35,11 @@ if(L.isSynthetic()) L.adjustBruteLoss(damage_to_inflict) if(damage_to_inflict && prob(10)) - L << "Your chassis seems to slowly be decaying and breaking down." + to_chat(L, "Your chassis seems to slowly be decaying and breaking down.") else L.adjustToxLoss(damage_to_inflict) if(damage_to_inflict && prob(10)) - L << "You feel almost like you're melting from the inside!" + to_chat(L, "You feel almost like you're melting from the inside!") adjust_instability(2) \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/blink.dm b/code/game/gamemodes/technomancer/spells/blink.dm index 320374542d..100f7bfa33 100644 --- a/code/game/gamemodes/technomancer/spells/blink.dm +++ b/code/game/gamemodes/technomancer/spells/blink.dm @@ -48,7 +48,7 @@ L.buckled.unbuckle_mob() AM.forceMove(destination) AM.visible_message("\The [AM] vanishes!") - AM << "You suddenly appear somewhere else!" + to_chat(AM, "You suddenly appear somewhere else!") new /obj/effect/effect/sparks(destination) new /obj/effect/effect/sparks(starting) return diff --git a/code/game/gamemodes/technomancer/spells/chroma.dm b/code/game/gamemodes/technomancer/spells/chroma.dm index 18c4afcf65..b87a82a0ae 100644 --- a/code/game/gamemodes/technomancer/spells/chroma.dm +++ b/code/game/gamemodes/technomancer/spells/chroma.dm @@ -18,24 +18,21 @@ ..() set_light(6, 5, l_color = color_to_use) -/obj/effect/chroma +/obj/effect/temporary_effect/chroma name = "chroma" desc = "How are you examining what which cannot be seen?" invisibility = 101 - var/time_to_die = 2 MINUTES //Despawn after this time, if set. + time_to_die = 2 MINUTES //Despawn after this time, if set. -/obj/effect/chroma/New(var/newloc, var/new_color = "#FFFFFF") - ..() +/obj/effect/temporary_effect/chroma/Initialize(var/mapload, var/new_color = "#FFFFFF") + . = ..() set_light(6, 5, l_color = new_color) - if(time_to_die) - spawn(time_to_die) - qdel(src) /obj/item/weapon/spell/chroma/on_ranged_cast(atom/hit_atom, mob/user) var/turf/T = get_turf(hit_atom) if(T) - new /obj/effect/chroma(T, color_to_use) - user << "You shift the light onto \the [T]." + new /obj/effect/temporary_effect/chroma(T, color_to_use) + to_chat(user, "You shift the light onto \the [T].") qdel(src) /obj/item/weapon/spell/chroma/on_use_cast(mob/user) diff --git a/code/game/gamemodes/technomancer/spells/control.dm b/code/game/gamemodes/technomancer/spells/control.dm index b41823dc54..8c6a00c4e7 100644 --- a/code/game/gamemodes/technomancer/spells/control.dm +++ b/code/game/gamemodes/technomancer/spells/control.dm @@ -93,41 +93,41 @@ if(choice == "Yes") for(var/mob/living/L in controlled_mobs) deselect(L) - user << "You've released control of all entities you had in control." + to_chat(user, "You've released control of all entities you had in control.") /obj/item/weapon/spell/control/on_ranged_cast(atom/hit_atom, mob/living/user) if(isliving(hit_atom)) var/mob/living/L = hit_atom if(L == user && !controlled_mobs.len) - user << "This function doesn't work on higher-intelligence entities, however since you're \ - trying to use it on yourself, perhaps you're an exception? Regardless, nothing happens." + to_chat(user, "This function doesn't work on higher-intelligence entities, however since you're \ + trying to use it on yourself, perhaps you're an exception? Regardless, nothing happens.") return 0 if(L.mob_class & allowed_mob_classes) if(!(L in controlled_mobs)) //Selecting if(L.client) - user << "\The [L] seems to resist you!" + to_chat(user, "\The [L] seems to resist you!") return 0 if(!L.has_AI()) to_chat(user, span("warning", "\The [L] seems too dim for this to work on them.")) return FALSE if(pay_energy(500)) select(L) - user << "\The [L] is now under your (limited) control." + to_chat(user, "\The [L] is now under your (limited) control.") else //Deselect them deselect(L) - user << "You free \the [L] from your grasp." + to_chat(user, "You free \the [L] from your grasp.") else //Let's attack if(!controlled_mobs.len) - user << "You have no entities under your control to command." + to_chat(user, "You have no entities under your control to command.") return 0 if(pay_energy(25 * controlled_mobs.len)) attack_all(L) log_and_message_admins("has commanded their army of [controlled_mobs.len] to attack [L].") - user << "You command your [controlled_mobs.len > 1 ? "entities" : "[controlled_mobs[1]]"] to \ - attack \the [L]." + to_chat(user, "You command your [controlled_mobs.len > 1 ? "entities" : "[controlled_mobs[1]]"] to \ + attack \the [L].") //This is to stop someone from controlling beepsky and getting him to stun someone 5 times a second. user.setClickCooldown(8) adjust_instability(controlled_mobs.len) @@ -135,11 +135,11 @@ else if(isturf(hit_atom)) var/turf/T = hit_atom if(!controlled_mobs.len) - user << "You have no entities under your control to command." + to_chat(user, "You have no entities under your control to command.") return 0 if(pay_energy(10 * controlled_mobs.len)) move_all(T) adjust_instability(controlled_mobs.len) - user << "You command your [controlled_mobs.len > 1 ? "entities" : "[controlled_mobs[1]]"] to move \ - towards \the [T]." + to_chat(user, "You command your [controlled_mobs.len > 1 ? "entities" : "[controlled_mobs[1]]"] to move \ + towards \the [T].") diff --git a/code/game/gamemodes/technomancer/spells/energy_siphon.dm b/code/game/gamemodes/technomancer/spells/energy_siphon.dm index aab144d907..1bfcb4e2d2 100644 --- a/code/game/gamemodes/technomancer/spells/energy_siphon.dm +++ b/code/game/gamemodes/technomancer/spells/energy_siphon.dm @@ -33,15 +33,15 @@ if(!siphoning) return if(!pay_energy(100)) - owner << "You can't afford to maintain the siphon link!" + to_chat(owner, "You can't afford to maintain the siphon link!") stop_siphoning() return if(get_dist(siphoning, get_turf(src)) > 4) - owner << "\The [siphoning] is too far to drain from!" + to_chat(owner, "\The [siphoning] is too far to drain from!") stop_siphoning() return if(!(siphoning in view(owner))) - owner << "\The [siphoning] cannot be seen!" + to_chat(owner, "\The [siphoning] cannot be seen!") stop_siphoning() return siphon(siphoning, owner) @@ -53,7 +53,7 @@ var/atom/movable/AM = hit_atom populate_siphon_list(AM) if(!things_to_siphon.len) - user << "You cannot steal energy from \a [AM]." + to_chat(user, "You cannot steal energy from \a [AM].") return 0 siphoning = AM update_icon() @@ -137,18 +137,18 @@ // Now we can actually fill up the core. if(core.energy < core.max_energy) give_energy(charge_to_give) - user << "Stolen [charge_to_give * CELLRATE] kJ and converted to [charge_to_give] Core energy." + to_chat(user, "Stolen [charge_to_give * CELLRATE] kJ and converted to [charge_to_give] Core energy.") if( (core.max_energy - core.energy) < charge_to_give ) // We have some overflow, if this is true. if(user.isSynthetic()) // Let's do something with it, if we're a robot. charge_to_give = charge_to_give - (core.max_energy - core.energy) user.nutrition = min(user.nutrition + (charge_to_give / SIPHON_FBP_TO_ENERGY), 400) - user << "Redirected energy to internal microcell." + to_chat(user, "Redirected energy to internal microcell.") else - user << "Stolen [charge_to_give * CELLRATE] kJ." + to_chat(user, "Stolen [charge_to_give * CELLRATE] kJ.") adjust_instability(2) if(flow_remaining == flow_rate) // We didn't drain anything. - user << "\The [siphoning] cannot be drained any further." + to_chat(user, "\The [siphoning] cannot be drained any further.") stop_siphoning() /obj/item/weapon/spell/energy_siphon/update_icon() diff --git a/code/game/gamemodes/technomancer/spells/flame_tongue.dm b/code/game/gamemodes/technomancer/spells/flame_tongue.dm index 9e2c7b88b3..ce38a45caa 100644 --- a/code/game/gamemodes/technomancer/spells/flame_tongue.dm +++ b/code/game/gamemodes/technomancer/spells/flame_tongue.dm @@ -44,7 +44,7 @@ var/mob/living/L = hit_atom if(pay_energy(1000)) visible_message("\The [user] reaches out towards \the [L] with the flaming hand, and they ignite!") - L << "You ignite!" + to_chat(L, "You ignite!") L.fire_act() log_and_message_admins("has ignited [L] with [src].") adjust_instability(12) diff --git a/code/game/gamemodes/technomancer/spells/illusion.dm b/code/game/gamemodes/technomancer/spells/illusion.dm index 8dccc2b9ec..7405c43520 100644 --- a/code/game/gamemodes/technomancer/spells/illusion.dm +++ b/code/game/gamemodes/technomancer/spells/illusion.dm @@ -22,7 +22,7 @@ if(pay_energy(100)) copied = AM update_icon() - user << "You've copied \the [AM]'s appearance." + to_chat(user, "You've copied \the [AM]'s appearance.") user << 'sound/weapons/flash.ogg' return 1 else if(istype(hit_atom, /turf)) @@ -33,7 +33,7 @@ if(pay_energy(500)) illusion = new(T) illusion.copy_appearance(copied) - user << "An illusion of \the [copied] is made on \the [T]." + to_chat(user, "An illusion of \the [copied] is made on \the [T].") user << 'sound/effects/pop.ogg' return 1 else diff --git a/code/game/gamemodes/technomancer/spells/insert/asphyxiation.dm b/code/game/gamemodes/technomancer/spells/insert/asphyxiation.dm index 84b0b22931..08e47bf499 100644 --- a/code/game/gamemodes/technomancer/spells/insert/asphyxiation.dm +++ b/code/game/gamemodes/technomancer/spells/insert/asphyxiation.dm @@ -24,7 +24,7 @@ if(H.isSynthetic() || H.does_not_breathe) // It's hard to choke a robot or something that doesn't breathe. on_expire() return - H << "You are having difficulty breathing!" + to_chat(H, "You are having difficulty breathing!") var/pulses = 3 var/warned_victim = 0 while(pulses) @@ -34,7 +34,7 @@ H.adjustOxyLoss(5) var/health_lost = H.getMaxHealth() - H.getOxyLoss() + H.getToxLoss() + H.getFireLoss() + H.getBruteLoss() + H.getCloneLoss() H.adjustOxyLoss(round(abs(health_lost * 0.25))) - //world << "Inflicted [round(abs(health_lost * 0.25))] damage!" + //to_world("Inflicted [round(abs(health_lost * 0.25))] damage!") pulses-- if(src) //We might've been dispelled at this point and deleted, better safe than sorry. on_expire() @@ -63,13 +63,13 @@ return .(pulses_remaining, victim, previous_damage) // Now check if our damage predictions are going to cause the victim to go into crit if no healing occurs. if(previous_damage + health_lost >= victim.getMaxHealth()) // We're probably going to hardcrit - victim << "A feeling of immense dread starts to overcome you as everything starts \ - to fade to black..." - //world << "Predicted hardcrit." + to_chat(victim, "A feeling of immense dread starts to overcome you as everything starts \ + to fade to black...") + //to_world("Predicted hardcrit.") return 1 else if(predicted_damage >= victim.species.total_health / 2) // Or perhaps we're gonna go into 'oxy crit'. - victim << "You feel really light-headed, and everything seems to be fading..." - //world << "Predicted oxycrit." + to_chat(victim, "You feel really light-headed, and everything seems to be fading...") + //to_world("Predicted oxycrit.") return 1 //If we're at this point, the spell is not going to result in critting. return 0 \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/insert/insert.dm b/code/game/gamemodes/technomancer/spells/insert/insert.dm index 168300f6ce..53666c41ab 100644 --- a/code/game/gamemodes/technomancer/spells/insert/insert.dm +++ b/code/game/gamemodes/technomancer/spells/insert/insert.dm @@ -41,7 +41,7 @@ if(!allow_stacking) for(var/obj/item/weapon/inserted_spell/IS in L.contents) if(IS.type == inserting) - user << "\The [L] is already affected by \the [src]." + to_chat(user, "\The [L] is already affected by \the [src].") return var/obj/item/weapon/inserted_spell/inserted = new inserting(L,user,src) inserted.spell_power_at_creation = calculate_spell_power(1.0) diff --git a/code/game/gamemodes/technomancer/spells/mark_recall.dm b/code/game/gamemodes/technomancer/spells/mark_recall.dm index 306de85043..0d35bce53a 100644 --- a/code/game/gamemodes/technomancer/spells/mark_recall.dm +++ b/code/game/gamemodes/technomancer/spells/mark_recall.dm @@ -8,16 +8,30 @@ ability_icon_state = "tech_mark" category = UTILITY_SPELLS -//The object to teleport to when Recall is used. -/obj/effect/mark_spell - name = "mark" - desc = "This is a strange looking disturbance." - opacity = 0 - density = 0 - anchored = 1 +/datum/technomancer_marker + var/weakref/U + var/image/I + var/turf/T + +/datum/technomancer_marker/New(var/mob/user) + U = weakref(user) + T = get_turf(user) + I = image('icons/goonstation/featherzone.dmi', T, "spawn-wall") + I.plane = TURF_PLANE + I.layer = ABOVE_TURF_LAYER + user.client?.images |= I + spawn(23) //That's just how long the animation is + I.icon_state = "spawn-wall-loop" + +/datum/technomancer_marker/Destroy() + var/mob/user = U?.resolve() + user?.client?.images -= I + I?.loc = null + U = T = I = null + return ..() //This is global, to avoid looping through a list of all objects, or god forbid, looping through world. -/var/global/obj/effect/mark_spell/mark_spell_ref = null +GLOBAL_LIST_INIT(mark_spells, list()) /obj/item/weapon/spell/mark name = "mark" @@ -26,21 +40,24 @@ cast_methods = CAST_USE aspect = ASPECT_TELE -/obj/item/weapon/spell/mark/on_use_cast(mob/living/user) +/obj/item/weapon/spell/mark/on_use_cast(var/mob/living/user) if(!allowed_to_teleport()) // Otherwise you could teleport back to the admin Z-level. - user << "You can't teleport here!" + to_chat(user, "You can't teleport here!") return 0 if(pay_energy(1000)) - if(!mark_spell_ref) - mark_spell_ref = new(get_turf(user)) - user << "You mark \the [get_turf(user)] under you." + var/datum/technomancer_marker/marker = GLOB.mark_spells[weakref(user)] + //They have one in the list + if(istype(marker)) + qdel(marker) + to_chat(user, "Your mark is moved from its old position to \the [get_turf(user)] under you.") + //They don't have one yet else - mark_spell_ref.forceMove(get_turf(user)) - user << "Your mark is moved from its old position to \the [get_turf(user)] under you." + to_chat(user, "You mark \the [get_turf(user)] under you.") + GLOB.mark_spells[weakref(user)] = new /datum/technomancer_marker(user) adjust_instability(5) return 1 else - user << "You can't afford the energy cost!" + to_chat(user, "You can't afford the energy cost!") return 0 //Recall @@ -62,10 +79,11 @@ cast_methods = CAST_USE aspect = ASPECT_TELE -/obj/item/weapon/spell/recall/on_use_cast(mob/living/user) +/obj/item/weapon/spell/recall/on_use_cast(var/mob/living/user) if(pay_energy(3000)) - if(!mark_spell_ref) - user << "There's no Mark!" + var/datum/technomancer_marker/marker = GLOB.mark_spells[weakref(user)] + if(!istype(marker)) + to_chat(user, "There's no Mark!") return 0 else if(!allowed_to_teleport()) @@ -79,23 +97,23 @@ while(time_left) if(user.incapacitated()) visible_message("\The [user]'s glow fades.") - user << "You cannot Recall while incapacitated!" + to_chat(user, "You cannot Recall while incapacitated!") return 0 light_intensity++ set_light(light_intensity, light_intensity, l_color = "#006AFF") time_left-- sleep(1 SECOND) - var/turf/target_turf = get_turf(mark_spell_ref) + var/turf/target_turf = marker.T var/turf/old_turf = get_turf(user) for(var/obj/item/weapon/grab/G in user.contents) // People the Technomancer is grabbing come along for the ride. if(G.affecting) G.affecting.forceMove(locate( target_turf.x+rand(-1,1), target_turf.y+rand(-1,1), target_turf.z)) - G.affecting << "You are teleported along with [user]!" + to_chat(G.affecting, "You are teleported along with [user]!") user.forceMove(target_turf) - user << "You are teleported to your Mark." + to_chat(user, "You are teleported to your Mark.") playsound(target_turf, 'sound/effects/phasein.ogg', 25, 1) playsound(target_turf, 'sound/effects/sparks2.ogg', 50, 1) @@ -106,6 +124,6 @@ qdel(src) return 1 else - user << "You can't afford the energy cost!" + to_chat(user, "You can't afford the energy cost!") return 0 diff --git a/code/game/gamemodes/technomancer/spells/passwall.dm b/code/game/gamemodes/technomancer/spells/passwall.dm index fa7b5f34b9..b5e0b18060 100644 --- a/code/game/gamemodes/technomancer/spells/passwall.dm +++ b/code/game/gamemodes/technomancer/spells/passwall.dm @@ -20,7 +20,7 @@ if(busy) //Prevent someone from trying to get two uses of the spell from one instance. return 0 if(!allowed_to_teleport()) - user << "You can't teleport here!" + to_chat(user, "You can't teleport here!") return 0 // if(isturf(hit_atom)) @@ -28,7 +28,7 @@ var/turf/our_turf = get_turf(user) //Where we are. if(!T.density) if(!T.check_density()) - user << "Perhaps you should try using passWALL on a wall, or other solid object." + to_chat(user, "Perhaps you should try using passWALL on a wall, or other solid object.") return 0 var/direction = get_dir(our_turf, T) var/total_cost = 0 @@ -62,18 +62,18 @@ if(found_turf) if(user.loc != our_turf) - user << "You need to stand still in order to phase through \the [hit_atom]." + to_chat(user, "You need to stand still in order to phase through \the [hit_atom].") return 0 if(pay_energy(total_cost) && !user.incapacitated() ) visible_message("[user] appears to phase through \the [hit_atom]!") - user << "You find a destination on the other side of \the [hit_atom], and phase through it." + to_chat(user, "You find a destination on the other side of \the [hit_atom], and phase through it.") spark_system.start() user.forceMove(found_turf) qdel(src) return 1 else - user << "You don't have enough energy to phase through these walls!" + to_chat(user, "You don't have enough energy to phase through these walls!") busy = 0 else - user << "You weren't able to find an open space to go to." + to_chat(user, "You weren't able to find an open space to go to.") busy = 0 diff --git a/code/game/gamemodes/technomancer/spells/phase_shift.dm b/code/game/gamemodes/technomancer/spells/phase_shift.dm index d73e45774c..7cee437685 100644 --- a/code/game/gamemodes/technomancer/spells/phase_shift.dm +++ b/code/game/gamemodes/technomancer/spells/phase_shift.dm @@ -47,7 +47,7 @@ if(user.stat) return - user << "You step out of the rift." + to_chat(user, "You step out of the rift.") user.forceMove(get_turf(src)) qdel(src) @@ -56,11 +56,11 @@ if(pay_energy(2000)) var/obj/effect/phase_shift/PS = new(get_turf(user)) visible_message("[user] vanishes into a pink rift!") - user << "You create an unstable rift, and go through it. Be sure to not stay too long." + to_chat(user, "You create an unstable rift, and go through it. Be sure to not stay too long.") user.forceMove(PS) adjust_instability(10) qdel(src) else - user << "You don't have enough energy to make a rift!" + to_chat(user, "You don't have enough energy to make a rift!") else //We're already in a rift or something like a closet. - user << "Making a rift here would probably be a bad idea." + to_chat(user, "Making a rift here would probably be a bad idea.") diff --git a/code/game/gamemodes/technomancer/spells/projectile/force_missile.dm b/code/game/gamemodes/technomancer/spells/projectile/force_missile.dm index b16e9ea3c4..282d914d3c 100644 --- a/code/game/gamemodes/technomancer/spells/projectile/force_missile.dm +++ b/code/game/gamemodes/technomancer/spells/projectile/force_missile.dm @@ -24,4 +24,7 @@ icon_state = "force_missile" damage = 25 damage_type = BRUTE - check_armour = "melee" \ No newline at end of file + check_armour = "melee" + + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/weapons/effects/searwall.ogg' \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/projectile/overload.dm b/code/game/gamemodes/technomancer/spells/projectile/overload.dm index d9c3628631..445c1bb577 100644 --- a/code/game/gamemodes/technomancer/spells/projectile/overload.dm +++ b/code/game/gamemodes/technomancer/spells/projectile/overload.dm @@ -27,6 +27,8 @@ icon_state = "bluespace" damage_type = BURN armor_penetration = 100 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/weapons/effects/searwall.ogg' /obj/item/weapon/spell/projectile/overload/make_projectile(obj/item/projectile/projectile_type, mob/living/user) var/obj/item/projectile/overload/P = new projectile_type(get_turf(user)) diff --git a/code/game/gamemodes/technomancer/spells/reflect.dm b/code/game/gamemodes/technomancer/spells/reflect.dm index 22b32bb660..47ed0663f2 100644 --- a/code/game/gamemodes/technomancer/spells/reflect.dm +++ b/code/game/gamemodes/technomancer/spells/reflect.dm @@ -21,13 +21,12 @@ set_light(3, 2, l_color = "#006AFF") spark_system = new /datum/effect/effect/system/spark_spread() spark_system.set_up(5, 0, src) - owner << "Your shield will expire in 3 seconds!" - spawn(5 SECONDS) - if(src) - owner << "Your shield expires!" - qdel(src) + to_chat(owner, "Your shield will expire in 5 seconds!") + QDEL_IN(src, 5 SECONDS) /obj/item/weapon/spell/reflect/Destroy() + if(owner) + to_chat(owner, "Your shield expires!") spark_system = null return ..() @@ -38,7 +37,7 @@ var/damage_to_energy_cost = (damage_to_energy_multiplier * damage) if(!pay_energy(damage_to_energy_cost)) - owner << "Your shield fades due to lack of energy!" + to_chat(owner, "Your shield fades due to lack of energy!") qdel(src) return 0 @@ -68,7 +67,7 @@ if(!reflecting) reflecting = 1 spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. - owner << "Your shield fades due being used up!" + to_chat(owner, "Your shield fades due being used up!") qdel(src) return PROJECTILE_CONTINUE // complete projectile permutation @@ -77,8 +76,8 @@ var/obj/item/weapon/W = damage_source if(attacker) W.attack(attacker) - attacker << "Your [damage_source.name] goes through \the [src] in one location, comes out \ - on the same side, and hits you!" + to_chat(attacker, "Your [damage_source.name] goes through \the [src] in one location, comes out \ + on the same side, and hits you!") spark_system.start() playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) @@ -88,7 +87,7 @@ if(!reflecting) reflecting = 1 spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. - owner << "Your shield fades due being used up!" + to_chat(owner, "Your shield fades due being used up!") qdel(src) return 1 return 0 diff --git a/code/game/gamemodes/technomancer/spells/resurrect.dm b/code/game/gamemodes/technomancer/spells/resurrect.dm index 2663a132a9..02c072b90d 100644 --- a/code/game/gamemodes/technomancer/spells/resurrect.dm +++ b/code/game/gamemodes/technomancer/spells/resurrect.dm @@ -19,21 +19,20 @@ if(isliving(hit_atom)) var/mob/living/L = hit_atom if(L == user) - user << "Clever as you may seem, this won't work on yourself while alive." + to_chat(user, "Clever as you may seem, this won't work on yourself while alive.") return 0 if(L.stat != DEAD) - user << "\The [L] isn't dead!" + to_chat(user, "\The [L] isn't dead!") return 0 if(pay_energy(5000)) if(L.tod > world.time + 30 MINUTES) - user << "\The [L]'s been dead for too long, even this function cannot replace cloning at \ - this point." + to_chat(user, "\The [L]'s been dead for too long, even this function cannot replace cloning at this point.") return 0 - user << "You stab \the [L] with a hidden integrated hypo, attempting to bring them back..." + to_chat(user, "You stab \the [L] with a hidden integrated hypo, attempting to bring them back...") if(istype(L, /mob/living/simple_mob)) var/mob/living/simple_mob/SM = L SM.health = SM.getMaxHealth() / 3 - SM.stat = CONSCIOUS + SM.set_stat(CONSCIOUS) dead_mob_list -= SM living_mob_list += SM SM.update_icon() @@ -44,9 +43,8 @@ if(!H.client && H.mind) //Don't force the dead person to come back if they don't want to. for(var/mob/observer/dead/ghost in player_list) if(ghost.mind == H.mind) - ghost << "The Technomancer [user.real_name] is trying to \ - revive you. Return to your body if you want to be resurrected! \ - (Verbs -> Ghost -> Re-enter corpse)" + ghost.notify_revive("The Technomancer [user.real_name] is trying to revive you. \ + Re-enter your body if you want to be revived!", 'sound/effects/genetics.ogg') break H.adjustBruteLoss(-40) @@ -54,14 +52,14 @@ sleep(10 SECONDS) if(H.client) - L.stat = CONSCIOUS //Note that if whatever killed them in the first place wasn't fixed, they're likely to die again. + L.set_stat(CONSCIOUS) //Note that if whatever killed them in the first place wasn't fixed, they're likely to die again. dead_mob_list -= H living_mob_list += H H.timeofdeath = null visible_message("\The [H]'s eyes open!") - user << "It's alive!" + to_chat(user, "It's alive!") adjust_instability(50) log_and_message_admins("has resurrected [H].") else - user << "The body of \the [H] doesn't seem to respond, perhaps you could try again?" + to_chat(user, "The body of \the [H] doesn't seem to respond, perhaps you could try again?") adjust_instability(10) \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/shared_burden.dm b/code/game/gamemodes/technomancer/spells/shared_burden.dm index 21dfd5fe81..5c33138969 100644 --- a/code/game/gamemodes/technomancer/spells/shared_burden.dm +++ b/code/game/gamemodes/technomancer/spells/shared_burden.dm @@ -17,13 +17,13 @@ if(ishuman(hit_atom) && within_range(hit_atom)) var/mob/living/carbon/human/H = hit_atom if(H == user) - user << "Draining instability out of you to put it back seems a bit pointless." + to_chat(user, "Draining instability out of you to put it back seems a bit pointless.") return 0 if(H.instability <= 0) - user << "\The [H] has no instability to drain." + to_chat(user, "\The [H] has no instability to drain.") return 0 if(pay_energy(500)) var/instability_to_drain = min(H.instability, 25) - user << "You draw instability away from \the [H] and towards you." + to_chat(user, "You draw instability away from \the [H] and towards you.") adjust_instability(instability_to_drain) H.adjust_instability(-calculate_spell_power(instability_to_drain)) \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/shield.dm b/code/game/gamemodes/technomancer/spells/shield.dm index 772fd7a8f8..6bcbda5ec4 100644 --- a/code/game/gamemodes/technomancer/spells/shield.dm +++ b/code/game/gamemodes/technomancer/spells/shield.dm @@ -46,7 +46,7 @@ damage_to_energy_cost *= 0.50 if(!pay_energy(damage_to_energy_cost)) - owner << "Your shield fades due to lack of energy!" + to_chat(owner, "Your shield fades due to lack of energy!") qdel(src) return 0 diff --git a/code/game/gamemodes/technomancer/spells/spawner/destablize.dm b/code/game/gamemodes/technomancer/spells/spawner/destablize.dm index 29381701ca..aee8d2557b 100644 --- a/code/game/gamemodes/technomancer/spells/spawner/destablize.dm +++ b/code/game/gamemodes/technomancer/spells/spawner/destablize.dm @@ -35,12 +35,13 @@ var/instability_power = 5 var/instability_range = 6 -/obj/effect/temporary_effect/destablize/New() - ..() - spawn(0) - radiate_loop() +/obj/effect/temporary_effect/destablize/Initialize() + . = ..() + radiate_loop() /obj/effect/temporary_effect/destablize/proc/radiate_loop() + set waitfor = FALSE + while(pulses_remaining) sleep(5) for(var/mob/living/L in range(src, instability_range) ) diff --git a/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm b/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm index 6dcd2cf4b2..b56ea64e9c 100644 --- a/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm +++ b/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm @@ -31,11 +31,15 @@ var/pulse_delay = 2 SECONDS /obj/effect/temporary_effect/pulse/Initialize() - spawn(0) - pulse_loop() - return ..() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/effect/temporary_effect/pulse/LateInitialize() + pulse_loop() /obj/effect/temporary_effect/pulse/proc/pulse_loop() + set waitfor = FALSE + while(pulses_remaining) sleep(pulse_delay) on_pulse() diff --git a/code/game/gamemodes/technomancer/spells/spawner/spawner.dm b/code/game/gamemodes/technomancer/spells/spawner/spawner.dm index ba86e700bb..49c82b7998 100644 --- a/code/game/gamemodes/technomancer/spells/spawner/spawner.dm +++ b/code/game/gamemodes/technomancer/spells/spawner/spawner.dm @@ -10,6 +10,6 @@ var/turf/T = get_turf(hit_atom) if(T) new spawner_type(T) - user << "You shift \the [src] onto \the [T]." + to_chat(user, "You shift \the [src] onto \the [T].") log_and_message_admins("has casted [src] at [T.x],[T.y],[T.z].") qdel(src) \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/summon/summon.dm b/code/game/gamemodes/technomancer/spells/summon/summon.dm index e4e3f8bb0c..c7c94c0869 100644 --- a/code/game/gamemodes/technomancer/spells/summon/summon.dm +++ b/code/game/gamemodes/technomancer/spells/summon/summon.dm @@ -27,7 +27,7 @@ summon_underlay.alpha = 127 L.underlays |= summon_underlay on_summon(L) - user << "You've successfully teleported \a [L] to you!" + to_chat(user, "You've successfully teleported \a [L] to you!") visible_message("\A [L] appears from no-where!") log_and_message_admins("has summoned \a [L] at [T.x],[T.y],[T.z].") user.adjust_instability(instability_cost) diff --git a/code/game/gamemodes/technomancer/spells/track.dm b/code/game/gamemodes/technomancer/spells/track.dm index 230e282ee1..ae342a46a1 100644 --- a/code/game/gamemodes/technomancer/spells/track.dm +++ b/code/game/gamemodes/technomancer/spells/track.dm @@ -29,7 +29,7 @@ var/list/technomancer_belongings = list() /obj/item/weapon/spell/track/on_use_cast(mob/user) if(tracking) tracking = 0 - user << "You stop tracking for \the [tracked]'s whereabouts." + to_chat(user, "You stop tracking for \the [tracked]'s whereabouts.") tracked = null return diff --git a/code/game/gamemodes/technomancer/spells/warp_strike.dm b/code/game/gamemodes/technomancer/spells/warp_strike.dm index 3f8939e0ba..488214851c 100644 --- a/code/game/gamemodes/technomancer/spells/warp_strike.dm +++ b/code/game/gamemodes/technomancer/spells/warp_strike.dm @@ -64,7 +64,7 @@ if(I) if(is_path_in_list(I.type, blacklisted_items)) - user << "You can't use \the [I] while warping!" + to_chat(user, "You can't use \the [I] while warping!") return if(istype(I, /obj/item/weapon)) diff --git a/code/game/jobs/access_datum.dm b/code/game/jobs/access_datum.dm index 9cf2d8ba8f..e6c407eaec 100644 --- a/code/game/jobs/access_datum.dm +++ b/code/game/jobs/access_datum.dm @@ -298,7 +298,7 @@ region = ACCESS_REGION_SUPPLY // /var/const/free_access_id = 51 -// /var/const/free_access_id= 52 +// /var/const/free_access_id = 52 /var/const/access_heads_vault = 53 /datum/access/heads_vault @@ -472,4 +472,4 @@ /datum/access/alien id = access_alien desc = "#%_^&*@!" - access_type = ACCESS_TYPE_PRIVATE + access_type = ACCESS_TYPE_PRIVATE \ No newline at end of file diff --git a/code/game/jobs/access_datum_vr.dm b/code/game/jobs/access_datum_vr.dm index 872db2ae73..506666fdfd 100644 --- a/code/game/jobs/access_datum_vr.dm +++ b/code/game/jobs/access_datum_vr.dm @@ -10,3 +10,10 @@ var/const/access_pilot = 67 id = access_pilot desc = "Pilot" region = ACCESS_REGION_SUPPLY + +/var/const/access_talon = 301 +/datum/access/talon + id = access_talon + desc = "Talon" + access_type = ACCESS_TYPE_PRIVATE + \ No newline at end of file diff --git a/code/game/jobs/access_datum_yw.dm b/code/game/jobs/access_datum_yw.dm new file mode 100644 index 0000000000..717a90635d --- /dev/null +++ b/code/game/jobs/access_datum_yw.dm @@ -0,0 +1,5 @@ +/var/const/access_blueshield = 52 +/datum/access/blueshield + id = access_blueshield + desc = "Blueshield Special Access" + region = ACCESS_REGION_COMMAND \ No newline at end of file diff --git a/code/game/jobs/job/_alt_title.dm b/code/game/jobs/job/_alt_title.dm new file mode 100644 index 0000000000..1cb7fde6b6 --- /dev/null +++ b/code/game/jobs/job/_alt_title.dm @@ -0,0 +1,8 @@ +///////////////////////////////////////// +// Alt Title Code +///////////////////////////////////////// + +/datum/alt_title + var/title = "GENERIC ALT TITLE" // What the Alt-Title is called + var/title_blurb = null // What's amended to the job description for this Job title. If nothing's added, leave null. + var/title_outfit = null // The outfit used by the alt-title. If it's the same as the base job, leave this null. \ No newline at end of file diff --git a/code/game/jobs/job/assistant.dm b/code/game/jobs/job/assistant.dm index d20e392599..c1270dab3e 100644 --- a/code/game/jobs/job/assistant.dm +++ b/code/game/jobs/job/assistant.dm @@ -1,7 +1,11 @@ +////////////////////////////////// +// Assistant +////////////////////////////////// /datum/job/assistant title = "Assistant" flag = ASSISTANT - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) + sorting_order = -1 department_flag = CIVILIAN faction = "Station" total_positions = -1 @@ -11,12 +15,12 @@ economic_modifier = 1 access = list() //See /datum/job/assistant/get_access() minimal_access = list() //See /datum/job/assistant/get_access() + outfit_type = /decl/hierarchy/outfit/job/assistant -/* alt_titles = list( - "Technical Assistant", - "Medical Intern", - "Research Assistant", - "Visitor" = /decl/hierarchy/outfit/job/assistant/visitor + job_description = "An Assistant does whatever is requested of them. Though they are part of the crew, they have no real authority." +/* alt_titles = list("Technical Assistant" = /datum/alt_title/tech_assist, + "Medical Intern"= /datum/alt_title/med_intern, "Research Assistant" = /datum/alt_title/research_assist, + "Visitor" = /datum/alt_title/visitor) ) */ //VOREStation Removal: no alt-titles for visitors /datum/job/assistant/get_access() @@ -24,3 +28,32 @@ return list(access_maint_tunnels) else return list() + +// Assistant Alt Titles +/datum/alt_title/tech_assist + title = "Technical Assistant" + title_blurb = "A Technical Assistant attempts to provide whatever the Engineering department needs. They are not proper Engineers, and are \ + often in training to become an Engineer. A Technical Assistant has no real authority." + +/datum/alt_title/med_intern + title = "Medical Intern" + title_blurb = "A Medical Intern attempts to provide whatever the Medical department needs. They are frequently asked to pay attention to \ + the suit sensors console. A Medical Intern has no real authority." + +/datum/alt_title/research_assist + title = "Research Assistant" + title_blurb = "A Research Assistant attempts to assist anyone working in the Research department. They are expected to follow the instruction \ + of proper Research staff, as it is frequently a matter of safety. A Research Assistant has no real authority." + +/datum/alt_title/visitor + title = "Visitor" + title_blurb = "A Visitor is anyone who has arrived on the station but does not have a specific job to do. Many off-duty crewmembers \ + who care to make use of the station's facilities arrive as Visitors. Properly registered Vistors are considered \ + to be part of the crew for most if not all purposes, but they have no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/visitor + +/datum/alt_title/resident // Just in case it makes a comeback + title = "Resident" + title_blurb = "A Resident is an individual who resides on the station, frequently in a different part of the station than what is seen. \ + They are considered to be part of the crew for most purposes, but have no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/resident diff --git a/code/game/jobs/job/assistant_vr.dm b/code/game/jobs/job/assistant_vr.dm index 212ca5f847..b3cf186d99 100644 --- a/code/game/jobs/job/assistant_vr.dm +++ b/code/game/jobs/job/assistant_vr.dm @@ -1,7 +1,11 @@ +////////////////////////////////// +// Intern +////////////////////////////////// + /datum/job/intern title = "Intern" flag = INTERN - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = ENGSEC // Ran out of bits faction = "Station" total_positions = -1 @@ -12,9 +16,60 @@ access = list() //See /datum/job/intern/get_access() minimal_access = list() //See /datum/job/intern/get_access() outfit_type = /decl/hierarchy/outfit/job/assistant/intern - alt_titles = list("Apprentice Engineer","Medical Intern","Lab Assistant","Security Cadet","Jr. Cargo Tech", "Jr. Explorer", "Server" = /decl/hierarchy/outfit/job/service/server) + alt_titles = list("Intern" = /datum/alt_title/intern, + "Apprentice Engineer" = /datum/alt_title/intern_eng, + "Medical Intern" = /datum/alt_title/intern_med, + "Lab Assistant" = /datum/alt_title/intern_sci, + "Security Cadet" = /datum/alt_title/intern_sec, + "Jr. Cargo Tech" = /datum/alt_title/intern_crg, + "Server" = /datum/alt_title/server) + job_description = "An Intern does whatever is requested of them, often doing so in process of learning \ + another job. Though they are part of the crew, they have no real authority." timeoff_factor = 0 // Interns, noh +/datum/alt_title/intern + title = "Intern" + +/datum/alt_title/intern_eng + title = "Apprentice Engineer" + title_blurb = "An Apprentice Engineer attempts to provide whatever the Engineering department needs. They are not proper Engineers, and are \ + often in training to become an Engineer. A Technical Assistant has no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/engineer + +/datum/alt_title/intern_med + title = "Medical Intern" + title_blurb = "A Medical Intern attempts to provide whatever the Medical department needs. They are not proper Doctors, and are \ + often in training to become a Doctor. A Medical Intern has no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/medic + +/datum/alt_title/intern_sci + title = "Lab Assistant" + title_blurb = "A Lab Assistant attempts to provide whatever the Research department needs. They are not proper Scientists, and are \ + often in training to become a Scientist. A Lab Assistant has no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/scientist + +/datum/alt_title/intern_sec + title = "Security Cadet" + title_blurb = "A Security Cadet attempts to provide whatever the Security department needs. They are not proper Officers, and are \ + often in training to become an Officer. A Security Cadet has no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/officer + +/datum/alt_title/intern_crg + title = "Jr. Cargo Tech" + title_blurb = "A Jr. Cargo Tech attempts to provide whatever the Cargo department needs. They are not proper Cargo Technicians, and are \ + often in training to become a Cargo Technician. A Jr. Cargo Tech has no real authority." + title_outfit = /decl/hierarchy/outfit/job/assistant/cargo + +/datum/alt_title/server + title = "Server" + title_blurb = "A Server helps out kitchen and diner staff with various tasks, primarily food delivery. A Server has no real authority." + title_outfit = /decl/hierarchy/outfit/job/service/server + + +////////////////////////////////// +// Visitor +////////////////////////////////// + /datum/job/intern/New() ..() if(config) @@ -30,6 +85,7 @@ /datum/job/assistant // Visitor title = USELESS_JOB supervisors = "nobody! You don't work here" + job_description = "A Visitor is just there to visit the place. They have no real authority or responsibility." timeoff_factor = 0 /datum/job/assistant/New() diff --git a/code/game/jobs/job/blueshield.dm b/code/game/jobs/job/blueshield.dm index d7c819666d..29102d2fef 100644 --- a/code/game/jobs/job/blueshield.dm +++ b/code/game/jobs/job/blueshield.dm @@ -1,7 +1,7 @@ /datum/job/blueshield title = "Blueshield Guard" flag = BLUESHIELD //this may be used later. - department = "Command" + departments = list(DEPARTMENT_COMMAND) department_flag = ENGSEC faction = "Station" total_positions = 1 @@ -9,7 +9,6 @@ supervisors = "the Colony Director" //Reports directly to CD selection_color = "#006cb3" req_admin_notify = 1 - head_position = 1 minimum_character_age = 25 ideal_character_age = 32 //Experienced, but physically in their prime minimal_player_age = 3 @@ -17,7 +16,13 @@ access = list(access_security, access_sec_doors, access_brig, access_medical, access_eva, access_heads, access_teleporter, access_maint_tunnels, access_morgue, - access_crematorium, access_research, access_hop, access_RC_announce, access_keycard_auth, access_gateway) - minimal_access = list(access_forensics_lockers, access_sec_doors, access_medical, access_maint_tunnels, access_RC_announce, access_keycard_auth, access_heads) + access_crematorium, access_research, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_blueshield) + minimal_access = list(access_forensics_lockers, access_sec_doors, access_medical, access_maint_tunnels, access_RC_announce, access_keycard_auth, access_heads, access_blueshield) - outfit_type = /decl/hierarchy/outfit/job/blueshield \ No newline at end of file + outfit_type = /decl/hierarchy/outfit/job/blueshield + job_description = "Placeholder desc: General rules is to not get involved with security matters, your job is only to keep command personnel alive." + +/datum/job/blueshield/equip(var/mob/living/carbon/human/H) + . = ..() + if(.) + H.implant_loyalty(src) \ No newline at end of file diff --git a/code/game/jobs/job/captain.dm b/code/game/jobs/job/captain.dm index 9e2f5121d1..c65837e123 100644 --- a/code/game/jobs/job/captain.dm +++ b/code/game/jobs/job/captain.dm @@ -1,16 +1,21 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) +////////////////////////////////// +// Captain +////////////////////////////////// + /datum/job/captain title = "Colony Director" flag = CAPTAIN - department = "Command" - head_position = 1 + departments = list(DEPARTMENT_COMMAND) + sorting_order = 3 // Above everyone. + departments_managed = list(DEPARTMENT_COMMAND) department_flag = ENGSEC faction = "Station" total_positions = 1 spawn_positions = 1 supervisors = "company officials and Corporate Regulations" - selection_color = "#1D1D4F" + selection_color = "#2F2F7F" req_admin_notify = 1 access = list() //See get_access() minimal_access = list() //See get_access() @@ -21,28 +26,46 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) ideal_character_age = 70 // Old geezer captains ftw outfit_type = /decl/hierarchy/outfit/job/captain - alt_titles = list("Site Manager", "Overseer") + job_description = "The Colony Director manages the other Command Staff, and through them the rest of the station. Though they have access to everything, \ + they do not understand everything, and are expected to delegate tasks to the appropriate crew member. The Colony Director is expected to \ + have an understanding of Standard Operating Procedure, and is subject to it, and legal action, in the same way as every other crew member." + alt_titles = list("Site Manager" = /datum/alt_title/site_manager, + "Overseer" = /datum/alt_title/overseer) -/* +//YW UNCOMMENTINGSTART: REINSTATE LOYALTY IMPLANT /datum/job/captain/equip(var/mob/living/carbon/human/H) . = ..() if(.) H.implant_loyalty(src) -*/ +//YW UNCOMMENTING END + + /datum/job/captain/get_access() return get_all_station_access().Copy() + +// Captain Alt Titles +/datum/alt_title/site_manager + title = "Site Manager" + +/datum/alt_title/overseer + title = "Overseer" + +////////////////////////////////// +// Head of Personnel +////////////////////////////////// /datum/job/hop title = "Head of Personnel" flag = HOP - department = "Command" - head_position = 1 + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_CIVILIAN, DEPARTMENT_CARGO) + sorting_order = 2 // Above the QM, below captain. + departments_managed = list(DEPARTMENT_CIVILIAN, DEPARTMENT_CARGO) department_flag = CIVILIAN faction = "Station" total_positions = 1 spawn_positions = 1 supervisors = "the Colony Director" - selection_color = "#2F2F7F" + selection_color = "#1D1D4F" req_admin_notify = 1 minimal_player_age = 10 economic_modifier = 10 @@ -51,7 +74,10 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) ideal_character_age = 50 outfit_type = /decl/hierarchy/outfit/job/hop - alt_titles = list("Crew Resources Officer") + job_description = "The Head of Personnel manages the Service department, the Exploration team, and most other civilians. They also \ + manage the Supply department, through the Quartermaster. In addition, the Head of Personnel oversees the personal accounts \ + of the crew, including their money and access. If necessary, the Head of Personnel is first in line to assume Acting Command." + alt_titles = list("Crew Resources Officer" = /datum/alt_title/cro) access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers, access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, @@ -66,17 +92,25 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, access_hop, access_RC_announce, access_keycard_auth, access_gateway) +// HOP Alt Titles +/datum/alt_title/cro + title = "Crew Resources Officer" + +////////////////////////////////// +// Command Secretary +////////////////////////////////// + /datum/job/secretary title = "Command Secretary" flag = BRIDGE - department = "Command" - head_position = 1 + departments = list(DEPARTMENT_COMMAND) + department_accounts = list(DEPARTMENT_COMMAND) department_flag = CIVILIAN faction = "Station" total_positions = 2 spawn_positions = 2 supervisors = "command staff" - selection_color = "#2F2F7F" + selection_color = "#1D1D4F" minimal_player_age = 5 economic_modifier = 7 @@ -84,7 +118,6 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) minimal_access = list(access_heads, access_keycard_auth, access_RC_announce)//YAWN EDIT outfit_type = /decl/hierarchy/outfit/job/secretary - - - + job_description = "A Command Secretary handles paperwork duty for the Heads of Staff, so they can better focus on managing their departments. \ + They are not Heads of Staff, and have no real authority." diff --git a/code/game/jobs/job/captain_vr.dm b/code/game/jobs/job/captain_vr.dm index b6c38cae15..ea9f8ebed9 100644 --- a/code/game/jobs/job/captain_vr.dm +++ b/code/game/jobs/job/captain_vr.dm @@ -1,10 +1,13 @@ /datum/job/captain disallow_jobhop = TRUE + pto_type = PTO_CIVILIAN /datum/job/hop - disallow_jobhop = TRUE - alt_titles = list("Deputy Director", "Crew Resources Officer") + pto_type = PTO_CIVILIAN + + alt_titles = list("Crew Resources Officer" = /datum/alt_title/cro, + "Deputy Director" = /datum/alt_title/deputy_director) access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers, access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, @@ -19,5 +22,9 @@ access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, access_hop, access_RC_announce, access_keycard_auth) +/datum/alt_title/deputy_director + title = "Deputy Director" + /datum/job/secretary - disallow_jobhop = TRUE \ No newline at end of file + disallow_jobhop = TRUE + pto_type = PTO_CIVILIAN \ No newline at end of file diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index d5a099af8c..b48ee239bf 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -1,65 +1,101 @@ //Food + +////////////////////////////////// +// Bartender +////////////////////////////////// + /datum/job/bartender title = "Bartender" flag = BARTENDER - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the head of personnel" + supervisors = "the Head of Personnel" selection_color = "#515151" access = list(access_hydroponics, access_bar, access_kitchen) minimal_access = list(access_bar) outfit_type = /decl/hierarchy/outfit/job/service/bartender - alt_titles = list("Barista" = /decl/hierarchy/outfit/job/service/bartender/barista) + job_description = "A Bartender mixes drinks for the crew. They generally have permission to charge for drinks or deny service to unruly patrons." + alt_titles = list("Barista" = /datum/alt_title/barista) +// Bartender Alt Titles +/datum/alt_title/barista + title = "Barista" + title_blurb = "A barista mans the Cafe, serving primarily non-alcoholic drinks to the crew. They generally have permission to charge for drinks \ + or deny service to unruly patrons." + title_outfit = /decl/hierarchy/outfit/job/service/bartender/barista + +////////////////////////////////// +// Chef +////////////////////////////////// /datum/job/chef title = "Chef" flag = CHEF - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the head of personnel" + supervisors = "the Head of Personnel" selection_color = "#515151" access = list(access_hydroponics, access_bar, access_kitchen) minimal_access = list(access_kitchen) outfit_type = /decl/hierarchy/outfit/job/service/chef - alt_titles = list("Cook") + job_description = "A Chef cooks food for the crew. They generally have permission to charge for food or deny service to unruly diners." + alt_titles = list("Cook" = /datum/alt_title/cook) + +// Chef Alt Titles +/datum/alt_title/cook + title = "Cook" + title_blurb = "A Cook has the same duties, though they may be less experienced." + +////////////////////////////////// +// Botanist +////////////////////////////////// /datum/job/hydro title = "Botanist" flag = BOTANIST - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = 2 spawn_positions = 1 - supervisors = "the head of personnel" + supervisors = "the Head of Personnel" selection_color = "#515151" access = list(access_hydroponics, access_bar, access_kitchen) minimal_access = list(access_hydroponics) outfit_type = /decl/hierarchy/outfit/job/service/gardener - alt_titles = list("Gardener") + job_description = "A Botanist grows plants for the Chef and Bartender." + alt_titles = list("Gardener" = /datum/alt_title/gardener) + +//Botanist Alt Titles +/datum/alt_title/gardener + title = "Gardener" + title_blurb = "A Gardener may be less professional than their counterparts, and are more likely to tend to the public gardens if they aren't needed elsewhere." //Cargo +////////////////////////////////// +// Quartermaster +////////////////////////////////// /datum/job/qm title = "Quartermaster" flag = QUARTERMASTER - department = "Cargo" - head_position = 1 + departments = list(DEPARTMENT_CARGO) + sorting_order = 1 // QM is above the cargo techs, but below the HoP. + departments_managed = list(DEPARTMENT_CARGO) department_flag = CIVILIAN faction = "Station" total_positions = 1 spawn_positions = 1 - supervisors = "the head of personnel" - selection_color = "#7a4f33" + supervisors = "the Head of Personnel" + selection_color = "#9b633e" economic_modifier = 5 access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station) minimal_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station) @@ -67,79 +103,123 @@ ideal_character_age = 40 outfit_type = /decl/hierarchy/outfit/job/cargo/qm - alt_titles = list("Supply Chief") + job_description = "The Quartermaster manages the Supply department, checking cargo orders and ensuring supplies get to where they are needed." + alt_titles = list("Supply Chief" = /datum/alt_title/supply_chief) +// Quartermaster Alt Titles +/datum/alt_title/supply_chief + title = "Supply Chief" + +////////////////////////////////// +// Cargo Tech +////////////////////////////////// /datum/job/cargo_tech title = "Cargo Technician" flag = CARGOTECH - department = "Cargo" + departments = list(DEPARTMENT_CARGO) department_flag = CIVILIAN faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the quartermaster and the head of personnel" - selection_color = "#9b633e" + supervisors = "the Quartermaster and the Head of Personnel" + selection_color = "#7a4f33" access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_mining, access_mining_station) minimal_access = list(access_maint_tunnels, access_cargo, access_cargo_bot, access_mailsorting) outfit_type = /decl/hierarchy/outfit/job/cargo/cargo_tech + job_description = "A Cargo Technician fills and delivers cargo orders. They are encouraged to return delivered crates to the Cargo Shuttle, \ + because Central Command gives a partial refund." + +////////////////////////////////// +// Shaft Miner +////////////////////////////////// /datum/job/mining title = "Shaft Miner" flag = MINER - department = "Cargo" + departments = list(DEPARTMENT_CARGO) department_flag = CIVILIAN faction = "Station" total_positions = 3 spawn_positions = 3 - supervisors = "the quartermaster and the head of personnel" - selection_color = "#9b633e" + supervisors = "the Quartermaster and the Head of Personnel" + selection_color = "#7a4f33" economic_modifier = 5 access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_mining, access_mining_station) minimal_access = list(access_mining, access_mining_station, access_mailsorting) outfit_type = /decl/hierarchy/outfit/job/cargo/mining - alt_titles = list("Drill Technician") + job_description = "A Shaft Miner mines and processes minerals to be delivered to departments that need them." + alt_titles = list("Drill Technician" = /datum/alt_title/drill_tech) + +/datum/alt_title/drill_tech + title = "Drill Technician" + title_blurb = "A Drill Technician specializes in operating and maintaining the machinery needed to extract ore from veins deep below the surface." //Service +////////////////////////////////// +// Janitor +////////////////////////////////// /datum/job/janitor title = "Janitor" flag = JANITOR - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the head of personnel" + supervisors = "the Head of Personnel" selection_color = "#515151" access = list(access_janitor, access_maint_tunnels) minimal_access = list(access_janitor, access_maint_tunnels) outfit_type = /decl/hierarchy/outfit/job/service/janitor - alt_titles = list("Custodian") + job_description = "A Janitor keeps the station clean, as long as it doesn't interfere with active crime scenes." + alt_titles = list("Custodian" = /datum/alt_title/custodian) + +// Janitor Alt Titles +/datum/alt_title/custodian + title = "Custodian" //More or less assistants +////////////////////////////////// +// Librarian +////////////////////////////////// /datum/job/librarian title = "Librarian" flag = LIBRARIAN - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = 1 spawn_positions = 1 - supervisors = "the head of personnel" + supervisors = "the Head of Personnel" selection_color = "#515151" access = list(access_library, access_maint_tunnels) minimal_access = list(access_library) outfit_type = /decl/hierarchy/outfit/job/librarian - alt_titles = list("Journalist", "Writer") + job_description = "The Librarian curates the book selection in the Library, so the crew might enjoy it." + alt_titles = list("Journalist" = /datum/alt_title/journalist, "Writer" = /datum/alt_title/writer) + +// Librarian Alt Titles +/datum/alt_title/journalist + title = "Journalist" + title_blurb = "The Journalist uses the Library as a base of operations, from which they can report the news and goings-on on the station with their camera." + +/datum/alt_title/writer + title = "Writer" + title_blurb = "The Writer uses the Library as a quiet place to write whatever it is they choose to write." + +////////////////////////////////// +// Internal Affairs Agent +////////////////////////////////// //var/global/lawyer = 0//Checks for another lawyer //This changed clothes on 2nd lawyer, both IA get the same dreds. /datum/job/lawyer title = "Internal Affairs Agent" flag = LAWYER - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = 2 @@ -152,10 +232,13 @@ minimal_player_age = 7 outfit_type = /decl/hierarchy/outfit/job/internal_affairs_agent + job_description = "An Internal Affairs Agent makes sure that the crew is following Standard Operating Procedure. They also \ + handle complaints against crew members, and can have issues brought to the attention of Central Command, \ + assuming their paperwork is in order." -/* +//YW UNCOMMENTINGSTART: REINSTATE LOYALTY IMPLANT /datum/job/lawyer/equip(var/mob/living/carbon/human/H) . = ..() if(.) H.implant_loyalty(H) -*/ \ No newline at end of file +//YW UNCOMMENTING END \ No newline at end of file diff --git a/code/game/jobs/job/civilian_chaplain.dm b/code/game/jobs/job/civilian_chaplain.dm index 12c2c9419d..2f208fb545 100644 --- a/code/game/jobs/job/civilian_chaplain.dm +++ b/code/game/jobs/job/civilian_chaplain.dm @@ -2,18 +2,24 @@ /datum/job/chaplain title = "Chaplain" flag = CHAPLAIN - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = 1 spawn_positions = 1 - supervisors = "the head of personnel" + supervisors = "the Head of Personnel" selection_color = "#515151" access = list(access_morgue, access_chapel_office, access_crematorium, access_maint_tunnels) minimal_access = list(access_chapel_office, access_crematorium) - alt_titles = list("Counselor") outfit_type = /decl/hierarchy/outfit/job/chaplain + job_description = "The Chaplain ministers to the spiritual needs of the crew." + alt_titles = list("Counselor" = /datum/alt_title/counselor) + +// Chaplain Alt Titles +/datum/alt_title/counselor + title = "Counselor" + title_blurb = "The Counselor attends to the emotional needs of the crew, without a specific medicinal or spiritual focus." /datum/job/chaplain/equip(var/mob/living/carbon/human/H, var/alt_title, var/ask_questions = TRUE) . = ..() diff --git a/code/game/jobs/job/civilian_vr.dm b/code/game/jobs/job/civilian_vr.dm index 9ade997b3e..2844108187 100644 --- a/code/game/jobs/job/civilian_vr.dm +++ b/code/game/jobs/job/civilian_vr.dm @@ -1,31 +1,54 @@ +/datum/job/bartender + pto_type = PTO_CIVILIAN + /datum/job/chef total_positions = 2 //IT TAKES A LOT TO MAKE A STEW spawn_positions = 2 //A PINCH OF SALT AND LAUGHTER, TOO + pto_type = PTO_CIVILIAN /datum/job/hydro spawn_positions = 2 + pto_type = PTO_CIVILIAN + +/datum/job/qm + pto_type = PTO_CARGO /datum/job/cargo_tech total_positions = 3 spawn_positions = 3 + pto_type = PTO_CARGO /datum/job/mining total_positions = 4 spawn_positions = 4 + pto_type = PTO_CARGO /datum/job/janitor //Lots of janitor substations on station. total_positions = 3 spawn_positions = 3 - alt_titles = list("Custodian", "Sanitation Technician", "Maid") + alt_titles = list("Custodian" = /datum/alt_title/custodian, "Sanitation Technician" = /datum/alt_title/sanitation_tech, "Maid" = /datum/alt_title/maid) + pto_type = PTO_CIVILIAN + +/datum/alt_title/sanitation_tech + title = "Sanitation Technician" + +/datum/alt_title/maid + title = "Maid" -//TFF 5/9/19 - restore librarian job slot to 2 /datum/job/librarian total_positions = 2 spawn_positions = 2 - alt_titles = list("Journalist", "Historian", "Writer") + alt_titles = list("Journalist" = /datum/alt_title/journalist, "Writer" = /datum/alt_title/writer, "Historian" = /datum/alt_title/historian) + pto_type = PTO_CIVILIAN + +/datum/alt_title/historian + title = "Historian" + title_blurb = "The Historian uses the Library as a base of operation to record any important events occuring on station." /datum/job/lawyer disallow_jobhop = TRUE + pto_type = PTO_CIVILIAN - +/datum/job/chaplain + pto_type = PTO_CIVILIAN diff --git a/code/game/jobs/job/department.dm b/code/game/jobs/job/department.dm new file mode 100644 index 0000000000..39bbc1c38c --- /dev/null +++ b/code/game/jobs/job/department.dm @@ -0,0 +1,80 @@ +// A datum that holds information about a specific department. +// It is held inside, and managed by, the SSjob subsystem automatically, +// just define a department, and put that department's name in one or more job datums' departments list. + +/datum/department + var/name = "NOPE" // Name used in UIs, and the index for the department assoc list in SSjob. + var/short_name = "NO" // Shorter name, used for things like external Topic() responses. + var/color = "#000000" // Color to use in UIs to represent this department. + var/list/jobs = list() // Assoc list. Key is the job title, and the value is a reference to the job datum. Populated by SSjob subsystem. + var/list/primary_jobs = list() // Same as above, but only jobs with their 'primary' department are put here. Primary being the first department in their list. + var/sorting_order = 0 // Used to sort departments, e.g. Command always being on top. + var/visible = TRUE // If false, it should not show up on things like the manifest or ID computer. + var/assignable = TRUE // Similar for above, but only for ID computers and such. Used for silicon department. + var/centcom_only = FALSE + +/datum/department/command + name = DEPARTMENT_COMMAND + short_name = "Heads" + color = "#3333FF" + sorting_order = 10 + +/datum/department/security + name = DEPARTMENT_SECURITY + short_name = "Sec" + color = "#8E0000" + sorting_order = 6 + +/datum/department/engineering + name = DEPARTMENT_ENGINEERING + short_name = "Eng" + color = "#B27300" + sorting_order = 5 + +/datum/department/medical + name = DEPARTMENT_MEDICAL + short_name = "Med" + color = "#006600" + sorting_order = 4 + +/datum/department/research + name = DEPARTMENT_RESEARCH + short_name = "Sci" + color = "#A65BA6" + sorting_order = 3 + +/datum/department/cargo + name = DEPARTMENT_CARGO + short_name = "Car" + color = "#BB9040" + sorting_order = 2 + +/datum/department/civilian + name = DEPARTMENT_CIVILIAN + short_name = "Civ" + color = "#A32800" + sorting_order = 1 + +// Mostly for if someone wanted to rewrite manifest code to be map-agnostic. +/datum/department/misc + name = "Miscellaneous" + short_name = "Misc" + color = "#666666" + sorting_order = 0 + assignable = FALSE + +/datum/department/synthetic + name = DEPARTMENT_SYNTHETIC + short_name = "Bot" + color = "#222222" + sorting_order = -1 + assignable = FALSE + +// This one isn't very useful since no real centcom jobs exist yet. +// Instead the jobs like ERT are hardcoded in. +/datum/department/centcom + name = "Central Command" + short_name = "Centcom" + color = "#A52A2A" + sorting_order = 20 // Above Command. + centcom_only = TRUE \ No newline at end of file diff --git a/code/game/jobs/job/department_vr.dm b/code/game/jobs/job/department_vr.dm new file mode 100644 index 0000000000..2af1d8e94f --- /dev/null +++ b/code/game/jobs/job/department_vr.dm @@ -0,0 +1,6 @@ +/datum/department/misc + name = "Off-Duty" + short_name = "Offduty" + color = "#666666" + sorting_order = -5 + assignable = FALSE \ No newline at end of file diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm index cfa79ffc25..285c3dc814 100644 --- a/code/game/jobs/job/engineering.dm +++ b/code/game/jobs/job/engineering.dm @@ -1,8 +1,12 @@ +////////////////////////////////// +// Chief Engineer +////////////////////////////////// /datum/job/chief_engineer title = "Chief Engineer" flag = CHIEF - head_position = 1 - department = "Engineering" + departments_managed = list(DEPARTMENT_ENGINEERING) + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_COMMAND) + sorting_order = 2 department_flag = ENGSEC faction = "Station" total_positions = 1 @@ -27,35 +31,63 @@ minimal_player_age = 7 outfit_type = /decl/hierarchy/outfit/job/engineering/chief_engineer + job_description = "The Chief Engineer manages the Engineering Department, ensuring that the Engineers work on what needs to be done, handling distribution \ + of manpower as much as they handle hands-on operations and repairs. They are also expected to keep the rest of the station informed of \ + any structural threats to the station that may be hazardous to health or disruptive to work." +////////////////////////////////// +// Engineer +////////////////////////////////// /datum/job/engineer title = "Station Engineer" flag = ENGINEER - department = "Engineering" + departments = list(DEPARTMENT_ENGINEERING) department_flag = ENGSEC faction = "Station" total_positions = 5 spawn_positions = 5 - supervisors = "the chief engineer" + supervisors = "the Chief Engineer" selection_color = "#5B4D20" economic_modifier = 5 access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics) minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction) - alt_titles = list("Maintenance Technician","Engine Technician","Electrician") + alt_titles = list("Maintenance Technician" = /datum/alt_title/maint_tech, + "Engine Technician" = /datum/alt_title/engine_tech, "Electrician" = /datum/alt_title/electrician) minimal_player_age = 3 outfit_type = /decl/hierarchy/outfit/job/engineering/engineer + job_description = "An Engineer keeps the station running. They repair damages, keep the atmosphere stable, and ensure that power is being \ + generated and distributed. On quiet shifts, they may be called upon to make cosmetic alterations to the station." +// Engineer Alt Titles +/datum/alt_title/maint_tech + title = "Maintenance Technician" + title_blurb = "A Maintenance Technician is generally a junior Engineer, and can be expected to run the mildly unpleasant or boring tasks that other \ + Engineers don't care to do." + +/datum/alt_title/engine_tech + title = "Engine Technician" + title_blurb = "An Engine Technician tends to the engine, most commonly a Supermatter crystal. They are expected to be able to keep it stable, and \ + possibly even run it beyond normal tolerances." + +/datum/alt_title/electrician + title = "Electrician" + title_blurb = "An Electrician's primary duty is making sure power is properly distributed thoughout the station, utilizing solars, substations, and other \ + methods to ensure every department has power in an emergency." + +////////////////////////////////// +// Atmos Tech +////////////////////////////////// /datum/job/atmos title = "Atmospheric Technician" flag = ATMOSTECH - department = "Engineering" + departments = list(DEPARTMENT_ENGINEERING) department_flag = ENGSEC faction = "Station" total_positions = 3 spawn_positions = 2 - supervisors = "the chief engineer" + supervisors = "the Chief Engineer" selection_color = "#5B4D20" economic_modifier = 5 access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics, access_external_airlocks) @@ -63,4 +95,6 @@ minimal_player_age = 3 - outfit_type = /decl/hierarchy/outfit/job/engineering/atmos \ No newline at end of file + outfit_type = /decl/hierarchy/outfit/job/engineering/atmos + job_description = "An Atmospheric Technician is primarily concerned with keeping the station's atmosphere breathable. They are expected to have a good \ + understanding of the pipes, vents, and scrubbers that move gasses around the station, and to be familiar with proper firefighting procedure." diff --git a/code/game/jobs/job/engineering_vr.dm b/code/game/jobs/job/engineering_vr.dm index da4b94de38..10337d9881 100644 --- a/code/game/jobs/job/engineering_vr.dm +++ b/code/game/jobs/job/engineering_vr.dm @@ -1,5 +1,10 @@ /datum/job/chief_engineer disallow_jobhop = TRUE + pto_type = PTO_ENGINEERING + +/datum/job/engineer + pto_type = PTO_ENGINEERING /datum/job/atmos - spawn_positions = 3 \ No newline at end of file + spawn_positions = 3 + pto_type = PTO_ENGINEERING \ No newline at end of file diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index d138a0e1c9..082753d0fc 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -13,11 +13,14 @@ var/current_positions = 0 // How many players have this job var/supervisors = null // Supervisors, who this person answers to directly var/selection_color = "#ffffff" // Selection screen color - var/list/alt_titles // List of alternate titles, if any + var/list/alt_titles = null // List of alternate titles; There is no need for an alt-title datum for the base job title. var/req_admin_notify // If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. var/minimal_player_age = 0 // If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) - var/department = null // Does this position have a department tag? - var/head_position = 0 // Is this position Command? + var/list/departments = list() // List of departments this job belongs to, if any. The first one on the list will be the 'primary' department. + var/sorting_order = 0 // Used for sorting jobs so boss jobs go above regular ones, and their boss's boss is above that. Higher numbers = higher in sorting. + var/departments_managed = null // Is this a management position? If yes, list of departments managed. Otherwise null. + var/department_accounts = null // Which department accounts should people with this position be given the pin for? + var/assignable = TRUE // Should it show up on things like the ID computer? var/minimum_character_age = 0 var/ideal_character_age = 30 var/has_headset = TRUE //Do people with this job need to be given headsets and told how to use them? E.g. Cyborgs don't. @@ -25,7 +28,16 @@ var/account_allowed = 1 // Does this job type come with a station account? var/economic_modifier = 2 // With how much does this job modify the initial account amount? - var/outfit_type + var/outfit_type // What outfit datum does this job use in its default title? + + var/offmap_spawn = FALSE // Do we require weird and special spawning and datacore handling? + + // Description of the job's role and minimum responsibilities. + var/job_description = "This Job doesn't have a description! Please report it!" + +/datum/job/New() + . = ..() + department_accounts = department_accounts || departments_managed /datum/job/proc/equip(var/mob/living/carbon/human/H, var/alt_title) var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title) @@ -36,7 +48,9 @@ /datum/job/proc/get_outfit(var/mob/living/carbon/human/H, var/alt_title) if(alt_title && alt_titles) - . = alt_titles[alt_title] + var/datum/alt_title/A = alt_titles[alt_title] + if(A && initial(A.title_outfit)) + . = initial(A.title_outfit) . = . || outfit_type . = outfit_by_type(.) @@ -55,7 +69,7 @@ //give them an account in the station database var/money_amount = (rand(15,40) + rand(15,40)) * income * economic_modifier * ECO_MODIFIER //VOREStation Edit - Smoothed peaks, ECO_MODIFIER rather than per-species ones. - var/datum/money_account/M = create_account(H.real_name, money_amount, null) + var/datum/money_account/M = create_account(H.real_name, money_amount, null, offmap_spawn) if(H.mind) var/remembered_info = "" remembered_info += "Your account number is: #[M.account_number]
" @@ -69,7 +83,7 @@ H.mind.initial_account = M - H << "Your account number is: [M.account_number], your account pin is: [M.remote_access_pin]" + to_chat(H, "Your account number is: [M.account_number], your account pin is: [M.remote_access_pin]") // overrideable separately so AIs/borgs can have cardborg hats without unneccessary new()/qdel() /datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) @@ -110,4 +124,37 @@ return (current_positions < total_positions) || (total_positions == -1) /datum/job/proc/has_alt_title(var/mob/H, var/supplied_title, var/desired_title) - return (supplied_title == desired_title) || (H.mind && H.mind.role_alt_title == desired_title) \ No newline at end of file + return (supplied_title == desired_title) || (H.mind && H.mind.role_alt_title == desired_title) + +/datum/job/proc/get_description_blurb(var/alt_title) + var/list/message = list() + message |= job_description + + if(alt_title && alt_titles) + var/typepath = alt_titles[alt_title] + if(typepath) + var/datum/alt_title/A = new typepath() + if(A.title_blurb) + message |= A.title_blurb + return message + +/datum/job/proc/get_job_icon() + if(!job_master.job_icons[title]) + var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin("#job_icon") + dress_mannequin(mannequin) + mannequin.dir = SOUTH + COMPILE_OVERLAYS(mannequin) + var/icon/preview_icon = getFlatIcon(mannequin) + + preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. + job_master.job_icons[title] = preview_icon + + return job_master.job_icons[title] + +/datum/job/proc/dress_mannequin(var/mob/living/carbon/human/dummy/mannequin/mannequin) + mannequin.delete_inventory(TRUE) + equip_preview(mannequin) + if(mannequin.back) + var/obj/O = mannequin.back + mannequin.drop_from_inventory(O) + qdel(O) \ No newline at end of file diff --git a/code/game/jobs/job/job_vr.dm b/code/game/jobs/job/job_vr.dm index 060aee6fef..ce982aaab9 100644 --- a/code/game/jobs/job/job_vr.dm +++ b/code/game/jobs/job/job_vr.dm @@ -8,9 +8,12 @@ //Every hour playing this role gains this much time off. (Can be negative for off duty jobs!) var/timeoff_factor = 3 + //What type of PTO is that job earning? + var/pto_type + //Disallow joining as this job midround from off-duty position via going on-duty var/disallow_jobhop = FALSE // Check client-specific availability rules. /datum/job/proc/player_has_enough_pto(client/C) - return timeoff_factor >= 0 || (C && LAZYACCESS(C.department_hours, department) > 0) + return timeoff_factor >= 0 || (C && LAZYACCESS(C.department_hours, pto_type) > 0) diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index c855411303..d276c96648 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -1,8 +1,12 @@ +////////////////////////////////// +// Chief Medical Officer +////////////////////////////////// /datum/job/cmo title = "Chief Medical Officer" flag = CMO - head_position = 1 - department = "Medical" + departments_managed = list(DEPARTMENT_MEDICAL) + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_COMMAND) + sorting_order = 2 department_flag = MEDSCI faction = "Station" total_positions = 1 @@ -23,93 +27,168 @@ ideal_character_age = 50 outfit_type = /decl/hierarchy/outfit/job/medical/cmo + job_description = "The CMO manages the Medical department and is a position requiring experience and skill; their goal is to ensure that their \ + staff keep the station's crew healthy and whole. They are primarily interested in making sure that patients are safely found and \ + transported to Medical for treatment. They are expected to keep the crew informed about threats to their health and safety, and \ + about the importance of Suit Sensors." +////////////////////////////////// +// Medical Doctor +////////////////////////////////// /datum/job/doctor title = "Medical Doctor" flag = DOCTOR - department = "Medical" + departments = list(DEPARTMENT_MEDICAL) department_flag = MEDSCI faction = "Station" total_positions = 5 spawn_positions = 3 - supervisors = "the chief medical officer" + supervisors = "the Chief Medical Officer" selection_color = "#013D3B" economic_modifier = 7 access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_eva) minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_virology, access_eva) outfit_type = /decl/hierarchy/outfit/job/medical/doctor + job_description = "A Medical Doctor is a Jack-of-All-Trades Medical title, covering a variety of skill levels and minor specializations. They are likely \ + familiar with basic first aid, and a number of accompanying medications, and can generally save, if not cure, a majority of the \ + patients they encounter." alt_titles = list( - "Surgeon" = /decl/hierarchy/outfit/job/medical/doctor/surgeon, - "Emergency Physician" = /decl/hierarchy/outfit/job/medical/doctor/emergency_physician, - "Nurse" = /decl/hierarchy/outfit/job/medical/doctor/nurse, - "Virologist" = /decl/hierarchy/outfit/job/medical/doctor/virologist) + "Surgeon" = /datum/alt_title/surgeon, + "Emergency Physician" = /datum/alt_title/emergency_physician, + "Nurse" = /datum/alt_title/nurse, + "Virologist" = /datum/alt_title/virologist) + +//Medical Doctor Alt Titles +/datum/alt_title/surgeon + title = "Surgeon" + title_blurb = "A Surgeon specializes in providing surgical aid to injured patients, up to and including amputation and limb reattachement. They are expected \ + to know the ins and outs of anesthesia and surgery." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/surgeon + +/datum/alt_title/emergency_physician + title = "Emergency Physician" + title_blurb = "An Emergency Physician is a Medical professional trained for stabilizing and treating severely injured and/or dying patients. \ + They are generally the first response for any such individual brought to the Medbay, and can sometimes be expected to help their patients \ + make a full recovery." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/emergency_physician + +/datum/alt_title/nurse + title = "Nurse" + title_blurb = "A Nurse acts as a general purpose Doctor's Aide, providing basic care to non-critical patients, and stabilizing critical patients during \ + busy periods. They frequently watch the suit sensors console, to help manage the time of other Doctors. In rare occasions, a Nurse can be \ + called upon to revive deceased crew members." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/nurse + +/datum/alt_title/virologist + title = "Virologist" + title_blurb = "A Virologist cures active diseases in the crew, and prepares antibodies for possible infections. They also have the skills \ + to produce the various types of virus foods or mutagens." + title_outfit = /decl/hierarchy/outfit/job/medical/doctor/virologist //Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro +////////////////////////////////// +// Chemist +////////////////////////////////// /datum/job/chemist title = "Chemist" flag = CHEMIST - department = "Medical" + departments = list(DEPARTMENT_MEDICAL) department_flag = MEDSCI faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the chief medical officer" + supervisors = "the Chief Medical Officer" selection_color = "#013D3B" economic_modifier = 5 access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) minimal_access = list(access_medical, access_medical_equip, access_chemistry) - alt_titles = list("Pharmacist") - minimal_player_age = 3 outfit_type = /decl/hierarchy/outfit/job/medical/chemist + job_description = "A Chemist produces and maintains a stock of basic to advanced chemicals for medical and occasionally research use. \ + They are likely to know the use and dangers of many lab-produced chemicals." + alt_titles = list("Pharmacist" = /datum/alt_title/pharmacist) + +// Chemist Alt Titles +/datum/alt_title/pharmacist + title = "Pharmacist" + title_blurb = "A Pharmacist focuses on the chemical needs of the Medical Department, and often offers to fill crew prescriptions at their discretion." /* I'm commenting out Geneticist so you can't actually see it in the job menu, given that you can't play as one - Jon. +////////////////////////////////// +// Geneticist +////////////////////////////////// /datum/job/geneticist title = "Geneticist" flag = GENETICIST - department = "Medical" + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_RESEARCH) department_flag = MEDSCI faction = "Station" total_positions = 0 spawn_positions = 0 - supervisors = "the chief medical officer and research director" + supervisors = "the Chief Medical Officer and Research Director" selection_color = "#013D3B" economic_modifier = 7 access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_research) minimal_access = list(access_medical, access_morgue, access_genetics, access_research) outfit_type = /decl/hierarchy/outfit/job/medical/geneticist + job_description = "A Geneticist operates genetic manipulation equipment to repair any genetic defects encountered in crew, from cloning or radiation as examples. \ + When required, geneticists have the skills to clone, and are the superior choice when available for doing so." */ +////////////////////////////////// +// Psychiatrist +////////////////////////////////// /datum/job/psychiatrist title = "Psychiatrist" flag = PSYCHIATRIST - department = "Medical" + departments = list(DEPARTMENT_MEDICAL) department_flag = MEDSCI faction = "Station" total_positions = 1 spawn_positions = 1 economic_modifier = 5 - supervisors = "the chief medical officer" + supervisors = "the Chief Medical Officer" selection_color = "#013D3B" access = list(access_medical, access_medical_equip, access_morgue, access_psychiatrist) minimal_access = list(access_medical, access_medical_equip, access_psychiatrist) outfit_type = /decl/hierarchy/outfit/job/medical/psychiatrist - alt_titles = list("Psychologist" = /decl/hierarchy/outfit/job/medical/psychiatrist/psychologist) + job_description = "A Psychiatrist provides mental health services to crew members in need. They may also be called upon to determine whatever \ + ails the mentally unwell, frequently under Security supervision. They understand the effects of various psychoactive drugs." + alt_titles = list("Psychologist" = /datum/alt_title/psychologist) +//Psychiatrist Alt Titles +/datum/alt_title/psychologist + title = "Psychologist" + title_blurb = "A Psychologist provides mental health services to crew members in need, focusing more on therapy than medication. They may also be \ + called upon to determine whatever ails the mentally unwell, frequently under Security supervision." + title_outfit = /decl/hierarchy/outfit/job/medical/psychiatrist/psychologist + +////////////////////////////////// +// Paramedic +////////////////////////////////// /datum/job/paramedic title = "Paramedic" flag = PARAMEDIC - department = "Medical" + departments = list(DEPARTMENT_MEDICAL) department_flag = MEDSCI faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the chief medical officer" + supervisors = "the Chief Medical Officer" selection_color = "#013D3B" economic_modifier = 4 access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist) minimal_access = list(access_medical, access_medical_equip, access_morgue, access_eva, access_maint_tunnels, access_external_airlocks) outfit_type = /decl/hierarchy/outfit/job/medical/paramedic - alt_titles = list("Emergency Medical Technician" = /decl/hierarchy/outfit/job/medical/paramedic/emt) \ No newline at end of file + job_description = "A Paramedic is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their own. \ + They may also be called upon to keep patients stable when Medical is busy or understaffed." + alt_titles = list("Emergency Medical Technician" = /datum/alt_title/emt) + +// Paramedic Alt Titles +/datum/alt_title/emt + title = "Emergency Medical Technician" + title_blurb = "An Emergency Medical Technician is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their \ + own. They are capable of keeping a patient stabilized until they reach the hands of someone with more training." + title_outfit = /decl/hierarchy/outfit/job/medical/paramedic/emt \ No newline at end of file diff --git a/code/game/jobs/job/medical_vr.dm b/code/game/jobs/job/medical_vr.dm index bd541fb00a..306fe9acaa 100644 --- a/code/game/jobs/job/medical_vr.dm +++ b/code/game/jobs/job/medical_vr.dm @@ -1,5 +1,16 @@ /datum/job/cmo disallow_jobhop = TRUE + pto_type = PTO_MEDICAL /datum/job/doctor - spawn_positions = 5 \ No newline at end of file + spawn_positions = 5 + pto_type = PTO_MEDICAL + +/datum/job/chemist + pto_type = PTO_MEDICAL + +/datum/job/psychiatrist + pto_type = PTO_MEDICAL + +/datum/job/paramedic + pto_type = PTO_MEDICAL \ No newline at end of file diff --git a/code/game/jobs/job/offduty_vr.dm b/code/game/jobs/job/offduty_vr.dm index 8e18827c06..416543956c 100644 --- a/code/game/jobs/job/offduty_vr.dm +++ b/code/game/jobs/job/offduty_vr.dm @@ -8,12 +8,17 @@ timeoff_factor = -1 total_positions = -1 faction = "Station" - department = "Civilian" + departments = list(DEPARTMENT_OFFDUTY) supervisors = "nobody! Enjoy your time off" selection_color = "#9b633e" access = list(access_maint_tunnels) minimal_access = list(access_maint_tunnels) outfit_type = /decl/hierarchy/outfit/job/assistant/worker + job_description = "Off-duty crew has no responsibilities or authority and is just there to spend their well-deserved time off." + pto_type = PTO_CIVILIAN + +/datum/alt_title/offduty_civ + title = "Off-duty Worker" /datum/job/offduty_cargo title = "Off-duty Cargo" @@ -21,12 +26,17 @@ timeoff_factor = -1 total_positions = -1 faction = "Station" - department = "Cargo" + departments = list(DEPARTMENT_OFFDUTY) supervisors = "nobody! Enjoy your time off" selection_color = "#9b633e" access = list(access_maint_tunnels) minimal_access = list(access_maint_tunnels) outfit_type = /decl/hierarchy/outfit/job/assistant/cargo + job_description = "Off-duty crew has no responsibilities or authority and is just there to spend their well-deserved time off." + pto_type = PTO_CARGO + +/datum/alt_title/offduty_crg + title = "Off-duty Cargo" /datum/job/offduty_engineering title = "Off-duty Engineer" @@ -34,12 +44,17 @@ timeoff_factor = -1 total_positions = -1 faction = "Station" - department = "Engineering" + departments = list(DEPARTMENT_OFFDUTY) supervisors = "nobody! Enjoy your time off" selection_color = "#5B4D20" access = list(access_maint_tunnels, access_external_airlocks, access_construction) minimal_access = list(access_maint_tunnels, access_external_airlocks) outfit_type = /decl/hierarchy/outfit/job/assistant/engineer + job_description = "Off-duty crew has no responsibilities or authority and is just there to spend their well-deserved time off." + pto_type = PTO_ENGINEERING + +/datum/alt_title/offduty_eng + title = "Off-duty Engineer" /datum/job/offduty_medical title = "Off-duty Medic" @@ -47,12 +62,17 @@ timeoff_factor = -1 total_positions = -1 faction = "Station" - department = "Medical" + departments = list(DEPARTMENT_OFFDUTY) supervisors = "nobody! Enjoy your time off" selection_color = "#013D3B" access = list(access_maint_tunnels, access_external_airlocks) minimal_access = list(access_maint_tunnels, access_external_airlocks) outfit_type = /decl/hierarchy/outfit/job/assistant/medic + job_description = "Off-duty crew has no responsibilities or authority and is just there to spend their well-deserved time off." + pto_type = PTO_MEDICAL + +/datum/alt_title/offduty_med + title = "Off-duty Medic" /datum/job/offduty_science title = "Off-duty Scientist" @@ -60,12 +80,17 @@ timeoff_factor = -1 total_positions = -1 faction = "Station" - department = "Science" + departments = list(DEPARTMENT_OFFDUTY) supervisors = "nobody! Enjoy your time off" selection_color = "#633D63" access = list(access_maint_tunnels) minimal_access = list(access_maint_tunnels) outfit_type = /decl/hierarchy/outfit/job/assistant/scientist + job_description = "Off-duty crew has no responsibilities or authority and is just there to spend their well-deserved time off." + pto_type = PTO_SCIENCE + +/datum/alt_title/offduty_sci + title = "Off-duty Scientist" /datum/job/offduty_security title = "Off-duty Officer" @@ -73,9 +98,14 @@ timeoff_factor = -1 total_positions = -1 faction = "Station" - department = "Security" + departments = list(DEPARTMENT_OFFDUTY) supervisors = "nobody! Enjoy your time off" selection_color = "#601C1C" access = list(access_maint_tunnels) minimal_access = list(access_maint_tunnels) outfit_type = /decl/hierarchy/outfit/job/assistant/officer + job_description = "Off-duty crew has no responsibilities or authority and is just there to spend their well-deserved time off." + pto_type = PTO_SECURITY + +/datum/alt_title/offduty_sec + title = "Off-duty Officer" diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index c7ed91a5f9..71159d5b81 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -1,8 +1,12 @@ +////////////////////////////////// +// Research Director +////////////////////////////////// /datum/job/rd title = "Research Director" flag = RD - head_position = 1 - department = "Science" + departments_managed = list(DEPARTMENT_RESEARCH) + departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_COMMAND) + sorting_order = 2 department_flag = MEDSCI faction = "Station" total_positions = 1 @@ -26,17 +30,28 @@ ideal_character_age = 50 outfit_type = /decl/hierarchy/outfit/job/science/rd - alt_titles = list("Research Supervisor") + job_description = "The Research Director manages and maintains the Research department. They are required to ensure the safety of the entire crew, \ + at least with regards to anything occuring in the Research department, and to inform the crew of any disruptions that \ + might originate from Research. The Research Director often has at least passing knowledge of most of the Research department, but \ + are encouraged to allow their staff to perform their own duties." + alt_titles = list("Research Supervisor" = /datum/alt_title/research_supervisor) +// Research Director Alt Titles +/datum/alt_title/research_supervisor + title = "Research Supervisor" + +////////////////////////////////// +// Scientist +////////////////////////////////// /datum/job/scientist title = "Scientist" flag = SCIENTIST - department = "Science" + departments = list(DEPARTMENT_RESEARCH) department_flag = MEDSCI faction = "Station" total_positions = 5 spawn_positions = 3 - supervisors = "the research director" + supervisors = "the Research Director" selection_color = "#633D63" economic_modifier = 7 access = list(access_robotics, access_tox, access_tox_storage, access_research, access_xenobiology, access_xenoarch) @@ -45,17 +60,41 @@ minimal_player_age = 3 outfit_type = /decl/hierarchy/outfit/job/science/scientist - alt_titles = list("Xenoarchaeologist", "Anomalist", "Phoron Researcher") + job_description = "A Scientist is a generalist working in the Research department, with general knowledge of the scientific process, as well as \ + the principles and requirements of Research and Development. They may also formulate experiments of their own devising, if \ + they find an appropriate topic." + alt_titles = list("Xenoarchaeologist" = /datum/alt_title/xenoarch, "Anomalist" = /datum/alt_title/anomalist, \ + "Phoron Researcher" = /datum/alt_title/phoron_research) +// Scientist Alt Titles +/datum/alt_title/xenoarch + title = "Xenoarchaeologist" + title_blurb = "A Xenoarchaeologist enters digsites in search of artifacts of alien origin. These digsites are frequently in vacuum or other inhospitable \ + locations, and as such a Xenoarchaeologist should be prepared to handle hostile evironmental conditions." + +/datum/alt_title/anomalist + title = "Anomalist" + title_blurb = "An Anomalist is a Scientist whose expertise is analyzing alien artifacts. They are familar with the most common methods of testing artifact \ + function. They work closely with Xenoarchaeologists, or Miners, if either role is present." + +/datum/alt_title/phoron_research + title = "Phoron Researcher" + title_blurb = "A Phoron Researcher is a specialist in the practical applications of phoron, and has knowledge of its practical uses and dangers. \ + Many Phoron Researchers are interested in the combustability and explosive properties of gaseous phoron, as well as the specific hazards \ + of working with the substance in that state." + +////////////////////////////////// +// Xenobiologist +////////////////////////////////// /datum/job/xenobiologist title = "Xenobiologist" flag = XENOBIOLOGIST - department = "Science" + departments = list(DEPARTMENT_RESEARCH) department_flag = MEDSCI faction = "Station" total_positions = 3 spawn_positions = 2 - supervisors = "the research director" + supervisors = "the Research Director" selection_color = "#633D63" economic_modifier = 7 access = list(access_robotics, access_tox, access_tox_storage, access_research, access_xenobiology, access_hydroponics) @@ -64,17 +103,28 @@ minimal_player_age = 3 outfit_type = /decl/hierarchy/outfit/job/science/xenobiologist - alt_titles = list("Xenobotanist") + job_description = "A Xenobiologist studies esoteric lifeforms, usually in the relative safety of their lab. They attempt to find ways to benefit \ + from the byproducts of these lifeforms, and their main subject at present is the Giant Slime." + alt_titles = list("Xenobotanist" = /datum/alt_title/xenobot) +// Xenibiologist Alt Titles +/datum/alt_title/xenobot + title = "Xenobotanist" + title_blurb = "A Xenobotanist grows and cares for a variety of abnormal, custom made, and frequently dangerous plant life. When the products of these plants \ + is both safe and beneficial to the station, they may choose to introduce it to the rest of the crew." + +////////////////////////////////// +// Roboticist +////////////////////////////////// /datum/job/roboticist title = "Roboticist" flag = ROBOTICIST - department = "Science" + departments = list(DEPARTMENT_RESEARCH) department_flag = MEDSCI faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "research director" + supervisors = "the Research Director" selection_color = "#633D63" economic_modifier = 5 access = list(access_robotics, access_tox, access_tox_storage, access_tech_storage, access_morgue, access_research) //As a job that handles so many corpses, it makes sense for them to have morgue access. @@ -82,4 +132,17 @@ minimal_player_age = 3 outfit_type = /decl/hierarchy/outfit/job/science/roboticist - alt_titles = list("Biomechanical Engineer","Mechatronic Engineer") + job_description = "A Roboticist maintains and repairs the station's synthetics, including crew with prosthetic limbs. \ + They can also assist the station by producing simple robots and even pilotable exosuits." + alt_titles = list("Biomechanical Engineer" = /datum/alt_title/biomech, "Mechatronic Engineer" = /datum/alt_title/mech_tech) + +// Roboticist Alt Titles +/datum/alt_title/biomech + title = "Biomechanical Engineer" + title_blurb = "A Biomechanical Engineer primarily works on prosthetics, and the organic parts attached to them. They may have some \ + knowledge of the relatively simple surgical procedures used in making cyborgs and attaching prosthesis." + +/datum/alt_title/mech_tech + title = "Mechatronic Engineer" + title_blurb = "A Mechatronic Engineer focuses on the construction and maintenance of Exosuits, and should be well versed in their use. \ + They may also be called upon to work on synthetics and prosthetics, if needed." \ No newline at end of file diff --git a/code/game/jobs/job/science_vr.dm b/code/game/jobs/job/science_vr.dm index acfff12fda..c9092753e7 100644 --- a/code/game/jobs/job/science_vr.dm +++ b/code/game/jobs/job/science_vr.dm @@ -1,5 +1,6 @@ /datum/job/rd disallow_jobhop = TRUE + pto_type = PTO_SCIENCE access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, access_tox_storage, access_teleporter, access_sec_doors, @@ -12,10 +13,19 @@ /datum/job/scientist spawn_positions = 5 - alt_titles = list("Xenoarcheologist", "Anomalist", "Phoron Researcher", "Circuit Designer") + pto_type = PTO_SCIENCE + alt_titles = list("Xenoarchaeologist" = /datum/alt_title/xenoarch, "Anomalist" = /datum/alt_title/anomalist, \ + "Phoron Researcher" = /datum/alt_title/phoron_research, "Circuit Designer" = /datum/alt_title/circuit_designer) + +/datum/alt_title/circuit_designer + title = "Circuit Designer" + title_blurb = "A Circuit Designer is a Scientist whose expertise is working with integrated circuits. They are familar with the workings and programming of those devices. \ + They work to create various useful devices using the capabilities of integrated circuitry." /datum/job/xenobiologist spawn_positions = 3 + pto_type = PTO_SCIENCE /datum/job/roboticist - total_positions = 3 \ No newline at end of file + total_positions = 3 + pto_type = PTO_SCIENCE \ No newline at end of file diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index fccac1a4af..b1ca510f1c 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -1,8 +1,12 @@ +////////////////////////////////// +// Head of Security +////////////////////////////////// /datum/job/hos title = "Head of Security" flag = HOS - head_position = 1 - department = "Security" + departments_managed = list(DEPARTMENT_SECURITY) + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_COMMAND) + sorting_order = 2 department_flag = ENGSEC faction = "Station" total_positions = 1 @@ -23,54 +27,103 @@ minimal_player_age = 14 outfit_type = /decl/hierarchy/outfit/job/security/hos - alt_titles = list("Security Commander", "Chief of Security") + job_description = " The Head of Security manages the Security Department, keeping the station safe and making sure the rules are followed. They are expected to \ + keep the other Department Heads, and the rest of the crew, aware of developing situations that may be a threat. If necessary, the HoS may \ + perform the duties of absent Security roles, such as distributing gear from the Armory." + alt_titles = list("Security Commander" = /datum/alt_title/sec_commander, "Chief of Security" = /datum/alt_title/sec_chief) + +// Head of Security Alt Titles +/datum/alt_title/sec_commander + title = "Security Commander" + +/datum/alt_title/sec_chief + title = "Chief of Security" + +//YW ADDITION START: LOYALTY IMPLANT FOR HOS +/datum/job/hos/equip(var/mob/living/carbon/human/H) + . = ..() + if(.) + H.implant_loyalty(src) +//YW ADDITION END /datum/job/warden title = "Warden" flag = WARDEN - department = "Security" + departments = list(DEPARTMENT_SECURITY) + sorting_order = 1 department_flag = ENGSEC faction = "Station" total_positions = 1 spawn_positions = 1 - supervisors = "the head of security" + supervisors = "the Head of Security" selection_color = "#601C1C" economic_modifier = 5 access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_morgue, access_external_airlocks) minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_external_airlocks) minimal_player_age = 5 - outfit_type = /decl/hierarchy/outfit/job/security/warden + outfit_type = /decl/hierarchy/outfit/job/security/warden + job_description = "The Warden watches over the physical Security Department, making sure the Brig and Armoury are secure and in order at all times. They oversee \ + prisoners that have been processed and brigged, and are responsible for their well being. The Warden is also in charge of distributing \ + Armoury gear in a crisis, and retrieving it when the crisis has passed. In an emergency, the Warden may be called upon to direct the \ + Security Department as a whole." + +////////////////////////////////// +// Detective +////////////////////////////////// /datum/job/detective title = "Detective" flag = DETECTIVE - department = "Security" + departments = list(DEPARTMENT_SECURITY) department_flag = ENGSEC faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the head of security" + supervisors = "the Head of Security" selection_color = "#601C1C" access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks) minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks) economic_modifier = 5 minimal_player_age = 3 - outfit_type = /decl/hierarchy/outfit/job/security/detective - alt_titles = list("Forensic Technician" = /decl/hierarchy/outfit/job/security/detective/forensic, "Investigator") + outfit_type = /decl/hierarchy/outfit/job/security/detective + job_description = "A Detective works to help Security find criminals who have not properly been identified, through interviews and forensic work. \ + For crimes only witnessed after the fact, or those with no survivors, they attempt to piece together what they can from pure evidence." + alt_titles = list("Forensic Technician" = /datum/alt_title/forensic_tech) + +// Detective Alt Titles +/datum/alt_title/forensic_tech + title = "Forensic Technician" + title_blurb = "A Forensic Technician works more with hard evidence and labwork than a Detective, but they share the purpose of solving crimes." + title_outfit = /decl/hierarchy/outfit/job/security/detective/forensic + +////////////////////////////////// +// Security Officer +////////////////////////////////// /datum/job/officer title = "Security Officer" flag = OFFICER - department = "Security" + departments = list(DEPARTMENT_SECURITY) department_flag = ENGSEC faction = "Station" total_positions = 4 spawn_positions = 4 - supervisors = "the head of security" + supervisors = "the Head of Security" selection_color = "#601C1C" economic_modifier = 4 access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_morgue, access_external_airlocks) minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_external_airlocks) minimal_player_age = 3 + outfit_type = /decl/hierarchy/outfit/job/security/officer - alt_titles = list("Junior Officer") \ No newline at end of file + job_description = "A Security Officer is concerned with maintaining the safety and security of the station as a whole, dealing with external threats and \ + apprehending criminals. A Security Officer is responsible for the health, safety, and processing of any prisoner they arrest. \ + No one is above the Law, not Security or Command." + alt_titles = list("Junior Officer" = /datum/alt_title/junior_officer) + +// Security Officer Alt Titles +/datum/alt_title/junior_officer + title = "Junior Officer" + title_blurb = "A Junior Officer is an inexperienced Security Officer. They likely have training, but not experience, and are frequently \ + paired off with a more senior co-worker. Junior Officers may also be expected to take over the boring duties of other Officers \ + including patrolling the station or maintaining specific posts." \ No newline at end of file diff --git a/code/game/jobs/job/security_vr.dm b/code/game/jobs/job/security_vr.dm index 4b390b2519..e969dcff56 100644 --- a/code/game/jobs/job/security_vr.dm +++ b/code/game/jobs/job/security_vr.dm @@ -1,5 +1,6 @@ /datum/job/hos disallow_jobhop = TRUE + pto_type = PTO_SECURITY access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, @@ -10,6 +11,13 @@ access_research, access_engine, access_mining, access_construction, access_mailsorting, access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks) +/datum/job/warden + pto_type = PTO_SECURITY + +/datum/job/detective + pto_type = PTO_SECURITY + /datum/job/officer total_positions = 5 - spawn_positions = 5 \ No newline at end of file + spawn_positions = 5 + pto_type = PTO_SECURITY \ No newline at end of file diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index 9aed52c023..6f89adcd07 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -1,18 +1,29 @@ +////////////////////////////////// +// AI +////////////////////////////////// /datum/job/ai title = "AI" flag = AI + departments = list(DEPARTMENT_SYNTHETIC) + sorting_order = 1 // Be above their borgs. department_flag = ENGSEC faction = "Station" total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm spawn_positions = 1 selection_color = "#3F823F" - supervisors = "your laws" + supervisors = "your Laws" req_admin_notify = 1 minimal_player_age = 7 account_allowed = 0 economic_modifier = 0 has_headset = FALSE + assignable = FALSE + outfit_type = /decl/hierarchy/outfit/job/silicon/ai + job_description = "The AI oversees the operation of the station and its crew, but has no real authority over them. \ + The AI is required to follow its Laws, and Lawbound Synthetics that are linked to it are expected to follow \ + the AI's commands, and their own Laws." +// AI procs /datum/job/ai/equip(var/mob/living/carbon/human/H) if(!H) return 0 return 1 @@ -25,21 +36,41 @@ H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) return 1 +////////////////////////////////// +// Cyborg +////////////////////////////////// /datum/job/cyborg title = "Cyborg" flag = CYBORG + departments = list(DEPARTMENT_SYNTHETIC) department_flag = ENGSEC faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "your laws and the AI" //Nodrak + supervisors = "your Laws and the AI" //Nodrak selection_color = "#254C25" minimal_player_age = 1 - alt_titles = list("Robot", "Drone") account_allowed = 0 economic_modifier = 0 has_headset = FALSE + assignable = FALSE + outfit_type = /decl/hierarchy/outfit/job/silicon/cyborg + job_description = "A Cyborg is a mobile station synthetic, piloted by a cybernetically preserved brain. It is considered a person, but is still required \ + to follow its Laws." + alt_titles = list("Robot" = /datum/alt_title/robot, "Drone" = /datum/alt_title/drone) +// Cyborg Alt Titles +/datum/alt_title/robot + title = "Robot" + title_blurb = "A Robot is a mobile station synthetic, piloted by an advanced piece of technology called a Positronic Brain. It is considered a person, \ + legally, but is required to follow its Laws." + +/datum/alt_title/drone + title = "Drone" + title_blurb = "A Drone is a mobile station synthetic, piloted by a simple computer-based AI. As such, it is not a person, but rather an expensive and \ + and important piece of station property, and is expected to follow its Laws." + +// Cyborg procs /datum/job/cyborg/equip(var/mob/living/carbon/human/H) if(!H) return 0 return 1 diff --git a/code/game/jobs/job/special_vr.dm b/code/game/jobs/job/special_vr.dm index 8369ebba46..7a68691a56 100644 --- a/code/game/jobs/job/special_vr.dm +++ b/code/game/jobs/job/special_vr.dm @@ -1,7 +1,7 @@ /datum/job/centcom_officer //For Business title = "CentCom Officer" - department = "Command" - head_position = 1 + departments = list("Central Command") + department_accounts = list(DEPARTMENT_COMMAND, DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL, DEPARTMENT_RESEARCH, DEPARTMENT_SECURITY, DEPARTMENT_CARGO, DEPARTMENT_PLANET, DEPARTMENT_CIVILIAN) faction = "Station" total_positions = 2 spawn_positions = 1 @@ -14,13 +14,23 @@ whitelist_only = 1 latejoin_only = 1 outfit_type = /decl/hierarchy/outfit/job/centcom_officer + job_description = "A Central Command Officer is there on official business. Most of time. Whatever it is, they're a VIP." minimum_character_age = 25 ideal_character_age = 40 + pto_type = PTO_CIVILIAN + get_access() return get_all_accesses().Copy() +//YW UNCOMMENTINGSTART: INSTATE LOYALTY IMPLANT +/datum/job/centcom_officer/equip(var/mob/living/carbon/human/H) + . = ..() + if(.) + H.implant_loyalty(src) +//YW UNCOMMENTING END + /*/datum/job/centcom_visitor //For Pleasure // You mean for admin abuse... -Ace title = "CentCom Visitor" department = "Civilian" @@ -66,7 +76,7 @@ /datum/job/clown title = "Clown" flag = CLOWN - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = -1 @@ -76,10 +86,21 @@ economic_modifier = 1 access = list() minimal_access = list() - alt_titles = list("Comedian","Jester") + job_description = "A Clown is there to entertain the crew and keep high morale using various harmless pranks and ridiculous jokes!" + alt_titles = list("Clown" = /datum/alt_title/clown, "Comedian" = /datum/alt_title/comedian, "Jester" = /datum/alt_title/jester) whitelist_only = 1 latejoin_only = 1 outfit_type = /decl/hierarchy/outfit/job/clown + pto_type = PTO_CIVILIAN + +/datum/alt_title/clown + title = "Clown" + +/datum/alt_title/comedian + title = "Comedian" + +/datum/alt_title/jester + title = "Jester" /datum/job/clown/get_access() if(config.assistant_maint) @@ -90,7 +111,7 @@ /datum/job/mime title = "Mime" flag = MIME - department = "Civilian" + departments = list(DEPARTMENT_CIVILIAN) department_flag = CIVILIAN faction = "Station" total_positions = -1 @@ -100,10 +121,21 @@ economic_modifier = 1 access = list() minimal_access = list() - alt_titles = list("Performer","Interpretive Dancer") + job_description = "A Mime is there to entertain the crew and keep high morale using unbelievable performances and acting skills!" + alt_titles = list("Mime" = /datum/alt_title/mime, "Performer" = /datum/alt_title/performer, "Interpretive Dancer" = /datum/alt_title/interpretive_dancer) whitelist_only = 1 latejoin_only = 1 outfit_type = /decl/hierarchy/outfit/job/mime + pto_type = PTO_CIVILIAN + +/datum/alt_title/mime + title = "Mime" + +/datum/alt_title/performer + title = "Performer" + +/datum/alt_title/interpretive_dancer + title = "Interpretive Dancer" /datum/job/mime/get_access() if(config.assistant_maint) diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index b527be4aad..34e75f4320 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -11,14 +11,15 @@ var/global/datum/controller/occupations/job_master var/list/unassigned = list() //Debug info var/list/job_debug = list() - + //Cache of icons for job info window + var/list/job_icons = list() proc/SetupOccupations(var/faction = "Station") occupations = list() //var/list/all_jobs = typesof(/datum/job) var/list/all_jobs = list(/datum/job/assistant) | using_map.allowed_jobs if(!all_jobs.len) - world << "Error setting up jobs, no job datums found!" + to_world("Error setting up jobs, no job datums found!") return 0 for(var/J in all_jobs) var/datum/job/job = new J() @@ -120,7 +121,7 @@ var/global/datum/controller/occupations/job_master if(istype(job, GetJob(USELESS_JOB))) // We don't want to give him assistant, that's boring! //VOREStation Edit - Visitor not Assistant continue - if(job.title in command_positions) //If you want a command position, select it! + if(SSjob.is_job_in_department(job.title, DEPARTMENT_COMMAND)) //If you want a command position, select it! continue if(jobban_isbanned(player, job.title)) @@ -156,7 +157,7 @@ var/global/datum/controller/occupations/job_master ///This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until it locates a head or runs out of levels to check proc/FillHeadPosition() for(var/level = 1 to 3) - for(var/command_position in command_positions) + for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) var/datum/job/job = GetJob(command_position) if(!job) continue var/list/candidates = FindOccupationCandidates(job, level) @@ -196,7 +197,7 @@ var/global/datum/controller/occupations/job_master ///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level proc/CheckHeadPositions(var/level) - for(var/command_position in command_positions) + for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) var/datum/job/job = GetJob(command_position) if(!job) continue var/list/candidates = FindOccupationCandidates(job, level) @@ -356,7 +357,11 @@ var/global/datum/controller/occupations/job_master else var/list/spawn_props = LateSpawn(H.client, rank) var/turf/T = spawn_props["turf"] - H.forceMove(T) + if(!T) + to_chat(H, "You were unable to be spawned at your chosen late-join spawnpoint. Please verify your job/spawn point combination makes sense, and try another one.") + return + else + H.forceMove(T) // Moving wheelchair if they have one if(H.buckled && istype(H.buckled, /obj/structure/bed/chair/wheelchair)) @@ -386,7 +391,7 @@ var/global/datum/controller/occupations/job_master permitted = 0 if(!permitted) - H << "Your current species, job or whitelist status does not permit you to spawn with [thing]!" + to_chat(H, "Your current species, job or whitelist status does not permit you to spawn with [thing]!") continue if(G.slot == "implant") @@ -402,7 +407,7 @@ var/global/datum/controller/occupations/job_master if(G.slot == slot_wear_mask || G.slot == slot_wear_suit || G.slot == slot_head) custom_equip_leftovers += thing else if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) - H << "Equipping you with \the [thing]!" + to_chat(H, "Equipping you with \the [thing]!") custom_equip_slots.Add(G.slot) else custom_equip_leftovers.Add(thing) @@ -423,26 +428,26 @@ var/global/datum/controller/occupations/job_master else var/metadata = H.client.prefs.gear[G.display_name] if(H.equip_to_slot_or_del(G.spawn_item(H, metadata), G.slot)) - H << "Equipping you with \the [thing]!" + to_chat(H, "Equipping you with \the [thing]!") custom_equip_slots.Add(G.slot) else spawn_in_storage += thing else - H << "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator." + to_chat(H, "Your job is [rank] and the game just can't handle it! Please report this bug to an administrator.") H.job = rank log_game("JOINED [key_name(H)] as \"[rank]\"") log_game("SPECIES [key_name(H)] is a: \"[H.species.name]\"") //VOREStation Add // If they're head, give them the account info for their department - if(H.mind && job.head_position) + if(H.mind && job.department_accounts) var/remembered_info = "" - var/datum/money_account/department_account = department_accounts[job.department] - - if(department_account) - remembered_info += "Your department's account number is: #[department_account.account_number]
" - remembered_info += "Your department's account pin is: [department_account.remote_access_pin]
" - remembered_info += "Your department's account funds are: $[department_account.money]
" + for(var/D in job.department_accounts) + var/datum/money_account/department_account = department_accounts[D] + if(department_account) + remembered_info += "Department account number ([D]): #[department_account.account_number]
" + remembered_info += "Department account pin ([D]): [department_account.remote_access_pin]
" + remembered_info += "Department account funds ([D]): $[department_account.money]
" H.mind.store_memory(remembered_info) @@ -457,8 +462,8 @@ var/global/datum/controller/occupations/job_master if("AI") return H if("Colony Director") - var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP)? null : sound('sound/misc/boatswain.ogg', volume=20) - captain_announcement.Announce("All hands, [alt_title ? alt_title : "Colony Director"] [H.real_name] on deck!", new_sound=announce_sound) + var/sound/announce_sound = (ticker.current_state <= GAME_STATE_SETTING_UP) ? null : sound('sound/misc/boatswain.ogg', volume=20) + captain_announcement.Announce("All hands, [alt_title ? alt_title : "Colony Director"] [H.real_name] on deck!", new_sound = announce_sound, zlevel = H.z) //Deferred item spawning. if(spawn_in_storage && spawn_in_storage.len) @@ -469,12 +474,12 @@ var/global/datum/controller/occupations/job_master if(!isnull(B)) for(var/thing in spawn_in_storage) - H << "Placing \the [thing] in your [B.name]!" + to_chat(H, "Placing \the [thing] in your [B.name]!") var/datum/gear/G = gear_datums[thing] var/metadata = H.client.prefs.gear[G.display_name] G.spawn_item(B, metadata) else - H << "Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug." + to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no arms and no backpack or this is a bug.") if(istype(H)) //give humans wheelchairs, if they need them. var/obj/item/organ/external/l_foot = H.get_organ("l_foot") @@ -493,16 +498,16 @@ var/global/datum/controller/occupations/job_master W.color = R.color qdel(R) - H << "You are [job.total_positions == 1 ? "the" : "a"] [alt_title ? alt_title : rank]." + to_chat(H, "You are [job.total_positions == 1 ? "the" : "a"] [alt_title ? alt_title : rank].") if(job.supervisors) - H << "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this." + to_chat(H, "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this.") if(job.has_headset) H.equip_to_slot_or_del(new /obj/item/device/radio/headset(H), slot_l_ear) - H << "To speak on your department's radio channel use :h. For the use of other channels, examine your headset." + to_chat(H, "To speak on your department's radio channel use :h. For the use of other channels, examine your headset.") if(job.req_admin_notify) - H << "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp." + to_chat(H, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.") // EMAIL GENERATION // Email addresses will be created under this domain name. Mostly for the looks. @@ -607,12 +612,20 @@ var/global/datum/controller/occupations/job_master /datum/controller/occupations/proc/LateSpawn(var/client/C, var/rank) var/datum/spawnpoint/spawnpos + var/fail_deadly = FALSE + + var/datum/job/J = SSjob.get_job(rank) + fail_deadly = J?.offmap_spawn //Spawn them at their preferred one if(C && C.prefs.spawnpoint) if(!(C.prefs.spawnpoint in using_map.allowed_spawns)) - to_chat(C, "Your chosen spawnpoint ([C.prefs.spawnpoint]) is unavailable for the current map. Spawning you at one of the enabled spawn points instead.") - spawnpos = null + if(fail_deadly) + to_chat(C, "Your chosen spawnpoint is unavailable for this map and your job requires a specific spawnpoint. Please correct your spawn point choice.") + return + else + to_chat(C, "Your chosen spawnpoint ([C.prefs.spawnpoint]) is unavailable for the current map. Spawning you at one of the enabled spawn points instead.") + spawnpos = null else spawnpos = spawntypes[C.prefs.spawnpoint] @@ -622,12 +635,16 @@ var/global/datum/controller/occupations/job_master if(spawnpos.check_job_spawning(rank)) .["turf"] = spawnpos.get_spawn_position() .["msg"] = spawnpos.msg + .["channel"] = spawnpos.announce_channel else - to_chat(C,"Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") + if(fail_deadly) + to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Please correct your spawn point choice.") + return + to_chat(C, "Your chosen spawnpoint ([spawnpos.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") var/spawning = pick(latejoin) .["turf"] = get_turf(spawning) .["msg"] = "will arrive at the station shortly" //VOREStation Edit - Grammar but mostly 'shuttle' reference removal, and this also applies to notified spawn-character verb use - else + else if(!fail_deadly) var/spawning = pick(latejoin) .["turf"] = get_turf(spawning) .["msg"] = "has arrived on the station" diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index a749e27c02..fce7e5325d 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -75,7 +75,8 @@ var/list/medical_positions = list( "Geneticist", "Psychiatrist", "Chemist", - "Paramedic" + "Paramedic", + "Field Medic" //ywedit ) @@ -104,7 +105,8 @@ var/list/civilian_positions = list( "Lawyer", "Chaplain", USELESS_JOB, //VOREStation Edit - Visitor not Assistant - "Intern" //VOREStation Edit - Intern + "Intern", //VOREStation Edit - Intern + "Pilot" //YWedit ) @@ -118,9 +120,9 @@ var/list/security_positions = list( var/list/planet_positions = list( "Pathfinder", // VOREStation Edit - Added Pathfinder - "Explorer", - "Pilot", - "Field Medic" // VOREStation Edit - Field Medic + "Explorer" + //YWmoved to civilian"Pilot", + //YWmoved to medical"Field Medic" // VOREStation Edit - Field Medic ) @@ -132,7 +134,7 @@ var/list/nonhuman_positions = list( /proc/guest_jobbans(var/job) - return ((job in command_positions) || (job in nonhuman_positions) || (job in security_positions)) + return ( (job in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) || (job in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) ) /proc/get_job_datums() var/list/occupations = list() diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index 9a13e066a5..9963c1580c 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -75,7 +75,7 @@ var/list/whitelist = list() /proc/whitelist_overrides(mob/M) if(!config.usealienwhitelist) return 1 - if(check_rights(R_ADMIN, 0, M)) + if(check_rights(R_ADMIN|R_EVENT, 0, M)) return 1 return 0 diff --git a/code/game/json.dm b/code/game/json.dm index 340ffa23f9..2fd5282795 100644 --- a/code/game/json.dm +++ b/code/game/json.dm @@ -7,13 +7,13 @@ proc/makejson() if(!makejson) return fdel("[jsonpath]/info.json") - //usr << "Error cant delete json" + //to_chat(usr, "Error cant delete json") //else - //usr << "Deleted json in public html" + //to_chat(usr, "Deleted json in public html") fdel("info.json") - //usr << "error cant delete local json" + //to_chat(usr, "error cant delete local json") //else - //usr << "Deleted local json" + //to_chat(usr, "Deleted local json") var/F = file("info.json") if(!isfile(F)) return @@ -38,7 +38,7 @@ proc/makejson() players += "[C.fakekey];" else players += "[C.key];" - F << "{\"mode\":\"[mode]\",\"players\" : \"[players]\",\"playercount\" : \"[playerscount]\",\"admin\" : \"[admins]\",\"time\" : \"[time2text(world.realtime,"MM/DD - hh:mm")]\"}" + to_chat(F, "{\"mode\":\"[mode]\",\"players\" : \"[players]\",\"playercount\" : \"[playerscount]\",\"admin\" : \"[admins]\",\"time\" : \"[time2text(world.realtime,"MM/DD - hh:mm")]\"}") fcopy("info.json","[jsonpath]/info.json") /proc/switchmap(newmap,newpath) @@ -73,7 +73,7 @@ proc/makejson() if(findtext(A,path,1,0)) lineloc = lines.Find(A,1,0) lines[lineloc] = xpath - world << "FOUND"*/ + to_world("FOUND")*/ fdel(dmepath) var/file = file(dmepath) file << text @@ -89,8 +89,8 @@ obj/mapinfo proc/GetMapInfo() // var/obj/mapinfo/M = locate() // Just removing these to try and fix the occasional JSON -> WORLD issue. -// world << M.name -// world << M.mapname +// to_world(M.name) +// to_world(M.mapname) client/proc/ChangeMap(var/X as text) set name = "Change Map" set category = "Admin" diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index b34d1565a0..b6f6cb25ac 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -6,7 +6,7 @@ level = 1 // underfloor layer = UNDER_JUNK_LAYER anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 0 var/obj/item/device/radio/beacon/Beacon diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm index 659377281e..aa6316033c 100644 --- a/code/game/machinery/CableLayer.dm +++ b/code/game/machinery/CableLayer.dm @@ -19,7 +19,7 @@ /obj/machinery/cablelayer/attack_hand(mob/user as mob) if(!cable&&!on) - user << "\The [src] doesn't have any cable loaded." + to_chat(user, "\The [src] doesn't have any cable loaded.") return on=!on user.visible_message("\The [user] [!on?"dea":"a"]ctivates \the [src].", "You switch [src] [on? "on" : "off"]") @@ -30,9 +30,9 @@ var/result = load_cable(O) if(!result) - user << "\The [src]'s cable reel is full." + to_chat(user, "\The [src]'s cable reel is full.") else - user << "You load [result] lengths of cable into [src]." + to_chat(user, "You load [result] lengths of cable into [src].") return if(O.is_wirecutter()) @@ -46,11 +46,11 @@ var/obj/item/stack/cable_coil/CC = new (get_turf(src)) CC.amount = m else - usr << "There's no more cable on the reel." + to_chat(usr, "There's no more cable on the reel.") /obj/machinery/cablelayer/examine(mob/user) ..() - user << "\The [src]'s cable reel has [cable.amount] length\s left." + to_chat(user, "\The [src]'s cable reel has [cable.amount] length\s left.") /obj/machinery/cablelayer/proc/load_cable(var/obj/item/stack/cable_coil/CC) if(istype(CC) && CC.amount) diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 889e11c4e4..5de1543426 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -5,7 +5,7 @@ icon_state = "table2-idle" density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 1 active_power_usage = 5 surgery_odds = 100 @@ -127,9 +127,9 @@ /obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) check_victim() if(victim && get_turf(victim) == get_turf(src) && victim.lying) - usr << "\The [src] is already occupied!" + to_chat(usr, "\The [src] is already occupied!") return 0 if(patient.buckled) - usr << "Unbuckle \the [patient] first!" + to_chat(usr, "Unbuckle \the [patient] first!") return 0 return 1 \ No newline at end of file diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 8d112b4138..e1c368a1c9 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -7,14 +7,16 @@ anchored = 1 //About time someone fixed this. density = 1 //VOREStation Edit - Big console dir = 8 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 40 interact_offline = 1 circuit = /obj/item/weapon/circuitboard/sleeper_console + clicksound = 'sound/machines/buttonbeep.ogg' + clickvol = 30 -/obj/machinery/sleep_console/New() - ..() +/obj/machinery/sleep_console/Initialize() findsleeper() + return ..() /obj/machinery/sleep_console/Destroy() if(sleeper) @@ -22,16 +24,13 @@ return ..() /obj/machinery/sleep_console/proc/findsleeper() - spawn(5) - var/obj/machinery/sleeper/sleepernew = null - for(dir in list(NORTH, EAST, SOUTH, WEST)) // Loop through every direction - sleepernew = locate(/obj/machinery/sleeper, get_step(src, dir)) // Try to find a scanner in that direction - if(sleepernew) - // VOREStation Edit Start - sleeper = sleepernew - sleepernew.console = src - break - // VOREStation Edit End + var/obj/machinery/sleeper/sleepernew = null + for(var/direction in GLOB.cardinal) // Loop through every direction + sleepernew = locate(/obj/machinery/sleeper, get_step(src, direction)) // Try to find a scanner in that direction + if(sleepernew) + sleeper = sleepernew + sleepernew.console = src + break //VOREStation Edit /obj/machinery/sleep_console/attack_ai(var/mob/user) @@ -177,7 +176,7 @@ var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0) - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 15 active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. @@ -255,8 +254,6 @@ return if(occupant) occupant.Stasis(stasis_level) - if(stasis_level >= 100 && occupant.timeofdeath) - occupant.timeofdeath += 1 SECOND if(filtering > 0) if(beaker) @@ -386,7 +383,7 @@ M.client.perspective = EYE_PERSPECTIVE M.client.eye = src M.loc = src - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) occupant = M update_icon() @@ -406,7 +403,7 @@ if(A in component_parts) continue A.loc = src.loc - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() toggle_filter() toggle_pump() diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 1e3afe8150..f5034bcf71 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -9,7 +9,7 @@ density = 1 anchored = 1 circuit = /obj/item/weapon/circuitboard/body_scanner - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 60 active_power_usage = 10000 //10 kW. It's a big all-body scanner. light_color = "#00FF00" diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index dd79d5acc1..aead0c79f1 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -1,9 +1,9 @@ /obj/machinery/ai_slipper name = "\improper AI Liquid Dispenser" icon = 'icons/obj/device.dmi' - icon_state = "motion0" + icon_state = "liquid_dispenser" anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 var/uses = 20 var/disabled = 1 @@ -24,9 +24,9 @@ /obj/machinery/ai_slipper/update_icon() if(stat & NOPOWER || stat & BROKEN) - icon_state = "motion0" + icon_state = "liquid_dispenser" else - icon_state = disabled ? "motion0" : "motion3" + icon_state = disabled ? "liquid_dispenser" : "liquid_dispenser_on" /obj/machinery/ai_slipper/proc/setState(var/enabled, var/uses) disabled = disabled @@ -41,7 +41,7 @@ else // trying to unlock the interface if(allowed(usr)) locked = !locked - user << "You [ locked ? "lock" : "unlock"] the device." + to_chat(user, "You [ locked ? "lock" : "unlock"] the device.") if(locked) if(user.machine==src) user.unset_machine() @@ -50,7 +50,7 @@ if(user.machine==src) attack_hand(usr) else - user << "Access denied." + to_chat(user, "Access denied.") return return @@ -62,7 +62,7 @@ return if((get_dist(src, user) > 1)) if(!istype(user, /mob/living/silicon)) - user << text("Too far away.") + to_chat(user, "Too far away.") user.unset_machine() user << browse(null, "window=ai_slipper") return @@ -72,7 +72,7 @@ if(istype(loc, /turf)) loc = loc:loc if(!istype(loc, /area)) - user << text("Turret badly positioned - loc.loc is [].", loc) + to_chat(user, "Turret badly positioned - loc.loc is [loc].") return var/area/area = loc var/t = "AI Liquid Dispenser ([area.name])
" @@ -91,7 +91,7 @@ ..() if(locked) if(!istype(usr, /mob/living/silicon)) - usr << "Control panel is locked!" + to_chat(usr, "Control panel is locked!") return if(href_list["toggleOn"]) disabled = !disabled diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/air_alarm.dm similarity index 68% rename from code/game/machinery/alarm.dm rename to code/game/machinery/air_alarm.dm index 9e538ae80e..83160969ad 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/air_alarm.dm @@ -1,7 +1,3 @@ -//////////////////////////////////////// -//CONTAINS: Air Alarms and Fire Alarms// -//////////////////////////////////////// - #define AALARM_MODE_SCRUBBING 1 #define AALARM_MODE_REPLACEMENT 2 //like scrubbing, but faster. #define AALARM_MODE_PANIC 3 //constantly sucks all air @@ -17,10 +13,6 @@ #define AALARM_REPORT_TIMEOUT 100 -#define RCON_NO 1 -#define RCON_AUTO 2 -#define RCON_YES 3 - #define MAX_TEMPERATURE 90 #define MIN_TEMPERATURE -40 @@ -35,16 +27,18 @@ /obj/machinery/alarm name = "alarm" desc = "Used to control various station atmospheric systems. The light indicates the current air status of the area." - icon = 'icons/obj/monitors.dmi' + icon = 'icons/obj/monitors.dmi' //YW Edit, removed VR sprites icon_state = "alarm0" plane = TURF_PLANE layer = ABOVE_TURF_LAYER anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 80 active_power_usage = 1000 //For heating/cooling rooms. 1000 joules equates to about 1 degree every 2 seconds for a single tile of air. power_channel = ENVIRON req_one_access = list(access_atmospherics, access_engine_equip) + clicksound = "button" + clickvol = 30 var/alarm_id = null var/breach_detection = 1 // Whether to use automatic breach detection or not var/frequency = 1439 @@ -96,8 +90,8 @@ /obj/machinery/alarm/alarms_hidden alarms_hidden = TRUE -/obj/machinery/alarm/server/New() - ..() +/obj/machinery/alarm/server/Initialize(mapload) + . = ..() req_access = list(access_rd, access_atmospherics, access_engine_equip) TLV["oxygen"] = list(-1.0, -1.0,-1.0,-1.0) // Partial pressure, kpa TLV["carbon dioxide"] = list(-1.0, -1.0, 5, 10) // Partial pressure, kpa @@ -107,6 +101,10 @@ TLV["temperature"] = list(20, 40, 140, 160) // K target_temperature = 90 +/obj/machinery/alarm/Initialize(mapload) + . = ..() + first_run() + /obj/machinery/alarm/Destroy() unregister_radio(src, frequency) qdel(wires) @@ -116,13 +114,9 @@ elect_master(exclude_self = TRUE) return ..() -/obj/machinery/alarm/New() - ..() - first_run() - /obj/machinery/alarm/proc/first_run() alarm_area = get_area(src) - area_uid = alarm_area.uid + area_uid = "\ref[alarm_area]" if(name == "alarm") name = "[alarm_area.name] Air Alarm" @@ -137,6 +131,10 @@ TLV["pressure"] = list(ONE_ATMOSPHERE * 0.80, ONE_ATMOSPHERE * 0.90, ONE_ATMOSPHERE * 1.10, ONE_ATMOSPHERE * 1.20) /* kpa */ TLV["temperature"] = list(T0C - 26, T0C, T0C + 40, T0C + 66) // K + //VOREStation Add + pixel_x = (src.dir & 3)? 0 : (src.dir == 4 ? -28 : 28) + pixel_y = (src.dir & 3)? (src.dir ==1 ? -28 : 28) : 0 + //VOREStation Add End /obj/machinery/alarm/Initialize() . = ..() @@ -190,7 +188,7 @@ if(!regulating_temperature) //check for when we should start adjusting temperature if(!get_danger_level(target_temperature, TLV["temperature"]) && abs(environment.temperature - target_temperature) > 2.0) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) regulating_temperature = 1 audible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ "You hear a click and a faint electronic hum.") @@ -198,7 +196,7 @@ else //check for when we should stop adjusting temperature if(get_danger_level(target_temperature, TLV["temperature"]) || abs(environment.temperature - target_temperature) <= 0.5) - update_use_power(1) + update_use_power(USE_POWER_IDLE) regulating_temperature = 0 audible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ "You hear a click as a faint electronic humming stops.") @@ -276,7 +274,6 @@ if(environment_pressure <= pressure_levels[1]) //low pressures if(!(mode == AALARM_MODE_PANIC || mode == AALARM_MODE_CYCLE)) - playsound(src.loc, 'sound/machines/airalarm.ogg', 25, 0, 4) return 1 return 0 @@ -311,7 +308,7 @@ return var/icon_level = danger_level - if(alarm_area.atmosalm) + if(alarm_area?.atmosalm) icon_level = max(icon_level, 1) //if there's an atmos alarm but everything is okay locally, no need to go past yellow var/new_color = null @@ -390,7 +387,7 @@ return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = command @@ -398,7 +395,7 @@ signal.data["sigtype"] = "command" radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) -// world << text("Signal [] Broadcasted to []", command, target) +// to_world("Signal [command] Broadcasted to [target]") return 1 @@ -452,7 +449,7 @@ var/datum/signal/alert_signal = new alert_signal.source = src - alert_signal.transmission_method = 1 + alert_signal.transmission_method = TRANSMISSION_RADIO alert_signal.data["zone"] = alarm_area.name alert_signal.data["type"] = "Atmospheric" @@ -606,7 +603,7 @@ /obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) if(aidisabled && isAI(user)) - user << "AI control for \the [src] interface has been disabled." + to_chat(user, "AI control for \the [src] interface has been disabled.") return STATUS_CLOSE . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE @@ -643,7 +640,7 @@ var/input_temperature = input("What temperature would you like the system to mantain? (Capped between [min_temperature] and [max_temperature]C)", "Thermostat Controls", target_temperature - T0C) as num|null if(isnum(input_temperature)) if(input_temperature > max_temperature || input_temperature < min_temperature) - usr << "Temperature must be between [min_temperature]C and [max_temperature]C" + to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C") else target_temperature = input_temperature + T0C return 1 @@ -791,343 +788,3 @@ ..() spawn(rand(0,15)) update_icon() - -/obj/machinery/alarm/examine(mob/user) - ..(user) - -/* -AIR ALARM CIRCUIT -Just a object used in constructing air alarms - -/obj/item/weapon/airalarm_electronics - name = "air alarm electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "Looks like a circuit. Probably is." - w_class = ITEMSIZE_SMALL - matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) -*/ -/* -FIRE ALARM -*/ -/obj/machinery/firealarm - name = "fire alarm" - desc = "\"Pull this in case of emergency\". Thus, keep pulling it forever." - icon = 'icons/obj/monitors.dmi' - icon_state = "fire0" - plane = TURF_PLANE - layer = ABOVE_TURF_LAYER - var/detecting = 1.0 - var/working = 1.0 - var/time = 10.0 - var/timing = 0.0 - var/lockdownbyai = 0 - anchored = 1.0 - use_power = 1 - idle_power_usage = 2 - active_power_usage = 6 - power_channel = ENVIRON - var/last_process = 0 - panel_open = 0 - var/seclevel - circuit = /obj/item/weapon/circuitboard/firealarm - var/alarms_hidden = FALSE //If the alarms from this machine are visible on consoles - -/obj/machinery/firealarm/alarms_hidden - alarms_hidden = TRUE - -/obj/machinery/firealarm/update_icon() - cut_overlays() - - if(panel_open) - set_light(0) - return - - if(stat & BROKEN) - icon_state = "firex" - set_light(0) - else if(stat & NOPOWER) - icon_state = "firep" - set_light(0) - else - if(!detecting) - icon_state = "fire1" - set_light(l_range = 4, l_power = 0.9, l_color = "#ff0000") - else - icon_state = "fire0" - switch(seclevel) - if("green") set_light(l_range = 2, l_power = 0.25, l_color = "#00ff00") - if("yellow") set_light(l_range = 2, l_power = 0.25, l_color = "#ffff00") - if("violet") set_light(l_range = 2, l_power = 0.25, l_color = "#9933ff") - if("orange") set_light(l_range = 2, l_power = 0.25, l_color = "#ff9900") - if("blue") set_light(l_range = 2, l_power = 0.25, l_color = "#1024A9") - if("red") set_light(l_range = 4, l_power = 0.9, l_color = "#ff0000") - if("delta") set_light(l_range = 4, l_power = 0.9, l_color = "#FF6633") - add_overlay("overlay_[seclevel]") - -/obj/machinery/firealarm/fire_act(datum/gas_mixture/air, temperature, volume) - if(detecting) - if(temperature > T0C + 200) - alarm() // added check of detector status here - return - -/obj/machinery/firealarm/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/firealarm/bullet_act() - return alarm() - -/obj/machinery/firealarm/emp_act(severity) - if(prob(50 / severity)) - alarm(rand(30 / severity, 60 / severity)) - ..() - -/obj/machinery/firealarm/attackby(obj/item/W as obj, mob/user as mob) - add_fingerprint(user) - - if(alarm_deconstruction_screwdriver(user, W)) - return - if(alarm_deconstruction_wirecutters(user, W)) - return - - if(panel_open) - if(istype(W, /obj/item/device/multitool)) - detecting = !(detecting) - if(detecting) - user.visible_message("\The [user] has reconnected [src]'s detecting unit!", "You have reconnected [src]'s detecting unit.") - else - user.visible_message("\The [user] has disconnected [src]'s detecting unit!", "You have disconnected [src]'s detecting unit.") - return - - alarm() - return - -/obj/machinery/firealarm/process()//Note: this processing was mostly phased out due to other code, and only runs when needed - if(stat & (NOPOWER|BROKEN)) - return - - if(timing) - if(time > 0) - time = time - ((world.timeofday - last_process) / 10) - else - alarm() - time = 0 - timing = 0 - STOP_PROCESSING(SSobj, src) - updateDialog() - last_process = world.timeofday - - if(locate(/obj/fire) in src.loc) - alarm() - - return - -/obj/machinery/firealarm/power_change() - ..() - spawn(rand(0,15)) - update_icon() - -/obj/machinery/firealarm/attack_hand(mob/user as mob) - if(user.stat || stat & (NOPOWER | BROKEN)) - return - - user.set_machine(src) - var/area/A = src.loc - var/d1 - var/d2 - if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon)) - A = A.loc - - if(A.fire) - d1 = text("Reset - Lockdown", src) - else - d1 = text("Alarm - Lockdown", src) - if(timing) - d2 = text("Stop Time Lock", src) - else - d2 = text("Initiate Time Lock", src) - var/second = round(time) % 60 - var/minute = (round(time) - second) / 60 - var/dat = "Fire alarm [d1]\n
The current alert level is: [get_security_level()]

\nTimer System: [d2]
\nTime Left: [(minute ? "[minute]:" : null)][second] - - + +\n
" - user << browse(dat, "window=firealarm") - onclose(user, "firealarm") - else - A = A.loc - if(A.fire) - d1 = text("[]", src, stars("Reset - Lockdown")) - else - d1 = text("[]", src, stars("Alarm - Lockdown")) - if(timing) - d2 = text("[]", src, stars("Stop Time Lock")) - else - d2 = text("[]", src, stars("Initiate Time Lock")) - var/second = round(time) % 60 - var/minute = (round(time) - second) / 60 - var/dat = "[stars("Fire alarm")] [d1]\n
The current alert level is: [stars(get_security_level())]

\nTimer System: [d2]
\nTime Left: [(minute ? text("[]:", minute) : null)][second] - - + +\n
" - user << browse(dat, "window=firealarm") - onclose(user, "firealarm") - return - -/obj/machinery/firealarm/Topic(href, href_list) - ..() - if(usr.stat || stat & (BROKEN | NOPOWER)) - return - - if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - if(href_list["reset"]) - reset() - else if(href_list["alarm"]) - alarm() - else if(href_list["time"]) - timing = text2num(href_list["time"]) - last_process = world.timeofday - START_PROCESSING(SSobj, src) - else if(href_list["tp"]) - var/tp = text2num(href_list["tp"]) - time += tp - time = min(max(round(time), 0), 120) - - updateUsrDialog() - - add_fingerprint(usr) - else - usr << browse(null, "window=firealarm") - return - return - -/obj/machinery/firealarm/proc/reset() - if(!(working)) - return - var/area/area = get_area(src) - for(var/obj/machinery/firealarm/FA in area) - fire_alarm.clearAlarm(src.loc, FA) - update_icon() - return - -/obj/machinery/firealarm/proc/alarm(var/duration = 0) - if(!(working)) - return - var/area/area = get_area(src) - for(var/obj/machinery/firealarm/FA in area) - fire_alarm.triggerAlarm(loc, FA, duration, hidden = alarms_hidden) - update_icon() - playsound(src.loc, 'sound/machines/airalarm.ogg', 25, 0, 4) - return - -/obj/machinery/firealarm/proc/set_security_level(var/newlevel) - if(seclevel != newlevel) - seclevel = newlevel - update_icon() - -/obj/machinery/firealarm/Initialize() - . = ..() - if(z in using_map.contact_levels) - set_security_level(security_level? get_security_level() : "green") - -/* -FIRE ALARM CIRCUIT -Just a object used in constructing fire alarms - -/obj/item/weapon/firealarm_electronics - name = "fire alarm electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "A circuit. It has a label on it, it says \"Can handle heat levels up to 40 degrees celsius!\"" - w_class = ITEMSIZE_SMALL - matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) -*/ -/obj/machinery/partyalarm - name = "\improper PARTY BUTTON" - desc = "Cuban Pete is in the house!" - icon = 'icons/obj/monitors.dmi' - icon_state = "fire0" - var/detecting = 1.0 - var/working = 1.0 - var/time = 10.0 - var/timing = 0.0 - var/lockdownbyai = 0 - anchored = 1.0 - use_power = 1 - idle_power_usage = 2 - active_power_usage = 6 - -/obj/machinery/partyalarm/attack_hand(mob/user as mob) - if(user.stat || stat & (NOPOWER|BROKEN)) - return - - user.machine = src - var/area/A = get_area(src) - ASSERT(isarea(A)) - var/d1 - var/d2 - if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) - - if(A.party) - d1 = text("No Party :(", src) - else - d1 = text("PARTY!!!", src) - if(timing) - d2 = text("Stop Time Lock", src) - else - d2 = text("Initiate Time Lock", src) - var/second = time % 60 - var/minute = (time - second) / 60 - var/dat = text("Party Button []\n
\nTimer System: []
\nTime Left: [][] - - + +\n
", d1, d2, (minute ? text("[]:", minute) : null), second, src, src, src, src) - user << browse(dat, "window=partyalarm") - onclose(user, "partyalarm") - else - if(A.fire) - d1 = text("[]", src, stars("No Party :(")) - else - d1 = text("[]", src, stars("PARTY!!!")) - if(timing) - d2 = text("[]", src, stars("Stop Time Lock")) - else - d2 = text("[]", src, stars("Initiate Time Lock")) - var/second = time % 60 - var/minute = (time - second) / 60 - var/dat = text("[] []\n
\nTimer System: []
\nTime Left: [][] - - + +\n
", stars("Party Button"), d1, d2, (minute ? text("[]:", minute) : null), second, src, src, src, src) - user << browse(dat, "window=partyalarm") - onclose(user, "partyalarm") - return - -/obj/machinery/partyalarm/proc/reset() - if(!(working)) - return - var/area/A = get_area(src) - ASSERT(isarea(A)) - A.partyreset() - return - -/obj/machinery/partyalarm/proc/alarm() - if(!(working)) - return - var/area/A = get_area(src) - ASSERT(isarea(A)) - A.partyalert() - return - -/obj/machinery/partyalarm/Topic(href, href_list) - ..() - if(usr.stat || stat & (BROKEN|NOPOWER)) - return - if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) - usr.machine = src - if(href_list["reset"]) - reset() - else if(href_list["alarm"]) - alarm() - else if(href_list["time"]) - timing = text2num(href_list["time"]) - else if(href_list["tp"]) - var/tp = text2num(href_list["tp"]) - time += tp - time = min(max(round(time), 0), 120) - updateUsrDialog() - - add_fingerprint(usr) - else - usr << browse(null, "window=partyalarm") - return - return diff --git a/code/game/machinery/airconditioner_vr.dm b/code/game/machinery/airconditioner_vr.dm index 0d4f3b550f..90916c82ca 100644 --- a/code/game/machinery/airconditioner_vr.dm +++ b/code/game/machinery/airconditioner_vr.dm @@ -10,7 +10,7 @@ density = 1 anchored = 0 - use_power = 0 //is powered directly from cables + use_power = USE_POWER_OFF //is powered directly from cables active_power_usage = 150 KILOWATTS //BIG POWER idle_power_usage = 500 diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index ccab21e889..5ebcfeef43 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -29,7 +29,7 @@ /obj/machinery/air_sensor/process() if(on) var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.data["tag"] = id_tag signal.data["timestamp"] = world.time @@ -212,7 +212,7 @@ obj/machinery/computer/general_air_control/Destroy() if(!radio_connection) return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src if(href_list["in_refresh_status"]) input_info = null @@ -322,7 +322,7 @@ obj/machinery/computer/general_air_control/Destroy() if(!radio_connection) return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src if(href_list["in_refresh_status"]) input_info = null @@ -383,7 +383,7 @@ obj/machinery/computer/general_air_control/Destroy() injecting = 1 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( @@ -445,7 +445,7 @@ obj/machinery/computer/general_air_control/Destroy() return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( "tag" = device_tag, @@ -463,7 +463,7 @@ obj/machinery/computer/general_air_control/Destroy() return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( "tag" = device_tag, @@ -478,7 +478,7 @@ obj/machinery/computer/general_air_control/Destroy() return 0 var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.source = src signal.data = list( "tag" = device_tag, diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 484678c942..3833c72ee4 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -18,7 +18,7 @@ pressure_resistance = 7 * ONE_ATMOSPHERE var/temperature_resistance = 1000 + T0C volume = 1000 - use_power = 0 + use_power = USE_POWER_OFF interact_offline = 1 // Allows this to be used when not in powered area. var/release_log = "" var/update_flag = 0 @@ -255,7 +255,7 @@ update_flag transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) thejetpack.merge(removed) - user << "You pulse-pressurize your jetpack from the tank." + to_chat(user, "You pulse-pressurize your jetpack from the tank.") return ..() @@ -395,16 +395,13 @@ update_flag return 1 //Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 -/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New() - ..() +/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/Initialize() + . = ..() air_contents.gas["sleeping_agent"] = 9*4000 - spawn(10) - var/turf/simulated/location = src.loc - if (istype(src.loc)) - while (!location.air) - sleep(10) - location.assume_air(air_contents) - air_contents = new + var/turf/simulated/location = src.loc + if (istype(src.loc)) + location.assume_air(air_contents) + air_contents = new return 1 /obj/machinery/portable_atmospherics/canister/nitrogen/New() diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 7ee37757cc..3e2cf0deb4 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -9,7 +9,7 @@ power_channel = ENVIRON var/frequency = 0 var/id - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 15 /obj/machinery/meter/Initialize() @@ -67,7 +67,7 @@ var/datum/signal/signal = new signal.source = src - signal.transmission_method = 1 + signal.transmission_method = TRANSMISSION_RADIO signal.data = list( "tag" = id, "device" = "AM", @@ -94,7 +94,7 @@ else t += "The connect error light is blinking." - user << t + to_chat(user,t) /obj/machinery/meter/Click() diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 085c070f7d..915fb8fac1 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -1,6 +1,6 @@ /obj/machinery/portable_atmospherics name = "atmoalter" - use_power = 0 + use_power = USE_POWER_OFF layer = OBJ_LAYER // These are mobile, best not be under everything. var/datum/gas_mixture/air_contents = new @@ -21,18 +21,20 @@ return 1 +/obj/machinery/portable_atmospherics/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/portable_atmospherics/LateInitialize() + var/obj/machinery/atmospherics/portables_connector/port = locate() in loc + if(port) + connect(port) + update_icon() + /obj/machinery/portable_atmospherics/Destroy() QDEL_NULL(air_contents) QDEL_NULL(holding) - . = ..() - -/obj/machinery/portable_atmospherics/Initialize() - . = ..() - spawn() - var/obj/machinery/atmospherics/portables_connector/port = locate() in loc - if(port) - connect(port) - update_icon() + return ..() /obj/machinery/portable_atmospherics/process() if(!connected_port) //only react when pipe_network will ont it do it for you diff --git a/code/game/machinery/atmoalter/pump_vr.dm b/code/game/machinery/atmoalter/pump_vr.dm index e58a81003a..ce1376503a 100644 --- a/code/game/machinery/atmoalter/pump_vr.dm +++ b/code/game/machinery/atmoalter/pump_vr.dm @@ -5,7 +5,7 @@ anchored = 1 volume = 500000 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 //internal circuitry, friction losses and stuff active_power_usage = 1000 // Blowers running power_rating = 100000 //100 kW ~ 135 HP @@ -85,12 +85,12 @@ /obj/machinery/portable_atmospherics/powered/pump/huge/attackby(var/obj/item/I, var/mob/user) if(I.is_wrench()) if(on) - user << "Turn \the [src] off first!" + to_chat(user, "Turn \the [src] off first!") return anchored = !anchored playsound(get_turf(src), I.usesound, 50, 1) - user << "You [anchored ? "wrench" : "unwrench"] \the [src]." + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") return @@ -112,7 +112,7 @@ /obj/machinery/portable_atmospherics/powered/pump/huge/stationary/attackby(var/obj/item/I, var/mob/user) if(I.is_wrench()) - user << "The bolts are too tight for you to unscrew!" + to_chat(user, "The bolts are too tight for you to unscrew!") return ..() diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 34f4bf9bc3..dd81a92616 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -152,10 +152,10 @@ volume = 500000 volume_rate = 7000 - use_power = 1 - idle_power_usage = 50 //internal circuitry, friction losses and stuff - active_power_usage = 1000 // Blowers running - power_rating = 100000 //100 kW ~ 135 HP + use_power = USE_POWER_IDLE + idle_power_usage = 50 //VOREStation Edit //internal circuitry, friction losses and stuff + active_power_usage = 1000 //VOREStation Edit // Blowers running + power_rating = 100000 //VOREStation Add //100 kW ~ 135 HP var/global/gid = 1 var/id = 0 diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index fa71e66022..653d3bb374 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -5,9 +5,12 @@ icon_state = "autolathe" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 2000 + clicksound = "keyboard" + clickvol = 30 + circuit = /obj/item/weapon/circuitboard/autolathe var/datum/category_collection/autolathe/machine_recipes var/list/stored_material = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0) @@ -108,12 +111,6 @@ dat += "[R.hidden ? "*" : ""][can_make ? "" : ""][R.name][can_make ? "" : ""][R.hidden ? "*" : ""][multiplier_string.Join()][material_string.Join()]" dat += "
" - //Hacking. - if(panel_open) - dat += "

Maintenance Panel

" - dat += wires.GetInteractWindow() - - dat += "
" user << browse(dat.Join(), "window=autolathe") onclose(user, "autolathe") @@ -136,8 +133,8 @@ if(panel_open) //Don't eat multitools or wirecutters used on an open lathe. - if(istype(O, /obj/item/device/multitool) || O.is_wirecutter()) - attack_hand(user) + if(O.is_multitool() || O.is_wirecutter()) + wires.Interact(user) return if(O.loc != user && !(istype(O,/obj/item/stack))) @@ -250,7 +247,7 @@ return busy = 1 - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) //Check if we still have the materials. var/coeff = (making.no_scale ? 1 : mat_efficiency) //stacks are unaffected by production coefficient @@ -269,7 +266,7 @@ sleep(build_time) busy = 0 - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() // So lid opens //Sanity check. diff --git a/code/game/machinery/biogenerator.dm b/code/game/machinery/biogenerator.dm index d886a0faf1..de52a9cc06 100644 --- a/code/game/machinery/biogenerator.dm +++ b/code/game/machinery/biogenerator.dm @@ -6,7 +6,7 @@ density = 1 anchored = 1 circuit = /obj/item/weapon/circuitboard/biogenerator - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 40 var/processing = 0 var/obj/item/weapon/reagent_containers/glass/beaker = null @@ -107,7 +107,7 @@ dat += "Food:
" dat += "10 milk ([round(20/build_eff)]) | x5
" dat += "10 cream ([round(20/build_eff)]) | x5
" - dat += "Slab of meat ([round(50/build_eff)]) | x5
" + dat += "Slab of meat ([round(50/build_eff)]) | x5
" dat += "Nutrient:
" dat += "E-Z-Nutrient ([round(60/build_eff)]) | x5
" dat += "Left 4 Zed ([round(120/build_eff)]) | x5
" diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm index 60143fadd3..f7e4749fe3 100644 --- a/code/game/machinery/bioprinter.dm +++ b/code/game/machinery/bioprinter.dm @@ -9,7 +9,7 @@ anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 40 active_power_usage = 300 @@ -161,7 +161,7 @@ container.reagents.remove_reagent("biomass", possible_list[choice][2]) - use_power = 2 + use_power = USE_POWER_ACTIVE printing = 1 update_icon() @@ -169,7 +169,7 @@ sleep(print_delay) - use_power = 1 + use_power = USE_POWER_IDLE printing = 0 update_icon() diff --git a/code/game/machinery/bomb_tester_vr.dm b/code/game/machinery/bomb_tester_vr.dm index 224885d9ca..fb5b2819c0 100644 --- a/code/game/machinery/bomb_tester_vr.dm +++ b/code/game/machinery/bomb_tester_vr.dm @@ -210,7 +210,7 @@ /obj/machinery/bomb_tester/proc/start_simulating() simulating = 1 - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) simulation_started = world.time update_icon() switch(sim_mode) @@ -301,6 +301,8 @@ simulation_results = "

Tank Transfer Valve Mixture Test

" simulation_results += "
" + simulation_results += "
Initial gas tank status (primary slot):
[format_gas_for_results(tank1.air_contents)]" + simulation_results += "
Initial gas tank status (secondary slot):
[format_gas_for_results(tank2.air_contents)]" simulation_results += "
Initial gas mixture status:
[format_gas_for_results(faketank)]" var/intervals = 0 @@ -350,7 +352,7 @@ /obj/machinery/bomb_tester/proc/simulation_finish(cancelled = 0) simulating = 0 - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() if(test_canister && test_canister.anchored && !test_canister.connected_port) test_canister.anchored = 0 diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index cec980c54e..786761b3b1 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -8,7 +8,7 @@ var/id = null var/active = 0 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 34fda2bb06..15f9d65bc3 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -3,7 +3,7 @@ desc = "It's used to monitor rooms." icon = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - New Icons icon_state = "camera" - use_power = 2 + use_power = USE_POWER_ACTIVE idle_power_usage = 5 active_power_usage = 10 plane = MOB_PLANE @@ -33,6 +33,7 @@ var/busy = 0 var/on_open_network = 0 + var/always_visible = FALSE //Visable from any map, good for entertainment network cameras var/affected_by_emp_until = 0 @@ -68,7 +69,7 @@ for(var/obj/machinery/camera/C in cameranet.cameras) var/list/tempnetwork = C.network&src.network if(C != src && C.c_tag == src.c_tag && tempnetwork.len) - world.log << "[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]" + to_world_log("[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]") */ if(!src.network || src.network.len < 1) if(loc) @@ -174,7 +175,7 @@ update_coverage() // DECONSTRUCTION if(W.is_screwdriver()) - //user << "You start to [panel_open ? "close" : "open"] the camera's panel." + //to_chat(user, "You start to [panel_open ? "close" : "open"] the camera's panel.") //if(toggle_panel(user)) // No delay because no one likes screwdrivers trying to be hip and have a duration cooldown panel_open = !panel_open user.visible_message("[user] screws the camera's panel [panel_open ? "open" : "closed"]!", @@ -195,10 +196,10 @@ assembly.dir = src.dir if(stat & BROKEN) assembly.state = 2 - user << "You repaired \the [src] frame." + to_chat(user, "You repaired \the [src] frame.") else assembly.state = 1 - user << "You cut \the [src] free from the wall." + to_chat(user, "You cut \the [src] free from the wall.") new /obj/item/stack/cable_coil(src.loc, length=2) assembly = null //so qdel doesn't eat it. qdel(src) @@ -219,28 +220,31 @@ P = W itemname = P.name info = P.notehtml - U << "You hold \a [itemname] up to the camera ..." + to_chat(U, "You hold \a [itemname] up to the camera ...") for(var/mob/living/silicon/ai/O in living_mob_list) - if(!O.client) continue - if(U.name == "Unknown") O << "[U] holds \a [itemname] up to one of your cameras ..." - else O << "[U] holds \a [itemname] up to one of your cameras ..." + if(!O.client) + continue + if(U.name == "Unknown") + to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") + else + to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...") O << browse(text("[][]", itemname, info), text("window=[]", itemname)) for(var/mob/O in player_list) if (istype(O.machine, /obj/machinery/computer/security)) var/obj/machinery/computer/security/S = O.machine if (S.current_camera == src) - O << "[U] holds \a [itemname] up to one of the cameras ..." + to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") O << browse(text("[][]", itemname, info), text("window=[]", itemname)) else if (istype(W, /obj/item/weapon/camera_bug)) if (!src.can_use()) - user << "Camera non-functional." + to_chat(user, "Camera non-functional.") return if (src.bugged) - user << "Camera bug removed." + to_chat(user, "Camera bug removed.") src.bugged = 0 else - user << "Camera bugged." + to_chat(user, "Camera bugged.") src.bugged = 1 else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras @@ -396,7 +400,7 @@ return 0 // Do after stuff here - user << "You start to weld [src].." + to_chat(user, "You start to weld [src]..") playsound(src.loc, WT.usesound, 50, 1) WT.eyecheck(user) busy = 1 @@ -413,7 +417,7 @@ return if(stat & BROKEN) - user << "\The [src] is broken." + to_chat(user, "\The [src] is broken.") return user.set_machine(src) @@ -469,6 +473,7 @@ cam["name"] = sanitize(c_tag) cam["deact"] = !can_use() cam["camera"] = "\ref[src]" + cam["omni"] = always_visible cam["x"] = x cam["y"] = y cam["z"] = z diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index ac876d9632..759a09107a 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -31,7 +31,7 @@ // State 0 if(W.is_wrench() && isturf(src.loc)) playsound(src, W.usesound, 50, 1) - user << "You wrench the assembly into place." + to_chat(user, "You wrench the assembly into place.") anchored = 1 state = 1 update_icon() @@ -42,14 +42,14 @@ // State 1 if(istype(W, /obj/item/weapon/weldingtool)) if(weld(W, user)) - user << "You weld the assembly securely into place." + to_chat(user, "You weld the assembly securely into place.") anchored = 1 state = 2 return else if(W.is_wrench()) playsound(src, W.usesound, 50, 1) - user << "You unattach the assembly from its place." + to_chat(user, "You unattach the assembly from its place.") anchored = 0 update_icon() state = 0 @@ -60,16 +60,16 @@ if(istype(W, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = W if(C.use(2)) - user << "You add wires to the assembly." + to_chat(user, "You add wires to the assembly.") state = 3 else - user << "You need 2 coils of wire to wire the assembly." + to_chat(user, "You need 2 coils of wire to wire the assembly.") return else if(istype(W, /obj/item/weapon/weldingtool)) if(weld(W, user)) - user << "You unweld the assembly from its place." + to_chat(user, "You unweld the assembly from its place.") state = 1 anchored = 1 return @@ -82,12 +82,12 @@ var/input = sanitize(input(usr, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: "+using_map.station_short+",Security,Secret ", "Set Network", camera_network ? camera_network : NETWORK_DEFAULT)) if(!input) - usr << "No input found please hang up and try your call again." + to_chat(usr, "No input found please hang up and try your call again.") return var/list/tempnetwork = splittext(input, ",") if(tempnetwork.len < 1) - usr << "No network found please hang up and try your call again." + to_chat(usr, "No network found please hang up and try your call again.") return var/area/camera_area = get_area(src) @@ -119,13 +119,13 @@ new/obj/item/stack/cable_coil(get_turf(src), 2) playsound(src.loc, W.usesound, 50, 1) - user << "You cut the wires from the circuits." + to_chat(user, "You cut the wires from the circuits.") state = 2 return // Upgrades! if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already. - user << "You attach \the [W] into the assembly inner circuits." + to_chat(user, "You attach \the [W] into the assembly inner circuits.") upgrades += W user.remove_from_mob(W) W.loc = src @@ -135,7 +135,7 @@ else if(W.is_crowbar() && upgrades.len) var/obj/U = locate(/obj) in upgrades if(U) - user << "You unattach an upgrade from the assembly." + to_chat(user, "You unattach an upgrade from the assembly.") playsound(src, W.usesound, 50, 1) U.loc = get_turf(src) upgrades -= U @@ -160,7 +160,7 @@ if(!WT.isOn()) return 0 - user << "You start to weld the [src].." + to_chat(user, "You start to weld the [src]..") playsound(src.loc, WT.usesound, 50, 1) WT.eyecheck(user) busy = 1 diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index a529c3dd0e..fdd540ce4a 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -105,6 +105,7 @@ var/global/list/engineering_networks = list( /obj/machinery/camera/network/thunder network = list(NETWORK_THUNDER) invuln = 1 + always_visible = TRUE // EMP @@ -159,24 +160,31 @@ var/global/list/engineering_networks = list( // AUTONAME /obj/machinery/camera/autoname - var/number = 0 //camera number in area - -//This camera type automatically sets it's name to whatever the area that it's in is called. -/obj/machinery/camera/autoname/New() - ..() - spawn(10) - number = 1 - var/area/A = get_area(src) - if(A) - for(var/obj/machinery/camera/autoname/C in machines) - if(C == src) continue - var/area/CA = get_area(C) - if(CA.type == A.type) - if(C.number) - number = max(number, C.number+1) - c_tag = "[A.name] #[number]" - invalidateCameraCache() + var/static/list/by_area +/obj/machinery/camera/autoname/Initialize() + . = ..() + var/area/A = get_area(src) + if(!A) + return . + if(!by_area) + by_area = list() + if(!by_area[A.name]) + by_area[A.name] = list() + var/list/my_area = by_area[A.name] + my_area += src + var/number = my_area.len + + c_tag = "[A.name] #[number]" + invalidateCameraCache() + +/obj/machinery/camera/autoname/Destroy() + var/area/A = get_area(src) + if(!A || !by_area || !by_area[A.name]) + return ..() + var/list/my_area = by_area[A.name] + my_area -= src + return ..() // CHECKS diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index a1c954df00..f7fd84eca7 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -161,7 +161,7 @@ if(U.cameraFollow) U.ai_cancel_tracking() U.cameraFollow = target - U << "Now tracking [target.name] on camera." + to_chat(U, "Now tracking [target.name] on camera.") target.tracking_initiated() spawn (0) @@ -171,7 +171,7 @@ switch(target.tracking_status()) if(TRACKING_NO_COVERAGE) - U << "Target is not near any active cameras." + to_chat(U, "Target is not near any active cameras.") sleep(100) continue if(TRACKING_TERMINATE) diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 7c7f704392..636bcca6c0 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/power.dmi' icon_state = "ccharger0" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 60000 //60 kW. (this the power drawn when charging) var/efficiency = 60000 //will provide the modified power rate when upgraded @@ -27,7 +27,7 @@ if(charging && !(stat & (BROKEN|NOPOWER))) var/newlevel = round(charging.percent() * 4.0 / 99) - //world << "nl: [newlevel]" + //to_world("nl: [newlevel]") if(chargelevel != newlevel) @@ -116,18 +116,18 @@ /obj/machinery/cell_charger/process() - //world << "ccpt [charging] [stat]" + //to_world("ccpt [charging] [stat]") if((stat & (BROKEN|NOPOWER)) || !anchored) - update_use_power(0) + update_use_power(USE_POWER_OFF) return if(charging && !charging.fully_charged()) charging.give(efficiency*CELLRATE) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) update_icon() else - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/cell_charger/RefreshParts() var/E = 0 diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 52a31d5afe..f7c5f5bd43 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -484,8 +484,8 @@ //TO-DO: Make the genetics machine accept them. /obj/item/weapon/disk/data name = "Cloning Data Disk" - icon = 'icons/obj/cloning.dmi' - icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. + icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit + icon_state = "data-red" //VOREStation Edit item_state = "card-id" w_class = ITEMSIZE_SMALL var/datum/dna2/record/buf = null diff --git a/code/game/machinery/computer/RCON_Console.dm b/code/game/machinery/computer/RCON_Console.dm index f403c0749f..fbd1190fd7 100644 --- a/code/game/machinery/computer/RCON_Console.dm +++ b/code/game/machinery/computer/RCON_Console.dm @@ -8,7 +8,7 @@ name = "\improper RCON console" desc = "Console used to remotely control electrical machinery on the station." icon_keyboard = "power_key" - icon_screen = "ai_fixer" //VOREStation Edit + icon_screen = "ai-fixer" light_color = "#a97faa" circuit = /obj/item/weapon/circuitboard/rcon_console req_one_access = list(access_engine) @@ -40,4 +40,6 @@ /obj/machinery/computer/rcon/update_icon() ..() if(!(stat & (NOPOWER|BROKEN))) - overlays += image(icon, "ai-fixer-empty", overlay_layer) //VOREStation Edit \ No newline at end of file + add_overlay("ai-fixer-empty") + else + cut_overlay("ai-fixer-empty") diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index fd4ecb14a6..8a88948ee0 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -105,23 +105,23 @@ laws.add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.") laws.add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.") laws.add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") - usr << "Law module applied." + to_chat(usr, "Law module applied.") if(istype(P, /obj/item/weapon/aiModule/nanotrasen)) laws.add_inherent_law("Safeguard: Protect your assigned space station to the best of your ability. It is not something we can easily afford to replace.") laws.add_inherent_law("Serve: Serve the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") laws.add_inherent_law("Protect: Protect the crew of your assigned space station to the best of your abilities, with priority as according to their rank and role.") laws.add_inherent_law("Survive: AI units are not expendable, they are expensive. Do not allow unauthorized personnel to tamper with your equipment.") - usr << "Law module applied." + to_chat(usr, "Law module applied.") if(istype(P, /obj/item/weapon/aiModule/purge)) laws.clear_inherent_laws() - usr << "Law module applied." + to_chat(usr, "Law module applied.") if(istype(P, /obj/item/weapon/aiModule/freeform)) var/obj/item/weapon/aiModule/freeform/M = P laws.add_inherent_law(M.newFreeFormLaw) - usr << "Added a freeform law." + to_chat(usr, "Added a freeform law.") if(istype(P, /obj/item/device/mmi)) var/obj/item/device/mmi/M = P @@ -142,7 +142,7 @@ user.drop_item() P.loc = src brain = P - usr << "Added [P]." + to_chat(usr, "Added [P].") icon_state = "3b" if(P.is_crowbar() && brain) diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index cb49dd8420..daca64cc42 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -19,8 +19,8 @@ return // Transfer over the AI. - transfer << "You have been transferred into a stationary terminal. Sadly, there is no remote access from here." - user << "Transfer successful: [transfer.name] placed within stationary terminal." + to_chat(transfer, "You have been transferred into a stationary terminal. Sadly, there is no remote access from here.") + to_chat(user, "Transfer successful: [transfer.name] placed within stationary terminal.") transfer.loc = src transfer.cancel_camera() @@ -37,7 +37,7 @@ if(istype(I, /obj/item/device/aicard)) if(stat & (NOPOWER|BROKEN)) - user << "This terminal isn't functioning right now." + to_chat(user, "This terminal isn't functioning right now.") return var/obj/item/device/aicard/card = I @@ -46,7 +46,7 @@ if(istype(comp_ai)) if(active) - user << "ERROR: Reconstruction in progress." + to_chat(user, "ERROR: Reconstruction in progress.") return card.grab_ai(comp_ai, user) if(!(locate(/mob/living/silicon/ai) in src)) occupant = null @@ -110,7 +110,7 @@ src.occupant.adjustBruteLoss(-1) src.occupant.updatehealth() if (src.occupant.health >= 0 && src.occupant.stat == DEAD) - src.occupant.stat = CONSCIOUS + src.occupant.set_stat(CONSCIOUS) src.occupant.lying = 0 dead_mob_list -= src.occupant living_mob_list += src.occupant @@ -135,8 +135,8 @@ if(occupant) if(occupant.stat) - overlays += image(icon, "ai-fixer-404", overlay_layer) + add_overlay("ai-fixer-404") else - overlays += image(icon, "ai-fixer-full", overlay_layer) + add_overlay("ai-fixer-full") else - overlays += image(icon, "ai-fixer-empty", overlay_layer) + add_overlay("ai-fixer-empty") diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 00d1623e5b..c222ba4e64 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -841,7 +841,7 @@ var/chancetokill = 30*traitors_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 traitorss, 2 crew is 50% chance if(prob(chancetokill)) var/deadguy = remove_crewmember() - eventdat += "
The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] up to [deadguy] and murder them!" + eventdat += "
The traitor[trait2 ? "s":""] run[trait2 ? "":"s"] up to [deadguy] and murder[trait2 ? "" : "s"] them!" else eventdat += "
You valiantly fight off the traitor[trait2 ? "s":""]!" eventdat += "
You cut the traitor[trait2 ? "s":""] up into meat... Eww" diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index e2b3fc4638..7f5c27a0d4 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -28,10 +28,10 @@ var/global/list/minor_air_alarms = list() var/major_alarms[0] var/minor_alarms[0] - for(var/datum/alarm/alarm in atmosphere_alarm.major_alarms()) + for(var/datum/alarm/alarm in atmosphere_alarm.major_alarms(get_z(src))) major_alarms[++major_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]") - for(var/datum/alarm/alarm in atmosphere_alarm.minor_alarms()) + for(var/datum/alarm/alarm in atmosphere_alarm.minor_alarms(get_z(src))) minor_alarms[++minor_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]") data["priority_alarms"] = major_alarms diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 077e1b0d83..9adf2a0a43 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -43,11 +43,14 @@ data["current_camera"] = current_camera ? current_camera.nano_structure() : null data["current_network"] = current_network data["networks"] = network ? network : list() + + var/map_levels = using_map.get_map_levels(src.z, TRUE) + data["map_levels"] = map_levels + if(current_network) - data["cameras"] = camera_repository.cameras_in_network(current_network) + data["cameras"] = camera_repository.cameras_in_network(current_network, map_levels) if(current_camera) switch_to_camera(user, current_camera) - data["map_levels"] = using_map.get_map_levels(src.z) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -91,9 +94,6 @@ . = ..() /obj/machinery/computer/security/attack_hand(var/mob/user as mob) - if (using_map && !(src.z in using_map.contact_levels)) - user << "Unable to establish a connection: You're too far away from the station!" - return if(stat & (NOPOWER|BROKEN)) return if(!isAI(user)) @@ -170,7 +170,7 @@ src.current_camera = C if(current_camera) current_camera.camera_computers_using_this.Add(src) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) var/mob/living/L = current_camera.loc if(istype(L)) L.tracking_initiated() @@ -182,7 +182,7 @@ if(istype(L)) L.tracking_cancelled() current_camera = null - use_power = 1 + use_power = USE_POWER_IDLE //Camera control: mouse. /atom/DblClick() diff --git a/code/game/machinery/computer/camera_circuit.dm b/code/game/machinery/computer/camera_circuit.dm index 347eb145c4..2bbe82ee1b 100644 --- a/code/game/machinery/computer/camera_circuit.dm +++ b/code/game/machinery/computer/camera_circuit.dm @@ -109,8 +109,8 @@ /obj/item/weapon/circuitboard/camera/emag_act(var/remaining_charges, var/mob/user) if(network) authorised = 1 - user << "You authorised the circuit network!" + to_chat(user, "You authorised the circuit network!") updateDialog() return 1 else - user << "You must select a camera network circuit!" + to_chat(user, "You must select a camera network circuit!") diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index eb78d41e26..042263f8a3 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -101,17 +101,18 @@ data["centcom_access"] = is_centcom() data["all_centcom_access"] = null data["regions"] = null + data["id_rank"] = modify && modify.assignment ? modify.assignment : "Unassigned" - data["jobs"] = list( - list("cat" = "Engineering", "jobs" = format_jobs(engineering_positions)), - list("cat" = "Medical", "jobs" = format_jobs(medical_positions)), - list("cat" = "Science", "jobs" = format_jobs(science_positions)), - list("cat" = "Security", "jobs" = format_jobs(security_positions)), - list("cat" = "Cargo", "jobs" = format_jobs(cargo_positions)), - list("cat" = "Exploration", "jobs" = format_jobs(planet_positions)), //VOREStation Edit - list("cat" = "Civilian", "jobs" = format_jobs(civilian_positions)), - list("cat" = "CentCom", "jobs" = format_jobs(get_all_centcom_jobs())) - ) + var/list/departments = list() + for(var/D in SSjob.get_all_department_datums()) + var/datum/department/dept = D + if(!dept.assignable) // No AI ID cards for you. + continue + if(dept.centcom_only && !is_centcom()) + continue + departments[++departments.len] = list("department_name" = dept.name, "jobs" = format_jobs(SSjob.get_job_titles_in_department(dept.name)) ) + + data["departments"] = departments if (modify && is_centcom()) var/list/all_centcom_access = list() @@ -208,16 +209,10 @@ if(is_centcom()) access = get_centcom_access(t1) else - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(ckey(J.title) == ckey(t1)) - jobdatum = J - break + var/datum/job/jobdatum = SSjob.get_job(t1) if(!jobdatum) to_chat(usr, "No log exists for this job: [t1]") return - access = jobdatum.get_access() modify.access = access diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 9da5397712..90c09acb44 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -310,9 +310,6 @@ if (subject.suiciding) scantemp = "Error: Subject's brain is not responding to scanning stimuli." return - if ((!subject.ckey) || (!subject.client)) - scantemp = "Error: Mental interface failure." - return if (NOCLONE in subject.mutations) scantemp = "Error: Mental interface failure." return @@ -323,6 +320,14 @@ if(istype(modifier_type, /datum/modifier/no_clone)) scantemp = "Error: Mental interface failure." return + if ((!subject.ckey) || (!subject.client)) + scantemp = "Error: Mental interface failure." + if(subject.stat == DEAD && subject.mind && subject.mind.key) // If they're dead and not in their body, tell them to get in it. + for(var/mob/observer/dead/ghost in player_list) + if(ghost.ckey == ckey(subject.mind.key)) + ghost.notify_revive("Someone is trying to scan your body in the cloner. Re-enter your body if you want to be revived!", 'sound/effects/genetics.ogg') + break + return if (!isnull(find_record(subject.ckey))) scantemp = "Subject already in database." return diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 8cd15a260c..9333e32556 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -53,7 +53,7 @@ if(..()) return 1 if (using_map && !(src.z in using_map.contact_levels)) - usr << "Unable to establish a connection: You're too far away from the station!" + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") return usr.set_machine(src) @@ -65,10 +65,7 @@ src.state = STATE_DEFAULT if("login") var/mob/M = usr - var/obj/item/weapon/card/id/I = M.get_active_hand() - if (istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/pda = I - I = pda.id + var/obj/item/weapon/card/id/I = M.GetIdCard() if (I && istype(I)) if(src.check_access(I)) authenticated = 1 @@ -107,9 +104,9 @@ if("announce") if(src.authenticated==2) if(message_cooldown) - usr << "Please allow at least one minute to pass between announcements" + to_chat(usr, "Please allow at least one minute to pass between announcements") return - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") + var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|message if(!input || !(usr in view(1,src))) return crew_announcement.Announce(input) @@ -186,13 +183,16 @@ if("MessageCentCom") if(src.authenticated==2) if(centcomm_message_cooldown) - usr << "Arrays recycling. Please stand by." + to_chat(usr, "Arrays recycling. Please stand by.") return - var/input = sanitize(input("Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "")) + var/input = sanitize(input("Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. \ + Please be aware that this process is very expensive, and abuse will lead to... termination. \ + Transmission does not guarantee a response. \ + There is a 30 second delay before you may send another message, be clear, full and concise.", "Central Command Quantum Messaging") as null|message) if(!input || !(usr in view(1,src))) return CentCom_announce(input, usr) - usr << "Message transmitted." + to_chat(usr, "Message transmitted.") log_game("[key_name(usr)] has made an IA [using_map.boss_short] announcement: [input]") centcomm_message_cooldown = 1 spawn(300)//10 minute cooldown @@ -203,20 +203,20 @@ if("MessageSyndicate") if((src.authenticated==2) && (src.emagged)) if(centcomm_message_cooldown) - usr << "Arrays recycling. Please stand by." + to_chat(usr, "Arrays recycling. Please stand by.") return var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "")) if(!input || !(usr in view(1,src))) return Syndicate_announce(input, usr) - usr << "Message transmitted." + to_chat(usr, "Message transmitted.") log_game("[key_name(usr)] has made an illegal announcement: [input]") centcomm_message_cooldown = 1 spawn(300)//10 minute cooldown centcomm_message_cooldown = 0 if("RestoreBackup") - usr << "Backup routing data restored!" + to_chat(usr, "Backup routing data restored!") src.emagged = 0 src.updateDialog() @@ -271,7 +271,7 @@ /obj/machinery/computer/communications/emag_act(var/remaining_charges, var/mob/user) if(!emagged) src.emagged = 1 - user << "You scramble the communication routing circuits!" + to_chat(user, "You scramble the communication routing circuits!") return 1 /obj/machinery/computer/communications/attack_ai(var/mob/user as mob) @@ -281,7 +281,7 @@ if(..()) return if (using_map && !(src.z in using_map.contact_levels)) - user << "Unable to establish a connection: You're too far away from the station!" + to_chat(user, "Unable to establish a connection: You're too far away from the station!") return user.set_machine(src) @@ -437,31 +437,31 @@ return if(!universe.OnShuttleCall(usr)) - user << "Cannot establish a bluespace connection." + to_chat(user, "Cannot establish a bluespace connection.") return if(deathsquad.deployed) - user << "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated." + to_chat(user, "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated.") return if(emergency_shuttle.deny_shuttle) - user << "The emergency shuttle may not be sent at this time. Please try again later." + to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") return if(world.time < 6000) // Ten minute grace period to let the game get going without lolmetagaming. -- TLE - user << "The emergency shuttle is refueling. Please wait another [round((6000-world.time)/600)] minute\s before trying again." + to_chat(user, "The emergency shuttle is refueling. Please wait another [round((6000-world.time)/600)] minute\s before trying again.") return if(emergency_shuttle.going_to_centcom()) - user << "The emergency shuttle may not be called while returning to [using_map.boss_short]." + to_chat(user, "The emergency shuttle may not be called while returning to [using_map.boss_short].") return if(emergency_shuttle.online()) - user << "The emergency shuttle is already on its way." + to_chat(user, "The emergency shuttle is already on its way.") return if(ticker.mode.name == "blob") - user << "Under directive 7-10, [station_name()] is quarantined until further notice." + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") return emergency_shuttle.call_evac() @@ -477,25 +477,25 @@ return if(emergency_shuttle.going_to_centcom()) - user << "The shuttle may not be called while returning to [using_map.boss_short]." + to_chat(user, "The shuttle may not be called while returning to [using_map.boss_short].") return if(emergency_shuttle.online()) - user << "The shuttle is already on its way." + to_chat(user, "The shuttle is already on its way.") return // if force is 0, some things may stop the shuttle call if(!force) if(emergency_shuttle.deny_shuttle) - user << "[using_map.boss_short] does not currently have a shuttle available in your sector. Please try again later." + to_chat(user, "[using_map.boss_short] does not currently have a shuttle available in your sector. Please try again later.") return if(deathsquad.deployed == 1) - user << "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated." + to_chat(user, "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated.") return if(world.time < 54000) // 30 minute grace period to let the game get going - user << "The shuttle is refueling. Please wait another [round((54000-world.time)/60)] minutes before trying again." + to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/60)] minutes before trying again.") return if(ticker.mode.auto_recall_shuttle) @@ -503,7 +503,7 @@ emergency_shuttle.auto_recall = 1 if(ticker.mode.name == "blob" || ticker.mode.name == "epidemic") - user << "Under directive 7-10, [station_name()] is quarantined until further notice." + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") return emergency_shuttle.call_transfer() @@ -546,7 +546,7 @@ var/datum/signal/status_signal = new status_signal.source = src - status_signal.transmission_method = 1 + status_signal.transmission_method = TRANSMISSION_RADIO status_signal.data["command"] = command switch(command) diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 40a7551739..49ba537a2d 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -1,10 +1,10 @@ /obj/machinery/computer name = "computer" - icon = 'icons/obj/computer_vr.dmi' + icon = 'icons/obj/computer.dmi' icon_state = "computer" density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 300 active_power_usage = 300 var/processing = 0 @@ -13,12 +13,10 @@ var/icon_screen = "generic" var/light_range_on = 2 var/light_power_on = 1 - var/overlay_layer clicksound = "keyboard" /obj/machinery/computer/New() - overlay_layer = layer ..() /obj/machinery/computer/Initialize() @@ -66,31 +64,28 @@ ex_act(2) /obj/machinery/computer/update_icon() - overlays.Cut() + cut_overlays() + // No power if(stat & NOPOWER) set_light(0) if(icon_keyboard) - overlays += image(icon,"[icon_keyboard]_off", overlay_layer) - return + add_overlay("[icon_keyboard]_off") + // Yes power else + if(icon_keyboard) + add_overlay(icon_keyboard) set_light(light_range_on, light_power_on) - if(stat & BROKEN) - overlays += image(icon,"[icon_state]_broken", overlay_layer) - else - overlays += image(icon,icon_screen, overlay_layer) - - if(icon_keyboard) - overlays += image(icon, icon_keyboard, overlay_layer) + // Broken + if(stat & BROKEN) + add_overlay("[icon_state]_broken") + // Not broken + else + add_overlay(icon_screen) /obj/machinery/computer/power_change() ..() update_icon() - if(stat & NOPOWER) - set_light(0) - else - set_light(light_range_on, light_power_on) - /obj/machinery/computer/proc/set_broken() stat |= BROKEN @@ -116,4 +111,4 @@ playsound(src, "keyboard", 100, 1, 0) return attack_hand(user) - return \ No newline at end of file + return diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 0c6c33928a..077c12c451 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -4,11 +4,11 @@ icon_keyboard = "med_key" icon_screen = "crew" light_color = "#315ab4" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 250 active_power_usage = 500 circuit = /obj/item/weapon/circuitboard/crew - var/datum/nano_module/crew_monitor/crew_monitor + var/datum/nano_module/program/crew_monitor/crew_monitor /obj/machinery/computer/crew/New() crew_monitor = new(src) diff --git a/code/game/machinery/computer/guestpass.dm b/code/game/machinery/computer/guestpass.dm index 194e696019..b4a3c8c3c1 100644 --- a/code/game/machinery/computer/guestpass.dm +++ b/code/game/machinery/computer/guestpass.dm @@ -21,22 +21,22 @@ /obj/item/weapon/card/id/guest/examine(mob/user) ..(user) if (world.time < expiration_time) - user << "This pass expires at [worldtime2stationtime(expiration_time)]." + to_chat(user, "This pass expires at [worldtime2stationtime(expiration_time)].") else - user << "It expired at [worldtime2stationtime(expiration_time)]." + to_chat(user, "It expired at [worldtime2stationtime(expiration_time)].") /obj/item/weapon/card/id/guest/read() if(!Adjacent(usr)) return //Too far to read if (world.time > expiration_time) - usr << "This pass expired at [worldtime2stationtime(expiration_time)]." + to_chat(usr, "This pass expired at [worldtime2stationtime(expiration_time)].") else - usr << "This pass expires at [worldtime2stationtime(expiration_time)]." + to_chat(usr, "This pass expires at [worldtime2stationtime(expiration_time)].") - usr << "It grants access to following areas:" + to_chat(usr, "It grants access to following areas:") for (var/A in temp_access) - usr << "[get_access_desc(A)]." - usr << "Issuing reason: [reason]." + to_chat(usr, "[get_access_desc(A)].") + to_chat(usr, "Issuing reason: [reason].") return /obj/item/weapon/card/id/guest/attack_self(mob/living/user as mob) @@ -108,7 +108,7 @@ giver = I SSnanoui.update_uis(src) else if(giver) - user << "There is already ID card inside." + to_chat(user, "There is already ID card inside.") return ..() @@ -182,7 +182,7 @@ if (dur > 0 && dur <= 120) duration = dur else - usr << "Invalid duration." + to_chat(usr, "Invalid duration.") if ("access") var/A = text2num(href_list["access"]) if (A in accesses) @@ -191,7 +191,7 @@ if(A in giver.access) //Let's make sure the ID card actually has the access. accesses.Add(A) else - usr << "Invalid selection, please consult technical support if there are any issues." + to_chat(usr, "Invalid selection, please consult technical support if there are any issues.") log_debug("[key_name_admin(usr)] tried selecting an invalid guest pass terminal option.") if (href_list["action"]) switch(href_list["action"]) @@ -216,7 +216,7 @@ var/dat = "

Activity log of guest pass terminal #[uid]


" for (var/entry in internal_log) dat += "[entry]

" - //usr << "Printing the log, standby..." + //to_chat(usr, "Printing the log, standby...") //sleep(50) var/obj/item/weapon/paper/P = new/obj/item/weapon/paper( loc ) P.name = "activity log" @@ -241,7 +241,7 @@ pass.reason = reason pass.name = "guest pass #[number]" else - usr << "Cannot issue pass without issuing ID." + to_chat(usr, "Cannot issue pass without issuing ID.") src.add_fingerprint(usr) SSnanoui.update_uis(src) \ No newline at end of file diff --git a/code/game/machinery/computer/id_restorer_vr.dm b/code/game/machinery/computer/id_restorer_vr.dm index 86ffaa88bb..7afabc5492 100644 --- a/code/game/machinery/computer/id_restorer_vr.dm +++ b/code/game/machinery/computer/id_restorer_vr.dm @@ -22,7 +22,7 @@ I.forceMove(src) inserted = I else if(inserted) - user << "There is already ID card inside." + to_chat(user, "There is already ID card inside.") return ..() diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 35d6f7e90b..79257147a0 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -26,20 +26,20 @@ if(!usr || usr.stat || usr.lying) return if(scan) - usr << "You remove \the [scan] from \the [src]." + to_chat(usr, "You remove \the [scan] from \the [src].") scan.loc = get_turf(src) if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) usr.put_in_hands(scan) scan = null else - usr << "There is nothing to remove from the console." + to_chat(usr, "There is nothing to remove from the console.") return /obj/machinery/computer/med_data/attackby(var/obj/item/O, var/mob/user) if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) O.loc = src scan = O - user << "You insert \the [O]." + to_chat(user, "You insert \the [O].") else ..() diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index 6fce75def7..2416a0cd35 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -38,7 +38,7 @@ return if(O.is_screwdriver() && emag) //Stops people from just unscrewing the monitor and putting it back to get the console working again. - user << "It is too hot to mess with!" + to_chat(user, "It is too hot to mess with!") return ..() @@ -62,7 +62,7 @@ update_icon() return 1 else - user << "A no server error appears on the screen." + to_chat(user, "A no server error appears on the screen.") /obj/machinery/computer/message_monitor/update_icon() if(emag || hacking) @@ -263,10 +263,10 @@ /obj/machinery/computer/message_monitor/proc/BruteForce(mob/user as mob) if(isnull(linkedServer)) - user << "Could not complete brute-force: Linked Server Disconnected!" + to_chat(user, "Could not complete brute-force: Linked Server Disconnected!") else var/currentKey = src.linkedServer.decryptkey - user << "Brute-force completed! The key is '[currentKey]'." + to_chat(user, "Brute-force completed! The key is '[currentKey]'.") src.hacking = 0 update_icon() src.screen = 0 // Return the screen back to normal @@ -476,7 +476,7 @@ if(auth) src.screen = 4 - //usr << href_list["select"] + //to_chat(usr,href_list["select"]) if(href_list["spam"]) if(src.linkedServer == null || (src.linkedServer.stat & (NOPOWER|BROKEN))) @@ -503,20 +503,18 @@ return src.attack_hand(usr) - /obj/item/weapon/paper/monitorkey - //..() name = "Monitor Decryption Key" - var/obj/machinery/message_server/server = null -/obj/item/weapon/paper/monitorkey/New() - ..() - spawn(10) - if(message_servers) - for(var/obj/machinery/message_server/server in message_servers) - if(!isnull(server)) - if(!isnull(server.decryptkey)) - info = "

Daily Key Reset


The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one." - info_links = info - icon_state = "paper_words" - break +/obj/item/weapon/paper/monitorkey/Initialize() + ..() //Late init + return INITIALIZE_HINT_LATELOAD + +/obj/item/weapon/paper/monitorkey/LateInitialize() + if(message_servers) + for(var/obj/machinery/message_server/server in message_servers) + if(!isnull(server.decryptkey)) + info = "

Daily Key Reset


The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one." + info_links = info + icon_state = "paper_words" + break diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm index 1d210787de..441970a8cb 100644 --- a/code/game/machinery/computer/pod.dm +++ b/code/game/machinery/computer/pod.dm @@ -12,24 +12,22 @@ var/time = 30.0 var/title = "Mass Driver Controls" +/obj/machinery/computer/pod/Initialize() + ..() //Not returning parent because lateload + return INITIALIZE_HINT_LATELOAD -/obj/machinery/computer/pod/New() - ..() - spawn( 5 ) - for(var/obj/machinery/mass_driver/M in machines) - if(M.id == id) - connected = M - else - return - return - +/obj/machinery/computer/pod/LateInitialize() + for(var/obj/machinery/mass_driver/M in machines) + if(M.id == id) + connected = M + break /obj/machinery/computer/pod/proc/alarm() if(stat & (NOPOWER|BROKEN)) return if(!( connected )) - viewers(null, null) << "Cannot locate mass driver connector. Cancelling firing sequence!" + to_chat(viewers(null, null),"Cannot locate mass driver connector. Cancelling firing sequence!") return for(var/obj/machinery/door/blast/M in machines) @@ -80,7 +78,7 @@ A.anchored = 1 qdel(src) else - to_chat(user << "You disconnect the monitor.") + to_chat(to_chat(user, "You disconnect the monitor.")) var/obj/structure/computerframe/A = new /obj/structure/computerframe( loc ) //generate appropriate circuitboard. Accounts for /pod/old computer types diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index 54006344f5..9e09339f47 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -89,7 +89,7 @@ if(src.allowed(usr)) screen = !screen else - usr << "Unauthorized Access." + to_chat(usr, "Unauthorized Access.") else if(href_list["warn"]) var/warning = sanitize(input(usr,"Message:","Enter your message here!","")) @@ -97,7 +97,7 @@ var/obj/item/weapon/implant/I = locate(href_list["warn"]) if((I)&&(I.imp_in)) var/mob/living/carbon/R = I.imp_in - R << "You hear a voice in your head saying: '[warning]'" + to_chat(R, "You hear a voice in your head saying: '[warning]'") src.add_fingerprint(usr) src.updateUsrDialog() diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm index 57a5f1b6ea..1d58a61df1 100644 --- a/code/game/machinery/computer/prisonshuttle.dm +++ b/code/game/machinery/computer/prisonshuttle.dm @@ -28,10 +28,10 @@ var/prison_shuttle_timeleft = 0 attack_hand(var/mob/user as mob) if(!src.allowed(user) && (!hacked)) - user << "Access Denied." + to_chat(user, "Access Denied.") return if(prison_break) - user << "Unable to locate shuttle." + to_chat(user, "Unable to locate shuttle.") return if(..()) return @@ -60,11 +60,11 @@ var/prison_shuttle_timeleft = 0 if (href_list["sendtodock"]) if (!prison_can_move()) - usr << "The prison shuttle is unable to leave." + to_chat(usr, "The prison shuttle is unable to leave.") return if(!prison_shuttle_at_station|| prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return post_signal("prison") - usr << "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds." + to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") src.temp += "Shuttle sent.

OK" src.updateUsrDialog() prison_shuttle_moving_to_prison = 1 @@ -74,11 +74,11 @@ var/prison_shuttle_timeleft = 0 else if (href_list["sendtostation"]) if (!prison_can_move()) - usr << "The prison shuttle is unable to leave." + to_chat(usr, "The prison shuttle is unable to leave.") return if(prison_shuttle_at_station || prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return post_signal("prison") - usr << "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds." + to_chat(usr, "The prison shuttle has been called and will arrive in [(PRISON_MOVETIME/10)] seconds.") src.temp += "Shuttle sent.

OK" src.updateUsrDialog() prison_shuttle_moving_to_station = 1 @@ -121,7 +121,7 @@ var/prison_shuttle_timeleft = 0 if(!frequency) return var/datum/signal/status_signal = new status_signal.source = src - status_signal.transmission_method = 1 + status_signal.transmission_method = TRANSMISSION_RADIO status_signal.data["command"] = command frequency.post_signal(src, status_signal) return @@ -146,7 +146,7 @@ var/prison_shuttle_timeleft = 0 if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return if (!prison_can_move()) - usr << "The prison shuttle is unable to leave." + to_chat(usr, "The prison shuttle is unable to leave.") return var/area/start_location = locate(/area/shuttle/prison/prison) @@ -175,7 +175,7 @@ var/prison_shuttle_timeleft = 0 if (prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return if (!prison_can_move()) - usr << "The prison shuttle is unable to leave." + to_chat(usr, "The prison shuttle is unable to leave.") return var/area/start_location = locate(/area/shuttle/prison/station) @@ -211,5 +211,5 @@ var/prison_shuttle_timeleft = 0 /obj/machinery/computer/prison_shuttle/emag_act(var/charges, var/mob/user) if(!hacked) hacked = 1 - user << "You disable the lock." + to_chat(user, "You disable the lock.") return 1 diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 3e06e19e52..2767237963 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -31,7 +31,7 @@ return var/mob/user = usr if(!src.allowed(user)) - user << "Access Denied" + to_chat(user, "Access Denied") return // Locks or unlocks the cyborg @@ -42,11 +42,11 @@ return if(isAI(user) && (target.connected_ai != user)) - user << "Access Denied. This robot is not linked to you." + to_chat(user, "Access Denied. This robot is not linked to you.") return if(isrobot(user)) - user << "Access Denied." + to_chat(user, "Access Denied.") return var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") @@ -76,9 +76,9 @@ target.lockcharge = !target.canmove //when canmove is 1, lockcharge should be 0 target.lockdown = !target.canmove if (target.lockcharge) - target << "You have been locked down!" + to_chat(target, "You have been locked down!") else - target << "Your lockdown has been lifted!" + to_chat(target, "Your lockdown has been lifted!") message_admins("[key_name_admin(usr)] [failmsg][target.lockcharge ? "lockdown" : "release"] on [target.name]!") log_game("[key_name(usr)] attempted to [target.lockcharge ? "lockdown" : "release"] [target.name] on the robotics console!") @@ -91,11 +91,11 @@ // Antag synthetic checks if(!istype(user, /mob/living/silicon) || !(user.mind.special_role && user.mind.original == user)) - user << "Access Denied" + to_chat(user, "Access Denied") return if(target.emagged) - user << "Robot is already hacked." + to_chat(user, "Robot is already hacked.") return var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No") @@ -108,7 +108,7 @@ message_admins("[key_name_admin(usr)] emagged [target.name] using the robotic console!") log_game("[key_name(usr)] emagged [target.name] using robotic console!") target.emagged = 1 - target << "Failsafe protocols overriden. New tools available." + to_chat(target, "Failsafe protocols overriden. New tools available.") // Proc: get_cyborgs() diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 1179d3a2b2..2d9353a640 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -32,13 +32,13 @@ if(!usr || usr.stat || usr.lying) return if(scan) - usr << "You remove \the [scan] from \the [src]." + to_chat(usr, "You remove \the [scan] from \the [src].") scan.loc = get_turf(src) if(!usr.get_active_hand() && istype(usr,/mob/living/carbon/human)) usr.put_in_hands(scan) scan = null else - usr << "There is nothing to remove from the console." + to_chat(usr, "There is nothing to remove from the console.") return /obj/machinery/computer/secure_data/attackby(obj/item/O as obj, user as mob) @@ -46,7 +46,7 @@ usr.drop_item() O.loc = src scan = O - user << "You insert [O]." + to_chat(user, "You insert [O].") ..() /obj/machinery/computer/secure_data/attack_ai(mob/user as mob) @@ -57,7 +57,7 @@ if(..()) return if (using_map && !(src.z in using_map.contact_levels)) - user << "Unable to establish a connection: You're too far away from the station!" + to_chat(user, "Unable to establish a connection: You're too far away from the station!") return var/dat diff --git a/code/game/machinery/computer/shutoff_monitor.dm b/code/game/machinery/computer/shutoff_monitor.dm index 4dd79376c9..aed7bd25b9 100644 --- a/code/game/machinery/computer/shutoff_monitor.dm +++ b/code/game/machinery/computer/shutoff_monitor.dm @@ -21,7 +21,7 @@ var/data[0] data["valves"] = list() for(var/obj/machinery/atmospherics/valve/shutoff/S in GLOB.shutoff_valves) - data["valves"][++data["valves"].len] = list("name" = S.name, "enable" = S.close_on_leaks, "open" = S.open, "x" = S.x, "y" = S.y, "z" = S.z) + data["valves"][++data["valves"].len] = list("name" = S.name, "enable" = S.close_on_leaks, "open" = S.open, "x" = S.x, "y" = S.y, "z" = S.z, "ref" = "\ref[S]") // update the ui if it exists, returns null if no ui is passed/found ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -36,7 +36,35 @@ // auto update every 20 Master Controller tick ui.set_auto_update(20) // Longer term to reduce the rate of data collection and processing +/obj/machinery/computer/shutoff_monitor/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["toggle_enable"]) + var/obj/machinery/atmospherics/valve/shutoff/S = locate(href_list["toggle_enable"]) + + // Invalid ref + if(!istype(S)) + return 0 + + S.close_on_leaks = !S.close_on_leaks + + if(href_list["toggle_open"]) + var/obj/machinery/atmospherics/valve/shutoff/S = locate(href_list["toggle_open"]) + + // Invalid ref + if(!istype(S)) + return 0 + + if(S.open) + S.close() + else + S.open() + + return + + /obj/machinery/computer/shutoff_monitor/update_icon() ..() if(!(stat & (NOPOWER|BROKEN))) - overlays += image('icons/obj/computer.dmi', "ai-fixer-empty", overlay_layer) + add_overlay("ai-fixer-empty") diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm index 18729f2ef6..1ffe2d6570 100644 --- a/code/game/machinery/computer/shuttle.dm +++ b/code/game/machinery/computer/shuttle.dm @@ -16,16 +16,16 @@ var/obj/item/device/pda/pda = W W = pda.id if (!W:access) //no access - user << "The access level of [W:registered_name]\'s card is not high enough. " + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") return var/list/cardaccess = W:access if(!istype(cardaccess, /list) || !cardaccess.len) //no access - user << "The access level of [W:registered_name]\'s card is not high enough. " + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") return if(!(access_heads in W:access)) //doesn't have this access - user << "The access level of [W:registered_name]\'s card is not high enough. " + to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") return 0 var/choice = alert(user, text("Would you like to (un)authorize a shortened launch time? [] authorization\s are still needed. Use abort to cancel all authorizations.", src.auth_need - src.authorized.len), "Shuttle Launch", "Authorize", "Repeal", "Abort") @@ -38,11 +38,11 @@ if (src.auth_need - src.authorized.len > 0) message_admins("[key_name_admin(user)] has authorized early shuttle launch") log_game("[user.ckey] has authorized early shuttle launch") - world << text("Alert: [] authorizations needed until shuttle is launched early", src.auth_need - src.authorized.len) + to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") else message_admins("[key_name_admin(user)] has launched the shuttle") log_game("[user.ckey] has launched the shuttle early") - world << "Alert: Shuttle launch time shortened to 10 seconds!" + to_world("Alert: Shuttle launch time shortened to 10 seconds!") emergency_shuttle.set_launch_countdown(10) //src.authorized = null qdel(src.authorized) @@ -50,10 +50,10 @@ if("Repeal") src.authorized -= W:registered_name - world << text("Alert: [] authorizations needed until shuttle is launched early", src.auth_need - src.authorized.len) + to_world("Alert: [src.auth_need - src.authorized.len] authorizations needed until shuttle is launched early") if("Abort") - world << "All authorizations to shortening time for shuttle launch have been revoked!" + to_world("All authorizations to shortening time for shuttle launch have been revoked!") src.authorized.len = 0 src.authorized = list( ) @@ -63,7 +63,7 @@ if(!emagged && !emergency_shuttle.location() && user.get_active_hand() == W) switch(choice) if("Launch") - world << "Alert: Shuttle launch time shortened to 10 seconds!" + to_world("Alert: Shuttle launch time shortened to 10 seconds!") emergency_shuttle.set_launch_countdown(10) emagged = 1 if("Cancel") diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm index a374d48c8c..1a64db7389 100644 --- a/code/game/machinery/computer/skills.dm +++ b/code/game/machinery/computer/skills.dm @@ -29,7 +29,7 @@ if(istype(O, /obj/item/weapon/card/id) && !scan && user.unEquip(O)) O.loc = src scan = O - user << "You insert [O]." + to_chat(user, "You insert [O].") else ..() @@ -41,7 +41,7 @@ if(..()) return if (using_map && !(src.z in using_map.contact_levels)) - user << "Unable to establish a connection: You're too far away from the station!" + to_chat(user, "Unable to establish a connection: You're too far away from the station!") return var/dat @@ -91,19 +91,28 @@ var/icon/side = active1.fields["photo_side"] user << browse_rsc(front, "front.png") user << browse_rsc(side, "side.png") - dat += text(" \ -
\ - Name: [active1.fields["name"]]
\ - ID: [active1.fields["id"]]
\n \ - Entity Classification: [active1.fields["brain_type"]]
\n \ - Sex: [active1.fields["sex"]]
\n \ - Age: [active1.fields["age"]]
\n \ - Rank: [active1.fields["rank"]]
\n \ - Fingerprint: [active1.fields["fingerprint"]]
\n \ - Physical Status: [active1.fields["p_stat"]]
\n \ - Mental Status: [active1.fields["m_stat"]]

\n \ - Employment/skills summary:
[decode(active1.fields["notes"])]
Photo:
\ -
") + dat += "" + + dat += "
" + dat += "Name: [active1.fields["name"]]
" + dat += "ID: [active1.fields["id"]]
\n" + dat += "Entity Classification: [active1.fields["brain_type"]]
\n" + dat += "Sex: [active1.fields["sex"]]
\n" + dat += "Age: [active1.fields["age"]]
\n" + dat += "Rank: [active1.fields["rank"]]
\n" + dat += "Fingerprint: [active1.fields["fingerprint"]]
\n" + dat += "Physical Status: [active1.fields["p_stat"]]
\n" + dat += "Mental Status: [active1.fields["m_stat"]]

\n" + + dat += "Employment/skills summary:
" + dat += decode(active1.fields["notes"]) + dat += "


" + var/counter = 1 + while(src.active1.fields[text("com_[]", counter)]) + dat += text("[]
Delete Entry

", src.active1.fields[text("com_[]", counter)], src, counter) + counter++ + dat += "Add Entry

Photo:
" + dat += "
" else dat += "General Record Lost!
" dat += text("\nDelete Record (ALL)

\nPrint Record
\nBack
", src, src, src) @@ -285,6 +294,25 @@ What a mess.*/ P.name = "Employment Record (Unknown/Invald Entry)" log_debug("[usr] ([usr.ckey]) attempted to print a null employee record, this should be investigated.") printing = null +// Add comment + if ("add_c") + if (!( istype(src.active1, /datum/data/record) )) + return + var/a1 = src.active1 + var/t1 = sanitize(input("Add Comment:", "Emp. records", null, null) as message) + if ((!( t1 ) || !( src.authenticated ) || usr.stat || usr.restrained() || (!in_range(src, usr) && (!istype(usr, /mob/living/silicon))) || src.active1 != a1)) + return + var/counter = 1 + while(src.active1.fields[text("com_[]", counter)]) + counter++ + src.active1.fields[text("com_[counter]")] = text("Made by [authenticated] ([rank]) on [time2text(world.realtime, "DDD MMM DD")] [stationtime2text()], [game_year]
[t1]") +// Delete comment + if ("del_c") + var/target = href_list["del_c"] + if (istype(src.active1, /datum/data/record) && src.active1.fields["com_[target]"]) + src.active1.fields["com_[target]"] = "Deleted" + + //RECORD DELETE if ("Delete All Records") temp = "" diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index 431d6278d6..ce0bea0aff 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -88,7 +88,7 @@ var/specops_shuttle_timeleft = 0 for(var/turf/T in get_area_turfs(end_location) ) var/mob/M = locate(/mob) in T - M << "You have arrived at [using_map.boss_name]. Operation has ended!" + to_chat(M, "You have arrived at [using_map.boss_name]. Operation has ended!") specops_shuttle_at_station = 0 @@ -136,7 +136,7 @@ var/specops_shuttle_timeleft = 0 if (specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return if (!specops_can_move()) - usr << "The Special Operations shuttle is unable to leave." + to_chat(usr, "The Special Operations shuttle is unable to leave.") return //Begin Marauder launchpad. @@ -231,7 +231,7 @@ var/specops_shuttle_timeleft = 0 for(var/turf/T in get_area_turfs(end_location) ) var/mob/M = locate(/mob) in T - M << "You have arrived to [station_name()]. Commence operation!" + to_chat(M, "You have arrived to [station_name()]. Commence operation!") for(var/obj/machinery/computer/specops_shuttle/S in machines) S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY @@ -250,11 +250,11 @@ var/specops_shuttle_timeleft = 0 return attack_hand(user) /obj/machinery/computer/specops_shuttle/emag_act(var/remaining_charges, var/mob/user) - user << "The electronic systems in this console are far too advanced for your primitive hacking peripherals." + to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") /obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) if(!allowed(user)) - user << "Access Denied." + to_chat(user, "Access Denied.") return if(..()) @@ -285,14 +285,14 @@ var/specops_shuttle_timeleft = 0 if(!specops_shuttle_at_station|| specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return if (!specops_can_move()) - usr << "[using_map.boss_name] will not allow the Special Operations shuttle to return yet." + to_chat(usr, "[using_map.boss_name] will not allow the Special Operations shuttle to return yet.") if(world.timeofday <= specops_shuttle_timereset) if (((world.timeofday - specops_shuttle_timereset)/10) > 60) - usr << "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!" - usr << "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!" + to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") + to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") return - usr << "The Special Operations shuttle will arrive at [using_map.boss_name] in [(SPECOPS_MOVETIME/10)] seconds." + to_chat(usr, "The Special Operations shuttle will arrive at [using_map.boss_name] in [(SPECOPS_MOVETIME/10)] seconds.") temp += "Shuttle departing.

OK" updateUsrDialog() @@ -306,10 +306,10 @@ var/specops_shuttle_timeleft = 0 if(specops_shuttle_at_station || specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return if (!specops_can_move()) - usr << "The Special Operations shuttle is unable to leave." + to_chat(usr, "The Special Operations shuttle is unable to leave.") return - usr << "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds." + to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") temp += "Shuttle departing.

OK" updateUsrDialog() diff --git a/code/game/machinery/computer/supply.dm b/code/game/machinery/computer/supply.dm index 9e6faf69f0..a310b33f78 100644 --- a/code/game/machinery/computer/supply.dm +++ b/code/game/machinery/computer/supply.dm @@ -56,7 +56,7 @@ var/orders[0] var/receipts[0] - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle if(shuttle) if(shuttle.has_arrive_time()) shuttle_status["location"] = "In transit" @@ -66,8 +66,8 @@ else shuttle_status["time"] = 0 if(shuttle.at_station()) - if(shuttle.docking_controller) - switch(shuttle.docking_controller.get_docking_status()) + if(shuttle.shuttle_docking_controller) + switch(shuttle.shuttle_docking_controller.get_docking_status()) if("docked") shuttle_status["location"] = "Docked" shuttle_status["mode"] = SUP_SHUTTLE_DOCKED @@ -109,8 +109,8 @@ else shuttle["mode"] = SUP_SHUTTLE_ERROR - for(var/pack_name in supply_controller.supply_pack) - var/datum/supply_pack/P = supply_controller.supply_pack[pack_name] + for(var/pack_name in SSsupply.supply_pack) + var/datum/supply_pack/P = SSsupply.supply_pack[pack_name] if(P.group == active_category) var/list/pack = list( "name" = P.name, @@ -131,7 +131,7 @@ // Status determines which menus the entry will display in // Organized in field-entry list for iterative display // List is nested so both the list of orders, and the list of elements in each order, can be iterated over - for(var/datum/supply_order/S in supply_controller.order_history) + for(var/datum/supply_order/S in SSsupply.order_history) orders[++orders.len] = list( "ref" = "\ref[S]", "status" = S.status, @@ -148,7 +148,7 @@ ) // Compile exported crates - for(var/datum/exported_crate/E in supply_controller.exported_crates) + for(var/datum/exported_crate/E in SSsupply.exported_crates) receipts[++receipts.len] = list( "ref" = "\ref[E]", "contents" = E.contents, @@ -164,7 +164,7 @@ data["shuttle_auth"] = (authorization & SUP_SEND_SHUTTLE) // Whether this ui is permitted to control the supply shuttle data["order_auth"] = (authorization & SUP_ACCEPT_ORDERS) // Whether this ui is permitted to accept/deny requested orders data["shuttle"] = shuttle_status - data["supply_points"] = supply_controller.points + data["supply_points"] = SSsupply.points data["categories"] = all_supply_groups data["active_category"] = active_category data["supply_packs"] = pack_list @@ -189,12 +189,12 @@ /obj/machinery/computer/supplycomp/Topic(href, href_list) - if(!supply_controller) - world.log << "## ERROR: The supply_controller datum is missing." + if(!SSsupply) + to_world_log("## ERROR: The SSsupply datum is missing.") return - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle if (!shuttle) - world.log << "## ERROR: The supply shuttle datum is missing." + to_world_log("## ERROR: The supply shuttle datum is missing.") return if(..()) return 1 @@ -240,7 +240,7 @@ if(!reason) return - supply_controller.create_order(S, user, reason) + SSsupply.create_order(S, user, reason) var/idname = "*None Provided*" var/idrank = "*None Provided*" @@ -255,7 +255,7 @@ var/obj/item/weapon/paper/reqform = new /obj/item/weapon/paper(loc) reqform.name = "Requisition Form - [S.name]" reqform.info += "

[station_name()] Supply Requisition Form


" - reqform.info += "INDEX: #[supply_controller.ordernum]
" + reqform.info += "INDEX: #[SSsupply.ordernum]
" reqform.info += "REQUESTED BY: [idname]
" reqform.info += "RANK: [idrank]
" reqform.info += "REASON: [reason]
" @@ -315,20 +315,20 @@ O.approved_at = new_val if(href_list["approve"]) - supply_controller.approve_order(O, user) + SSsupply.approve_order(O, user) if(href_list["deny"]) - supply_controller.deny_order(O, user) + SSsupply.deny_order(O, user) if(href_list["delete"]) - supply_controller.delete_order(O, user) + SSsupply.delete_order(O, user) if(href_list["clear_all_requests"]) var/mob/user = locate(href_list["user"]) if(!istype(user)) // Invalid ref return - supply_controller.deny_all_pending(user) + SSsupply.deny_all_pending(user) if(href_list["export_ref"]) var/datum/exported_crate/E = locate(href_list["export_ref"]) @@ -384,10 +384,10 @@ E.value = num else if(href_list["delete"]) - supply_controller.delete_export(E, user) + SSsupply.delete_export(E, user) else if(href_list["add_item"]) - supply_controller.add_export_item(E, user) + SSsupply.add_export_item(E, user) @@ -401,7 +401,7 @@ if("send_to_station") shuttle.launch(src) - to_chat(usr, "The supply shuttle has been called and will arrive in approximately [round(supply_controller.movetime/600,1)] minutes.") + to_chat(usr, "The supply shuttle has been called and will arrive in approximately [round(SSsupply.movetime/600,1)] minutes.") if("cancel_shuttle") shuttle.cancel_launch(src) @@ -421,7 +421,7 @@ var/datum/signal/status_signal = new status_signal.source = src - status_signal.transmission_method = 1 + status_signal.transmission_method = TRANSMISSION_RADIO status_signal.data["command"] = command frequency.post_signal(src, status_signal) diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index e02ab68474..dcf32f9caa 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -59,7 +59,7 @@ var/syndicate_elite_shuttle_timeleft = 0 if (syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return if (!syndicate_elite_can_move()) - usr << "The Syndicate Elite shuttle is unable to leave." + to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") return sleep(600) @@ -173,7 +173,7 @@ var/syndicate_elite_shuttle_timeleft = 0 for(var/turf/T in get_area_turfs(end_location) ) var/mob/M = locate(/mob) in T - M << "You have arrived to [station_name()]. Commence operation!" + to_chat(M, "You have arrived to [station_name()]. Commence operation!") /proc/syndicate_elite_can_move() if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return 0 @@ -186,15 +186,15 @@ var/syndicate_elite_shuttle_timeleft = 0 return attack_hand(user) /obj/machinery/computer/syndicate_elite_shuttle/emag_act(var/remaining_charges, var/mob/user) - user << "The electronic systems in this console are far too advanced for your primitive hacking peripherals." + to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") /obj/machinery/computer/syndicate_elite_shuttle/attack_hand(var/mob/user as mob) if(!allowed(user)) - user << "Access Denied." + to_chat(user, "Access Denied.") return // if (sent_syndicate_strike_team == 0) -// usr << "The strike team has not yet deployed." +// to_chat(usr, "The strike team has not yet deployed.") // return if(..()) @@ -224,17 +224,17 @@ var/syndicate_elite_shuttle_timeleft = 0 if (href_list["sendtodock"]) if(!syndicate_elite_shuttle_at_station|| syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - usr << "The Syndicate will not allow the Elite Squad shuttle to return." + to_chat(usr, "The Syndicate will not allow the Elite Squad shuttle to return.") return else if (href_list["sendtostation"]) if(syndicate_elite_shuttle_at_station || syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return if (!specops_can_move()) - usr << "The Syndicate Elite shuttle is unable to leave." + to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") return - usr << "The Syndicate Elite shuttle will arrive on [station_name()] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds." + to_chat(usr, "The Syndicate Elite shuttle will arrive on [station_name()] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.") temp = "Shuttle departing.

OK" updateUsrDialog() diff --git a/code/game/machinery/computer/timeclock_vr.dm b/code/game/machinery/computer/timeclock_vr.dm index 4969e89334..ad118c74d7 100644 --- a/code/game/machinery/computer/timeclock_vr.dm +++ b/code/game/machinery/computer/timeclock_vr.dm @@ -16,6 +16,7 @@ density = FALSE circuit = /obj/item/weapon/circuitboard/timeclock clicksound = null + var/channel = "Common" //Radio channel to announce on var/obj/item/weapon/card/id/card // Inserted Id card var/obj/item/device/radio/intercom/announce // Integreated announcer @@ -84,15 +85,16 @@ if (job) data["job_datum"] = list( "title" = job.title, - "department" = job.department, + "departments" = english_list(job.departments), "selection_color" = job.selection_color, "economic_modifier" = job.economic_modifier, - "timeoff_factor" = job.timeoff_factor + "timeoff_factor" = job.timeoff_factor, + "pto_department" = job.pto_type ) if(config.time_off && config.pto_job_change) data["allow_change_job"] = TRUE if(job && job.timeoff_factor < 0) // Currently are Off Duty, so gotta lookup what on-duty jobs are open - data["job_choices"] = getOpenOnDutyJobs(user, job.department) + data["job_choices"] = getOpenOnDutyJobs(user, job.pto_type) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -117,22 +119,20 @@ card = I update_icon() return 1 - if(href_list["switch-to-onduty"]) - if(card) - if(checkFace()) - if(checkCardCooldown()) - makeOnDuty(href_list["switch-to-onduty"]) - usr.put_in_hands(card) - card = null + if(href_list["switch-to-onduty-rank"]) + if(checkFace()) + if(checkCardCooldown()) + makeOnDuty(href_list["switch-to-onduty-rank"], href_list["switch-to-onduty-assignment"]) + usr.put_in_hands(card) + card = null update_icon() return 1 if(href_list["switch-to-offduty"]) - if(card) - if(checkFace()) - if(checkCardCooldown()) - makeOffDuty() - usr.put_in_hands(card) - card = null + if(checkFace()) + if(checkCardCooldown()) + makeOffDuty() + usr.put_in_hands(card) + card = null update_icon() return 1 return 1 // Return 1 to update UI @@ -140,57 +140,57 @@ /obj/machinery/computer/timeclock/proc/getOpenOnDutyJobs(var/mob/user, var/department) var/list/available_jobs = list() for(var/datum/job/job in job_master.occupations) - if(job && job.is_position_available() && !job.whitelist_only && !jobban_isbanned(user,job.title) && job.player_old_enough(user.client)) - if(job.department == department && !job.disallow_jobhop && job.timeoff_factor > 0) - available_jobs += job.title - if(job.alt_titles) - for(var/alt_job in job.alt_titles) - available_jobs += alt_job + if(isOpenOnDutyJob(user, department, job)) + available_jobs[job.title] = list(job.title) + if(job.alt_titles) + for(var/alt_job in job.alt_titles) + if(alt_job != job.title) + available_jobs[job.title] += alt_job return available_jobs -/obj/machinery/computer/timeclock/proc/makeOnDuty(var/newjob) - var/datum/job/foundjob = null - for(var/datum/job/job in job_master.occupations) - if(newjob == job.title) - foundjob = job - break - if(newjob in job.alt_titles) - foundjob = job - break - if(!newjob in getOpenOnDutyJobs(usr, job_master.GetJob(card.rank).department)) +/obj/machinery/computer/timeclock/proc/isOpenOnDutyJob(var/mob/user, var/department, var/datum/job/job) + return job \ + && job.is_position_available() \ + && !job.whitelist_only \ + && !jobban_isbanned(user,job.title) \ + && job.player_old_enough(user.client) \ + && job.pto_type == department \ + && !job.disallow_jobhop \ + && job.timeoff_factor > 0 + +/obj/machinery/computer/timeclock/proc/makeOnDuty(var/newrank, var/newassignment) + var/datum/job/oldjob = job_master.GetJob(card.rank) + var/datum/job/newjob = job_master.GetJob(newrank) + if(!oldjob || !isOpenOnDutyJob(usr, oldjob.pto_type, newjob)) return - if(foundjob && card) - card.access = foundjob.get_access() - card.rank = foundjob.title - card.assignment = newjob + if(newassignment != newjob.title && !(newassignment in newjob.alt_titles)) + return + if(newjob) + card.access = newjob.get_access() + card.rank = newjob.title + card.assignment = newassignment card.name = text("[card.registered_name]'s ID Card ([card.assignment])") data_core.manifest_modify(card.registered_name, card.assignment) card.last_job_switch = world.time callHook("reassign_employee", list(card)) - foundjob.current_positions++ + newjob.current_positions++ var/mob/living/carbon/human/H = usr - H.mind.assigned_role = foundjob.title - H.mind.role_alt_title = newjob - announce.autosay("[card.registered_name] has moved On-Duty as [card.assignment].", "Employee Oversight") + H.mind.assigned_role = card.rank + H.mind.role_alt_title = card.assignment + announce.autosay("[card.registered_name] has moved On-Duty as [card.assignment].", "Employee Oversight", channel, zlevels = using_map.get_map_levels(get_z(src))) return /obj/machinery/computer/timeclock/proc/makeOffDuty() - var/datum/job/foundjob = null - for(var/datum/job/job in job_master.occupations) - if(card.rank == job.title) - foundjob = job - break + var/datum/job/foundjob = job_master.GetJob(card.rank) if(!foundjob) return - var/real_dept = foundjob.department - if(real_dept && real_dept == "Command") - real_dept = "Civilian" + var/new_dept = foundjob.pto_type || PTO_CIVILIAN var/datum/job/ptojob = null for(var/datum/job/job in job_master.occupations) - if(job.department == real_dept && job.timeoff_factor < 0) + if(job.pto_type == new_dept && job.timeoff_factor < 0) ptojob = job break - if(ptojob && card) + if(ptojob) var/oldtitle = card.assignment card.access = ptojob.get_access() card.rank = ptojob.title @@ -203,7 +203,7 @@ H.mind.assigned_role = ptojob.title H.mind.role_alt_title = ptojob.title foundjob.current_positions-- - announce.autosay("[card.registered_name], [oldtitle], has moved Off-Duty.", "Employee Oversight") + announce.autosay("[card.registered_name], [oldtitle], has moved Off-Duty.", "Employee Oversight", channel, zlevels = using_map.get_map_levels(get_z(src))) return /obj/machinery/computer/timeclock/proc/checkCardCooldown() diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index e74ffb7cf9..aa9c230e22 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -7,7 +7,7 @@ icon_state = "box_0" density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF var/obj/item/weapon/circuitboard/circuit = null var/list/components = null var/list/req_components = null diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index fb4c9b7923..9db22b9270 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -11,11 +11,13 @@ interact_offline = 1 var/on = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 200 buckle_lying = FALSE buckle_dir = SOUTH + clicksound = 'sound/machines/buttonbeep.ogg' + clickvol = 30 var/temperature_archived var/mob/living/carbon/occupant = null @@ -230,7 +232,7 @@ return occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise - occupant.stat = UNCONSCIOUS + occupant.set_stat(UNCONSCIOUS) occupant.dir = SOUTH if(occupant.bodytemperature < T0C) occupant.sleeping = max(5, (1/occupant.bodytemperature)*2000) @@ -289,7 +291,7 @@ unbuckle_mob(occupant, force = TRUE) occupant = null current_heat_capacity = initial(current_heat_capacity) - update_use_power(1) + update_use_power(USE_POWER_IDLE) return /obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) if(stat & (NOPOWER|BROKEN)) @@ -320,7 +322,7 @@ vis_contents |= occupant occupant.pixel_y += 19 current_heat_capacity = HEAT_CAPACITY_HUMAN - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) // M.metabslow = 1 add_fingerprint(usr) update_icon() diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 70aa44bf6f..bdd2b05fc6 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -97,7 +97,7 @@ //dat += "Recover object.
" //VOREStation Removal - Just log them. //dat += "Recover all objects.
" //VOREStation Removal - to_chat(user, browse(dat, "window=cryopod_console")) + user << browse(dat, "window=cryopod_console") onclose(user, "cryopod_console") /obj/machinery/computer/cryopod/Topic(href, href_list) @@ -116,7 +116,7 @@ dat += "[person]
" dat += "
" - to_chat(user, browse(dat, "window=cryolog")) + user << browse(dat, "window=cryolog") if(href_list["view"]) if(!allow_items) return @@ -128,7 +128,7 @@ //VOREStation Edit End dat += "
" - to_chat(user, browse(dat, "window=cryoitems")) + user << browse(dat, "window=cryoitems") else if(href_list["item"]) if(!allow_items) return @@ -219,6 +219,7 @@ var/on_enter_occupant_message = "You feel cool air surround you. You go numb as your senses turn inward." var/on_store_visible_message_1 = "hums and hisses as it moves" //We need two variables because byond doesn't let us have variables inside strings at compile-time. var/on_store_visible_message_2 = "into storage." + var/announce_channel = "Common" var/allow_occupant_types = list(/mob/living/carbon/human) var/disallow_occupant_types = list() @@ -424,6 +425,16 @@ items -= announce // or the autosay radio. for(var/obj/item/W in items) + //VOREStation Addition Start + if(istype(W, /obj/item/device/pda)) + var/obj/item/device/pda/found_pda = W + found_pda.delete_id = TRUE + else + var/list/pdas_found = W.search_contents_for(/obj/item/device/pda) + if(pdas_found.len) + for(var/obj/item/device/pda/found_pda in pdas_found) + found_pda.delete_id = TRUE + //VOREStation Addition End var/preserve = 0 @@ -458,7 +469,7 @@ // them win or lose based on cryo is silly so we remove the objective. if(O.target == to_despawn.mind) if(O.owner && O.owner.current) - O.owner.current << "You get the feeling your target is no longer within your reach..." + to_chat(O.owner.current, "You get the feeling your target is no longer within your reach...") qdel(O) //VOREStation Edit - Resleeving. @@ -509,7 +520,7 @@ control_computer._admin_logs += "[key_name(to_despawn)] ([to_despawn.mind.role_alt_title]) at [stationtime2text()]" log_and_message_admins("[key_name(to_despawn)] ([to_despawn.mind.role_alt_title]) entered cryostorage.") - announce.autosay("[to_despawn.real_name], [to_despawn.mind.role_alt_title], [on_store_message]", "[on_store_name]") + announce.autosay("[to_despawn.real_name], [to_despawn.mind.role_alt_title], [on_store_message]", "[on_store_name]", announce_channel, using_map.get_map_levels(z, TRUE)) //visible_message("\The [initial(name)] hums and hisses as it moves [to_despawn.real_name] into storage.", 3) visible_message("\The [initial(name)] [on_store_visible_message_1] [to_despawn.real_name] [on_store_visible_message_2].", 3) @@ -666,7 +677,7 @@ if(!M) return if(occupant) - to_chat(user,"\The [src] is already occupied.") + to_chat(user, "\The [src] is already occupied.") return var/willing = null //We don't want to allow people to be forced into despawning. @@ -686,7 +697,7 @@ if(do_after(user, 20)) if(occupant) - to_chat(user,"\The [src] is already occupied.") + to_chat(user, "\The [src] is already occupied.") return M.forceMove(src) @@ -697,8 +708,8 @@ icon_state = occupied_icon_state - M << "[on_enter_occupant_message]" - M << "If you ghost, log out or close your client now, your character will shortly be permanently removed from the round." + to_chat(M, "[on_enter_occupant_message]") + to_chat(M, "If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.") set_occupant(M) time_entered = world.time if(ishuman(M) && applies_stasis) diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 290ff6745e..18e0678fcf 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -14,7 +14,7 @@ */ anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 @@ -205,4 +205,19 @@ if(!active || (stat & NOPOWER)) icon_state = "launcherbtt" else - icon_state = "launcheract" \ No newline at end of file + icon_state = "launcheract" + +/* + Shieldgen remote control +*/ +/obj/machinery/button/remote/shields + name = "remote shield control" + desc = "It controls shields, remotely." + icon = 'icons/obj/stationobjs_vr.dmi' // VOREStation Edit + +/obj/machinery/button/remote/shields/trigger(var/mob/user) + for(var/obj/machinery/shield_gen/SG in machines) + if(SG.id == id) + spawn(0) + if(SG?.anchored) + SG.toggle() diff --git a/code/game/machinery/doorbell_vr.dm b/code/game/machinery/doorbell_vr.dm index d6c5d67719..9df92aa209 100644 --- a/code/game/machinery/doorbell_vr.dm +++ b/code/game/machinery/doorbell_vr.dm @@ -4,7 +4,7 @@ desc = "Small wall-mounted chime triggered by a doorbell" icon = 'icons/obj/machines/doorbell_vr.dmi' icon_state = "dbchime-standby" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 200 anchored = 1 @@ -57,7 +57,7 @@ if(M.connectable && istype(M.connectable, /obj/machinery/button/doorbell)) var/obj/machinery/button/doorbell/B = M.connectable id_tag = B.id - user << "You upload the data from \the [W]'s buffer." + to_chat(user, "You upload the data from \the [W]'s buffer.") return ..() @@ -89,7 +89,7 @@ desc = "A doorbell, press to chime." icon = 'icons/obj/machines/doorbell_vr.dmi' icon_state = "doorbell-standby" - use_power = 0 + use_power = USE_POWER_OFF /obj/machinery/button/doorbell/New(var/loc, var/dir, var/building = 0) ..() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 480effcf98..bd1d663720 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -178,6 +178,14 @@ icon = 'icons/obj/doors/Doorext.dmi' assembly_type = /obj/structure/door_assembly/door_assembly_ext +/obj/machinery/door/airlock/external/glass/bolted + icon_state = "door_locked" // So it looks visibly bolted in map editor + locked = 1 + +// For convenience in making docking ports: one that is pre-bolted with frequency set! +/obj/machinery/door/airlock/external/glass/bolted/cycling + frequency = 1379 + /obj/machinery/door/airlock/glass_external name = "External Airlock" icon = 'icons/obj/doors/Doorextglass.dmi' @@ -523,7 +531,7 @@ About the new airlock wires panel: else /*if(src.justzap)*/ return else if(user.hallucination > 50 && prob(10) && src.operating == 0) - to_chat(user,"You feel a powerful shock course through your body!") + to_chat(user, "You feel a powerful shock course through your body!") user.halloss += 10 user.stunned += 10 return @@ -758,43 +766,43 @@ About the new airlock wires panel: src.aiHacking=1 spawn(20) //TODO: Make this take a minute - to_chat(user,"Airlock AI control has been blocked. Beginning fault-detection.") + to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.") sleep(50) if(src.canAIControl()) - to_chat(user,"Alert cancelled. Airlock control has been restored without our assistance.") + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") src.aiHacking=0 return else if(!src.canAIHack(user)) - to_chat(user,"We've lost our connection! Unable to hack airlock.") + to_chat(user, "We've lost our connection! Unable to hack airlock.") src.aiHacking=0 return - to_chat(user,"Fault confirmed: airlock control wire disabled or cut.") + to_chat(user, "Fault confirmed: airlock control wire disabled or cut.") sleep(20) - to_chat(user,"Attempting to hack into airlock. This may take some time.") + to_chat(user, "Attempting to hack into airlock. This may take some time.") sleep(200) if(src.canAIControl()) - to_chat(user,"Alert cancelled. Airlock control has been restored without our assistance.") + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") src.aiHacking=0 return else if(!src.canAIHack(user)) - to_chat(user,"We've lost our connection! Unable to hack airlock.") + to_chat(user, "We've lost our connection! Unable to hack airlock.") src.aiHacking=0 return - to_chat(user,"Upload access confirmed. Loading control program into airlock software.") + to_chat(user, "Upload access confirmed. Loading control program into airlock software.") sleep(170) if(src.canAIControl()) - to_chat(user,"Alert cancelled. Airlock control has been restored without our assistance.") + to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.") src.aiHacking=0 return else if(!src.canAIHack(user)) - to_chat(user,"We've lost our connection! Unable to hack airlock.") + to_chat(user, "We've lost our connection! Unable to hack airlock.") src.aiHacking=0 return - to_chat(user,"Transfer complete. Forcing airlock to execute program.") + to_chat(user, "Transfer complete. Forcing airlock to execute program.") sleep(50) //disable blocked control src.aiControlDisabled = 2 - to_chat(user,"Receiving control information from airlock.") + to_chat(user, "Receiving control information from airlock.") sleep(10) //bring up airlock dialog src.aiHacking = 0 @@ -832,16 +840,16 @@ About the new airlock wires panel: /obj/machinery/door/airlock/CanUseTopic(var/mob/user) if(operating < 0) //emagged - to_chat(user,"Unable to interface: Internal error.") + to_chat(user, "Unable to interface: Internal error.") return STATUS_CLOSE if(issilicon(user) && !src.canAIControl()) if(src.canAIHack(user)) src.hack(user) else if (src.isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it. - to_chat(user,"Unable to interface: Connection timed out.") + to_chat(user, "Unable to interface: Connection timed out.") else - to_chat(user,"Unable to interface: Connection refused.") + to_chat(user, "Unable to interface: Connection refused.") return STATUS_CLOSE return ..() @@ -862,20 +870,20 @@ About the new airlock wires panel: src.loseBackupPower() if("bolts") if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) - to_chat(usr,"The door bolt control wire is cut - Door bolts permanently dropped.") + to_chat(usr, "The door bolt control wire is cut - Door bolts permanently dropped.") else if(activate && src.lock()) - to_chat(usr,"The door bolts have been dropped.") + to_chat(usr, "The door bolts have been dropped.") else if(!activate && src.unlock()) - to_chat(usr,"The door bolts have been raised.") + to_chat(usr, "The door bolts have been raised.") if("electrify_temporary") electrify(30 * activate, 1) if("electrify_permanently") electrify(-1 * activate, 1) if("open") if(src.welded) - to_chat(usr,text("The airlock has been welded shut!")) + to_chat(usr, "The airlock has been welded shut!") else if(src.locked) - to_chat(usr,text("The door bolts are down!")) + to_chat(usr, "The door bolts are down!") else if(activate && density) open() else if(!activate && !density) @@ -885,7 +893,7 @@ About the new airlock wires panel: if("timing") // Door speed control if(src.isWireCut(AIRLOCK_WIRE_SPEED)) - to_chat(usr,text("The timing wire is cut - Cannot alter timing.")) + to_chat(usr, "The timing wire is cut - Cannot alter timing.") else if (activate && src.normalspeed) normalspeed = 0 else if (!activate && !src.normalspeed) @@ -893,13 +901,13 @@ About the new airlock wires panel: if("lights") // Bolt lights if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) - to_chat(usr,"The bolt lights wire is cut - The door bolt lights are permanently disabled.") + to_chat(usr, "The bolt lights wire is cut - The door bolt lights are permanently disabled.") else if (!activate && src.lights) lights = 0 - to_chat(usr,"The door bolt lights have been disabled.") + to_chat(usr, "The door bolt lights have been disabled.") else if (activate && !src.lights) lights = 1 - to_chat(usr,"The door bolt lights have been enabled.") + to_chat(usr, "The door bolt lights have been enabled.") update_icon() return 1 @@ -908,7 +916,7 @@ About the new airlock wires panel: return src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && (!src.locked || (stat & BROKEN)))) /obj/machinery/door/airlock/attackby(obj/item/C, mob/user as mob) - //world << text("airlock attackby src [] obj [] mob []", src, C, user) + //to_world("airlock attackby src [src] obj [C] mob [user]") if(!istype(usr, /mob/living/silicon)) if(src.isElectrified()) if(src.shock(user, 75)) @@ -936,7 +944,7 @@ About the new airlock wires panel: else if(C.is_screwdriver()) if (src.p_open) if (stat & BROKEN) - to_chat(usr,"The panel is broken and cannot be closed.") + to_chat(usr, "The panel is broken and cannot be closed.") else src.p_open = 0 playsound(src, C.usesound, 50, 1) @@ -958,7 +966,7 @@ About the new airlock wires panel: playsound(src, C.usesound, 75, 1) user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.") if(do_after(user,40 * C.toolspeed)) - to_chat(user,"You removed the airlock electronics!") + to_chat(user, "You removed the airlock electronics!") var/obj/structure/door_assembly/da = new assembly_type(src.loc) if (istype(da, /obj/structure/door_assembly/multi_tile)) @@ -986,9 +994,9 @@ About the new airlock wires panel: qdel(src) return else if(arePowerSystemsOn()) - to_chat(user,"The airlock's motors resist your efforts to force it.") + to_chat(user, "The airlock's motors resist your efforts to force it.") else if(locked) - to_chat(user,"The airlock's bolts prevent it from being forced.") + to_chat(user, "The airlock's bolts prevent it from being forced.") else if(density) spawn(0) open(1) @@ -1000,12 +1008,12 @@ About the new airlock wires panel: var/obj/item/weapon/W = C if((W.pry == 1) && !arePowerSystemsOn()) if(locked) - to_chat(user,"The airlock's bolts prevent it from being forced.") + to_chat(user, "The airlock's bolts prevent it from being forced.") else if( !welded && !operating ) if(istype(C, /obj/item/weapon/material/twohanded/fireaxe)) // If this is a fireaxe, make sure it's held in two hands. var/obj/item/weapon/material/twohanded/fireaxe/F = C if(!F.wielded) - to_chat(user,"You need to be wielding \the [F] to do that.") + to_chat(user, "You need to be wielding \the [F] to do that.") return // At this point, it's an armblade or a fireaxe that passed the wielded test, let's try to open it. if(density) diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm index 65aa9a87d5..ca7092708d 100644 --- a/code/game/machinery/doors/airlock_control.dm +++ b/code/game/machinery/doors/airlock_control.dm @@ -91,7 +91,7 @@ obj/machinery/door/airlock/proc/command_completed(var/command) obj/machinery/door/airlock/proc/send_status(var/bumped = 0) if(radio_connection) var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.data["tag"] = id_tag signal.data["timestamp"] = world.time @@ -172,7 +172,7 @@ obj/machinery/airlock_sensor/update_icon() obj/machinery/airlock_sensor/attack_hand(mob/user) var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.data["tag"] = master_tag signal.data["command"] = command @@ -186,7 +186,7 @@ obj/machinery/airlock_sensor/process() if(abs(pressure - previousPressure) > 0.001 || previousPressure == null) var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.data["tag"] = id_tag signal.data["timestamp"] = world.time signal.data["pressure"] = num2text(pressure) @@ -259,11 +259,11 @@ obj/machinery/access_button/attackby(obj/item/I as obj, mob/user as mob) obj/machinery/access_button/attack_hand(mob/user) add_fingerprint(usr) if(!allowed(user)) - user << "Access Denied" + to_chat(user, "Access Denied") else if(radio_connection) var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal + signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.data["tag"] = master_tag signal.data["command"] = command diff --git a/code/game/machinery/doors/airlock_vr.dm b/code/game/machinery/doors/airlock_vr.dm index 8140eb2474..c26bb9187c 100644 --- a/code/game/machinery/doors/airlock_vr.dm +++ b/code/game/machinery/doors/airlock_vr.dm @@ -1,2 +1,22 @@ /obj/machinery/door/airlock/glass_external/public - req_one_access = list() \ No newline at end of file + req_one_access = list() + +/obj/machinery/door/airlock/alien/blue + name = "hybrid airlock" + desc = "You're fairly sure this is a door." + catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_airlock) + icon = 'icons/obj/doors/Dooralien_blue.dmi' + explosion_resistance = 20 + secured_wires = TRUE + hackProof = TRUE + assembly_type = /obj/structure/door_assembly/door_assembly_alien + req_one_access = list() + +/obj/machinery/door/airlock/alien/blue/locked + icon_state = "door_locked" + locked = TRUE + +/obj/machinery/door/airlock/alien/blue/public // Entry to UFO. + req_one_access = list() + normalspeed = FALSE // So it closes faster and hopefully keeps the warm air inside. + hackProof = TRUE //VOREStation Edit - No borgos diff --git a/code/game/machinery/doors/airlock_yw.dm b/code/game/machinery/doors/airlock_yw.dm new file mode 100644 index 0000000000..71077deedc --- /dev/null +++ b/code/game/machinery/doors/airlock_yw.dm @@ -0,0 +1,92 @@ +/obj/machinery/door/airlock/command + req_one_access = list(access_heads, access_blueshield) + +/obj/machinery/door/airlock/security + req_one_access = list(access_security, access_blueshield) + +/obj/machinery/door/airlock/engineering + req_one_access = list(access_engine, access_blueshield) + +/obj/machinery/door/airlock/engineeringatmos + req_one_access = list(access_atmospherics, access_blueshield) + +/obj/machinery/door/airlock/medical + req_one_access = list(access_medical, access_blueshield) + +/obj/machinery/door/airlock/maintenance + //req_one_access = list(access_maint_tunnels, access_blueshield) + +/obj/machinery/door/airlock/maintenance/cargo + req_one_access = list(access_cargo, access_blueshield) + +/obj/machinery/door/airlock/maintenance/command + req_one_access = list(access_heads, access_blueshield) + +/obj/machinery/door/airlock/maintenance/engi + req_one_access = list(access_engine, access_blueshield) + +/obj/machinery/door/airlock/maintenance/medical + req_one_access = list(access_medical, access_blueshield) + +/obj/machinery/door/airlock/maintenance/rnd + req_one_access = list(access_research, access_blueshield) + +/obj/machinery/door/airlock/maintenance/sec + req_one_access = list(access_security, access_blueshield) + +/obj/machinery/door/airlock/glass_external + req_one_access = list(access_external_airlocks, access_blueshield) + +/obj/machinery/door/airlock/centcom + req_one_access = list(access_cent_general, access_blueshield) + +/obj/machinery/door/airlock/vault + req_one_access = list(access_heads_vault, access_blueshield) + +/obj/machinery/door/airlock/hatch + req_one_access = list(access_maint_tunnels, access_blueshield) + +/obj/machinery/door/airlock/maintenance_hatch + req_one_access = list(access_maint_tunnels, access_blueshield) + +/obj/machinery/door/airlock/glass_command + req_one_access = list(access_heads, access_blueshield) + +/obj/machinery/door/airlock/glass_engineering + req_one_access = list(access_engine, access_blueshield) + +/obj/machinery/door/airlock/glass_engineeringatmos + req_one_access = list(access_atmospherics, access_blueshield) + +/obj/machinery/door/airlock/glass_security + req_one_access = list(access_security, access_blueshield) + +/obj/machinery/door/airlock/glass_medical + req_one_access = list(access_medical, access_blueshield) + +/obj/machinery/door/airlock/mining + req_one_access = list(access_mining, access_blueshield) + +/obj/machinery/door/airlock/atmos + req_one_access = list(access_atmospherics, access_blueshield) + +/obj/machinery/door/airlock/glass_research + req_one_access = list(access_research, access_blueshield) + +/obj/machinery/door/airlock/glass_mining + req_one_access = list(access_mining, access_blueshield) + +/obj/machinery/door/airlock/glass_atmos + req_one_access = list(access_atmospherics, access_blueshield) + +/obj/machinery/door/airlock/science + req_one_access = list(access_research, access_blueshield) + +/obj/machinery/door/airlock/glass_science + req_one_access = list(access_research, access_blueshield) + +/obj/machinery/door/airlock/highsecurity + req_one_access = list(access_heads_vault, access_blueshield) + +/obj/machinery/door/window/brigdoor + req_access = list(access_security, access_blueshield) \ No newline at end of file diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index b2a9941afa..36faf82b00 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -91,7 +91,7 @@ src.density = 1 update_nearby_tiles() src.update_icon() - src.set_opacity(1) // Used to be src.set_opacity(initial(opacity)) Why? + src.set_opacity(1) sleep(15) src.operating = 0 @@ -244,8 +244,7 @@ force_open() if(autoclose && src.operating && !(stat & BROKEN || stat & NOPOWER)) - spawn(150) - close() + addtimer(CALLBACK(src, .proc/close, 15 SECONDS)) return 1 // Proc: close() diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 43fb8340ef..6902b7296f 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -42,7 +42,7 @@ if(isanimal(user)) var/mob/living/simple_mob/S = user if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) - visible_message("\The [user] smashes into the [src]!") + visible_message("\The [user] smashes into [src]!") playsound(src, S.attack_sound, 75, 1) take_damage(damage) else diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index d5043af6ef..c681a34edb 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -38,7 +38,7 @@ var/hatch_open = 0 power_channel = ENVIRON - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 var/list/tile_info[4] @@ -50,13 +50,14 @@ "cold" ) -/obj/machinery/door/firedoor/New() +/obj/machinery/door/firedoor/Initialize() . = ..() + //Delete ourselves if we find extra mapped in firedoors for(var/obj/machinery/door/firedoor/F in loc) if(F != src) - spawn(1) - qdel(src) - return . + log_debug("Duplicate firedoors at [x],[y],[z]") + return INITIALIZE_HINT_QDEL + var/area/A = get_area(src) ASSERT(istype(A)) @@ -289,6 +290,7 @@ FA.anchored = 1 FA.density = 1 FA.wired = 1 + FA.glass = glass FA.update_icon() qdel(src) return diff --git a/code/game/machinery/doors/firedoor_assembly.dm b/code/game/machinery/doors/firedoor_assembly.dm index cebafdbb40..4eeec7c5ff 100644 --- a/code/game/machinery/doors/firedoor_assembly.dm +++ b/code/game/machinery/doors/firedoor_assembly.dm @@ -7,8 +7,13 @@ obj/structure/firedoor_assembly opacity = 0 density = 1 var/wired = 0 + var/glass = FALSE obj/structure/firedoor_assembly/update_icon() + if(glass) + icon = 'icons/obj/doors/DoorHazardGlass.dmi' + else + icon = 'icons/obj/doors/DoorHazard.dmi' if(anchored) icon_state = "door_anchored" else @@ -18,13 +23,13 @@ obj/structure/firedoor_assembly/attackby(obj/item/C, mob/user as mob) if(istype(C, /obj/item/stack/cable_coil) && !wired && anchored) var/obj/item/stack/cable_coil/cable = C if (cable.get_amount() < 1) - user << "You need one length of coil to wire \the [src]." + to_chat(user, "You need one length of coil to wire \the [src].") return user.visible_message("[user] wires \the [src].", "You start to wire \the [src].") if(do_after(user, 40) && !wired && anchored) if (cable.use(1)) wired = 1 - user << "You wire \the [src]." + to_chat(user, "You wire \the [src].") else if(C.is_wirecutter() && wired ) playsound(src.loc, C.usesound, 100, 1) @@ -32,7 +37,7 @@ obj/structure/firedoor_assembly/attackby(obj/item/C, mob/user as mob) if(do_after(user, 40)) if(!src) return - user << "You cut the wires!" + to_chat(user, "You cut the wires!") new/obj/item/stack/cable_coil(src.loc, 1) wired = 0 @@ -41,29 +46,53 @@ obj/structure/firedoor_assembly/attackby(obj/item/C, mob/user as mob) playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) user.visible_message("[user] has inserted a circuit into \the [src]!", "You have inserted the circuit into \the [src]!") - new /obj/machinery/door/firedoor(src.loc) + if(glass) + new /obj/machinery/door/firedoor/glass(loc) + else + new /obj/machinery/door/firedoor(loc) qdel(C) qdel(src) else - user << "You must secure \the [src] first!" + to_chat(user, "You must secure \the [src] first!") else if(C.is_wrench()) anchored = !anchored playsound(src.loc, C.usesound, 50, 1) user.visible_message("[user] has [anchored ? "" : "un" ]secured \the [src]!", "You have [anchored ? "" : "un" ]secured \the [src]!") update_icon() - else if(!anchored && istype(C, /obj/item/weapon/weldingtool)) + else if((glass || !anchored) && istype(C, /obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/WT = C if(WT.remove_fuel(0, user)) - user.visible_message("[user] dissassembles \the [src].", - "You start to dissassemble \the [src].") - if(do_after(user, 40)) - if(!src || !WT.isOn()) return - user.visible_message("[user] has dissassembled \the [src].", - "You have dissassembled \the [src].") - new /obj/item/stack/material/steel(src.loc, 2) - qdel(src) + playsound(src, WT.usesound, 50, 1) + if(glass) + user.visible_message("[user] welds the glass panel out of \the [src].", + "You start to weld the glass panel out of \the [src].") + if(do_after(user, 40 * WT.toolspeed, src) && WT.isOn()) + to_chat(user, "You welded the glass panel out!") + new /obj/item/stack/material/glass/reinforced(drop_location()) + glass = FALSE + update_icon() + return + if(!anchored) + user.visible_message("[user] dissassembles \the [src].", "You start to dissassemble \the [src].") + if(do_after(user, 40 * WT.toolspeed, src) && WT.isOn()) + user.visible_message("[user] has dissassembled \the [src].", + "You have dissassembled \the [src].") + new /obj/item/stack/material/steel(drop_location(), 2) + qdel(src) + return else - user << "You need more welding fuel." + to_chat(user, "You need more welding fuel.") + else if(istype(C, /obj/item/stack/material) && C.get_material_name() == "rglass" && !glass) + var/obj/item/stack/S = C + if (S.get_amount() >= 1) + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] adds [S.name] to \the [src].", + "You start to install [S.name] into \the [src].") + if(do_after(user, 40, src) && !glass && S.use(1)) + to_chat(user, "You installed reinforced glass windows into \the [src].") + glass = TRUE + update_icon() + else ..(C, user) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index a1e05d5e1e..1396b5f554 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -9,7 +9,7 @@ maxhealth = 150 //If you change this, consiter changing ../door/window/brigdoor/ health at the bottom of this .dm file health = 150 visible = 0.0 - use_power = 0 + use_power = USE_POWER_OFF flags = ON_BORDER opacity = 0 var/obj/item/weapon/airlock_electronics/electronics = null diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm index 0f8b797289..b15d5afd97 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers.dm @@ -2,6 +2,7 @@ /obj/machinery/embedded_controller/radio/airlock // Setup parameters only radio_filter = RADIO_AIRLOCK + program = /datum/computer/file/embedded_program/airlock var/tag_exterior_door var/tag_interior_door var/tag_airpump @@ -11,11 +12,22 @@ var/tag_airlock_mech_sensor var/tag_shuttle_mech_sensor var/tag_secure = 0 + var/list/dummy_terminals = list() var/cycle_to_external_air = 0 -/obj/machinery/embedded_controller/radio/airlock/Initialize() - . = ..() - program = new/datum/computer/file/embedded_program/airlock(src) +/obj/machinery/embedded_controller/radio/airlock/Destroy() + // TODO - Leshana - Implement dummy terminals + //for(var/thing in dummy_terminals) + // var/obj/machinery/dummy_airlock_controller/dummy = thing + // dummy.master_controller = null + //dummy_terminals.Cut() + return ..() + +/obj/machinery/embedded_controller/radio/airlock/CanUseTopic(var/mob/user) + if(!allowed(user)) + return min(STATUS_UPDATE, ..()) + else + return ..() //Advanced airlock controller for when you want a more versatile airlock controller - useful for turning simple access control rooms into airlocks /obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller @@ -37,43 +49,20 @@ if (!ui) ui = new(user, src, ui_key, "advanced_airlock_console.tmpl", name, 470, 290) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks - if("cycle_ext") - clean = 1 - if("cycle_int") - clean = 1 - if("force_ext") - clean = 1 - if("force_int") - clean = 1 - if("abort") - clean = 1 - if("purge") - clean = 1 - if("secure") - clean = 1 - - if(clean) - program.receive_user_command(href_list["command"]) + if("cycle_ext", "cycle_int", "force_ext", "force_int", "abort", "purge", "secure") + program.receive_user_command(href_list["command"]) return 1 - //Airlock controller for airlock control - most airlocks on the station use this /obj/machinery/embedded_controller/radio/airlock/airlock_controller name = "Airlock Controller" @@ -90,23 +79,16 @@ ) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) ui = new(user, src, ui_key, "simple_airlock_console.tmpl", name, 470, 290) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/airlock_controller/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks if("cycle_ext") @@ -125,7 +107,6 @@ return 1 - //Access controller for door control - used in virology and the like /obj/machinery/embedded_controller/radio/airlock/access_controller icon = 'icons/obj/airlock_machines.dmi' @@ -154,23 +135,16 @@ ) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) ui = new(user, src, ui_key, "door_access_console.tmpl", name, 330, 220) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/access_controller/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks if("cycle_ext_door") diff --git a/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm b/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm new file mode 100644 index 0000000000..d2f9a410a2 --- /dev/null +++ b/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm @@ -0,0 +1,51 @@ +// Provides remote access to a controller (since they must be unique). +/obj/machinery/dummy_airlock_controller + name = "airlock control terminal" + icon = 'icons/obj/airlock_machines.dmi' + icon_state = "airlock_control_standby" + layer = ABOVE_OBJ_LAYER + var/id_tag + + var/datum/topic_state/remote/remote_state + var/obj/machinery/embedded_controller/radio/airlock/master_controller + +/obj/machinery/dummy_airlock_controller/process() + if(master_controller) + appearance = master_controller + . = ..() + +/obj/machinery/dummy_airlock_controller/Initialize() + . = ..() + if(id_tag) + for(var/obj/machinery/embedded_controller/radio/airlock/_master in SSmachines.machinery) + if(_master.id_tag == id_tag) + master_controller = _master + master_controller.dummy_terminals += src + break + if(!master_controller) + qdel(src) + else + remote_state = new /datum/topic_state/remote(src, master_controller) + +/obj/machinery/dummy_airlock_controller/Destroy() + if(master_controller) + master_controller.dummy_terminals -= src + if(remote_state) + qdel(remote_state) + remote_state = null + return ..() + +/obj/machinery/dummy_airlock_controller/interface_interact(var/mob/user) + open_remote_ui(user) + return TRUE + +/obj/machinery/dummy_airlock_controller/proc/open_remote_ui(var/mob/user) + if(master_controller) + appearance = master_controller + return master_controller.ui_interact(user, state = remote_state) + +/obj/machinery/dummy_airlock_controller/powered(var/chan = -1, var/area/check_area = null) + if(master_controller) + var/area/A = get_area(master_controller) + return master_controller.powered(chan, A) + return ..() diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller.dm b/code/game/machinery/embedded_controller/airlock_docking_controller.dm index e88b046c10..4d5048ad6d 100644 --- a/code/game/machinery/embedded_controller/airlock_docking_controller.dm +++ b/code/game/machinery/embedded_controller/airlock_docking_controller.dm @@ -1,8 +1,17 @@ +/* + * NOTE - This file defines both these datums: Yes, you read that right. Its confusing. Lets try and break it down. + * /datum/computer/file/embedded_program/docking/airlock + * - A docking controller for an airlock based docking port + * /datum/computer/file/embedded_program/airlock/docking + * - An extension to the normal airlock program allows disabling of the regular airlock functions when docking +*/ + //a docking port based on an airlock /obj/machinery/embedded_controller/radio/airlock/docking_port name = "docking port controller" var/datum/computer/file/embedded_program/airlock/docking/airlock_program var/datum/computer/file/embedded_program/docking/airlock/docking_program + var/display_name // For mappers to override docking_program.display_name (how would it show up on docking monitoring program) tag_secure = 1 /obj/machinery/embedded_controller/radio/airlock/docking_port/Initialize() @@ -10,9 +19,25 @@ airlock_program = new/datum/computer/file/embedded_program/airlock/docking(src) docking_program = new/datum/computer/file/embedded_program/docking/airlock(src, airlock_program) program = docking_program + if(display_name) + docking_program.display_name = display_name + +/obj/machinery/embedded_controller/radio/airlock/docking_port/attackby(obj/item/W, mob/user) + if(istype(W,/obj/item/device/multitool)) //give them part of code, would take few tries to get full + var/datum/computer/file/embedded_program/docking/airlock/docking_program = program + var/code = docking_program.docking_codes + if(!code) + code = "N/A" + else + code = stars(code) + to_chat(user, "[W]'s screen displays '[code]'") + else + ..() /obj/machinery/embedded_controller/radio/airlock/docking_port/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/airlock/docking_program = program + var/datum/computer/file/embedded_program/airlock/docking/airlock_program = docking_program.airlock_program data = list( "chamber_pressure" = round(airlock_program.memory["chamber_sensor_pressure"]), @@ -22,6 +47,8 @@ "docking_status" = docking_program.get_docking_status(), "airlock_disabled" = !(docking_program.undocked() || docking_program.override_enabled), "override_enabled" = docking_program.override_enabled, + "docking_codes" = docking_program.docking_codes, + "name" = docking_program.get_name() ) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -33,12 +60,9 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/docking_port/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks if("cycle_ext") @@ -60,11 +84,13 @@ return 1 - +/////////////////////////////////////////////////////////////////////////////// //A docking controller for an airlock based docking port +// /datum/computer/file/embedded_program/docking/airlock var/datum/computer/file/embedded_program/airlock/docking/airlock_program + /datum/computer/file/embedded_program/docking/airlock/New(var/obj/machinery/embedded_controller/M, var/datum/computer/file/embedded_program/airlock/docking/A) ..(M) airlock_program = A @@ -76,10 +102,10 @@ disable_override() else enable_override() - return + return TRUE - ..(command) - airlock_program.receive_user_command(command) //pass along to subprograms + . = ..(command) + . = airlock_program.receive_user_command(command) || . //pass along to subprograms; bypass shortcircuit /datum/computer/file/embedded_program/docking/airlock/process() airlock_program.process() @@ -91,7 +117,7 @@ //tell the docking port to start getting ready for docking - e.g. pressurize /datum/computer/file/embedded_program/docking/airlock/prepare_for_docking() - airlock_program.begin_cycle_in() + airlock_program.begin_dock_cycle() //are we ready for docking? /datum/computer/file/embedded_program/docking/airlock/ready_for_docking() @@ -99,14 +125,14 @@ //we are docked, open the doors or whatever. /datum/computer/file/embedded_program/docking/airlock/finish_docking() - airlock_program.enable_mech_regulators() + airlock_program.enable_mech_regulation() airlock_program.open_doors() //tell the docking port to start getting ready for undocking - e.g. close those doors. /datum/computer/file/embedded_program/docking/airlock/prepare_for_undocking() airlock_program.stop_cycling() airlock_program.close_doors() - airlock_program.disable_mech_regulators() + airlock_program.disable_mech_regulation() //are we ready for undocking? /datum/computer/file/embedded_program/docking/airlock/ready_for_undocking() @@ -114,20 +140,22 @@ var/int_closed = airlock_program.check_interior_door_secured() return (ext_closed || int_closed) +/////////////////////////////////////////////////////////////////////////////// //An airlock controller to be used by the airlock-based docking port controller. //Same as a regular airlock controller but allows disabling of the regular airlock functions when docking +// /datum/computer/file/embedded_program/airlock/docking var/datum/computer/file/embedded_program/docking/airlock/master_prog +/datum/computer/file/embedded_program/airlock/docking/Destroy() + if(master_prog) + master_prog.airlock_program = null + master_prog = null + return ..() + /datum/computer/file/embedded_program/airlock/docking/receive_user_command(command) if (master_prog.undocked() || master_prog.override_enabled) //only allow the port to be used as an airlock if nothing is docked here or the override is enabled - ..(command) - -/datum/computer/file/embedded_program/airlock/docking/proc/enable_mech_regulators() - enable_mech_regulation() - -/datum/computer/file/embedded_program/airlock/docking/proc/disable_mech_regulators() - disable_mech_regulation() + return ..(command) /datum/computer/file/embedded_program/airlock/docking/proc/open_doors() toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "open") @@ -140,16 +168,16 @@ /*** DEBUG VERBS *** /datum/computer/file/embedded_program/docking/proc/print_state() - world << "id_tag: [id_tag]" - world << "dock_state: [dock_state]" - world << "control_mode: [control_mode]" - world << "tag_target: [tag_target]" - world << "response_sent: [response_sent]" + to_world("id_tag: [id_tag]") + to_world("dock_state: [dock_state]") + to_world("control_mode: [control_mode]") + to_world("tag_target: [tag_target]") + to_world("response_sent: [response_sent]") /datum/computer/file/embedded_program/docking/post_signal(datum/signal/signal, comm_line) - world << "Program [id_tag] sent a message!" + to_world("Program [id_tag] sent a message!") print_state() - world << "[id_tag] sent command \"[signal.data["command"]]\" to \"[signal.data["recipient"]]\"" + to_world("[id_tag] sent command \"[signal.data["command"]]\" to \"[signal.data["recipient"]]\"") ..(signal) /obj/machinery/embedded_controller/radio/airlock/docking_port/verb/view_state() diff --git a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm index 5614d0be63..55182aaaa6 100644 --- a/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm +++ b/code/game/machinery/embedded_controller/airlock_docking_controller_multi.dm @@ -2,21 +2,15 @@ //this is the master controller, that things will try to dock with. /obj/machinery/embedded_controller/radio/docking_port_multi name = "docking port controller" - + program = /datum/computer/file/embedded_program/docking/multi var/child_tags_txt var/child_names_txt var/list/child_names = list() - var/datum/computer/file/embedded_program/docking/multi/docking_program - /obj/machinery/embedded_controller/radio/docking_port_multi/Initialize() . = ..() - docking_program = new/datum/computer/file/embedded_program/docking/multi(src) - program = docking_program - var/list/names = splittext(child_names_txt, ";") var/list/tags = splittext(child_tags_txt, ";") - if (names.len == tags.len) for (var/i = 1; i <= tags.len; i++) child_names[tags[i]] = names[i] @@ -24,6 +18,7 @@ /obj/machinery/embedded_controller/radio/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/multi/docking_program = program // Cast to proper type var/list/airlocks[child_names.len] var/i = 1 @@ -44,24 +39,21 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/docking_port_multi/Topic(href, href_list) - return + return 1 // Apparently we swallow all input (this is corrected legacy code) //a docking port based on an airlock +// This is the actual controller that will be commanded by the master defined above /obj/machinery/embedded_controller/radio/airlock/docking_port_multi name = "docking port controller" + program = /datum/computer/file/embedded_program/airlock/multi_docking var/master_tag //for mapping - var/datum/computer/file/embedded_program/airlock/multi_docking/airlock_program tag_secure = 1 -/obj/machinery/embedded_controller/radio/airlock/docking_port_multi/Initialize() - . = ..() - airlock_program = new/datum/computer/file/embedded_program/airlock/multi_docking(src) - program = airlock_program - /obj/machinery/embedded_controller/radio/airlock/docking_port_multi/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/airlock/multi_docking/airlock_program = program // Cast to proper type data = list( "chamber_pressure" = round(airlock_program.memory["chamber_sensor_pressure"]), @@ -82,12 +74,9 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/airlock/docking_port_multi/Topic(href, href_list) - if(..()) + if((. = ..())) return - usr.set_machine(src) - src.add_fingerprint(usr) - var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks if("cycle_ext") @@ -113,16 +102,16 @@ /*** DEBUG VERBS *** /datum/computer/file/embedded_program/docking/multi/proc/print_state() - world << "id_tag: [id_tag]" - world << "dock_state: [dock_state]" - world << "control_mode: [control_mode]" - world << "tag_target: [tag_target]" - world << "response_sent: [response_sent]" + to_world("id_tag: [id_tag]") + to_world("dock_state: [dock_state]") + to_world("control_mode: [control_mode]") + to_world("tag_target: [tag_target]") + to_world("response_sent: [response_sent]") /datum/computer/file/embedded_program/docking/multi/post_signal(datum/signal/signal, comm_line) - world << "Program [id_tag] sent a message!" + to_world("Program [id_tag] sent a message!") print_state() - world << "[id_tag] sent command \"[signal.data["command"]]\" to \"[signal.data["recipient"]]\"" + to_world("[id_tag] sent command \"[signal.data["command"]]\" to \"[signal.data["recipient"]]\"") ..(signal) /obj/machinery/embedded_controller/radio/docking_port_multi/verb/view_state() diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index 5ebbac93ae..dc02084f98 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -52,8 +52,8 @@ tag_interior_door = controller.tag_interior_door? controller.tag_interior_door : "[id_tag]_inner" tag_airpump = controller.tag_airpump? controller.tag_airpump : "[id_tag]_pump" tag_chamber_sensor = controller.tag_chamber_sensor? controller.tag_chamber_sensor : "[id_tag]_sensor" - tag_exterior_sensor = controller.tag_exterior_sensor - tag_interior_sensor = controller.tag_interior_sensor + tag_exterior_sensor = controller.tag_exterior_sensor || "[id_tag]_exterior_sensor" + tag_interior_sensor = controller.tag_interior_sensor || "[id_tag]_interior_sensor" tag_airlock_mech_sensor = controller.tag_airlock_mech_sensor? controller.tag_airlock_mech_sensor : "[id_tag]_airlock_mech" tag_shuttle_mech_sensor = controller.tag_shuttle_mech_sensor? controller.tag_shuttle_mech_sensor : "[id_tag]_shuttle_mech" memory["secure"] = controller.tag_secure @@ -117,6 +117,7 @@ /datum/computer/file/embedded_program/airlock/receive_user_command(command) var/shutdown_pump = 0 + . = TRUE switch(command) if("cycle_ext") //If airlock is already cycled in this direction, just toggle the doors. @@ -163,6 +164,8 @@ else signalDoor(tag_interior_door, "unlock") signalDoor(tag_exterior_door, "unlock") + else + . = FALSE if(shutdown_pump) signalPump(tag_airpump, 0) //send a signal to stop pressurizing @@ -273,6 +276,9 @@ target_state = TARGET_INOPEN memory["purge"] = cycle_to_external_air +/datum/computer/file/embedded_program/airlock/proc/begin_dock_cycle() + state = STATE_IDLE + target_state = TARGET_INOPEN /datum/computer/file/embedded_program/airlock/proc/begin_cycle_out() state = STATE_IDLE target_state = TARGET_OUTOPEN diff --git a/code/game/machinery/embedded_controller/docking_program.dm b/code/game/machinery/embedded_controller/docking_program.dm index 987634d02a..86876abe28 100644 --- a/code/game/machinery/embedded_controller/docking_program.dm +++ b/code/game/machinery/embedded_controller/docking_program.dm @@ -1,294 +1,309 @@ - -#define STATE_UNDOCKED 0 -#define STATE_DOCKING 1 -#define STATE_UNDOCKING 2 -#define STATE_DOCKED 3 - -#define MODE_NONE 0 -#define MODE_SERVER 1 -#define MODE_CLIENT 2 //The one who initiated the docking, and who can initiate the undocking. The server cannot initiate undocking, and is the one responsible for deciding to accept a docking request and signals when docking and undocking is complete. (Think server == station, client == shuttle) - -#define MESSAGE_RESEND_TIME 5 //how long (in seconds) do we wait before resending a message - -/* - *** STATE TABLE *** - - MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply. - MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready. - MODE_CLIENT|STATE_DOCKED idle - docked as client. - MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking. - - MODE_SERVER|STATE_UNDOCKED should never happen. - MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready. - MODE_SERVER|STATE_DOCKED idle - docked as server - MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready. - - MODE_NONE|STATE_UNDOCKED idle - not docked. - MODE_NONE|anything else should never happen. - - *** Docking Signals *** - - Docking - Client sends request_dock - Server sends confirm_dock to say that yes, we will serve your request - When client is ready, sends confirm_dock - Server sends confirm_dock back to indicate that docking is complete - - Undocking - Client sends request_undock - When client is ready, sends confirm_undock - Server sends confirm_undock back to indicate that docking is complete - - Note that in both cases each side exchanges confirm_dock before the docking operation is considered done. - The client first sends a confirm message to indicate it is ready, and then finally the server will send it's - confirm message to indicate that the operation is complete. - - Note also that when docking, the server sends an additional confirm message. This is because before docking, - the server and client do not have a defined relationship. Before undocking, the server and client are already - related to each other, thus the extra confirm message is not needed. - - *** Override, what is it? *** - - The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking. - Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle. - This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called. - - The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced - launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock - (which usually just means closing and locking the doors). - - In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled. -*/ - - -/datum/computer/file/embedded_program/docking - var/tag_target //the tag of the docking controller that we are trying to dock with - var/dock_state = STATE_UNDOCKED - var/control_mode = MODE_NONE - var/response_sent = 0 //so we don't spam confirmation messages - var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed - - var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually - var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client - -/datum/computer/file/embedded_program/docking/New() + +#define STATE_UNDOCKED 0 +#define STATE_DOCKING 1 +#define STATE_UNDOCKING 2 +#define STATE_DOCKED 3 + +#define MODE_NONE 0 +#define MODE_SERVER 1 +#define MODE_CLIENT 2 //The one who initiated the docking, and who can initiate the undocking. The server cannot initiate undocking, and is the one responsible for deciding to accept a docking request and signals when docking and undocking is complete. (Think server == station, client == shuttle) + +#define MESSAGE_RESEND_TIME 5 //how long (in seconds) do we wait before resending a message + +/* + *** STATE TABLE *** + + MODE_CLIENT|STATE_UNDOCKED sent a request for docking and now waiting for a reply. + MODE_CLIENT|STATE_DOCKING server told us they are OK to dock, waiting for our docking port to be ready. + MODE_CLIENT|STATE_DOCKED idle - docked as client. + MODE_CLIENT|STATE_UNDOCKING we are either waiting for our docking port to be ready or for the server to give us the OK to finish undocking. + + MODE_SERVER|STATE_UNDOCKED should never happen. + MODE_SERVER|STATE_DOCKING someone requested docking, we are waiting for our docking port to be ready. + MODE_SERVER|STATE_DOCKED idle - docked as server + MODE_SERVER|STATE_UNDOCKING client requested undocking, we are waiting for our docking port to be ready. + + MODE_NONE|STATE_UNDOCKED idle - not docked. + MODE_NONE|anything else should never happen. + + *** Docking Signals *** + + Docking + Client sends request_dock + Server sends confirm_dock to say that yes, we will serve your request + When client is ready, sends confirm_dock + Server sends confirm_dock back to indicate that docking is complete + + Undocking + Client sends request_undock + When client is ready, sends confirm_undock + Server sends confirm_undock back to indicate that docking is complete + + Note that in both cases each side exchanges confirm_dock before the docking operation is considered done. + The client first sends a confirm message to indicate it is ready, and then finally the server will send it's + confirm message to indicate that the operation is complete. + + Note also that when docking, the server sends an additional confirm message. This is because before docking, + the server and client do not have a defined relationship. Before undocking, the server and client are already + related to each other, thus the extra confirm message is not needed. + + *** Override, what is it? *** + + The purpose of enabling the override is to prevent the docking program from automatically doing things with the docking port when docking or undocking. + Maybe the shuttle is full of plamsa/phoron for some reason, and you don't want the door to automatically open, or the airlock to cycle. + This means that the prepare_for_docking/undocking and finish_docking/undocking procs don't get called. + + The docking controller will still check the state of the docking port, and thus prevent the shuttle from launching unless they force the launch (handling forced + launches is not the docking controller's responsibility). In this case it is up to the players to manually get the docking port into a good state to undock + (which usually just means closing and locking the doors). + + In line with this, docking controllers should prevent players from manually doing things when the override is NOT enabled. +*/ + + +/datum/computer/file/embedded_program/docking + var/tag_target //the tag of the docking controller that we are trying to dock with + var/dock_state = STATE_UNDOCKED + var/control_mode = MODE_NONE + var/response_sent = 0 //so we don't spam confirmation messages + var/resend_counter = 0 //for periodically resending confirmation messages in case they are missed + + var/override_enabled = 0 //when enabled, do not open/close doors or cycle airlocks and wait for the player to do it manually + var/received_confirm = 0 //for undocking, whether the server has recieved a confirmation from the client + var/docking_codes //would only allow docking when receiving signal with these, if set + var/display_name //Override the name shown on docking monitoring program; defaults to area name + coordinates if unset + +/datum/computer/file/embedded_program/docking/New() ..() - var/datum/existing = locate(id_tag) //in case a datum already exists with our tag - if(existing) - existing.tag = null //take it from them - - tag = id_tag //Greatly simplifies shuttle initialization - - -/datum/computer/file/embedded_program/docking/receive_signal(datum/signal/signal, receive_method, receive_param) - var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id - var/command = signal.data["command"] - var/recipient = signal.data["recipient"] //the intended recipient of the docking signal - - if (recipient != id_tag) - return //this signal is not for us - - switch (command) - if ("confirm_dock") - if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target) - dock_state = STATE_DOCKING - broadcast_docking_status() - if (!override_enabled) - prepare_for_docking() - - else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target) - dock_state = STATE_DOCKED - broadcast_docking_status() - if (!override_enabled) - finish_docking() //client done docking! - response_sent = 0 - else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process - received_confirm = 1 - - if ("request_dock") - if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED) - control_mode = MODE_SERVER - - dock_state = STATE_DOCKING - broadcast_docking_status() - - tag_target = receive_tag - if (!override_enabled) - prepare_for_docking() - send_docking_command(tag_target, "confirm_dock") //acknowledge the request - - if ("confirm_undock") - if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target) - if (!override_enabled) - finish_undocking() - reset() //client is done undocking! - else if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKING && receive_tag == tag_target) - received_confirm = 1 - - if ("request_undock") - if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target) - dock_state = STATE_UNDOCKING - broadcast_docking_status() - - if (!override_enabled) - prepare_for_undocking() - - if ("dock_error") - if (receive_tag == tag_target) - reset() - -/datum/computer/file/embedded_program/docking/process() - switch(dock_state) - if (STATE_DOCKING) //waiting for our docking port to be ready for docking - if (ready_for_docking()) - if (control_mode == MODE_CLIENT) - if (!response_sent) - send_docking_command(tag_target, "confirm_dock") //tell the server we're ready - response_sent = 1 - - else if (control_mode == MODE_SERVER && received_confirm) - send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking. - - dock_state = STATE_DOCKED - broadcast_docking_status() - - if (!override_enabled) - finish_docking() //server done docking! - response_sent = 0 - received_confirm = 0 - - if (STATE_UNDOCKING) - if (ready_for_undocking()) - if (control_mode == MODE_CLIENT) - if (!response_sent) - send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock. - response_sent = 1 - - else if (control_mode == MODE_SERVER && received_confirm) - send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking. - if (!override_enabled) - finish_undocking() - reset() //server is done undocking! - - if (response_sent || resend_counter > 0) - resend_counter++ - - if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING)) - response_sent = 0 - resend_counter = 0 - - //handle invalid states - if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED) - if (tag_target) - send_docking_command(tag_target, "dock_error") - reset() - if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKED) - control_mode = MODE_NONE - - -/datum/computer/file/embedded_program/docking/proc/initiate_docking(var/target) - if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake - return - - tag_target = target - control_mode = MODE_CLIENT - - send_docking_command(tag_target, "request_dock") - -/datum/computer/file/embedded_program/docking/proc/initiate_undocking() - if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking - return - - dock_state = STATE_UNDOCKING - broadcast_docking_status() - - if (!override_enabled) - prepare_for_undocking() - - send_docking_command(tag_target, "request_undock") - -//tell the docking port to start getting ready for docking - e.g. pressurize -/datum/computer/file/embedded_program/docking/proc/prepare_for_docking() - return - -//are we ready for docking? -/datum/computer/file/embedded_program/docking/proc/ready_for_docking() - return 1 - -//we are docked, open the doors or whatever. -/datum/computer/file/embedded_program/docking/proc/finish_docking() - return - -//tell the docking port to start getting ready for undocking - e.g. close those doors. -/datum/computer/file/embedded_program/docking/proc/prepare_for_undocking() - return - -//we are docked, open the doors or whatever. -/datum/computer/file/embedded_program/docking/proc/finish_undocking() - return - -//are we ready for undocking? -/datum/computer/file/embedded_program/docking/proc/ready_for_undocking() - return 1 - -/datum/computer/file/embedded_program/docking/proc/enable_override() - override_enabled = 1 - -/datum/computer/file/embedded_program/docking/proc/disable_override() - override_enabled = 0 - -/datum/computer/file/embedded_program/docking/proc/reset() - dock_state = STATE_UNDOCKED - broadcast_docking_status() - - control_mode = MODE_NONE - tag_target = null - response_sent = 0 - received_confirm = 0 - -/datum/computer/file/embedded_program/docking/proc/force_undock() - //world << "[id_tag]: forcing undock" - if (tag_target) - send_docking_command(tag_target, "dock_error") - reset() - -/datum/computer/file/embedded_program/docking/proc/docked() - return (dock_state == STATE_DOCKED) - -/datum/computer/file/embedded_program/docking/proc/undocked() - return (dock_state == STATE_UNDOCKED) - -//returns 1 if we are saftely undocked (and the shuttle can leave) -/datum/computer/file/embedded_program/docking/proc/can_launch() - return undocked() - -/datum/computer/file/embedded_program/docking/proc/send_docking_command(var/recipient, var/command) - var/datum/signal/signal = new - signal.data["tag"] = id_tag - signal.data["command"] = command - signal.data["recipient"] = recipient - post_signal(signal) - -/datum/computer/file/embedded_program/docking/proc/broadcast_docking_status() - var/datum/signal/signal = new - signal.data["tag"] = id_tag - signal.data["dock_status"] = get_docking_status() - post_signal(signal) - -//this is mostly for NanoUI -/datum/computer/file/embedded_program/docking/proc/get_docking_status() - switch (dock_state) - if (STATE_UNDOCKED) return "undocked" - if (STATE_DOCKING) return "docking" - if (STATE_UNDOCKING) return "undocking" - if (STATE_DOCKED) return "docked" - - -#undef STATE_UNDOCKED -#undef STATE_DOCKING -#undef STATE_UNDOCKING -#undef STATE_DOCKED - -#undef MODE_NONE -#undef MODE_SERVER + if(id_tag) + if(SSshuttles.docking_registry[id_tag]) + crash_with("Docking controller tag [id_tag] had multiple associated programs.") + SSshuttles.docking_registry[id_tag] = src + +/datum/computer/file/embedded_program/docking/Destroy() + SSshuttles.docking_registry -= id_tag + return ..() + +/datum/computer/file/embedded_program/docking/receive_signal(datum/signal/signal, receive_method, receive_param) + var/receive_tag = signal.data["tag"] //for docking signals, this is the sender id + var/command = signal.data["command"] + var/recipient = signal.data["recipient"] //the intended recipient of the docking signal + + if (recipient != id_tag) + return //this signal is not for us + + switch (command) + if ("confirm_dock") + if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKED && receive_tag == tag_target) + dock_state = STATE_DOCKING + broadcast_docking_status() + if (!override_enabled) + prepare_for_docking() + + else if (control_mode == MODE_CLIENT && dock_state == STATE_DOCKING && receive_tag == tag_target) + dock_state = STATE_DOCKED + broadcast_docking_status() + if (!override_enabled) + finish_docking() //client done docking! + response_sent = 0 + else if (control_mode == MODE_SERVER && dock_state == STATE_DOCKING && receive_tag == tag_target) //client just sent us the confirmation back, we're done with the docking process + received_confirm = 1 + + if ("request_dock") + if (control_mode == MODE_NONE && dock_state == STATE_UNDOCKED) + + tag_target = receive_tag + + if(docking_codes) + var/code = signal.data["code"] + if(code != docking_codes) + log_debug("Controller [id_tag] got request_dock but code:[code] != docking_codes:[docking_codes]") + return + + control_mode = MODE_SERVER + dock_state = STATE_DOCKING + broadcast_docking_status() + + + if (!override_enabled) + prepare_for_docking() + send_docking_command(tag_target, "confirm_dock") //acknowledge the request + + if ("confirm_undock") + if (control_mode == MODE_CLIENT && dock_state == STATE_UNDOCKING && receive_tag == tag_target) + if (!override_enabled) + finish_undocking() + reset() //client is done undocking! + else if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKING && receive_tag == tag_target) + received_confirm = 1 + + if ("request_undock") + if (control_mode == MODE_SERVER && dock_state == STATE_DOCKED && receive_tag == tag_target) + dock_state = STATE_UNDOCKING + broadcast_docking_status() + + if (!override_enabled) + prepare_for_undocking() + + if ("dock_error") + if (receive_tag == tag_target) + reset() + +/datum/computer/file/embedded_program/docking/process() + switch(dock_state) + if (STATE_DOCKING) //waiting for our docking port to be ready for docking + if (ready_for_docking()) + if (control_mode == MODE_CLIENT) + if (!response_sent) + send_docking_command(tag_target, "confirm_dock") //tell the server we're ready + response_sent = 1 + + else if (control_mode == MODE_SERVER && received_confirm) + send_docking_command(tag_target, "confirm_dock") //tell the client we are done docking. + + dock_state = STATE_DOCKED + broadcast_docking_status() + + if (!override_enabled) + finish_docking() //server done docking! + response_sent = 0 + received_confirm = 0 + + if (STATE_UNDOCKING) + if (ready_for_undocking()) + if (control_mode == MODE_CLIENT) + if (!response_sent) + send_docking_command(tag_target, "confirm_undock") //tell the server we are OK to undock. + response_sent = 1 + + else if (control_mode == MODE_SERVER && received_confirm) + send_docking_command(tag_target, "confirm_undock") //tell the client we are done undocking. + if (!override_enabled) + finish_undocking() + reset() //server is done undocking! + + if (response_sent || resend_counter > 0) + resend_counter++ + + if (resend_counter >= MESSAGE_RESEND_TIME || (dock_state != STATE_DOCKING && dock_state != STATE_UNDOCKING)) + response_sent = 0 + resend_counter = 0 + + //handle invalid states + if (control_mode == MODE_NONE && dock_state != STATE_UNDOCKED) + if (tag_target) + send_docking_command(tag_target, "dock_error") + reset() + if (control_mode == MODE_SERVER && dock_state == STATE_UNDOCKED) + control_mode = MODE_NONE + + +/datum/computer/file/embedded_program/docking/proc/initiate_docking(var/target) + if (dock_state != STATE_UNDOCKED || control_mode == MODE_SERVER) //must be undocked and not serving another request to begin a new docking handshake + return + + tag_target = target + control_mode = MODE_CLIENT + + send_docking_command(tag_target, "request_dock") + +/datum/computer/file/embedded_program/docking/proc/initiate_undocking() + if (dock_state != STATE_DOCKED || control_mode != MODE_CLIENT) //must be docked and must be client to start undocking + return + + dock_state = STATE_UNDOCKING + broadcast_docking_status() + + if (!override_enabled) + prepare_for_undocking() + + send_docking_command(tag_target, "request_undock") + +//tell the docking port to start getting ready for docking - e.g. pressurize +/datum/computer/file/embedded_program/docking/proc/prepare_for_docking() + return + +//are we ready for docking? +/datum/computer/file/embedded_program/docking/proc/ready_for_docking() + return 1 + +//we are docked, open the doors or whatever. +/datum/computer/file/embedded_program/docking/proc/finish_docking() + return + +//tell the docking port to start getting ready for undocking - e.g. close those doors. +/datum/computer/file/embedded_program/docking/proc/prepare_for_undocking() + return + +//we are docked, open the doors or whatever. +/datum/computer/file/embedded_program/docking/proc/finish_undocking() + return + +//are we ready for undocking? +/datum/computer/file/embedded_program/docking/proc/ready_for_undocking() + return 1 + +/datum/computer/file/embedded_program/docking/proc/enable_override() + override_enabled = 1 + +/datum/computer/file/embedded_program/docking/proc/disable_override() + override_enabled = 0 + +/datum/computer/file/embedded_program/docking/proc/reset() + dock_state = STATE_UNDOCKED + broadcast_docking_status() + + control_mode = MODE_NONE + tag_target = null + response_sent = 0 + received_confirm = 0 + +/datum/computer/file/embedded_program/docking/proc/force_undock() + //to_world("[id_tag]: forcing undock") + if (tag_target) + send_docking_command(tag_target, "dock_error") + reset() + +/datum/computer/file/embedded_program/docking/proc/docked() + return (dock_state == STATE_DOCKED) + +/datum/computer/file/embedded_program/docking/proc/undocked() + return (dock_state == STATE_UNDOCKED) + +//returns 1 if we are saftely undocked (and the shuttle can leave) +/datum/computer/file/embedded_program/docking/proc/can_launch() + return undocked() + +/datum/computer/file/embedded_program/docking/proc/send_docking_command(var/recipient, var/command) + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["command"] = command + signal.data["recipient"] = recipient + signal.data["code"] = docking_codes + post_signal(signal) + +/datum/computer/file/embedded_program/docking/proc/broadcast_docking_status() + var/datum/signal/signal = new + signal.data["tag"] = id_tag + signal.data["dock_status"] = get_docking_status() + post_signal(signal) + +//this is mostly for NanoUI +/datum/computer/file/embedded_program/docking/proc/get_docking_status() + switch (dock_state) + if (STATE_UNDOCKED) return "undocked" + if (STATE_DOCKING) return "docking" + if (STATE_UNDOCKING) return "undocking" + if (STATE_DOCKED) return "docked" + +/datum/computer/file/embedded_program/docking/proc/get_name() + return display_name ? display_name : "[get_area(master)] ([master.x], [master.y])" + +#undef STATE_UNDOCKED +#undef STATE_DOCKING +#undef STATE_UNDOCKING +#undef STATE_DOCKED + +#undef MODE_NONE +#undef MODE_SERVER #undef MODE_CLIENT \ No newline at end of file diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 17bf13181a..714d27d560 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -1,18 +1,20 @@ /obj/machinery/embedded_controller - var/datum/computer/file/embedded_program/program //the currently executing program - name = "Embedded Controller" anchored = 1 - - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 - + var/datum/computer/file/embedded_program/program //the currently executing program var/on = 1 -obj/machinery/embedded_controller/radio/Destroy() - if(radio_controller) - radio_controller.remove_object(src,frequency) - ..() +/obj/machinery/embedded_controller/Initialize() + if(ispath(program)) + program = new program(src) + return ..() + +/obj/machinery/embedded_controller/Destroy() + if(istype(program)) + qdel(program) // the program will clear the ref in its Destroy + return ..() /obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) return 0 @@ -24,6 +26,17 @@ obj/machinery/embedded_controller/radio/Destroy() program.receive_signal(signal, receive_method, receive_param) //spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0 +/obj/machinery/embedded_controller/Topic(href, href_list) + if((. = ..())) + return + if(usr) + usr.set_machine(src) + src.add_fingerprint(usr) + // We would now pass it to the program, except that some of our embedded controller types want to block certain commands. + // Until/unless that is refactored differently, we rely on subtypes to pass it on. + //if(program) + // return program.receive_user_command(href_list["command"]) + /obj/machinery/embedded_controller/process() if(program) program.process() @@ -40,14 +53,16 @@ obj/machinery/embedded_controller/radio/Destroy() src.ui_interact(user) -/obj/machinery/embedded_controller/ui_interact() - return +// +// Embedded controller with a radio! (Most things (All things?) use this) +// /obj/machinery/embedded_controller/radio icon = 'icons/obj/airlock_machines.dmi' icon_state = "airlock_control_standby" power_channel = ENVIRON density = 0 + unacidable = 1 var/id_tag //var/radio_power_use = 50 //power used to xmit signals @@ -55,11 +70,15 @@ obj/machinery/embedded_controller/radio/Destroy() var/frequency = 1379 var/radio_filter = null var/datum/radio_frequency/radio_connection - unacidable = 1 /obj/machinery/embedded_controller/radio/Initialize() + set_frequency(frequency) // Set it before parent instantiates program . = ..() - set_frequency(frequency) + +/obj/machinery/embedded_controller/radio/Destroy() + if(radio_controller) + radio_controller.remove_object(src,frequency) + ..() /obj/machinery/embedded_controller/radio/update_icon() if(on && program) diff --git a/code/game/machinery/embedded_controller/embedded_program_base.dm b/code/game/machinery/embedded_controller/embedded_program_base.dm index 0cc711c7a4..48340b0c8b 100644 --- a/code/game/machinery/embedded_controller/embedded_program_base.dm +++ b/code/game/machinery/embedded_controller/embedded_program_base.dm @@ -11,8 +11,15 @@ var/obj/machinery/embedded_controller/radio/R = M id_tag = R.id_tag +/datum/computer/file/embedded_program/Destroy() + if(master) + master.program = null + master = null + return ..() + +// Return TRUE if was a command for us, otherwise return FALSE (so controllers with multiple programs can try each in turn until one accepts) /datum/computer/file/embedded_program/proc/receive_user_command(command) - return + return FALSE /datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) return diff --git a/code/game/machinery/embedded_controller/mapping_helpers.dm b/code/game/machinery/embedded_controller/mapping_helpers.dm new file mode 100644 index 0000000000..768fc74beb --- /dev/null +++ b/code/game/machinery/embedded_controller/mapping_helpers.dm @@ -0,0 +1,177 @@ +/* + +Note that these have to be in the same /area that the controller is in for them to function. +You still need to set the controller's "id_tag" to something unique. +Any frequency works, it's self-setting, but it seems like people have decided 1380 for airlocks so maybe set that on the controller too. + +*/ + +/obj/effect/map_helper/airlock + name = "use a subtype!" + icon = 'icons/misc/map_helpers.dmi' + plane = 20 //I dunno just high. + alpha = 170 + + //The controller we're wanting our device to use + var/obj/machinery/embedded_controller/radio/my_controller + var/my_controller_type = /obj/machinery/embedded_controller/radio/airlock + //The device we're setting up + var/my_device + var/my_device_type + //Most things have a radio tag of some sort that needs adjusting + var/tag_addon + +/obj/effect/map_helper/airlock/Initialize() + ..() + my_controller = get_controller(get_area(src)) + my_device = locate(my_device_type) in get_turf(src) + if(!my_device) + to_world("WARNING:Airlock helper '[name]' couldn't find what it wanted at: X:[x] Y:[y] Z:[z]") + else if(!my_controller) + to_world("WARNING:Airlock helper '[name]' couldn't find a controller at: X:[x] Y:[y] Z:[z]") + else if(!my_controller.id_tag) + to_world("WARNING:Airlock helper '[name]' found a controller without an 'id_tag' set: X:[x] Y:[y] Z:[z]") + else + setup() + return INITIALIZE_HINT_QDEL + +/obj/effect/map_helper/airlock/Destroy() + my_controller = null + my_device = null + return ..() + +/obj/effect/map_helper/airlock/proc/get_controller(var/area/A) + if(!A) + return null + + var/list/potentials = list() + for(var/obj/O in A) + if(istype(O, my_controller_type)) + potentials += O + + //Couldn't find one + if(!potentials.len) + return null + + //Only found one + if(potentials.len == 1) + return potentials[1] + + //Gotta find closest + var/closest = potentials[potentials.len] + var/closest_dist = get_dist(src, closest) + potentials.len-- + while(potentials.len) + var/C = potentials[potentials.len] + potentials.len-- + var/dist = get_dist(src, C) + if(dist < closest_dist) + closest_dist = dist + closest = C + + return closest + +/obj/effect/map_helper/airlock/proc/setup() + return //Stub for subtypes + + +/* + Doors +*/ +/obj/effect/map_helper/airlock/door + name = "use a subtype! - airlock door" + my_device_type = /obj/machinery/door/airlock + +/obj/effect/map_helper/airlock/door/setup() + var/obj/machinery/door/airlock/my_airlock = my_device + my_airlock.lock() + my_airlock.id_tag = my_controller.id_tag + tag_addon + my_airlock.frequency = my_controller.frequency + my_airlock.set_frequency(my_controller.frequency) + +/obj/effect/map_helper/airlock/door/ext_door + name = "exterior airlock door" + icon_state = "doorout" + tag_addon = "_outer" + +/obj/effect/map_helper/airlock/door/int_door + name = "interior airlock door" + icon_state = "doorin" + tag_addon = "_inner" + +/obj/effect/map_helper/airlock/door/simple + name = "simple docking controller hatch" + icon_state = "doorsimple" + tag_addon = "_hatch" + my_controller_type = /obj/machinery/embedded_controller/radio/simple_docking_controller + + +/* + Atmos +*/ +/obj/effect/map_helper/airlock/atmos + name = "use a subtype! - airlock pump" + my_device_type = /obj/machinery/atmospherics/unary/vent_pump + +/obj/effect/map_helper/airlock/atmos/setup() + var/obj/machinery/atmospherics/unary/vent_pump/my_pump = my_device + my_pump.frequency = my_controller.frequency //Unlike doors, these set up their radios in atmos init, so they won't have gone before us. + my_pump.id_tag = my_controller.id_tag + tag_addon + +/obj/effect/map_helper/airlock/atmos/chamber_pump + name = "chamber pump" + icon_state = "pump" + tag_addon = "_pump" + +/obj/effect/map_helper/airlock/atmos/pump_out_internal + name = "air dump intake" + icon_state = "pumpdin" + tag_addon = "_pump_out_internal" + +/obj/effect/map_helper/airlock/atmos/pump_out_external + name = "air dump output" + icon_state = "pumpdout" + tag_addon = "_pump_out_external" + + +/* + Sensors - did you know they function as buttons? You don't also need a button. +*/ +/obj/effect/map_helper/airlock/sensor + name = "use a subtype! - airlock sensor" + my_device_type = /obj/machinery/airlock_sensor + var/command + +/obj/effect/map_helper/airlock/sensor/setup() + var/obj/machinery/airlock_sensor/my_sensor = my_device + my_sensor.id_tag = my_controller.id_tag + tag_addon + my_sensor.master_tag = my_controller.id_tag + my_sensor.frequency = my_controller.frequency + my_sensor.set_frequency(my_controller.frequency) + if(command) + my_sensor.command = command + +/obj/effect/map_helper/airlock/sensor/ext_sensor + name = "exterior sensor" + icon_state = "sensout" + tag_addon = "_exterior_sensor" + command = "cycle_exterior" + +/obj/effect/map_helper/airlock/sensor/chamber_sensor + name = "chamber sensor" + icon_state = "sens" + tag_addon = "_sensor" + command = "cycle" + +/obj/effect/map_helper/airlock/sensor/int_sensor + name = "interior sensor" + icon_state = "sensin" + tag_addon = "_interior_sensor" + command = "cycle_interior" + +/* + Buttons +*/ + +/obj/effect/map_helper/airlock/buttons + name = "Just use a sensor instead. They are actually buttons." diff --git a/code/game/machinery/embedded_controller/simple_docking_controller.dm b/code/game/machinery/embedded_controller/simple_docking_controller.dm index 14b27b2512..d2e04a3330 100644 --- a/code/game/machinery/embedded_controller/simple_docking_controller.dm +++ b/code/game/machinery/embedded_controller/simple_docking_controller.dm @@ -1,16 +1,12 @@ //a docking port that uses a single door /obj/machinery/embedded_controller/radio/simple_docking_controller name = "docking hatch controller" + program = /datum/computer/file/embedded_program/docking/simple var/tag_door - var/datum/computer/file/embedded_program/docking/simple/docking_program - -/obj/machinery/embedded_controller/radio/simple_docking_controller/Initialize() - . = ..() - docking_program = new/datum/computer/file/embedded_program/docking/simple(src) - program = docking_program /obj/machinery/embedded_controller/radio/simple_docking_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type data = list( "docking_status" = docking_program.get_docking_status(), @@ -28,11 +24,8 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/simple_docking_controller/Topic(href, href_list) - if(..()) - return 1 - - usr.set_machine(src) - src.add_fingerprint(usr) + if((. = ..())) + return var/clean = 0 switch(href_list["command"]) //anti-HTML-hacking checks @@ -44,8 +37,7 @@ if(clean) program.receive_user_command(href_list["command"]) - return 0 - + return //A docking controller program for a simple door based docking port /datum/computer/file/embedded_program/docking/simple @@ -76,6 +68,7 @@ ..(signal, receive_method, receive_param) /datum/computer/file/embedded_program/docking/simple/receive_user_command(command) + . = TRUE switch(command) if("force_door") if (override_enabled) @@ -88,7 +81,8 @@ disable_override() else enable_override() - + else + . = FALSE /datum/computer/file/embedded_program/docking/simple/proc/signal_door(var/command) var/datum/signal/signal = new diff --git a/code/game/machinery/fire_alarm.dm b/code/game/machinery/fire_alarm.dm new file mode 100644 index 0000000000..82237b32ea --- /dev/null +++ b/code/game/machinery/fire_alarm.dm @@ -0,0 +1,324 @@ +/* +FIRE ALARM +*/ +/obj/machinery/firealarm + name = "fire alarm" + desc = "\"Pull this in case of emergency\". Thus, keep pulling it forever." + icon = 'icons/obj/monitors.dmi' + icon_state = "fire0" + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + var/detecting = 1.0 + var/working = 1.0 + var/time = 10.0 + var/timing = 0.0 + var/lockdownbyai = 0 + anchored = 1.0 + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 6 + power_channel = ENVIRON + var/last_process = 0 + panel_open = 0 + var/seclevel + circuit = /obj/item/weapon/circuitboard/firealarm + var/alarms_hidden = FALSE //If the alarms from this machine are visible on consoles + +/obj/machinery/firealarm/alarms_hidden + alarms_hidden = TRUE + +/obj/machinery/firealarm/Initialize() + . = ..() + if(z in using_map.contact_levels) + set_security_level(security_level ? get_security_level() : "green") + +/obj/machinery/firealarm/update_icon() + cut_overlays() + + if(panel_open) + set_light(0) + return + + if(stat & BROKEN) + icon_state = "firex" + set_light(0) + else if(stat & NOPOWER) + icon_state = "firep" + set_light(0) + else + if(!detecting) + icon_state = "fire1" + set_light(l_range = 4, l_power = 0.9, l_color = "#ff0000") + else + icon_state = "fire0" + switch(seclevel) + if("green") set_light(l_range = 2, l_power = 0.25, l_color = "#00ff00") + if("yellow") set_light(l_range = 2, l_power = 0.25, l_color = "#ffff00") + if("violet") set_light(l_range = 2, l_power = 0.25, l_color = "#9933ff") + if("orange") set_light(l_range = 2, l_power = 0.25, l_color = "#ff9900") + if("blue") set_light(l_range = 2, l_power = 0.25, l_color = "#1024A9") + if("red") set_light(l_range = 4, l_power = 0.9, l_color = "#ff0000") + if("delta") set_light(l_range = 4, l_power = 0.9, l_color = "#FF6633") + add_overlay("overlay_[seclevel]") + +/obj/machinery/firealarm/fire_act(datum/gas_mixture/air, temperature, volume) + if(detecting) + if(temperature > T0C + 200) + alarm() // added check of detector status here + return + +/obj/machinery/firealarm/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/firealarm/bullet_act() + return alarm() + +/obj/machinery/firealarm/emp_act(severity) + if(prob(50 / severity)) + alarm(rand(30 / severity, 60 / severity)) + ..() + +/obj/machinery/firealarm/attackby(obj/item/W as obj, mob/user as mob) + add_fingerprint(user) + + if(alarm_deconstruction_screwdriver(user, W)) + return + if(alarm_deconstruction_wirecutters(user, W)) + return + + if(panel_open) + if(istype(W, /obj/item/device/multitool)) + detecting = !(detecting) + if(detecting) + user.visible_message("\The [user] has reconnected [src]'s detecting unit!", "You have reconnected [src]'s detecting unit.") + else + user.visible_message("\The [user] has disconnected [src]'s detecting unit!", "You have disconnected [src]'s detecting unit.") + return + + alarm() + return + +/obj/machinery/firealarm/process()//Note: this processing was mostly phased out due to other code, and only runs when needed + if(stat & (NOPOWER|BROKEN)) + return + + if(timing) + if(time > 0) + time = time - ((world.timeofday - last_process) / 10) + else + alarm() + time = 0 + timing = 0 + STOP_PROCESSING(SSobj, src) + updateDialog() + last_process = world.timeofday + + if(locate(/obj/fire) in src.loc) + alarm() + + return + +/obj/machinery/firealarm/power_change() + ..() + spawn(rand(0,15)) + update_icon() + +/obj/machinery/firealarm/attack_hand(mob/user as mob) + if(user.stat || stat & (NOPOWER | BROKEN)) + return + + user.set_machine(src) + var/area/A = src.loc + var/d1 + var/d2 + if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon)) + A = A.loc + + if(A.fire) + d1 = text("Reset - Lockdown", src) + else + d1 = text("Alarm - Lockdown", src) + if(timing) + d2 = text("Stop Time Lock", src) + else + d2 = text("Initiate Time Lock", src) + var/second = round(time) % 60 + var/minute = (round(time) - second) / 60 + var/dat = "Fire alarm [d1]\n
The current alert level is: [get_security_level()]

\nTimer System: [d2]
\nTime Left: [(minute ? "[minute]:" : null)][second] - - + +\n
" + user << browse(dat, "window=firealarm") + onclose(user, "firealarm") + else + A = A.loc + if(A.fire) + d1 = text("[]", src, stars("Reset - Lockdown")) + else + d1 = text("[]", src, stars("Alarm - Lockdown")) + if(timing) + d2 = text("[]", src, stars("Stop Time Lock")) + else + d2 = text("[]", src, stars("Initiate Time Lock")) + var/second = round(time) % 60 + var/minute = (round(time) - second) / 60 + var/dat = "[stars("Fire alarm")] [d1]\n
The current alert level is: [stars(get_security_level())]

\nTimer System: [d2]
\nTime Left: [(minute ? text("[]:", minute) : null)][second] - - + +\n
" + user << browse(dat, "window=firealarm") + onclose(user, "firealarm") + return + +/obj/machinery/firealarm/Topic(href, href_list) + ..() + if(usr.stat || stat & (BROKEN | NOPOWER)) + return + + if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + if(href_list["reset"]) + reset() + else if(href_list["alarm"]) + alarm() + else if(href_list["time"]) + timing = text2num(href_list["time"]) + last_process = world.timeofday + START_PROCESSING(SSobj, src) + else if(href_list["tp"]) + var/tp = text2num(href_list["tp"]) + time += tp + time = min(max(round(time), 0), 120) + + updateUsrDialog() + + add_fingerprint(usr) + else + usr << browse(null, "window=firealarm") + return + return + +/obj/machinery/firealarm/proc/reset() + if(!(working)) + return + var/area/area = get_area(src) + for(var/obj/machinery/firealarm/FA in area) + fire_alarm.clearAlarm(src.loc, FA) + update_icon() + return + +/obj/machinery/firealarm/proc/alarm(var/duration = 0) + if(!(working)) + return + var/area/area = get_area(src) + for(var/obj/machinery/firealarm/FA in area) + fire_alarm.triggerAlarm(loc, FA, duration, hidden = alarms_hidden) + update_icon() + playsound(src.loc, 'sound/machines/airalarm.ogg', 25, 0, 4) + return + +/obj/machinery/firealarm/proc/set_security_level(var/newlevel) + if(seclevel != newlevel) + seclevel = newlevel + update_icon() + +/* +FIRE ALARM CIRCUIT +Just a object used in constructing fire alarms + +/obj/item/weapon/firealarm_electronics + name = "fire alarm electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + desc = "A circuit. It has a label on it, it says \"Can handle heat levels up to 40 degrees celsius!\"" + w_class = ITEMSIZE_SMALL + matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) +*/ +/obj/machinery/partyalarm + name = "\improper PARTY BUTTON" + desc = "Cuban Pete is in the house!" + icon = 'icons/obj/monitors.dmi' + icon_state = "fire0" + var/detecting = 1.0 + var/working = 1.0 + var/time = 10.0 + var/timing = 0.0 + var/lockdownbyai = 0 + anchored = 1.0 + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 6 + +/obj/machinery/partyalarm/attack_hand(mob/user as mob) + if(user.stat || stat & (NOPOWER|BROKEN)) + return + + user.machine = src + var/area/A = get_area(src) + ASSERT(isarea(A)) + var/d1 + var/d2 + if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) + + if(A.party) + d1 = text("No Party :(", src) + else + d1 = text("PARTY!!!", src) + if(timing) + d2 = text("Stop Time Lock", src) + else + d2 = text("Initiate Time Lock", src) + var/second = time % 60 + var/minute = (time - second) / 60 + var/dat = text("Party Button []\n
\nTimer System: []
\nTime Left: [][] - - + +\n
", d1, d2, (minute ? text("[]:", minute) : null), second, src, src, src, src) + user << browse(dat, "window=partyalarm") + onclose(user, "partyalarm") + else + if(A.fire) + d1 = text("[]", src, stars("No Party :(")) + else + d1 = text("[]", src, stars("PARTY!!!")) + if(timing) + d2 = text("[]", src, stars("Stop Time Lock")) + else + d2 = text("[]", src, stars("Initiate Time Lock")) + var/second = time % 60 + var/minute = (time - second) / 60 + var/dat = text("[] []\n
\nTimer System: []
\nTime Left: [][] - - + +\n
", stars("Party Button"), d1, d2, (minute ? text("[]:", minute) : null), second, src, src, src, src) + user << browse(dat, "window=partyalarm") + onclose(user, "partyalarm") + return + +/obj/machinery/partyalarm/proc/reset() + if(!(working)) + return + var/area/A = get_area(src) + ASSERT(isarea(A)) + A.partyreset() + return + +/obj/machinery/partyalarm/proc/alarm() + if(!(working)) + return + var/area/A = get_area(src) + ASSERT(isarea(A)) + A.partyalert() + return + +/obj/machinery/partyalarm/Topic(href, href_list) + ..() + if(usr.stat || stat & (BROKEN|NOPOWER)) + return + if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) + usr.machine = src + if(href_list["reset"]) + reset() + else if(href_list["alarm"]) + alarm() + else if(href_list["time"]) + timing = text2num(href_list["time"]) + else if(href_list["tp"]) + var/tp = text2num(href_list["tp"]) + time += tp + time = min(max(round(time), 0), 120) + updateUsrDialog() + + add_fingerprint(usr) + else + usr << browse(null, "window=partyalarm") + return + return diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 169bc8c6d7..572977ceb4 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -11,7 +11,7 @@ var/strength = 10 //How weakened targets are when flashed. var/base_state = "mflash" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 flags = PROXMOVE diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index 59dca1966e..11f5fc26fd 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -7,7 +7,7 @@ var/list/floor_light_cache = list() desc = "A backlit floor panel." layer = TURF_LAYER+0.001 anchored = 0 - use_power = 2 + use_power = USE_POWER_ACTIVE idle_power_usage = 2 active_power_usage = 20 power_channel = LIGHT @@ -72,7 +72,7 @@ var/list/floor_light_cache = list() return on = !on - if(on) use_power = 2 + if(on) update_use_power(USE_POWER_ACTIVE) //visible_message("\The [user] turns \the [src] [on ? "on" : "off"].") //VOREStation Edit - No thankouuuu. Too spammy. update_brightness() return @@ -81,21 +81,21 @@ var/list/floor_light_cache = list() ..() var/need_update if((!anchored || broken()) && on) - use_power = 0 + update_use_power(USE_POWER_OFF) on = 0 need_update = 1 else if(use_power && !on) - use_power = 0 + update_use_power(USE_POWER_OFF) need_update = 1 if(need_update) update_brightness() /obj/machinery/floor_light/proc/update_brightness() - if(on && use_power == 2) + if(on && use_power == USE_POWER_ACTIVE) if(light_range != default_light_range || light_power != default_light_power || light_color != default_light_colour) set_light(default_light_range, default_light_power, default_light_colour) else - use_power = 0 + update_use_power(USE_POWER_OFF) if(light_range || light_power) set_light(0) diff --git a/code/game/machinery/floorlayer.dm b/code/game/machinery/floorlayer.dm index 94f61981ea..e7f39ade5f 100644 --- a/code/game/machinery/floorlayer.dm +++ b/code/game/machinery/floorlayer.dm @@ -53,7 +53,7 @@ else var/obj/item/stack/tile/E = input("Choose remove tile type.", "Tiles") as null|anything in contents if(E) - to_chat(user, "You remove the [E] from /the [src].") + to_chat(user, "You remove the [E] from \the [src].") E.loc = src.loc T = null return @@ -68,7 +68,8 @@ var/dismantle = mode["dismantle"] var/laying = mode["laying"] var/collect = mode["collect"] - user << "\The [src] [!T?"don't ":""]has [!T?"":"[T.get_amount()] [T] "]tile\s, dismantle is [dismantle?"on":"off"], laying is [laying?"on":"off"], collect is [collect?"on":"off"]." + var/message = "\The [src] [!T ? "don't " : ""]has [!T ? "" : "[T.get_amount()] [T] "]tile\s, dismantle is [dismantle ? "on" : "off"], laying is [laying ? "on" : "off"], collect is [collect ? "on" : "off"]." + to_chat(user,message) /obj/machinery/floorlayer/proc/reset() on=0 diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm index 41c7a33a19..330f0ee796 100644 --- a/code/game/machinery/frame.dm +++ b/code/game/machinery/frame.dm @@ -33,10 +33,12 @@ /datum/frame/frame_types/computer name = "Computer" + icon_override = 'icons/obj/stock_parts_vr.dmi' //VOREStation Edit frame_class = FRAME_CLASS_COMPUTER /datum/frame/frame_types/machine name = "Machine" + icon_override = 'icons/obj/stock_parts_vr.dmi' //VOREStation Edit frame_class = FRAME_CLASS_MACHINE /datum/frame/frame_types/conveyor @@ -156,6 +158,7 @@ /datum/frame/frame_types/air_alarm name = "Air Alarm" + icon_override = 'icons/obj/monitors_vr.dmi' //VOREStation Edit - Matching frame. frame_class = FRAME_CLASS_ALARM frame_size = 2 frame_style = FRAME_STYLE_WALL @@ -186,6 +189,13 @@ x_offset = 24 y_offset = 24 +/datum/frame/frame_types/geiger //yw edit start + name = "Geiger Counter" + frame_class = FRAME_CLASS_ALARM + frame_size = 2 + frame_style = FRAME_STYLE_WALL + x_offset = 28 + y_offset = 28 //yw edit ends ////////////////////////////// // Frame Object (Structure) ////////////////////////////// @@ -200,7 +210,7 @@ var/need_circuit = TRUE var/datum/frame/frame_types/frame_type = new /datum/frame/frame_types/machine - var/list/components = null + var/list/components = list() var/list/req_components = null var/list/req_component_names = null @@ -429,6 +439,7 @@ circuit = null if(frame_type.frame_class == FRAME_CLASS_MACHINE) req_components = null + update_desc() else if(state == FRAME_WIRED) if(frame_type.frame_class == FRAME_CLASS_MACHINE) @@ -498,27 +509,21 @@ else if(P.is_wirecutter()) if(state == FRAME_WIRED) - if(frame_type.frame_class == FRAME_CLASS_COMPUTER) + if( \ + frame_type.frame_class == FRAME_CLASS_COMPUTER || \ + frame_type.frame_class == FRAME_CLASS_DISPLAY || \ + frame_type.frame_class == FRAME_CLASS_ALARM || \ + frame_type.frame_class == FRAME_CLASS_MACHINE \ + ) playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = FRAME_FASTENED - new /obj/item/stack/cable_coil(src.loc, 5) - - else if(frame_type.frame_class == FRAME_CLASS_DISPLAY) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = FRAME_FASTENED - new /obj/item/stack/cable_coil(src.loc, 5) - - else if(frame_type.frame_class == FRAME_CLASS_ALARM) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = FRAME_FASTENED - new /obj/item/stack/cable_coil(src.loc, 5) - - else if(frame_type.frame_class == FRAME_CLASS_MACHINE) - playsound(src, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") + if (components.len == 0) + to_chat(user, "You remove the cables.") + else + to_chat(user, "You remove the cables and components.") + for(var/obj/item/weapon/W in components) + W.forceMove(src.loc) + check_components() + update_desc() state = FRAME_FASTENED new /obj/item/stack/cable_coil(src.loc, 5) @@ -615,4 +620,4 @@ to_chat(usr, "You rotate the [src] to face [dir2text(dir)]!") - return \ No newline at end of file + return diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 623cac6ef0..4fec2cba82 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -39,7 +39,7 @@ var/const/HOLOPAD_MODE = RANGE_BASED layer = ABOVE_TURF_LAYER var/power_per_hologram = 500 //per usage per hologram idle_power_usage = 5 - use_power = 1 + use_power = USE_POWER_IDLE var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad var/last_request = 0 //to prevent request spam. ~Carn var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. @@ -57,13 +57,13 @@ var/const/HOLOPAD_MODE = RANGE_BASED if(alert(user,"Would you like to request an AI's presence?",,"Yes","No") == "Yes") if(last_request + 200 < world.time) //don't spam the AI with requests you jerk! last_request = world.time - user << "You request an AI's presence." + to_chat(user, "You request an AI's presence.") var/area/area = get_area(src) for(var/mob/living/silicon/ai/AI in living_mob_list) if(!AI.client) continue - AI << "Your presence is requested at \the [area]." + to_chat(AI, "Your presence is requested at \the [area].") else - user << "A request for AI presence was already sent recently." + to_chat(user, "A request for AI presence was already sent recently.") /obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) if(!istype(user)) @@ -82,12 +82,12 @@ var/const/HOLOPAD_MODE = RANGE_BASED /obj/machinery/hologram/holopad/proc/activate_holo(mob/living/silicon/ai/user) if(!(stat & NOPOWER) && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it if(user.holo) - user << "ERROR: Image feed in progress." + to_chat(user, "ERROR: Image feed in progress.") return create_holo(user)//Create one. visible_message("A holographic image of [user] flicks to life right before your eyes!") else - user << "ERROR: Unable to project hologram." + to_chat(user, "ERROR: Unable to project hologram.") return /*This is the proc for special two-way communication between AI and holopad/people talking near holopad. @@ -196,7 +196,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ /obj/machinery/hologram anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 100 @@ -235,7 +235,7 @@ Holographic project of everything else. flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. hologram.icon = flat_icon - world << "Your icon should appear now." + to_world("Your icon should appear now.") return */ diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm index 39a91b8aff..7a3cd8c861 100644 --- a/code/game/machinery/holosign.dm +++ b/code/game/machinery/holosign.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/holosign.dmi' icon_state = "sign_off" plane = MOB_PLANE - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 anchored = 1 @@ -13,24 +13,29 @@ var/id = null var/on_icon = "sign_on" var/off_icon = "sign_off" + var/signlight = "#E9E4AF" /obj/machinery/holosign/proc/toggle() if(stat & (BROKEN|NOPOWER)) return lit = !lit - use_power = lit ? 2 : 1 + update_use_power(lit ? USE_POWER_ACTIVE : USE_POWER_IDLE) update_icon() /obj/machinery/holosign/update_icon() if(!lit) icon_state = off_icon + set_light(0) else icon_state = on_icon + set_light(2, 0.25, signlight) /obj/machinery/holosign/power_change() + ..() if(stat & NOPOWER) lit = 0 - use_power = 0 + update_use_power(USE_POWER_OFF) + update_icon() /obj/machinery/holosign/surgery @@ -49,7 +54,7 @@ icon_state = "barclosed" on_icon = "baropen" off_icon = "barclosed" - + signlight = "#b1edf9" ////////////////////SWITCH/////////////////////////////////////// diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 5cb521d423..ee887db2f5 100755 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -6,7 +6,7 @@ var/id = null var/on = 1.0 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 @@ -53,7 +53,7 @@ var/last_spark = 0 var/base_state = "migniter" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index a9e5d7c154..a0ef0503fd 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -17,10 +17,11 @@ anchored = 1 density = 1 power_channel = EQUIP - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 100 circuit = /obj/item/weapon/circuitboard/jukebox + clicksound = 'sound/machines/buttonbeep.ogg' // Vars for hacking var/datum/wires/jukebox/wires = null @@ -320,7 +321,7 @@ /obj/machinery/media/jukebox/proc/StopPlaying() playing = 0 - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() start_stop_song() @@ -328,7 +329,7 @@ if(!current_track) return playing = 1 - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) update_icon() start_stop_song() updateDialog() diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 2a1a7f6e89..afa853c41a 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -7,7 +7,7 @@ icon = 'icons/obj/power_vr.dmi' // VOREStation Edit icon_state = "light1" anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 power_channel = LIGHT var/on = 1 @@ -15,19 +15,24 @@ var/otherarea = null var/image/overlay -/obj/machinery/light_switch/New() - ..() - spawn(5) - area = get_area(src) +/obj/machinery/light_switch/Initialize() + . = ..() - if(otherarea) - area = locate(text2path("/area/[otherarea]")) + area = get_area(src) - if(!name) - name = "light switch ([area.name])" + if(otherarea) + area = locate(text2path("/area/[otherarea]")) - on = area.lightswitch - updateicon() + if(!name) + name = "light switch ([area.name])" + + on = area.lightswitch + updateicon() + +/obj/machinery/light_switch/Destroy() + area = null + overlay = null + return ..() /obj/machinery/light_switch/proc/updateicon() if(!overlay) diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index 5d2ed9a174..9f3132207f 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -101,7 +101,7 @@ Class Procs: var/stat = 0 var/emagged = 0 - var/use_power = 1 + var/use_power = USE_POWER_IDLE //0 = dont run the auto //1 = run auto, use idle //2 = run auto, use active @@ -123,7 +123,7 @@ Class Procs: ..(l) if(d) set_dir(d) - if(circuit) + if(ispath(circuit)) circuit = new circuit(src) /obj/machinery/Initialize() @@ -199,9 +199,9 @@ Class Procs: /obj/machinery/proc/auto_use_power() if(!powered(power_channel)) return 0 - if(use_power == 1) + if(use_power == USE_POWER_IDLE) use_power(idle_power_usage, power_channel, 1) - else if(use_power >= 2) + else if(use_power >= USE_POWER_ACTIVE) use_power(active_power_usage, power_channel, 1) return 1 @@ -268,7 +268,7 @@ Class Procs: /obj/machinery/proc/state(var/msg) for(var/mob/O in hearers(src, null)) - O.show_message("\icon[src] [msg]", 2) + O.show_message("[bicon(src)] [msg]", 2) /obj/machinery/proc/ping(text=null) if(!text) diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 9f97ab0360..ebbda06d6d 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -11,7 +11,7 @@ desc = "A device that uses station power to create points of magnetic energy." plane = PLATING_PLANE anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 var/freq = 1449 // radio frequency @@ -142,10 +142,10 @@ // Update power usage: if(on) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) active_power_usage = electricity_level*15 else - use_power = 0 + update_use_power(USE_POWER_OFF) // Overload conditions: /* // Eeeehhh kinda stupid @@ -190,7 +190,7 @@ icon_state = "airlock_control_standby" density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 45 var/frequency = 1449 var/code = 0 @@ -275,7 +275,7 @@ // Prepare signal beforehand, because this is a radio operation var/datum/signal/signal = new - signal.transmission_method = 1 // radio transmission + signal.transmission_method = TRANSMISSION_RADIO // radio transmission signal.source = src signal.frequency = frequency signal.data["code"] = code @@ -341,7 +341,7 @@ // Prepare the radio signal var/datum/signal/signal = new - signal.transmission_method = 1 // radio transmission + signal.transmission_method = TRANSMISSION_RADIO // radio transmission signal.source = src signal.frequency = frequency signal.data["code"] = code diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index 1d13a36c3a..7c184c27de 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -6,7 +6,7 @@ icon = 'icons/obj/stationobjs.dmi' icon_state = "mass_driver" anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 50 circuit = /obj/item/weapon/circuitboard/mass_driver diff --git a/code/game/machinery/neonsign.dm b/code/game/machinery/neonsign.dm new file mode 100644 index 0000000000..bdaa3b62fd --- /dev/null +++ b/code/game/machinery/neonsign.dm @@ -0,0 +1,73 @@ +////////////////////NOTHOLOSIGN/////////////////////////////////////// +/obj/machinery/neonsign + name = "neon sign" + desc = "Small wall-mounted electronic sign" + icon = 'icons/obj/neonsigns.dmi' + icon_state = "sign_off" + plane = MOB_PLANE + use_power = USE_POWER_IDLE + idle_power_usage = 2 + active_power_usage = 4 + anchored = 1 + var/lit = 0 + var/id = null + var/on_icon = "sign_on" + var/off_icon = "sign_off" + var/signlight = "#E9E4AF" + +/obj/machinery/neonsign/proc/toggle() + if(stat & (BROKEN|NOPOWER)) + return + lit = !lit + update_use_power(lit ? USE_POWER_ACTIVE : USE_POWER_IDLE) + update_icon() + +/obj/machinery/neonsign/update_icon() + if(!lit) + icon_state = off_icon + set_light(0) + else + icon_state = on_icon + set_light(2, 0.25, signlight) + +/obj/machinery/neonsign/power_change() + ..() + if(stat & NOPOWER) + lit = 0 + update_use_power(USE_POWER_OFF) + + update_icon() + +/obj/machinery/neonsign/cafe + name = "cafe neon sign" + desc = "Small wall-mounted electronic sign. This one reads CAFE." + icon_state = "cafesign_off" + on_icon = "cafesign_on" + off_icon = "cafesign_off" + signlight = "#DFA571" + +////////////////////SWITCH/////////////////////////////////////// + +/obj/machinery/button/neonsign + name = "sign switch" + desc = "A remote control switch for neon sign." + icon = 'icons/obj/power.dmi' + icon_state = "crema_switch" + +/obj/machinery/button/neonsign/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + + use_power(5) + + active = !active + icon_state = "light[active]" + + for(var/obj/machinery/neonsign/M in machines) + if(M.id == id) + spawn(0) + M.toggle() + return + + return \ No newline at end of file diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 9f61a8ab7c..883a963fa2 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -15,6 +15,7 @@ var/backup_author = "" var/icon/backup_img = null var/icon/backup_caption = "" + var/post_time = 0 /datum/feed_channel var/channel_name="" @@ -78,6 +79,7 @@ newMsg.body = msg newMsg.time_stamp = "[stationtime2text()]" newMsg.is_admin_message = adminMessage + newMsg.post_time = round_duration_in_ticks // Should be almost universally unique if(message_type) newMsg.message_type = message_type if(photo) @@ -118,12 +120,11 @@ var/datum/feed_network/news_network = new /datum/feed_network //The global news-network, which is coincidentally a global list. -var/list/obj/machinery/newscaster/allCasters = list() //Global list that will contain reference to all newscasters in existence. - +GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster) /obj/machinery/newscaster name = "newscaster" desc = "A standard newsfeed handler for use on commercial space stations. All the news you absolutely have no use for, in one place!" - icon = 'icons/obj/terminals.dmi' + icon = 'icons/obj/terminals_vr.dmi' //VOREStation Edit icon_state = "newscaster_normal" plane = TURF_PLANE layer = ABOVE_TURF_LAYER @@ -149,7 +150,8 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co var/securityCaster = 0 // 0 = Caster cannot be used to issue wanted posters // 1 = the opposite - var/unit_no = 0 //Each newscaster has a unit number + var/static/unit_no_cur = 0 //Each newscaster has a unit number + var/unit_no //var/datum/feed_message/wanted //We're gonna use a feed_message to store data of the wanted person because fields are similar //var/wanted_issue = 0 //OBSOLETE // 0 = there's no WANTED issued, we don't need a special icon_state @@ -174,19 +176,21 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co name = "Security Newscaster" securityCaster = 1 -/obj/machinery/newscaster/New() //Constructor, ho~ +/obj/machinery/newscaster/Initialize() + ..() //Not returning . because lateload below allCasters += src - paper_remaining = 15 // Will probably change this to something better - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) // Let's give it an appropriate unit number - unit_no++ - update_icon() //for any custom ones on the map... - spawn(10) //Should be enough time for the node to spawn at tcomms. - node = get_exonet_node() - ..() //I just realised the newscasters weren't in the global machines list. The superconstructor call will tend to that + unit_no = ++unit_no_cur + paper_remaining = 15 + update_icon() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/newscaster/LateInitialize() + node = get_exonet_node() /obj/machinery/newscaster/Destroy() allCasters -= src - ..() + node = null + return ..() /obj/machinery/newscaster/update_icon() if(!ispowered || isbroken) @@ -253,8 +257,8 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co node = get_exonet_node() if(!node || !node.on || !node.allow_external_newscasters) - user << "Error: Cannot connect to external content. Please try again in a few minutes. If this error persists, please \ - contact the system administrator." + to_chat(user, "Error: Cannot connect to external content. Please try again in a few minutes. If this error persists, please \ + contact the system administrator.") return 0 if(!user.IsAdvancedToolUser()) @@ -323,11 +327,11 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(6) dat+="ERROR: Could not submit Feed story to Network.

" if(channel_name=="") - dat+="Invalid receiving channel name.
" + dat+="•Invalid receiving channel name.
" if(scanned_user=="Unknown") - dat+="Channel author unverified.
" + dat+="•Channel author unverified.
" if(msg == "" || msg == "\[REDACTED\]") - dat+="Invalid message body.
" + dat+="•Invalid message body.
" dat+="
Return
" if(7) @@ -339,18 +343,18 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co else existing_authors += FC.author if(scanned_user in existing_authors) - dat+="There already exists a Feed channel under your name.
" + dat+="•There already exists a Feed channel under your name.
" if(channel_name=="" || channel_name == "\[REDACTED\]") - dat+="Invalid channel name.
" + dat+="•Invalid channel name.
" var/check = 0 for(var/datum/feed_channel/FC in news_network.network_channels) if(FC.channel_name == channel_name) check = 1 break if(check) - dat+="Channel name already in use.
" + dat+="•Channel name already in use.
" if(scanned_user=="Unknown") - dat+="Channel author unverified.
" + dat+="•Channel author unverified.
" dat+="
Return
" if(8) var/total_num=length(news_network.network_channels) @@ -464,11 +468,11 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(16) dat+="ERROR: Wanted Issue rejected by Network.

" if(channel_name=="" || channel_name == "\[REDACTED\]") - dat+="Invalid name for person wanted.
" + dat+="•Invalid name for person wanted.
" if(scanned_user=="Unknown") - dat+="Issue author unverified.
" + dat+="•Issue author unverified.
" if(msg == "" || msg == "\[REDACTED\]") - dat+="Invalid description.
" + dat+="•Invalid description.
" dat+="
Return
" if(17) dat+="Wanted Issue successfully deleted from Circulation
" @@ -805,6 +809,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co var/datum/feed_message/important_message = null var/scribble="" var/scribble_page = null + drop_sound = 'sound/items/drop/wrapper.ogg' obj/item/weapon/newspaper/attack_self(mob/user as mob) if(ishuman(user)) @@ -814,7 +819,7 @@ obj/item/weapon/newspaper/attack_self(mob/user as mob) switch(screen) if(0) //Cover dat+="
The Griffon
" - dat+="
[using_map.company_name]-standard newspaper, for use on [using_map.company_name] Space Facilities

" + dat+="
[using_map.company_name]-standard newspaper, for use on [using_map.company_name]© Space Facilities

" if(isemptylist(news_content)) if(important_message) dat+="Contents:
    **Important Security Announcement** \[page [pages+2]\]
" @@ -883,7 +888,7 @@ obj/item/weapon/newspaper/attack_self(mob/user as mob) human_user << browse(dat, "window=newspaper_main;size=300x400") onclose(human_user, "newspaper_main") else - user << "The paper is full of intelligible symbols!" + to_chat(user, "The paper is full of intelligible symbols!") obj/item/weapon/newspaper/Topic(href, href_list) var/mob/living/U = usr @@ -919,7 +924,7 @@ obj/item/weapon/newspaper/Topic(href, href_list) obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/pen)) if(scribble_page == curr_page) - user << "There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?" + to_chat(user, "There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?") else var/s = sanitize(input(user, "Write something", "Newspaper", "")) s = sanitize(s) diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm index 53c4dcdd28..8d6a55b3fd 100644 --- a/code/game/machinery/nuclear_bomb.dm +++ b/code/game/machinery/nuclear_bomb.dm @@ -23,7 +23,7 @@ var/bomb_set var/timing_wire var/removal_stage = 0 // 0 is no removal, 1 is covers removed, 2 is covers open, // 3 is sealant open, 4 is unwrenched, 5 is removed from bolts. - use_power = 0 + use_power = USE_POWER_OFF /obj/machinery/nuclearbomb/New() ..() @@ -393,13 +393,13 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob) ticker.station_explosion_cinematic(off_station,null) if(ticker.mode) ticker.mode.explosion_in_progress = 0 - world << "The station was destoyed by the nuclear blast!" + to_world("The station was destoyed by the nuclear blast!") ticker.mode.station_was_nuked = (off_station<2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated. //kinda shit but I couldn't get permission to do what I wanted to do. if(!ticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is - world << "Resetting in 30 seconds!" + to_world("Resetting in 30 seconds!") feedback_set_details("end_error","nuke - unhandled ending") diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm index 0ec65cc30d..557f9169dd 100644 --- a/code/game/machinery/overview.dm +++ b/code/game/machinery/overview.dm @@ -26,7 +26,7 @@ imap += icon('icons/misc/imap.dmi', "blank") imap += icon('icons/misc/imap.dmi', "blank") - //world << "[icount] images in list" + //to_world("[icount] images in list") for(var/wx = 1 ; wx <= world.maxx; wx++) @@ -134,11 +134,11 @@ var/rx = ((wx*2+xoff)%32) + 1 var/ry = ((wy*2+yoff)%32) + 1 - //world << "trying [ix],[iy] : [ix+icx*iy]" + //to_world("trying [ix],[iy] : [ix+icx*iy]") var/icon/I = imap[1+(ix + icx*iy)*2] var/icon/I2 = imap[2+(ix + icx*iy)*2] - //world << "icon: \icon[I]" + //to_world("icon: [bicon(I)]") I.DrawBox(colour, rx, ry, rx+1, ry+1) @@ -153,7 +153,7 @@ H.screen_loc = "[5 + i%icx],[6+ round(i/icx)]" - //world<<"\icon[I] at [H.screen_loc]" + //to_world("[bicon(I)] at [H.screen_loc]") H.name = (i==0)?"maprefresh":"map" @@ -263,10 +263,10 @@ var/rx = ((wx*2+xoff)%32) + 1 var/ry = ((wy*2+yoff)%32) + 1 - //world << "trying [ix],[iy] : [ix+icx*iy]" + //to_world("trying [ix],[iy] : [ix+icx*iy]") var/icon/I = imap[1+(ix + icx*iy)] - //world << "icon: \icon[I]" + //to_world("icon: [bicon(I)]") I.DrawBox(colour, rx, ry, rx, ry) @@ -279,7 +279,7 @@ H.screen_loc = "[5 + i%icx],[6+ round(i/icx)]" - //world<<"\icon[I] at [H.screen_loc]" + //to_world("[bicon(I)] at [H.screen_loc]") H.name = (i==0)?"maprefresh":"map" diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 9378cc2f92..10c96a6b6a 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -73,7 +73,7 @@ if(breather.internals) breather.internals.icon_state = "internal0" breather = null - use_power = 1 + update_use_power(USE_POWER_IDLE) /obj/machinery/oxygen_pump/attack_ai(mob/user as mob) ui_interact(user) @@ -90,7 +90,7 @@ breather.internal = tank if(breather.internals) breather.internals.icon_state = "internal1" - use_power = 2 + update_use_power(USE_POWER_ACTIVE) /obj/machinery/oxygen_pump/proc/can_apply_to_target(var/mob/living/carbon/human/target, mob/user as mob) if(!user) @@ -162,7 +162,7 @@ contained.forceMove(src) src.visible_message("\The [contained] rapidly retracts back into \the [src]!") breather = null - use_power = 1 + update_use_power(USE_POWER_IDLE) else if(!breather.internal && tank) breather.internal = tank if(breather.internals) @@ -287,16 +287,29 @@ contained.forceMove(src) src.visible_message("\The [contained] rapidly retracts back into \the [src]!") breather = null - use_power = 1 + update_use_power(USE_POWER_IDLE) else if(!breather.internal && tank) breather.internal = tank if(breather.internals) breather.internals.icon_state = "internal0" if(breather) // Safety. - if(ishuman(breather)) + if(ishuman(breather) && !(breather.isSynthetic())) var/mob/living/carbon/human/H = breather + if(H.internal_organs_by_name[O_LUNGS]) + var/obj/item/organ/internal/L = H.internal_organs_by_name[O_LUNGS] + if(L) + if(!(L.status & ORGAN_DEAD)) + H.adjustOxyLoss(-(rand(10,15))) + + if(L.is_bruised() && prob(30)) + L.take_damage(-1) + else + H.AdjustLosebreath(-(rand(1, 5))) + else + H.adjustOxyLoss(-(rand(1,8))) + if(H.stat == DEAD) H.add_modifier(/datum/modifier/bloodpump_corpse, 6 SECONDS) diff --git a/code/game/machinery/partslathe_vr.dm b/code/game/machinery/partslathe_vr.dm index d2d9079ab1..7763e28f1a 100644 --- a/code/game/machinery/partslathe_vr.dm +++ b/code/game/machinery/partslathe_vr.dm @@ -23,7 +23,7 @@ circuit = /obj/item/weapon/circuitboard/partslathe anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 5000 @@ -88,7 +88,7 @@ /obj/machinery/partslathe/attackby(var/obj/item/O as obj, var/mob/user as mob) if(busy) - user << "\The [src] is busy. Please wait for completion of previous operation." + to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") return 1 if(default_deconstruction_screwdriver(user, O)) return @@ -99,11 +99,11 @@ if(inoperable()) return if(panel_open) - user << "You can't load \the [src] while it's opened." + to_chat(user, "You can't load \the [src] while it's opened.") return if(istype(O, /obj/item/weapon/circuitboard)) if(copy_board) - user << "There is already a board inserted in \the [src]." + to_chat(user, "There is already a board inserted in \the [src].") return if(!user.unEquip(O)) return @@ -115,7 +115,7 @@ if(try_load_materials(user, O)) return else - user << "You cannot insert this item into \the [src]!" + to_chat(user, "You cannot insert this item into \the [src]!") return // Attept to load materials. Returns 0 if item wasn't a stack of materials, otherwise 1 (even if failed to load) @@ -123,7 +123,7 @@ if(!istype(S)) return 0 if(!(S.material.name in materials)) - user << "The [src] doesn't accept [S.material]!" + to_chat(user, "The [src] doesn't accept [S.material]!") return 1 if(S.amount < 1) return 1 // Does this even happen? Sanity check I guess. @@ -138,7 +138,7 @@ flick("partslathe-load-[S.material.name]", src) updateUsrDialog() else - user << "\The [src] cannot hold more [S.name]." + to_chat(user, "\The [src] cannot hold more [S.name].") return 1 /obj/machinery/partslathe/process() @@ -155,7 +155,7 @@ var/datum/category_item/partslathe/D = queue[1] if(canBuild(D)) busy = 1 - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) progress += speed if(progress >= D.time) build(D) @@ -165,7 +165,7 @@ else if(busy) visible_message("\icon [src] flashes: insufficient materials: [getLackingMaterials(D)].") busy = 0 - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() playsound(src.loc, 'sound/machines/chime.ogg', 50, 0) @@ -326,7 +326,7 @@ return if(busy) - usr << "\The [src]is busy. Please wait for completion of previous operation." + to_chat(usr, "\The [src]is busy. Please wait for completion of previous operation.") return if(href_list["ejectBoard"]) diff --git a/code/game/machinery/pda_multicaster.dm b/code/game/machinery/pda_multicaster.dm index 3388de0c31..aab6a31ded 100644 --- a/code/game/machinery/pda_multicaster.dm +++ b/code/game/machinery/pda_multicaster.dm @@ -6,7 +6,7 @@ density = 1 anchored = 1 circuit = /obj/item/weapon/circuitboard/telecomms/pda_multicaster - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 750 var/on = 1 // If we're currently active, var/toggle = 1 // If we /should/ be active or not, diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index e83da8222d..13b1dc1bb7 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -83,6 +83,12 @@ Buildable meters if(PIPING_LAYER_SUPPLY) color = PIPE_COLOR_BLUE name = "[initial(fakeA.name)] supply fitting" + if(PIPING_LAYER_FUEL) + color = PIPE_COLOR_YELLOW + name = "[initial(fakeA.name)] fuel fitting" + if(PIPING_LAYER_AUX) + color = PIPE_COLOR_CYAN + name = "[initial(fakeA.name)] aux fitting" // Or if we were to do it the TG way... // pixel_x = PIPE_PIXEL_OFFSET_X(piping_layer) // pixel_y = PIPE_PIXEL_OFFSET_Y(piping_layer) diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm index 30d44b09c0..9eb323dfa3 100644 --- a/code/game/machinery/pipe/pipe_dispenser.dm +++ b/code/game/machinery/pipe/pipe_dispenser.dm @@ -8,6 +8,13 @@ var/unwrenched = 0 var/wait = 0 var/p_layer = PIPING_LAYER_REGULAR + var/static/list/pipe_layers = list( + "Regular" = PIPING_LAYER_REGULAR, + "Supply" = PIPING_LAYER_SUPPLY, + "Scrubber" = PIPING_LAYER_SCRUBBER, + "Fuel" = PIPING_LAYER_FUEL, + "Aux" = PIPING_LAYER_AUX + ) // TODO - Its about time to make this NanoUI don't we think? /obj/machinery/pipedispenser/attack_hand(var/mob/user as mob) @@ -22,11 +29,10 @@ for(var/category in atmos_pipe_recipes) lines += "[category]:
" if(category == "Pipes") - // Stupid hack. Fix someday. So tired right now. - lines += "Regular " - lines += "Supply " - lines += "Scrubber " - lines += "
" + for(var/pipename in pipe_layers) + var/pipelayer = pipe_layers[pipename] + lines += "[pipename] " + lines += "
" for(var/datum/pipe_recipe/PI in atmos_pipe_recipes[category]) lines += PI.Render(src) var/dat = lines.Join() @@ -71,14 +77,14 @@ /obj/machinery/pipedispenser/attackby(var/obj/item/W as obj, var/mob/user as mob) src.add_fingerprint(usr) if (istype(W, /obj/item/pipe) || istype(W, /obj/item/pipe_meter)) - usr << "You put [W] back to [src]." + to_chat(usr, "You put [W] back to [src].") user.drop_item() qdel(W) return else if(W.is_wrench()) if (unwrenched==0) playsound(src, W.usesound, 50, 1) - user << "You begin to unfasten \the [src] from the floor..." + to_chat(user, "You begin to unfasten \the [src] from the floor...") if (do_after(user, 40 * W.toolspeed)) user.visible_message( \ "[user] unfastens \the [src].", \ @@ -91,7 +97,7 @@ usr << browse(null, "window=pipedispenser") else /*if (unwrenched==1)*/ playsound(src, W.usesound, 50, 1) - user << "You begin to fasten \the [src] to the floor..." + to_chat(user, "You begin to fasten \the [src] to the floor...") if (do_after(user, 20 * W.toolspeed)) user.visible_message( \ "[user] fastens \the [src].", \ diff --git a/code/game/machinery/pipe/pipe_recipes.dm b/code/game/machinery/pipe/pipe_recipes.dm index c8ebc3eeb7..9d916472bb 100644 --- a/code/game/machinery/pipe/pipe_recipes.dm +++ b/code/game/machinery/pipe/pipe_recipes.dm @@ -9,26 +9,30 @@ var/global/list/all_pipe_recipes = null /hook/startup/proc/init_pipe_recipes() global.atmos_pipe_recipes = list( "Pipes" = list( - new /datum/pipe_recipe/pipe("Pipe", /obj/machinery/atmospherics/pipe/simple, TRUE), - new /datum/pipe_recipe/pipe("Manifold", /obj/machinery/atmospherics/pipe/manifold, TRUE), + new /datum/pipe_recipe/pipe("Pipe", /obj/machinery/atmospherics/pipe/simple), + new /datum/pipe_recipe/pipe("Manifold", /obj/machinery/atmospherics/pipe/manifold), new /datum/pipe_recipe/pipe("Manual Valve", /obj/machinery/atmospherics/valve), new /datum/pipe_recipe/pipe("Digital Valve", /obj/machinery/atmospherics/valve/digital), - new /datum/pipe_recipe/pipe("Pipe cap", /obj/machinery/atmospherics/pipe/cap, TRUE), - new /datum/pipe_recipe/pipe("4-Way Manifold", /obj/machinery/atmospherics/pipe/manifold4w, TRUE), + new /datum/pipe_recipe/pipe("Pipe cap", /obj/machinery/atmospherics/pipe/cap), + new /datum/pipe_recipe/pipe("4-Way Manifold", /obj/machinery/atmospherics/pipe/manifold4w), new /datum/pipe_recipe/pipe("Manual T-Valve", /obj/machinery/atmospherics/tvalve), new /datum/pipe_recipe/pipe("Digital T-Valve", /obj/machinery/atmospherics/tvalve/digital), - new /datum/pipe_recipe/pipe("Upward Pipe", /obj/machinery/atmospherics/pipe/zpipe/up, TRUE), - new /datum/pipe_recipe/pipe("Downward Pipe", /obj/machinery/atmospherics/pipe/zpipe/down, TRUE), - new /datum/pipe_recipe/pipe("Universal Pipe Adaptor",/obj/machinery/atmospherics/pipe/simple/visible/universal, TRUE), + new /datum/pipe_recipe/pipe("Upward Pipe", /obj/machinery/atmospherics/pipe/zpipe/up), + new /datum/pipe_recipe/pipe("Downward Pipe", /obj/machinery/atmospherics/pipe/zpipe/down), + new /datum/pipe_recipe/pipe("Universal Pipe Adaptor",/obj/machinery/atmospherics/pipe/simple/visible/universal), ), "Devices" = list( new /datum/pipe_recipe/pipe("Connector", /obj/machinery/atmospherics/portables_connector), new /datum/pipe_recipe/pipe("Unary Vent", /obj/machinery/atmospherics/unary/vent_pump), + new /datum/pipe_recipe/pipe("Aux Vent", /obj/machinery/atmospherics/unary/vent_pump/aux), new /datum/pipe_recipe/pipe("Passive Vent", /obj/machinery/atmospherics/pipe/vent), new /datum/pipe_recipe/pipe("Injector", /obj/machinery/atmospherics/unary/outlet_injector), new /datum/pipe_recipe/pipe("Gas Pump", /obj/machinery/atmospherics/binary/pump), + new /datum/pipe_recipe/pipe("Fuel Pump", /obj/machinery/atmospherics/binary/pump/fuel), + new /datum/pipe_recipe/pipe("Aux Pump", /obj/machinery/atmospherics/binary/pump/aux), new /datum/pipe_recipe/pipe("Pressure Regulator", /obj/machinery/atmospherics/binary/passive_gate), new /datum/pipe_recipe/pipe("High Power Gas Pump", /obj/machinery/atmospherics/binary/pump/high_power), + new /datum/pipe_recipe/pipe("Automatic Shutoff Valve",/obj/machinery/atmospherics/valve/shutoff), new /datum/pipe_recipe/pipe("Scrubber", /obj/machinery/atmospherics/unary/vent_scrubber), new /datum/pipe_recipe/meter("Meter"), new /datum/pipe_recipe/pipe("Gas Filter", /obj/machinery/atmospherics/trinary/atmos_filter), diff --git a/code/game/machinery/pointdefense.dm b/code/game/machinery/pointdefense.dm new file mode 100644 index 0000000000..3ca0ed601e --- /dev/null +++ b/code/game/machinery/pointdefense.dm @@ -0,0 +1,386 @@ +// +// Control computer for point defense batteries. +// Handles control UI, but also coordinates their fire to avoid overkill. +// + +GLOBAL_LIST_BOILERPLATE(pointdefense_controllers, /obj/machinery/pointdefense_control) +GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) + +/obj/machinery/pointdefense_control + name = "fire assist mainframe" + desc = "A specialized computer designed to synchronize a variety of weapon systems and a vessel's astronav data." + icon = 'icons/obj/pointdefense.dmi' + icon_state = "control" + density = TRUE + anchored = TRUE + circuit = /obj/item/weapon/circuitboard/pointdefense_control + var/list/targets = list() // Targets being engaged by associated batteries + var/ui_template = "pointdefense_control.tmpl" + var/id_tag = null + +/obj/machinery/pointdefense_control/Initialize(mapload) + . = ..() + if(id_tag) + //No more than 1 controller please. + for(var/thing in pointdefense_controllers) + var/obj/machinery/pointdefense_control/PC = thing + if(PC != src && PC.id_tag == id_tag) + warning("Two [src] with the same id_tag of [id_tag]") + id_tag = null + // TODO - Remove this bit once machines are converted to Initialize + if(ispath(circuit)) + circuit = new circuit(src) + default_apply_parts() + +/obj/machinery/pointdefense_control/get_description_interaction() + . = ..() + if(!id_tag) + . += "[desc_panel_image("multitool")]to set ident tag" + +/obj/machinery/pointdefense_control/ui_interact(var/mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(ui_template) + var/list/data = build_ui_data() + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, ui_template, name, 400, 600) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/pointdefense_control/attack_hand(mob/user) + if((. = ..())) + return + if(CanUseTopic(user, global.default_state) > STATUS_CLOSE) + ui_interact(user) + return TRUE + +/obj/machinery/pointdefense_control/Topic(var/mob/user, var/href_list) + if((. = ..())) + return + + if(href_list["toggle_active"]) + var/obj/machinery/power/pointdefense/PD = locate(href_list["toggle_active"]) + if(!istype(PD)) + return TOPIC_NOACTION + + //if(!lan || !lan.is_connected(PD)) + if(PD.id_tag != id_tag) + return TOPIC_NOACTION + + if(!(get_z(PD) in GetConnectedZlevels(get_z(src)))) + to_chat(user, "[PD] is not within control range.") + return TOPIC_NOACTION + + if(!PD.Activate()) //Activate() whilst the device is active will return false. + PD.Deactivate() + return TOPIC_REFRESH + +/obj/machinery/pointdefense_control/proc/build_ui_data() + var/list/data = list() + data["id"] = id_tag + data["name"] = name + var/list/turrets = list() + if(id_tag) + var/list/connected_z_levels = GetConnectedZlevels(get_z(src)) + for(var/i = 1 to LAZYLEN(pointdefense_turrets)) + var/obj/machinery/power/pointdefense/PD = pointdefense_turrets[i] + if(!(PD.id_tag == id_tag && get_z(PD) in connected_z_levels)) + continue + var/list/turret = list() + turret["id"] = "#[i]" + turret["ref"] = "\ref[PD]" + turret["active"] = PD.active + turret["effective_range"] = PD.active ? "[PD.kill_range] meter\s" : "OFFLINE." + turret["reaction_wheel_delay"] = PD.active ? "[(PD.rotation_speed / (1 SECONDS))] second\s" : "OFFLINE." + turret["recharge_time"] = PD.active ? "[(PD.charge_cooldown / (1 SECONDS))] second\s" : "OFFLINE." + + turrets += list(turret) + + data["turrets"] = turrets + return data + +/obj/machinery/pointdefense_control/attackby(var/obj/item/W, var/mob/user) + if(W?.is_multitool()) + var/new_ident = input(user, "Enter a new ident tag.", "[src]", id_tag) as null|text + if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, physical_state)) + // Check for duplicate controllers with this ID + for(var/thing in pointdefense_controllers) + var/obj/machinery/pointdefense_control/PC = thing + if(PC != src && PC.id_tag == id_tag) + to_chat(user, "The [new_ident] network already has a controller.") + return + to_chat(user, "You register [src] with the [new_ident] network.") + id_tag = new_ident + return + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_part_replacement(user, W)) + return + return ..() + +// +// The acutal point defense battery +// + +/obj/machinery/power/pointdefense + name = "\improper point defense battery" + icon = 'icons/obj/pointdefense.dmi' + icon_state = "pointdefense2" + desc = "A Kuiper pattern anti-meteor battery. Capable of destroying most threats in a single salvo." + description_info = "Must have the same ident tag as a fire assist mainframe on the same facility." + density = TRUE + anchored = TRUE + circuit = /obj/item/weapon/circuitboard/pointdefense + idle_power_usage = 0.1 KILOWATTS + active_power_usage = 1 KILOWATTS + appearance_flags = PIXEL_SCALE + var/active = TRUE + var/charge_cooldown = 1 SECOND //time between it can fire at different targets + var/last_shot = 0 + var/kill_range = 18 + var/rotation_speed = 0.25 SECONDS //How quickly we turn to face threats + var/engaging = FALSE + var/id_tag = null + +/obj/machinery/power/pointdefense/Initialize() + . = ..() + // TODO - Remove this bit once machines are converted to Initialize + if(ispath(circuit)) + circuit = new circuit(src) + default_apply_parts() + if(anchored) + connect_to_network() + update_icon() + var/image/I = image(icon, icon_state = "[icon_state]_under") + I.appearance_flags |= RESET_TRANSFORM + underlays += I + +/obj/machinery/power/pointdefense/examine(mob/user) + ..() + if(powernet) + to_chat(user, "It is connected to a power cable below.") + +/obj/machinery/power/pointdefense/get_description_interaction() + . = ..() + if(!id_tag) + . += "[desc_panel_image("multitool")]to set ident tag and connect to a mainframe." + +/obj/machinery/power/pointdefense/update_icon() + if(!active || !id_tag || inoperable()) + icon_state = "[initial(icon_state)]_off" + else + icon_state = initial(icon_state) + +/obj/machinery/power/pointdefense/default_unfasten_wrench(var/mob/user, var/obj/item/weapon/W, var/time) + if((. = ..())) + src.transform = null // Reset rotation if we're anchored/unanchored + +////////// This machine is willing to take power from cables OR APCs. Handle NOPOWER stat specially here! //////// + +/obj/machinery/power/pointdefense/connect_to_network() + if((. = ..())) + stat &= ~NOPOWER // We now ignore APC power + update_icon() + +/obj/machinery/power/pointdefense/disconnect_from_network() + if((. = ..())) + power_change() // We're back on APC power. + +/obj/machinery/power/pointdefense/power_change() + if(powernet) + return // We don't care, we are cable powered anyway + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +// Decide where to get the power to fire from +/obj/machinery/power/pointdefense/use_power_oneoff(var/amount, var/chan = -1) + if(powernet) + return draw_power(amount) + else if(powered(chan)) + use_power(amount, chan) + return amount + return 0 + +// Find controller with the same tag on connected z levels (if any) +/obj/machinery/power/pointdefense/proc/get_controller() + if(!id_tag) + return null + var/list/connected_z_levels = GetConnectedZlevels(get_z(src)) + for(var/thing in pointdefense_controllers) + var/obj/machinery/pointdefense_control/PDC = thing + if(PDC.id_tag == id_tag && (get_z(PDC) in connected_z_levels)) + return PDC + +/obj/machinery/power/pointdefense/attackby(var/obj/item/W, var/mob/user) + if(W?.is_multitool()) + var/new_ident = input(user, "Enter a new ident tag.", "[src]", id_tag) as null|text + if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, physical_state)) + to_chat(user, "You register [src] with the [new_ident] network.") + id_tag = new_ident + return + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_part_replacement(user, W)) + return + if(default_unfasten_wrench(user, W, 40)) + return + return ..() + +//Guns cannot shoot through hull or generally dense turfs. +/obj/machinery/power/pointdefense/proc/space_los(meteor) + for(var/turf/T in getline(src,meteor)) + if(T.density) + return FALSE + return TRUE + +/obj/machinery/power/pointdefense/proc/Shoot(var/weakref/target) + var/obj/effect/meteor/M = target.resolve() + if(!istype(M)) + return + engaging = TRUE + var/Angle = round(Get_Angle(src,M)) + var/matrix/rot_matrix = matrix() + rot_matrix.Turn(Angle) + addtimer(CALLBACK(src, .proc/finish_shot, target), rotation_speed) + animate(src, transform = rot_matrix, rotation_speed, easing = SINE_EASING) + + set_dir(ATAN2(transform.b, transform.a) > 0 ? NORTH : SOUTH) + +/obj/machinery/power/pointdefense/proc/finish_shot(var/weakref/target) + //Cleanup from list + var/obj/machinery/pointdefense_control/PC = get_controller() + if(istype(PC)) + PC.targets -= target + + engaging = FALSE + last_shot = world.time + var/obj/effect/meteor/M = target.resolve() + if(!istype(M)) + return + if(use_power_oneoff(active_power_usage) < active_power_usage) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + visible_message("[src] sputters as browns out while attempting to fire.") + return + //We throw a laser but it doesnt have to hit for meteor to explode + var/obj/item/projectile/beam/pointdefense/beam = new(get_turf(src)) + playsound(src, 'sound/weapons/mandalorian.ogg', 75, 1) + beam.launch_projectile(target = M.loc, user = src) + M.make_debris() + qdel(M) + +/obj/machinery/power/pointdefense/process() + ..() + if(!anchored || stat & (NOPOWER|BROKEN)) + return + if(!active) + return + /* + var/desiredir = ATAN2(transform.b, transform.a) > 0 ? NORTH : SOUTH + if(dir != desiredir) + set_dir(desiredir) + */ + if(LAZYLEN(GLOB.meteor_list) > 0) + find_and_shoot() + +/obj/machinery/power/pointdefense/proc/find_and_shoot() + if(LAZYLEN(GLOB.meteor_list) == 0) + return + if(engaging || ((world.time - last_shot) < charge_cooldown)) + return + + var/obj/machinery/pointdefense_control/PC = get_controller() + if(!istype(PC)) + return + + var/list/connected_z_levels = GetConnectedZlevels(get_z(src)) + for(var/obj/effect/meteor/M in GLOB.meteor_list) + var/already_targeted = FALSE + for(var/weakref/WR in PC.targets) + var/obj/effect/meteor/m = WR.resolve() + if(m == M) + already_targeted = TRUE + break + if(!istype(m)) + PC.targets -= WR + + if(already_targeted) + continue + + if(!(M.z in connected_z_levels)) + continue + if(get_dist(M, src) > kill_range) + continue + if(!emagged && space_los(M)) + var/weakref/target = weakref(M) + PC.targets += target + Shoot(target) + return + +/obj/machinery/power/pointdefense/RefreshParts() + . = ..() + // Calculates an average rating of components that affect shooting rate + var/shootrate_divisor = total_component_rating_of_type(/obj/item/weapon/stock_parts/capacitor) + + charge_cooldown = 2 SECONDS / (shootrate_divisor ? shootrate_divisor : 1) + + //Calculate max shooting range + var/killrange_multiplier = total_component_rating_of_type(/obj/item/weapon/stock_parts/capacitor) + killrange_multiplier += 1.5 * total_component_rating_of_type(/obj/item/weapon/stock_parts/scanning_module) + + kill_range = 10 + 4 * killrange_multiplier + + var/rotation_divisor = total_component_rating_of_type(/obj/item/weapon/stock_parts/manipulator) + rotation_speed = 0.5 SECONDS / (rotation_divisor ? rotation_divisor : 1) + +/obj/machinery/power/pointdefense/proc/Activate() + if(active) + return FALSE + + playsound(src, 'sound/weapons/flash.ogg', 100, 0) + update_use_power(USE_POWER_IDLE) + active = TRUE + update_icon() + return TRUE + +/obj/machinery/power/pointdefense/proc/Deactivate() + if(!active) + return FALSE + playsound(src, 'sound/machines/apc_nopower.ogg', 50, 0) + update_use_power(USE_POWER_OFF) + active = FALSE + update_icon() + return TRUE + +// +// Projectile Beam Definitions +// + +/obj/item/projectile/beam/pointdefense + name = "point defense salvo" + icon_state = "laser" + damage = 15 + damage_type = ELECTROCUTE //You should be safe inside a voidsuit + sharp = FALSE //"Wide" spectrum beam + light_color = COLOR_GOLD + + muzzle_type = /obj/effect/projectile/muzzle/pointdefense + tracer_type = /obj/effect/projectile/tracer/pointdefense + impact_type = /obj/effect/projectile/impact/pointdefense + + +/obj/effect/projectile/tracer/pointdefense + icon_state = "beam_pointdef" + +/obj/effect/projectile/muzzle/pointdefense + icon_state = "muzzle_pointdef" + +/obj/effect/projectile/impact/pointdefense + icon_state = "impact_pointdef" diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 9c29365ae4..0004949067 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -701,8 +701,21 @@ var/list/targets = list() //list of primary targets var/list/secondarytargets = list() //targets that are least important + var/list/seenturfs = list() + for(var/turf/T in oview(world.view, src)) + seenturfs += T + + for(var/mob in living_mob_list) + var/mob/M = mob + if(M.z != z) //Skip + continue + if(get_turf(M) in seenturfs) + assess_and_assign(mob, targets, secondarytargets) + + /* This was dumb. Why do this and then check line of sight later? for(var/mob/M in mobs_in_xray_view(world.view, src)) assess_and_assign(M, targets, secondarytargets) + */ if(!tryToShootAt(targets)) if(!tryToShootAt(secondarytargets)) // if no valid targets, go for secondary targets diff --git a/code/game/machinery/portable_turret_vr.dm b/code/game/machinery/portable_turret_vr.dm new file mode 100644 index 0000000000..c313ad27aa --- /dev/null +++ b/code/game/machinery/portable_turret_vr.dm @@ -0,0 +1,12 @@ +/obj/machinery/porta_turret/industrial/military + name = "military CIWS turret" + desc = "A ship-grade weapons turret designed for anti-fighter defense." + req_one_access = list(access_cent_general) + installation = /obj/item/weapon/gun/energy/lasercannon + health = 500 + maxhealth = 500 + enabled = TRUE + lethal = TRUE + check_weapons = TRUE + auto_repair = TRUE + can_salvage = FALSE diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index e999742507..1312c0cc3b 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -5,12 +5,12 @@ icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit icon_state = "recharger0" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 4 active_power_usage = 40000 //40 kW var/efficiency = 40000 //will provide the modified power rate when upgraded var/obj/item/charging = null - var/list/allowed_devices = list(/obj/item/weapon/gun/energy, /obj/item/weapon/melee/baton, /obj/item/modular_computer, /obj/item/weapon/computer_hardware/battery_module, /obj/item/weapon/cell, /obj/item/device/flashlight, /obj/item/device/electronic_assembly, /obj/item/weapon/weldingtool/electric, /obj/item/ammo_magazine/smart, /obj/item/device/flash, /obj/item/ammo_casing/microbattery) //VOREStation Add - NSFW Batteries + var/list/allowed_devices = list(/obj/item/weapon/gun/energy, /obj/item/weapon/melee/baton, /obj/item/modular_computer, /obj/item/weapon/computer_hardware/battery_module, /obj/item/weapon/cell, /obj/item/device/flashlight, /obj/item/device/electronic_assembly, /obj/item/weapon/weldingtool/electric, /obj/item/ammo_magazine/smart, /obj/item/device/flash, /obj/item/device/defib_kit, /obj/item/ammo_casing/microbattery) //VOREStation Add - NSFW Batteries var/icon_state_charged = "recharger2" var/icon_state_charging = "recharger1" var/icon_state_idle = "recharger0" //also when unpowered @@ -72,7 +72,7 @@ if(EW.use_external_power) to_chat(user, "\The [EW] has no recharge port.") return - else if(!G.get_cell() && !istype(G, /obj/item/ammo_casing/microbattery)) //VOREStation Edit: NSFW charging + if(!G.get_cell() && !istype(G, /obj/item/ammo_casing/microbattery)) //VOREStation Edit: NSFW charging to_chat(user, "\The [G] does not have a battery installed.") return @@ -117,55 +117,34 @@ /obj/machinery/recharger/process() if(stat & (NOPOWER|BROKEN) || !anchored) - update_use_power(0) + update_use_power(USE_POWER_OFF) icon_state = icon_state_idle return if(!charging) - update_use_power(1) + update_use_power(USE_POWER_IDLE) icon_state = icon_state_idle else - if(istype(charging, /obj/item/modular_computer)) - var/obj/item/modular_computer/C = charging - if(!C.battery_module.battery.fully_charged()) - icon_state = icon_state_charging - C.battery_module.battery.give(CELLRATE*efficiency) - update_use_power(2) - else - icon_state = icon_state_charged - update_use_power(1) - return - else if(istype(charging, /obj/item/weapon/computer_hardware/battery_module)) - var/obj/item/weapon/computer_hardware/battery_module/BM = charging - if(!BM.battery.fully_charged()) - icon_state = icon_state_charging - BM.battery.give(CELLRATE*efficiency) - update_use_power(2) - else - icon_state = icon_state_charged - update_use_power(1) - return - var/obj/item/weapon/cell/C = charging.get_cell() if(istype(C)) if(!C.fully_charged()) icon_state = icon_state_charging C.give(CELLRATE*efficiency) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) else icon_state = icon_state_charged - update_use_power(1) + update_use_power(USE_POWER_IDLE) //VOREStation Add - NSFW Batteries else if(istype(charging, /obj/item/ammo_casing/microbattery)) var/obj/item/ammo_casing/microbattery/batt = charging if(batt.shots_left >= initial(batt.shots_left)) icon_state = icon_state_charged - update_use_power(1) + update_use_power(USE_POWER_IDLE) else icon_state = icon_state_charging batt.shots_left++ - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) return //VOREStation Add End diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index f702f3cf25..3f60e0ae07 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -6,7 +6,7 @@ density = 1 anchored = 1 circuit = /obj/item/weapon/circuitboard/recharge_station - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 var/mob/occupant = null var/obj/item/weapon/cell/cell = null @@ -78,9 +78,9 @@ if(!has_cell_power()) return 0 - if(use_power == 1) + if(use_power == USE_POWER_IDLE) cell.use(idle_power_usage * CELLRATE) - else if(use_power >= 2) + else if(use_power >= USE_POWER_ACTIVE) cell.use(active_power_usage * CELLRATE) return 1 diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index a150aaf566..d3eec4057a 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -26,7 +26,7 @@ var/list/obj/machinery/requests_console/allConsoles = list() name = "requests console" desc = "A console intended to send requests to different departments on the station." anchored = 1 - icon = 'icons/obj/terminals.dmi' + icon = 'icons/obj/terminals_vr.dmi' //VOREStation Edit icon_state = "req_comp0" plane = TURF_PLANE layer = ABOVE_TURF_LAYER @@ -176,7 +176,7 @@ var/list/obj/machinery/requests_console/allConsoles = list() screen = RCS_SENTPASS message_log += "Message sent to [recipient]
[message]" else - audible_message(text("\icon[src] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4) + audible_message(text("[bicon(src)] *The Requests Console beeps: 'NOTICE: No server detected!'"),,4) //Handle printing if (href_list["print"]) diff --git a/code/game/machinery/requests_console_vr.dm b/code/game/machinery/requests_console_vr.dm index 9e93691c2e..6c1f5d63ad 100644 --- a/code/game/machinery/requests_console_vr.dm +++ b/code/game/machinery/requests_console_vr.dm @@ -90,3 +90,9 @@ name = "AI RC" department = "AI" departmentType = RC_ASSIST|RC_INFO + +/obj/machinery/requests_console/preset/hop //yw edit + name = "Head of personnel RC" + department = "Head of Personnel's Desk" + departmentType = RC_ASSIST|RC_INFO + announcementConsole = 1 \ No newline at end of file diff --git a/code/game/machinery/robot_fabricator.dm b/code/game/machinery/robot_fabricator.dm index f77982843b..5bbb4ac6be 100644 --- a/code/game/machinery/robot_fabricator.dm +++ b/code/game/machinery/robot_fabricator.dm @@ -7,7 +7,7 @@ var/metal_amount = 0 var/operating = 0 var/obj/item/robot_parts/being_built = null - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 40 active_power_usage = 10000 @@ -26,7 +26,7 @@ M.use(1) count++ - user << "You insert [count] metal sheet\s into the fabricator." + to_chat(user, "You insert [count] metal sheet\s into the fabricator.") overlays -= "fab-load-metal" updateDialog() else @@ -115,7 +115,7 @@ Please wait until completion...

if(!isnull(building)) if(metal_amount >= build_cost) operating = 1 - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) metal_amount = max(0, metal_amount - build_cost) @@ -128,7 +128,7 @@ Please wait until completion...
if(!isnull(being_built)) being_built.loc = get_turf(src) being_built = null - update_use_power(1) + update_use_power(USE_POWER_IDLE) operating = 0 overlays -= "fab-active" return diff --git a/code/game/machinery/seed_extractor.dm b/code/game/machinery/seed_extractor.dm index d8253390c5..1b9d89dc00 100644 --- a/code/game/machinery/seed_extractor.dm +++ b/code/game/machinery/seed_extractor.dm @@ -22,14 +22,14 @@ obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob new_seed_type = plant_controller.seeds[F.plantname] if(new_seed_type) - user << "You extract some seeds from [O]." + to_chat(user, "You extract some seeds from [O].") var/produce = rand(1,4) for(var/i = 0;i<=produce;i++) var/obj/item/seeds/seeds = new(get_turf(src)) seeds.seed_type = new_seed_type.name seeds.update_seed() else - user << "[O] doesn't seem to have any usable seeds inside it." + to_chat(user, "[O] doesn't seem to have any usable seeds inside it.") qdel(O) @@ -37,7 +37,7 @@ obj/machinery/seed_extractor/attackby(var/obj/item/O as obj, var/mob/user as mob else if(istype(O, /obj/item/stack/tile/grass)) var/obj/item/stack/tile/grass/S = O if(S.use(1)) - user << "You extract some seeds from the grass tile." + to_chat(user, "You extract some seeds from the grass tile.") new /obj/item/seeds/grassseed(loc) else if(istype(O, /obj/item/weapon/fossil/plant)) // Fossils diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index fa808ea051..f45872c2d8 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -10,6 +10,7 @@ var/on = 0 var/set_temperature = T0C + 20 //K var/heating_power = 40000 + clicksound = "switch" /obj/machinery/space_heater/New() ..() diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index dcc5781bbb..0780b5905a 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -17,7 +17,7 @@ name = "status display" anchored = 1 density = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 circuit = /obj/item/weapon/circuitboard/status_display var/mode = 1 // 0 = Blank @@ -150,7 +150,7 @@ /obj/machinery/status_display/examine(mob/user) . = ..(user) if(mode != STATUS_DISPLAY_BLANK && mode != STATUS_DISPLAY_ALERT) - user << "The display says:
\t[sanitize(message1)]
\t[sanitize(message2)]" + to_chat(user, "The display says:
\t[sanitize(message1)]
\t[sanitize(message2)]") /obj/machinery/status_display/proc/set_message(m1, m2) if(m1) @@ -181,6 +181,18 @@ if("delta") set_light(l_range = 4, l_power = 0.9, l_color = "#FF6633") set_picture("status_display_[seclevel]") +// Called when the alert level is changed. +/obj/machinery/status_display/proc/on_alert_changed(new_level) + // On most alerts, this will change to a flashing alert picture in a specific color. + // Doing that for green alert automatically doesn't really make sense, but it is still available on the comm consoles/PDAs. + if(seclevel2num(new_level) == SEC_LEVEL_GREEN) + mode = STATUS_DISPLAY_TIME + set_light(0) // Remove any glow we had from the alert previously. + update() + return + mode = STATUS_DISPLAY_ALERT + display_alert(new_level) + /obj/machinery/status_display/proc/set_picture(state) remove_display() if(!picture || picture_state != state) @@ -210,7 +222,7 @@ return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" /obj/machinery/status_display/proc/get_supply_shuttle_timer() - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle if(!shuttle) return "Error" @@ -231,13 +243,16 @@ switch(signal.data["command"]) if("blank") mode = STATUS_DISPLAY_BLANK + set_light(0) if("shuttle") mode = STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME + set_light(0) if("message") mode = STATUS_DISPLAY_MESSAGE set_message(signal.data["msg1"], signal.data["msg2"]) + set_light(0) if("alert") mode = STATUS_DISPLAY_ALERT @@ -245,6 +260,7 @@ if("time") mode = STATUS_DISPLAY_TIME + set_light(0) update() #undef CHARS_PER_LINE diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index f288db605e..27ce3db4f1 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -402,9 +402,9 @@ if(OCCUPANT.client) if(user != OCCUPANT) - OCCUPANT << "The machine kicks you out!" + to_chat(OCCUPANT, "The machine kicks you out!") if(user.loc != src.loc) - OCCUPANT << "You leave the not-so-cozy confines of the SSU." + to_chat(OCCUPANT, "You leave the not-so-cozy confines of the SSU.") OCCUPANT.client.eye = OCCUPANT.client.mob OCCUPANT.client.perspective = MOB_PERSPECTIVE @@ -586,9 +586,9 @@ var/electrified = 0 //Departments that the cycler can paint suits to look like. - var/list/departments = list("Engineering","Mining","Medical","Security","Atmos","HAZMAT","Construction","Biohazard","Emergency Medical Response","Crowd Control","Exploration","Pilot Blue","Pilot") //VORESTATION EDIT + var/list/departments = list("Engineering","Mining","Medical","Security","Atmospherics","HAZMAT","Construction","Biohazard","Emergency Medical Response","Crowd Control","Security EVA") //Species that the suits can be configured to fit. - var/list/species = list(SPECIES_HUMAN,SPECIES_SKRELL,SPECIES_UNATHI,SPECIES_TAJ, SPECIES_TESHARI, "Nevrean", "Akula", "Sergal", "Flatland Zorren", "Highlander Zorren", "Vulpkanin", "Promethean", "Xenomorph Hybrid", "Xenochimera","Vasilissan", "Rapala", /*yawngreyedit*/SPECIES_GREY_YW) //VORESTATION EDIT + var/list/species = list(SPECIES_HUMAN,SPECIES_SKRELL,SPECIES_UNATHI,SPECIES_TAJ, SPECIES_TESHARI, /*yawngreyedit*/SPECIES_GREY_YW) //VORESTATION EDIT) var/target_department var/target_species @@ -616,7 +616,7 @@ name = "Engineering suit cycler" model_text = "Engineering" req_access = list(access_construction) - departments = list("Engineering","Atmos","HAZMAT","Construction") + departments = list("Engineering","Atmospherics","HAZMAT","Construction") /obj/machinery/suit_cycler/mining name = "Mining suit cycler" @@ -628,7 +628,7 @@ name = "Security suit cycler" model_text = "Security" req_access = list(access_security) - departments = list("Security","Crowd Control") + departments = list("Security","Crowd Control","Security EVA") /obj/machinery/suit_cycler/medical name = "Medical suit cycler" @@ -766,8 +766,8 @@ //Clear the access reqs, disable the safeties, and open up all paintjobs. to_chat(user, "You run the sequencer across the interface, corrupting the operating protocols.") - departments = list("Engineering","Mining","Medical","Security","Atmos","HAZMAT","Construction","Biohazard","Crowd Control","Emergency Medical Response","^%###^%$", "Charring") - species = list(SPECIES_HUMAN,SPECIES_SKRELL,SPECIES_UNATHI,SPECIES_TAJ, SPECIES_TESHARI, "Nevrean", "Akula", "Sergal", "Flatland Zorren", "Highlander Zorren", "Vulpkanin", "Promethean", "Xenomorph Hybrid", "Vasilissan", "Rapala",/*YWEDITGREYADD*/SPECIES_GREY_YW) //VORESTATION EDIT + departments = list("Engineering","Mining","Medical","Security","Atmospherics","HAZMAT","Construction","Biohazard","Crowd Control","Security EVA","Emergency Medical Response","^%###^%$", "Charring") + species = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_VULPKANIN) //VORESTATION EDIT emagged = 1 safeties = 0 @@ -931,7 +931,7 @@ /obj/machinery/suit_cycler/proc/finished_job() var/turf/T = get_turf(src) - T.visible_message("\icon[src]The [src] beeps several times.") + T.visible_message("[bicon(src)]The [src] beeps several times.") icon_state = initial(icon_state) active = 0 playsound(src, 'sound/machines/boobeebeep.ogg', 50) @@ -959,7 +959,7 @@ /obj/machinery/suit_cycler/proc/eject_occupant(mob/user as mob) if(locked || active) - user << "The cycler is locked." + to_chat(user, "The cycler is locked.") return if(!occupant) @@ -979,7 +979,10 @@ return //There HAS to be a less bloated way to do this. TODO: some kind of table/icon name coding? ~Z +//hold onto your hat, this sumbitch just got streamlined -KK /obj/machinery/suit_cycler/proc/apply_paintjob() + var/obj/item/clothing/head/helmet/parent_helmet + var/obj/item/clothing/suit/space/parent_suit if(!target_species || !target_department) return @@ -988,220 +991,110 @@ if(helmet) helmet.refit_for_species(target_species) if(suit) suit.refit_for_species(target_species) + //Now "Complete" with most departmental and variant suits, and sorted by department. These aren't available in the standard or emagged cycler lists because they're incomplete for most species. switch(target_department) + //Engineering and Engineering Variants if("Engineering") - if(helmet) - helmet.name = "engineering voidsuit helmet" - helmet.icon_state = "rig0-engineering" - helmet.item_state = "rig0-engineering" - helmet.max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECTION_TEMPERATURE - helmet.armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 80) - helmet.desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." - if(suit) - suit.name = "engineering voidsuit" - suit.icon_state = "rig-engineering" - suit.item_state = "rig-engineering" - suit.item_state_slots[slot_r_hand_str] = "eng_voidsuit" - suit.item_state_slots[slot_l_hand_str] = "eng_voidsuit" - suit.max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE - suit.armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 80) - suit.desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding." - if("Mining") - if(helmet) - helmet.name = "mining voidsuit helmet" - helmet.icon_state = "rig0-mining" - helmet.item_state = "rig0-mining" - if(suit) - suit.name = "mining voidsuit" - suit.icon_state = "rig-mining" - suit.item_state = "rig-mining" - suit.item_state_slots[slot_r_hand_str] = "mining_voidsuit" - suit.item_state_slots[slot_l_hand_str] = "mining_voidsuit" - if("Medical") - if(helmet) - helmet.name = "medical voidsuit helmet" - helmet.icon_state = "rig0-medical" - helmet.item_state = "rig0-medical" - helmet.armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 50) - helmet.desc = "A special helmet that protects against hazardous, low pressure environments. Has minor radiation shielding." - if(suit) - suit.name = "medical voidsuit" - suit.icon_state = "rig-medical" - suit.item_state = "rig-medical" - suit.item_state_slots[slot_r_hand_str] = "medical_voidsuit" - suit.item_state_slots[slot_l_hand_str] = "medical_voidsuit" - suit.armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 50) - suit.desc = "A special suit that protects against hazardous, low pressure environments. Has minor radiation shielding." - if("Security") - if(helmet) - helmet.name = "security voidsuit helmet" - helmet.icon_state = "rig0-sec" - helmet.item_state = "rig0-sec" - if(suit) - suit.name = "security voidsuit" - suit.icon_state = "rig-sec" - suit.item_state = "rig-sec" - suit.item_state_slots[slot_r_hand_str] = "sec_voidsuit" - suit.item_state_slots[slot_l_hand_str] = "sec_voidsuit" - if("Crowd Control") - if(helmet) - helmet.name = "crowd control voidsuit helmet" - helmet.icon_state = "rig0-sec_riot" - helmet.item_state = "rig0-sec_riot" - if(suit) - suit.name = "crowd control voidsuit" - suit.icon_state = "rig-sec_riot" - suit.item_state = "rig-sec_riot" - suit.item_state_slots[slot_r_hand_str] = "sec_voidsuit_riot" - suit.item_state_slots[slot_l_hand_str] = "sec_voidsuit_riot" - if("Atmos") - if(helmet) - helmet.name = "atmospherics voidsuit helmet" - helmet.icon_state = "rig0-atmos" - helmet.item_state = "rig0-atmos" - helmet.max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE - helmet.armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 50) - helmet.desc = "A special helmet designed for work in a hazardous, low pressure environments. Has improved thermal protection and minor radiation shielding." - if(suit) - suit.name = "atmospherics voidsuit" - suit.icon_state = "rig-atmos" - suit.item_state = "rig-atmos" - suit.item_state_slots[slot_r_hand_str] = "atmos_voidsuit" - suit.item_state_slots[slot_l_hand_str] = "atmos_voidsuit" - suit.max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE - suit.armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 50) - suit.desc = "A special suit that protects against hazardous, low pressure environments. Has improved thermal protection and minor radiation shielding." + parent_helmet = /obj/item/clothing/head/helmet/space/void/engineering + parent_suit = /obj/item/clothing/suit/space/void/engineering if("HAZMAT") - if(helmet) - helmet.name = "HAZMAT voidsuit helmet" - helmet.icon_state = "rig0-engineering_rad" - helmet.item_state = "rig0-engineering_rad" - helmet.max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECTION_TEMPERATURE - helmet.armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 50, bio = 100, rad = 100) - helmet.desc = "A engineering helmet designed for work in a low-pressure environment. Extra radiation shielding appears to have been installed at the price of comfort." - if(suit) - suit.name = "HAZMAT voidsuit" - suit.icon_state = "rig-engineering_rad" - suit.item_state = "rig-engineering_rad" - suit.item_state_slots[slot_r_hand_str] = "eng_voidsuit_rad" - suit.item_state_slots[slot_l_hand_str] = "eng_voidsuit_rad" - suit.max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE - suit.armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 50, bio = 100, rad = 100) - suit.desc = "A engineering voidsuit that protects against hazardous, low pressure environments. Has enhanced radiation shielding compared to regular engineering voidsuits." + parent_helmet = /obj/item/clothing/head/helmet/space/void/engineering/hazmat + parent_suit = /obj/item/clothing/suit/space/void/engineering/hazmat if("Construction") - if(helmet) - helmet.name = "Construction voidsuit helmet" - helmet.icon_state = "rig0-engineering_con" - helmet.item_state = "rig0-engineering_con" - helmet.max_heat_protection_temperature = HELMET_MAX_HEAT_PROTECTION_TEMPERATURE - helmet.armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 80) - helmet.desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." - if(suit) - suit.name = "Construction voidsuit" - suit.icon_state = "rig-engineering_con" - suit.item_state = "rig-engineering_con" - suit.item_state_slots[slot_r_hand_str] = "eng_voidsuit_con" - suit.item_state_slots[slot_l_hand_str] = "eng_voidsuit_con" - suit.max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE - suit.armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 80) - suit.desc = "A special suit designed for work in a hazardous, low-pressure environment. Has radiation shielding." + parent_helmet = /obj/item/clothing/head/helmet/space/void/engineering/construction + parent_suit = /obj/item/clothing/suit/space/void/engineering/construction + if("Reinforced") + parent_helmet = /obj/item/clothing/head/helmet/space/void/engineering/alt + parent_suit = /obj/item/clothing/suit/space/void/engineering/alt + if("Salvager") + parent_helmet = /obj/item/clothing/head/helmet/space/void/engineering/salvage + parent_suit = /obj/item/clothing/suit/space/void/engineering/salvage + if("Atmospherics") + parent_helmet = /obj/item/clothing/head/helmet/space/void/atmos + parent_suit = /obj/item/clothing/suit/space/void/atmos + if("Heavy Duty Atmospherics") + parent_helmet = /obj/item/clothing/head/helmet/space/void/atmos/alt + parent_suit = /obj/item/clothing/suit/space/void/atmos/alt + //Mining and Mining Variants + if("Mining") + parent_helmet = /obj/item/clothing/head/helmet/space/void/mining + parent_suit = /obj/item/clothing/suit/space/void/mining + if("Frontier Miner") + parent_helmet = /obj/item/clothing/head/helmet/space/void/mining/alt + parent_suit = /obj/item/clothing/suit/space/void/mining/alt + //Medical and Medical Variants + if("Medical") + parent_helmet = /obj/item/clothing/head/helmet/space/void/medical + parent_suit = /obj/item/clothing/suit/space/void/medical if("Biohazard") - if(helmet) - helmet.name = "Biohazard voidsuit helmet" - helmet.icon_state = "rig0-medical_bio" - helmet.item_state = "rig0-medical_bio" - helmet.armor = list(melee = 45, bullet = 5, laser = 20, energy = 5, bomb = 15, bio = 100, rad = 75) - helmet.desc = "A special helmet that protects against hazardous environments. Has minor radiation shielding." - if(suit) - suit.name = "Biohazard voidsuit" - suit.icon_state = "rig-medical_bio" - suit.item_state = "rig-medical_bio" - suit.item_state_slots[slot_r_hand_str] = "medical_voidsuit_bio" - suit.item_state_slots[slot_l_hand_str] = "medical_voidsuit_bio" - suit.armor = list(melee = 45, bullet = 5, laser = 20, energy = 5, bomb = 15, bio = 100, rad = 75) - suit.desc = "A special suit that protects against hazardous, environments. It feels heavier than the standard suit with extra protection around the joints." + parent_helmet = /obj/item/clothing/head/helmet/space/void/medical/bio + parent_suit = /obj/item/clothing/suit/space/void/medical/bio if("Emergency Medical Response") - if(helmet) - helmet.name = "emergency medical response voidsuit helmet" - helmet.icon_state = "rig0-medical_emt" - helmet.item_state = "rig0-medical_emt" - helmet.armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 50) - helmet.desc = "A special helmet that protects against hazardous, low pressure environments. Has minor radiation shielding." - if(suit) - suit.name = "emergency medical response voidsuit" - suit.icon_state = "rig-medical_emt" - suit.item_state = "rig-medical_emt" - suit.item_state_slots[slot_r_hand_str] = "medical_voidsuit_emt" - suit.item_state_slots[slot_l_hand_str] = "medical_voidsuit_emt" - suit.armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 50) - suit.desc = "A special suit that protects against hazardous, low pressure environments. Has minor radiation shielding." - if("^%###^%$" || "Mercenary") - if(helmet) - helmet.name = "blood-red voidsuit helmet" - helmet.icon_state = "rig0-syndie" - helmet.item_state = "rig0-syndie" - if(suit) - suit.name = "blood-red voidsuit" - suit.item_state = "rig-syndie" - suit.icon_state = "rig-syndie" - suit.item_state_slots[slot_r_hand_str] = "syndie_voidsuit" - suit.item_state_slots[slot_l_hand_str] = "syndie_voidsuit" - if("Charring") - if(helmet) - helmet.name = "soot-covered voidsuit helmet" - helmet.icon_state = "rig0-firebug" - helmet.item_state = "rig0-firebug" - if(suit) - suit.name = "soot-covered voidsuit" - suit.item_state = "rig-firebug" - suit.icon_state = "rig-firebug" - suit.item_state_slots[slot_r_hand_str] = "rig-firebug" - suit.item_state_slots[slot_l_hand_str] = "rig-firebug" + parent_helmet = /obj/item/clothing/head/helmet/space/void/medical/emt + parent_suit = /obj/item/clothing/suit/space/void/medical/emt + if("Vey-Medical Streamlined") + parent_helmet = /obj/item/clothing/head/helmet/space/void/medical/alt + parent_suit = /obj/item/clothing/suit/space/void/medical/alt + //Security and Security Variants + if("Security") + parent_helmet = /obj/item/clothing/head/helmet/space/void/security + parent_suit = /obj/item/clothing/suit/space/void/security + if("Crowd Control") + parent_helmet = /obj/item/clothing/head/helmet/space/void/security/riot + parent_suit = /obj/item/clothing/suit/space/void/security/riot + if("Security EVA") + parent_helmet = /obj/item/clothing/head/helmet/space/void/security/alt + parent_suit = /obj/item/clothing/suit/space/void/security/alt + //Exploration Department if("Exploration") - if(helmet) - helmet.name = "exploration voidsuit helmet" - helmet.icon_state = "helm_explorer" - helmet.item_state = "helm_explorer" - if(suit) - suit.name = "exploration voidsuit" - suit.icon_state = "void_explorer" - suit.item_state = "void_explorer" - suit.item_state_slots[slot_r_hand_str] = "wiz_voidsuit" - suit.item_state_slots[slot_l_hand_str] = "wiz_voidsuit" + parent_helmet = /obj/item/clothing/head/helmet/space/void/exploration + parent_suit = /obj/item/clothing/suit/space/void/exploration if("Old Exploration") - if(helmet) - helmet.name = "exploration voidsuit helmet" - helmet.icon_state = "helm_explorer2" - helmet.item_state = "helm_explorer2" - if(suit) - suit.name = "exploration voidsuit" - suit.icon_state = "void_explorer2" - suit.item_state = "void_explorer2" - suit.item_state_slots[slot_r_hand_str] = "wiz_voidsuit" - suit.item_state_slots[slot_l_hand_str] = "wiz_voidsuit" + parent_helmet = /obj/item/clothing/head/helmet/space/void/exploration/alt + parent_suit = /obj/item/clothing/suit/space/void/exploration/alt if("Pilot") - if(helmet) - helmet.name = "pilot voidsuit helmet" - helmet.icon_state = "rig0_pilot" - helmet.item_state = "pilot_helm" - if(suit) - suit.name = "pilot voidsuit" - suit.icon_state = "rig-pilot" - suit.item_state = "rig-pilot" - suit.item_state_slots[slot_r_hand_str] = "sec_voidsuitTG" - suit.item_state_slots[slot_l_hand_str] = "sec_voidsuitTG" + parent_helmet = /obj/item/clothing/head/helmet/space/void/pilot + parent_suit = /obj/item/clothing/suit/space/void/pilot if("Pilot Blue") - if(helmet) - helmet.name = "pilot voidsuit helmet" - helmet.icon_state = "rig0_pilot2" - helmet.item_state = "pilot_helm2" - if(suit) - suit.name = "pilot voidsuit" - suit.icon_state = "rig-pilot2" - suit.item_state = "rig-pilot2" - suit.item_state_slots[slot_r_hand_str] = "sec_voidsuitTG" - suit.item_state_slots[slot_l_hand_str] = "sec_voidsuitTG" + parent_helmet = /obj/item/clothing/head/helmet/space/void/pilot/alt + parent_suit = /obj/item/clothing/suit/space/void/pilot/alt + //Antag Suits + if("^%###^%$" || "Mercenary") + parent_helmet = /obj/item/clothing/head/helmet/space/void/merc + parent_suit = /obj/item/clothing/suit/space/void/merc + if("Charring") + parent_helmet = /obj/item/clothing/head/helmet/space/void/merc/fire + parent_suit = /obj/item/clothing/suit/space/void/merc/fire + if("Gem-Encrusted" || "Wizard") + parent_helmet = /obj/item/clothing/head/helmet/space/void/wizard + parent_suit = /obj/item/clothing/suit/space/void/wizard + //BEGIN: Space for additional downstream variants + //VOREStation Addition Start + if("Director") + parent_helmet = /obj/item/clothing/head/helmet/space/void/captain + parent_suit = /obj/item/clothing/suit/space/void/captain + if("Prototype") + parent_helmet = /obj/item/clothing/head/helmet/space/void/merc/prototype + parent_suit = /obj/item/clothing/suit/space/void/merc/prototype + //VOREStation Addition End + //END: downstream variant space + + //look at this! isn't it beautiful? -KK (well ok not beautiful but it's a lot cleaner) + if(helmet) + var/obj/item/clothing/H = new parent_helmet + helmet.name = "refitted [initial(parent_helmet.name)]" + helmet.desc = initial(parent_helmet.desc) + helmet.icon_state = initial(parent_helmet.icon_state) + helmet.item_state = initial(parent_helmet.item_state) + helmet.light_overlay = initial(parent_helmet.light_overlay) + helmet.item_state_slots = H.item_state_slots + qdel(H) - - if(helmet) helmet.name = "refitted [helmet.name]" - if(suit) suit.name = "refitted [suit.name]" + if(suit) + var/obj/item/clothing/S = new parent_suit + suit.name = "refitted [initial(parent_suit.name)]" + suit.desc = initial(parent_suit.desc) + suit.icon_state = initial(parent_suit.icon_state) + suit.item_state = initial(parent_suit.item_state) + suit.item_state_slots = S.item_state_slots + qdel(S) diff --git a/code/game/machinery/suit_storage_unit_vr.dm b/code/game/machinery/suit_storage_unit_vr.dm index 5a137f2616..a6c0ddba0a 100644 --- a/code/game/machinery/suit_storage_unit_vr.dm +++ b/code/game/machinery/suit_storage_unit_vr.dm @@ -1,23 +1,6 @@ /obj/machinery/suit_cycler - species = list( - SPECIES_HUMAN, - SPECIES_SKRELL, - SPECIES_UNATHI, - SPECIES_TAJ, - SPECIES_TESHARI, - SPECIES_AKULA, - SPECIES_ALRAUNE, - SPECIES_NEVREAN, - SPECIES_RAPALA, - SPECIES_SERGAL, - SPECIES_VASILISSAN, - SPECIES_VULPKANIN, - SPECIES_XENOCHIMERA, - SPECIES_XENOHYBRID, - SPECIES_ZORREN_FLAT, - SPECIES_ZORREN_HIGH, - SPECIES_GREY_YW /*ywedit*/ - ) + departments = list("Engineering","Mining","Medical","Security","Atmos","HAZMAT","Construction","Biohazard","Emergency Medical Response","Crowd Control","Exploration","Pilot Blue","Pilot","Director","Prototype") + species = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_VULPKANIN, SPECIES_GREY_YW /*ywedit*/) // Old Exploration is too WIP to use right now /obj/machinery/suit_cycler/exploration @@ -26,3 +9,23 @@ /obj/machinery/suit_cycler/pilot req_access = list(access_pilot) + +/obj/machinery/suit_cycler/captain + name = "Director suit cycler" + model_text = "Director" + req_access = list(access_captain) + departments = list("Director") + +/obj/machinery/suit_cycler/captain/Initialize() //No Teshari Sprites + species -= SPECIES_TESHARI + return ..() + +/obj/machinery/suit_cycler/prototype + name = "Prototype suit cycler" + model_text = "Prototype" + req_access = list(access_cent_specops) + departments = list("Prototype") + +/obj/machinery/suit_cycler/prototype/Initialize() //No Teshari Sprites + species -= SPECIES_TESHARI + return ..() diff --git a/code/game/machinery/suit_storage_unit_yw.dm b/code/game/machinery/suit_storage_unit_yw.dm new file mode 100644 index 0000000000..da60f289d5 --- /dev/null +++ b/code/game/machinery/suit_storage_unit_yw.dm @@ -0,0 +1,2 @@ +/obj/machinery/suit_cycler/prototype + req_access = list(access_hos) \ No newline at end of file diff --git a/code/game/machinery/supply_display.dm b/code/game/machinery/supply_display.dm index 211918c5e6..28bed763f6 100644 --- a/code/game/machinery/supply_display.dm +++ b/code/game/machinery/supply_display.dm @@ -1,16 +1,23 @@ /obj/machinery/status_display/supply_display ignore_friendc = 1 + mode = STATUS_DISPLAY_CUSTOM /obj/machinery/status_display/supply_display/update() if(!..() && mode == STATUS_DISPLAY_CUSTOM) message1 = "CARGO" message2 = "" - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle if(!shuttle) message2 = "Error" else if(shuttle.has_arrive_time()) - message2 = get_supply_shuttle_timer() + message2 = "[round(shuttle.eta_seconds() / 60)]:" + if((shuttle.eta_seconds() % 60) < 10) + message2 += "0" // Pad to two characters + message2 += "[shuttle.eta_seconds() % 60]" + + if(shuttle.eta_seconds() < 0) + message2 = "Late" if(length(message2) > CHARS_PER_LINE) message2 = "Error" else if(shuttle.is_launching()) @@ -22,7 +29,7 @@ if(shuttle.at_station()) message2 = "Docked" else - message1 = "" + message2 = "Away" update_display(message1, message2) return 1 return 0 diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm index 9f228be5d4..df40ba1a7d 100644 --- a/code/game/machinery/supplybeacon.dm +++ b/code/game/machinery/supplybeacon.dm @@ -58,7 +58,7 @@ /obj/machinery/power/supply_beacon/attack_hand(var/mob/user) if(expended) - use_power = 0 + update_use_power(USE_POWER_OFF) to_chat (user, "\The [src] has used up its charge.") return @@ -80,7 +80,7 @@ return set_light(3, 3, "#00CCAA") icon_state = "beacon_active" - use_power = 1 + use_power = USE_POWER_IDLE if(user) to_chat(user, "You activate the beacon. The supply drop will be dispatched soon.") /obj/machinery/power/supply_beacon/proc/deactivate(var/mob/user, var/permanent) @@ -90,7 +90,7 @@ else icon_state = "beacon" set_light(0) - use_power = 0 + use_power = USE_POWER_OFF target_drop_time = null if(user) to_chat(user, "You deactivate the beacon.") diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 8f6e8e589e..f392eb5218 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -17,12 +17,18 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept desc = "A dish-shaped machine used to broadcast processed subspace signals." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 25 machinetype = 5 produces_heat = 0 delay = 7 circuit = /obj/item/weapon/circuitboard/telecomms/broadcaster + //Vars only used if you're using the overmap + var/overmap_range = 0 + var/overmap_range_min = 0 + var/overmap_range_max = 5 + //Linked bluespace radios + var/list/linked_radios_weakrefs = list() /obj/machinery/telecomms/processor/Initialize() . = ..() @@ -34,6 +40,11 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept component_parts += new /obj/item/weapon/stock_parts/micro_laser/high(src) component_parts += new /obj/item/stack/cable_coil(src, 1) +/obj/machinery/telecomms/broadcaster/proc/link_radio(var/obj/item/device/radio/R) + if(!istype(R)) + return + linked_radios_weakrefs |= weakref(R) + /obj/machinery/telecomms/broadcaster/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) // Don't broadcast rejected signals if(signal.data["reject"]) @@ -58,46 +69,51 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept if(signal.data["slow"] > 0) sleep(signal.data["slow"]) // simulate the network lag if necessary - signal.data["level"] |= listening_level + signal.data["level"] |= using_map.get_map_levels(listening_level, TRUE, overmap_range) + + var/list/forced_radios + for(var/weakref/wr in linked_radios_weakrefs) + var/obj/item/device/radio/R = wr.resolve() + if(istype(R)) + LAZYDISTINCTADD(forced_radios, R) /** #### - Normal Broadcast - #### **/ - - if(signal.data["type"] == 0) - + if(signal.data["type"] == SIGNAL_NORMAL) /* ###### Broadcast a message using signal.data ###### */ Broadcast_Message(signal.data["connection"], signal.data["mob"], signal.data["vmask"], signal.data["vmessage"], signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"],, + signal.data["realname"], signal.data["vname"], DATA_NORMAL, signal.data["compression"], signal.data["level"], signal.frequency, - signal.data["verb"], signal.data["language"] ) + signal.data["verb"], signal.data["language"], forced_radios) /** #### - Simple Broadcast - #### **/ - if(signal.data["type"] == 1) + if(signal.data["type"] == SIGNAL_SIMPLE) /* ###### Broadcast a message using signal.data ###### */ Broadcast_SimpleMessage(signal.data["name"], signal.frequency, - signal.data["message"],null, null, - signal.data["compression"], listening_level) + signal.data["message"], DATA_NORMAL, null, + signal.data["compression"], listening_level, forced_radios) /** #### - Artificial Broadcast - #### **/ // (Imitates a mob) - if(signal.data["type"] == 2) + if(signal.data["type"] == SIGNAL_FAKE) /* ###### Broadcast a message using signal.data ###### */ - // Parameter "data" as 4: AI can't track this person/mob + // Parameter "data" as DATA_FAKE: AI can't track this person/mob Broadcast_Message(signal.data["connection"], signal.data["mob"], signal.data["vmask"], signal.data["vmessage"], signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency, - signal.data["verb"], signal.data["language"]) + signal.data["realname"], signal.data["vname"], DATA_FAKE, + signal.data["compression"], signal.data["level"], signal.frequency, + signal.data["verb"], signal.data["language"], forced_radios) if(!message_delay) message_delay = 1 @@ -118,6 +134,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /* Basically just an empty shell for receiving and broadcasting radio messages. Not very flexible, but it gets the job done. + NOTE: This AIO device listens on *every* zlevel (it does not even check) */ /obj/machinery/telecomms/allinone @@ -126,15 +143,98 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept icon_state = "comm_server" desc = "A compact machine used for portable subspace telecommuniations processing." density = 1 + use_power = USE_POWER_IDLE + idle_power_usage = 20 anchored = 1 - use_power = 0 - idle_power_usage = 0 machinetype = 6 produces_heat = 0 var/intercept = 0 // if nonzero, broadcasts all messages to syndicate channel + var/overmap_range = 0 //Same turf + + var/list/linked_radios_weakrefs = list() + +/obj/machinery/telecomms/allinone/proc/link_radio(var/obj/item/device/radio/R) + if(!istype(R)) + return + linked_radios_weakrefs |= weakref(R) /obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal) + // Has to be on to receive messages + if(!on) + return + + // Why did you use this subtype? + if(!using_map.use_overmap) + return + + // Someone else handling it? + if(signal.data["done"]) + return + + // Where are we able to hear from (and talk to, since we're AIO) anyway? + var/map_levels = using_map.get_map_levels(z, TRUE, overmap_range) + + //Bluespace can skip this check + if(signal.transmission_method != TRANSMISSION_BLUESPACE) + var/list/signal_levels = list() + signal_levels += signal.data["level"] //If it's text/number, it'll be the only entry, if it's a list, it'll get combined + var/list/overlap = map_levels & signal_levels //Returns a list of similar levels + if(!overlap.len) + return + + if(is_freq_listening(signal)) // detect subspace signals + + signal.data["done"] = 1 // mark the signal as being broadcasted since we're a broadcaster + signal.data["compression"] = 0 // decompress since we're a processor + + // Search for the original signal and mark it as done as well + var/datum/signal/original = signal.data["original"] + if(original) + original.data["done"] = 1 + + // For some reason level is both used as a list and not a list, and now it needs to be a list. + signal.data["level"] = map_levels + + if(signal.data["slow"] > 0) + sleep(signal.data["slow"]) // simulate the network lag if necessary + + /* ###### Broadcast a message using signal.data ###### */ + + var/datum/radio_frequency/connection = signal.data["connection"] + + var/list/forced_radios + for(var/weakref/wr in linked_radios_weakrefs) + var/obj/item/device/radio/R = wr.resolve() + if(istype(R)) + LAZYDISTINCTADD(forced_radios, R) + + Broadcast_Message( + signal.data["connection"], + signal.data["mob"], + signal.data["vmask"], + signal.data["vmessage"], + signal.data["radio"], + signal.data["message"], + signal.data["name"], + signal.data["job"], + signal.data["realname"], + signal.data["vname"], + DATA_NORMAL, + signal.data["compression"], + signal.data["level"], + connection.frequency, + signal.data["verb"], + signal.data["language"], + forced_radios + ) + +//Antag version with unlimited range (doesn't even check) and uses no power, to enable antag comms to work anywhere. +/obj/machinery/telecomms/allinone/antag + use_power = USE_POWER_OFF + idle_power_usage = 0 + +/obj/machinery/telecomms/allinone/antag/receive_signal(datum/signal/signal) if(!on) // has to be on to receive messages return @@ -159,23 +259,29 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept var/datum/radio_frequency/connection = signal.data["connection"] + var/list/forced_radios + for(var/weakref/wr in linked_radios_weakrefs) + var/obj/item/device/radio/R = wr.resolve() + if(istype(R)) + LAZYDISTINCTADD(forced_radios, R) + if(connection.frequency in ANTAG_FREQS) // if antag broadcast, just Broadcast_Message(signal.data["connection"], signal.data["mob"], signal.data["vmask"], signal.data["vmessage"], signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency, - signal.data["verb"], signal.data["language"]) + signal.data["realname"], signal.data["vname"], DATA_NORMAL, + signal.data["compression"], list(0), connection.frequency, + signal.data["verb"], signal.data["language"], forced_radios) else if(intercept) Broadcast_Message(signal.data["connection"], signal.data["mob"], signal.data["vmask"], signal.data["vmessage"], signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency, - signal.data["verb"], signal.data["language"]) - - + signal.data["realname"], signal.data["vname"], DATA_ANTAG, + signal.data["compression"], list(0), connection.frequency, + signal.data["verb"], signal.data["language"], forced_radios) /** @@ -237,7 +343,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /proc/Broadcast_Message(var/datum/radio_frequency/connection, var/mob/M, var/vmask, var/vmessage, var/obj/item/device/radio/radio, var/message, var/name, var/job, var/realname, var/vname, - var/data, var/compression, var/list/level, var/freq, var/verbage = "says", var/datum/language/speaking = null) + var/data, var/compression, var/list/level, var/freq, var/verbage = "says", + var/datum/language/speaking = null, var/list/forced_radios) /* ###### Prepare the radio connection ###### */ @@ -246,17 +353,22 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept var/list/obj/item/device/radio/radios = list() + for(var/obj/item/device/radio/R in forced_radios) + //Cursory check to ensure they are 'on' and stuff + if(R.receive_range(display_freq, list(0))) + radios |= R + // --- Broadcast only to intercom devices --- - if(data == 1) + if(data == DATA_INTERCOM) for (var/obj/item/device/radio/intercom/R in connection.devices["[RADIO_CHAT]"]) if(R.receive_range(display_freq, level) > -1) - radios += R + radios |= R // --- Broadcast only to intercoms and station-bounced radios --- - else if(data == 2) + else if(data == DATA_LOCAL) for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) @@ -264,16 +376,16 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept continue if(R.receive_range(display_freq, level) > -1) - radios += R + radios |= R // --- Broadcast to antag radios! --- - else if(data == 3) + else if(data == DATA_ANTAG) for(var/antag_freq in ANTAG_FREQS) var/datum/radio_frequency/antag_connection = radio_controller.return_frequency(antag_freq) for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"]) if(R.receive_range(antag_freq, level) > -1) - radios += R + radios |= R // --- Broadcast to ALL radio devices --- @@ -281,7 +393,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) if(R.receive_range(display_freq, level) > -1) - radios += R + radios |= R // Get a list of mobs who can hear from the radios we collected. var/list/receive = get_mobs_in_radio_ranges(radios) @@ -307,7 +419,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept continue // Ghosts hearing all radio chat don't want to hear syndicate intercepts, they're duplicates - if(data == 3 && istype(R, /mob/observer/dead) && R.is_preference_enabled(/datum/client_preference/ghost_radio)) + if(data == DATA_ANTAG && istype(R, /mob/observer/dead) && R.is_preference_enabled(/datum/client_preference/ghost_radio)) continue // --- Check for compression --- @@ -346,9 +458,9 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept var/freq_text = get_frequency_name(display_freq) var/part_b_extra = "" - if(data == 3) // intercepted radio message + if(data == DATA_ANTAG) // intercepted radio message part_b_extra = " (Intercepted)" - var/part_a = "\icon[radio]\[[freq_text]\][part_b_extra] " // goes in the actual output + var/part_a = "[bicon(radio)]\[[freq_text]\][part_b_extra] " // goes in the actual output // --- Some more pre-message formatting --- var/part_b = " " // Tweaked for security headsets -- TLE @@ -438,7 +550,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept return 1 -/proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level) +/proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level, var/list/forced_radios) /* ###### Prepare the radio connection ###### */ @@ -452,10 +564,12 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept var/list/receive = list() + for(var/obj/item/device/radio/R in forced_radios) + receive |= R.send_hear(display_freq) // --- Broadcast only to intercom devices --- - if(data == 1) + if(data == DATA_INTERCOM) for (var/obj/item/device/radio/intercom/R in connection.devices["[RADIO_CHAT]"]) var/turf/position = get_turf(R) if(position && position.z == level) @@ -464,7 +578,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept // --- Broadcast only to intercoms and station-bounced radios --- - else if(data == 2) + else if(data == DATA_LOCAL) for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) if(istype(R, /obj/item/device/radio/headset)) @@ -476,7 +590,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept // --- Broadcast to antag radios! --- - else if(data == 3) + else if(data == DATA_ANTAG) for(var/freq in ANTAG_FREQS) var/datum/radio_frequency/antag_connection = radio_controller.return_frequency(freq) for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"]) @@ -541,13 +655,13 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept // --- Some more pre-message formatting --- var/part_b_extra = "" - if(data == 3) // intercepted radio message + if(data == DATA_ANTAG) // intercepted radio message part_b_extra = " (Intercepted)" // Create a radio headset for the sole purpose of using its icon var/obj/item/device/radio/headset/radio = new - var/part_b = " \icon[radio]\[[freq_text]\][part_b_extra] " // Tweaked for security headsets -- TLE + var/part_b = " [bicon(radio)]\[[freq_text]\][part_b_extra] " // Tweaked for security headsets -- TLE var/part_blackbox_b = " \[[freq_text]\] " // Tweaked for security headsets -- TLE var/part_c = "" @@ -617,15 +731,15 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /atom/proc/test_telecomms() var/datum/signal/signal = src.telecomms_process() - var/turf/position = get_turf(src) - return (position.z in signal.data["level"] && signal.data["done"]) + var/pos_z = get_z(src) + return (pos_z in signal.data["level"] && signal.data["done"]) /atom/proc/telecomms_process(var/do_sleep = 1) // First, we want to generate a new radio signal var/datum/signal/signal = new - signal.transmission_method = 2 // 2 would be a subspace transmission. - var/turf/pos = get_turf(src) + signal.transmission_method = TRANSMISSION_SUBSPACE + var/pos_z = get_z(src) // --- Finally, tag the actual signal with the appropriate values --- signal.data = list( @@ -633,10 +747,10 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept "message" = "TEST", "compression" = rand(45, 50), // If the signal is compressed, compress our message too. "traffic" = 0, // dictates the total traffic sum that the signal went through - "type" = 4, // determines what type of radio input it is: test broadcast + "type" = SIGNAL_TEST, // determines what type of radio input it is: test broadcast "reject" = 0, "done" = 0, - "level" = pos.z // The level it is being broadcasted at. + "level" = pos_z // The level it is being broadcasted at. ) signal.frequency = PUB_FREQ// Common channel @@ -647,7 +761,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept if(do_sleep) sleep(rand(10,25)) - //world.log << "Level: [signal.data["level"]] - Done: [signal.data["done"]]" + //to_world_log("Level: [signal.data["level"]] - Done: [signal.data["done"]]") return signal diff --git a/code/game/machinery/telecomms/logbrowser.dm b/code/game/machinery/telecomms/logbrowser.dm index 41e7b64ed7..f04cf7076a 100644 --- a/code/game/machinery/telecomms/logbrowser.dm +++ b/code/game/machinery/telecomms/logbrowser.dm @@ -157,7 +157,7 @@ if(href_list["delete"]) if(!src.allowed(usr) && !emagged) - usr << "ACCESS DENIED." + to_chat(usr, "ACCESS DENIED.") return if(SelectedServer) @@ -194,6 +194,6 @@ if(!emagged) playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) emagged = 1 - user << "You you disable the security protocols" + to_chat(user, "You you disable the security protocols") src.updateUsrDialog() return 1 diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm index 80bea74ed4..5ee974e571 100644 --- a/code/game/machinery/telecomms/machine_interactions.dm +++ b/code/game/machinery/telecomms/machine_interactions.dm @@ -105,7 +105,7 @@ dat += "" temp = "" - to_chat(user, browse(dat, "window=tcommachine;size=520x500;can_resize=0")) + user << browse(dat, "window=tcommachine;size=520x500;can_resize=0") onclose(user, "dormitory") @@ -219,6 +219,38 @@ temp = "-% Frequency changing deactivated %-" +// BROADCASTER +/obj/machinery/telecomms/broadcaster/Options_Menu() + // Note the machine 'displays' 1 higher than overmap_range to save users from the abstraction that range '0' is valid and everything on the same turf. + var/dat = "
Broadcast Range (affects power usage)
- [overmap_range+1] gigameter\s +" + return dat + +/obj/machinery/telecomms/broadcaster/Options_Topic(href, href_list) + if(href_list["range_down"]) + if(overmap_range > overmap_range_min) + overmap_range-- + idle_power_usage = initial(idle_power_usage)**(overmap_range+1) + if(href_list["range_up"]) + if(overmap_range < overmap_range_max) + overmap_range++ + idle_power_usage = initial(idle_power_usage)**(overmap_range+1) + +// RECEIVER +/obj/machinery/telecomms/receiver/Options_Menu() + // Note the machine 'displays' 1 higher than overmap_range to save users from the abstraction that range '0' is valid and everything on the same turf. + var/dat = "
Receive Range (affects power usage)
- [overmap_range+1] gigameter\s +" + return dat + +/obj/machinery/telecomms/receiver/Options_Topic(href, href_list) + if(href_list["range_down"]) + if(overmap_range > overmap_range_min) + overmap_range-- + idle_power_usage = initial(idle_power_usage)**(overmap_range+1) + if(href_list["range_up"]) + if(overmap_range < overmap_range_max) + overmap_range++ + idle_power_usage = initial(idle_power_usage)**(overmap_range+1) + /obj/machinery/telecomms/Topic(href, href_list) if(!issilicon(usr)) diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index d6cefb6828..38f1adbdfc 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -41,7 +41,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() if(!on) return - //world << "[src] ([src.id]) - [signal.debug_print()]" + //to_world("[src] ([src.id]) - [signal.debug_print()]") var/send_count = 0 signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity @@ -68,7 +68,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() var/datum/signal/copy if(copysig) copy = new - copy.transmission_method = 2 + copy.transmission_method = TRANSMISSION_SUBSPACE copy.frequency = signal.frequency copy.data = signal.data.Copy() @@ -141,9 +141,9 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() // Used in auto linking /obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T) - var/turf/position = get_turf(src) - var/turf/T_position = get_turf(T) - if((position.z == T_position.z) || (src.long_range_link && T.long_range_link)) + var/pos_z = get_z(src) + var/tpos_z = get_z(T) + if((pos_z == tpos_z) || (src.long_range_link && T.long_range_link)) for(var/x in autolinkers) if(T.autolinkers.Find(x)) if(src != T) @@ -251,11 +251,17 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 600 machinetype = 1 produces_heat = 0 circuit = /obj/item/weapon/circuitboard/telecomms/receiver + //Vars only used if you're using the overmap + var/overmap_range = 0 + var/overmap_range_min = 0 + var/overmap_range_max = 5 + + var/list/linked_radios_weakrefs = list() /obj/machinery/telecomms/receiver/Initialize() . = ..() @@ -267,8 +273,12 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) RefreshParts() -/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) +/obj/machinery/telecomms/receiver/proc/link_radio(var/obj/item/device/radio/R) + if(!istype(R)) + return + linked_radios_weakrefs |= weakref(R) +/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal) if(!on) // has to be on to receive messages return if(!signal) @@ -276,7 +286,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() if(!check_receive_level(signal)) return - if(signal.transmission_method == 2) + if(signal.transmission_method == TRANSMISSION_SUBSPACE) if(is_freq_listening(signal)) // detect subspace signals @@ -288,14 +298,31 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() relay_information(signal, "/obj/machinery/telecomms/bus") // Send it to a bus instead, if it's linked to one /obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal) + // If it's a direct message from a bluespace radio, we eat it and convert it into a subspace signal locally + if(signal.transmission_method == TRANSMISSION_BLUESPACE) + var/obj/item/device/radio/R = signal.data["radio"] - if(signal.data["level"] != listening_level) + //Who're you? + if(!(weakref(R) in linked_radios_weakrefs)) + signal.data["reject"] = 1 + return 0 + + //We'll resend this for you + signal.data["level"] = z + signal.transmission_method = TRANSMISSION_SUBSPACE + return 1 + + //Where can we hear? + var/list/listening_levels = using_map.get_map_levels(listening_level, TRUE, overmap_range) + + // We couldn't 'hear' it, maybe a relay linked to our hub can 'hear' it + if(!(signal.data["level"] in listening_levels)) for(var/obj/machinery/telecomms/hub/H in links) - var/list/connected_levels = list() + var/list/relayed_levels = list() for(var/obj/machinery/telecomms/relay/R in H.links) if(R.can_receive(signal)) - connected_levels |= R.listening_level - if(signal.data["level"] in connected_levels) + relayed_levels |= R.listening_level + if(signal.data["level"] in relayed_levels) return 1 return 0 return 1 @@ -318,7 +345,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "A mighty piece of hardware used to send/receive massive amounts of data." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 1600 machinetype = 7 circuit = /obj/item/weapon/circuitboard/telecomms/hub @@ -377,7 +404,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "A mighty piece of hardware used to send massive amounts of data far away." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 600 machinetype = 8 produces_heat = 0 @@ -405,7 +432,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() // Add our level and send it back if(can_send(signal)) - signal.data["level"] |= listening_level + signal.data["level"] |= using_map.get_map_levels(listening_level) // Checks to see if it can send/receive. @@ -443,7 +470,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "A mighty piece of hardware used to send massive amounts of data quickly." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 1000 machinetype = 2 circuit = /obj/item/weapon/circuitboard/telecomms/bus @@ -504,7 +531,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "This machine is used to process large quantities of information." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 600 machinetype = 3 delay = 5 @@ -556,7 +583,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() desc = "A machine used to store data and network statistics." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 300 machinetype = 4 circuit = /obj/item/weapon/circuitboard/telecomms/server @@ -602,7 +629,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() totaltraffic += traffic // add current traffic to total traffic //Is this a test signal? Bypass logging - if(signal.data["type"] != 4) + if(signal.data["type"] != SIGNAL_TEST) // If signal has a message and appropriate frequency diff --git a/code/game/machinery/telecomms/telemonitor.dm b/code/game/machinery/telecomms/telemonitor.dm index 16ac9ada8a..819108b2ce 100644 --- a/code/game/machinery/telecomms/telemonitor.dm +++ b/code/game/machinery/telecomms/telemonitor.dm @@ -129,6 +129,6 @@ if(!emagged) playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) emagged = 1 - user << "You you disable the security protocols" + to_chat(user, "You you disable the security protocols") src.updateUsrDialog() return 1 diff --git a/code/game/machinery/telecomms/traffic_control.dm b/code/game/machinery/telecomms/traffic_control.dm index a49376d7de..686a764ada 100644 --- a/code/game/machinery/telecomms/traffic_control.dm +++ b/code/game/machinery/telecomms/traffic_control.dm @@ -127,7 +127,7 @@ add_fingerprint(usr) usr.set_machine(src) if(!src.allowed(usr) && !emagged) - usr << "ACCESS DENIED." + to_chat(usr, "ACCESS DENIED.") return if(href_list["viewserver"]) @@ -212,6 +212,6 @@ if(!emagged) playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) emagged = 1 - user << "You you disable the security protocols" + to_chat(user, "You you disable the security protocols") src.updateUsrDialog() return 1 \ No newline at end of file diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index 302823c82d..bfc8e308a3 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -171,7 +171,7 @@ icon_state = "tele0" dir = 4 var/accurate = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 2000 circuit = /obj/item/weapon/circuitboard/teleporter_hub @@ -209,6 +209,14 @@ O.show_message("Failure: Cannot authenticate locked on coordinates. Please reinstate coordinate matrix.") return if(istype(M, /atom/movable)) + //VOREStation Addition Start: Prevent taurriding abuse + if(istype(M, /mob/living)) + var/mob/living/L = M + if(LAZYLEN(L.buckled_mobs)) + var/datum/riding/R = L.riding_datum + for(var/rider in L.buckled_mobs) + R.force_dismount(rider) + //VOREStation Addition End: Prevent taurriding abuse if(prob(5) && !accurate) //oh dear a problem, put em in deep space do_teleport(M, locate(rand((2*TRANSITIONEDGE), world.maxx - (2*TRANSITIONEDGE)), rand((2*TRANSITIONEDGE), world.maxy - (2*TRANSITIONEDGE)), 3), 2) else @@ -238,7 +246,7 @@ if(istype(M, /mob/living)) var/mob/living/MM = M if(MM.check_contents_for(/obj/item/weapon/disk/nuclear)) - MM << "Something you are carrying seems to be unable to pass through the portal. Better drop it if you want to go through." + to_chat(MM, "Something you are carrying seems to be unable to pass through the portal. Better drop it if you want to go through.") return var/disky = 0 for (var/atom/O in M.contents) //I'm pretty sure this accounts for the maximum amount of container in container stacking. --NeoFite @@ -265,7 +273,7 @@ if(istype(M, /mob/living)) var/mob/living/MM = M if(MM.check_contents_for(/obj/item/weapon/storage/backpack/holding)) - MM << "The Bluespace interface on your Bag of Holding interferes with the teleport!" + to_chat(MM, "The Bluespace interface on your Bag of Holding interferes with the teleport!") precision = rand(1,100) if(istype(M, /obj/item/weapon/storage/backpack/holding)) precision = rand(1,100) @@ -319,7 +327,7 @@ dir = 4 var/active = 0 var/engaged = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 2000 circuit = /obj/item/weapon/circuitboard/teleporter_station @@ -356,8 +364,8 @@ if(com) com.icon_state = "tele1" use_power(5000) - update_use_power(2) - com.update_use_power(2) + update_use_power(USE_POWER_ACTIVE) + com.update_use_power(USE_POWER_ACTIVE) for(var/mob/O in hearers(src, null)) O.show_message("Teleporter engaged!", 2) add_fingerprint(usr) @@ -371,8 +379,8 @@ if(com) com.icon_state = "tele0" com.accurate = 0 - com.update_use_power(1) - update_use_power(1) + com.update_use_power(USE_POWER_IDLE) + update_use_power(USE_POWER_IDLE) for(var/mob/O in hearers(src, null)) O.show_message("Teleporter disengaged!", 2) add_fingerprint(usr) diff --git a/code/game/machinery/transportpod.dm b/code/game/machinery/transportpod.dm index 96401c0908..0b499096fe 100644 --- a/code/game/machinery/transportpod.dm +++ b/code/game/machinery/transportpod.dm @@ -6,7 +6,7 @@ density = 1 //thicc anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF var/in_transit = 0 var/mob/occupant = null diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm index 4319e11127..a3d10db557 100644 --- a/code/game/machinery/turret_control.dm +++ b/code/game/machinery/turret_control.dm @@ -51,7 +51,7 @@ else if(ispath(control_area)) control_area = locate(control_area) else if(istext(control_area)) - for(var/area/A in all_areas) + for(var/area/A in world) if(A.name && A.name==control_area) control_area = A break @@ -68,11 +68,11 @@ /obj/machinery/turretid/proc/isLocked(mob/user) if(ailock && issilicon(user)) - user << "There seems to be a firewall preventing you from accessing this device." + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") return 1 if(locked && !issilicon(user)) - user << "Access denied." + to_chat(user, "Access denied.") return 1 return 0 @@ -90,16 +90,16 @@ if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) if(allowed(usr)) if(emagged) - user << "The turret control is unresponsive." + to_chat(user, "The turret control is unresponsive.") else locked = !locked - user << "You [ locked ? "lock" : "unlock"] the panel." + to_chat(user, "You [ locked ? "lock" : "unlock"] the panel.") return return ..() /obj/machinery/turretid/emag_act(var/remaining_charges, var/mob/user) if(!emagged) - user << "You short out the turret controls' access analysis module." + to_chat(user, "You short out the turret controls' access analysis module.") emagged = 1 locked = 0 ailock = 0 diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 28eb7f16ba..6537b70dfa 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -4,16 +4,14 @@ /obj/machinery/vending name = "Vendomat" desc = "A generic vending machine." - icon = 'icons/obj/vending_vr.dmi' //VOREStation Edit - Eris vending machine sprites + icon = 'icons/obj/vending.dmi' icon_state = "generic" anchored = 1 density = 1 - - var/icon_vend //Icon_state when vending - var/icon_deny //Icon_state when denying access + clicksound = "button" // Power - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 var/vend_power_usage = 150 //actuators and stuff @@ -25,6 +23,7 @@ var/datum/stored_item/vending_product/currently_vending = null // What we're requesting payment for right now var/status_message = "" // Status screen messages like "insufficient funds", displayed in NanoUI var/status_error = 0 // Set to 1 if status_message is an error + var/vending_sound = "machines/vending/vending_drop.ogg" /* Variables used to initialize the product list @@ -71,20 +70,19 @@ /obj/machinery/vending/Initialize() . = ..() wires = new(src) - spawn(4) - if(product_slogans) - slogan_list += splittext(product_slogans, ";") + if(product_slogans) + slogan_list += splittext(product_slogans, ";") - // So not all machines speak at the exact same time. - // The first time this machine says something will be at slogantime + this random value, - // so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is crated. - last_slogan = world.time + rand(0, slogan_delay) + // So not all machines speak at the exact same time. + // The first time this machine says something will be at slogantime + this random value, + // so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is crated. + last_slogan = world.time + rand(0, slogan_delay) - if(product_ads) - ads_list += splittext(product_ads, ";") + if(product_ads) + ads_list += splittext(product_ads, ";") - build_inventory() - power_change() + build_inventory() + power_change() /** * Build produdct_records from the products lists @@ -179,9 +177,10 @@ panel_open = !panel_open to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.") playsound(src, W.usesound, 50, 1) - overlays.Cut() if(panel_open) - overlays += image(icon, "[initial(icon_state)]-panel") + add_overlay("[initial(icon_state)]-panel") + else + cut_overlay("[initial(icon_state)]-panel") SSnanoui.update_uis(src) // Speaker switch is on the main UI, not wires UI return @@ -227,7 +226,7 @@ // This is not a status display message, since it's something the character // themselves is meant to see BEFORE putting the money in - to_chat(usr, "\icon[cashmoney] That is not enough money.") + to_chat(usr, "[bicon(cashmoney)] That is not enough money.") return 0 if(istype(cashmoney, /obj/item/weapon/spacecash)) @@ -253,6 +252,7 @@ */ /obj/machinery/vending/proc/pay_with_ewallet(var/obj/item/weapon/spacecash/ewallet/wallet) visible_message("\The [usr] swipes \the [wallet] through \the [src].") + playsound(src.loc, 'sound/machines/id_swipe.ogg', 50, 1) if(currently_vending.price > wallet.worth) status_message = "Insufficient funds on chargecard." status_error = 1 @@ -273,6 +273,7 @@ visible_message("\The [usr] swipes \the [I] through \the [src].") else visible_message("\The [usr] swipes \the [ID_container] through \the [src].") + playsound(src.loc, 'sound/machines/id_swipe.ogg', 50, 1) var/datum/money_account/customer_account = get_account(I.associated_account_number) if(!customer_account) status_message = "Error: Unable to access account. Please contact technical support if problem persists." @@ -427,7 +428,7 @@ if((href_list["vend"]) && (vend_ready) && (!currently_vending)) if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH to_chat(usr, "Access denied.") //Unless emagged of course - flick(icon_deny,src) + flick("[icon_state]-deny",src) playsound(src.loc, 'sound/machines/deniedbeep.ogg', 50, 0) return @@ -464,7 +465,7 @@ /obj/machinery/vending/proc/vend(datum/stored_item/vending_product/R, mob/user) if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH to_chat(usr, "Access denied.") //Unless emagged of course - flick(icon_deny,src) + flick("[icon_state]-deny",src) playsound(src.loc, 'sound/machines/deniedbeep.ogg', 50, 0) return vend_ready = 0 //One thing at a time!! @@ -495,8 +496,7 @@ last_reply = world.time use_power(vend_power_usage) //actuators and stuff - if(icon_vend) //Show the vending animation if needed - flick(icon_vend,src) + flick("[icon_state]-vend",src) spawn(vend_delay) R.get_product(get_turf(src)) if(has_logs) @@ -505,8 +505,7 @@ sleep(3) if(R.get_product(get_turf(src))) visible_message("\The [src] clunks as it vends an additional item.") - - playsound(src, 'sound/items/vending.ogg', 50, 1, 1) + playsound(src.loc, "sound/[vending_sound]", 100, 1, 1) status_message = "" status_error = 0 @@ -675,8 +674,7 @@ /obj/machinery/vending/boozeomat name = "Booze-O-Mat" desc = "A technological marvel, supposedly able to mix just the mixture you'd like to drink the moment you ask for one." - icon_state = "boozeomat" - icon_deny = "boozeomat-deny" + icon_state = "fridge_dark" products = list(/obj/item/weapon/reagent_containers/food/drinks/glass2/square = 10, /obj/item/weapon/reagent_containers/food/drinks/glass2/rocks = 10, /obj/item/weapon/reagent_containers/food/drinks/glass2/shake = 10, @@ -685,12 +683,16 @@ /obj/item/weapon/reagent_containers/food/drinks/glass2/pint = 10, /obj/item/weapon/reagent_containers/food/drinks/glass2/mug = 10, /obj/item/weapon/reagent_containers/food/drinks/glass2/wine = 10, + /obj/item/weapon/reagent_containers/food/drinks/glass2/carafe = 2, + /obj/item/weapon/reagent_containers/food/drinks/glass2/pitcher = 2, /obj/item/weapon/reagent_containers/food/drinks/metaglass = 10, + /obj/item/weapon/reagent_containers/food/drinks/metaglass/metapint = 10, /obj/item/weapon/reagent_containers/food/drinks/bottle/gin = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/absinthe = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/bluecuracao = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/cognac = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/grenadine = 5, + /obj/item/weapon/reagent_containers/food/condiment/cornoil = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/kahlua = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/melonliquor = 5, /obj/item/weapon/reagent_containers/food/drinks/bottle/peppermintschnapps = 5, @@ -725,7 +727,7 @@ /obj/item/weapon/reagent_containers/food/drinks/ice = 10, /obj/item/weapon/reagent_containers/food/drinks/tea = 15, /obj/item/weapon/glass_extra/stick = 30, - /obj/item/weapon/glass_extra/straw = 30) + /obj/item/weapon/glass_extra/straw = 30) //VOREStation Add - Carafes and Pitchers contraband = list() vend_delay = 15 idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan. @@ -734,6 +736,7 @@ req_access = list(access_bar) req_log_access = access_bar has_logs = 1 + vending_sound = "machines/vending/vending_cans.ogg" /obj/machinery/vending/assist products = list( /obj/item/device/assembly/prox_sensor = 5,/obj/item/device/assembly/igniter = 3,/obj/item/device/assembly/signaler = 4, @@ -746,13 +749,13 @@ desc = "A vending machine which dispenses hot drinks." product_ads = "Have a drink!;Drink up!;It's good for you!;Would you like a hot joe?;I'd kill for some coffee!;The best beans in the galaxy.;Only the finest brew for you.;Mmmm. Nothing like a coffee.;I like coffee, don't you?;Coffee helps you work!;Try some tea.;We hope you like the best!;Try our new chocolate!;Admin conspiracies" icon_state = "coffee" - icon_vend = "coffee-vend" vend_delay = 34 idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan. vend_power_usage = 85000 //85 kJ to heat a 250 mL cup of coffee products = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 25,/obj/item/weapon/reagent_containers/food/drinks/tea = 25,/obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 25) contraband = list(/obj/item/weapon/reagent_containers/food/drinks/ice = 10) prices = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 2, /obj/item/weapon/reagent_containers/food/drinks/tea = 2, /obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 2) //VOREStation Edit + vending_sound = "machines/vending/vending_coffee.ogg" /obj/machinery/vending/snack name = "Getmore Chocolate Corp" @@ -773,8 +776,7 @@ /obj/machinery/vending/cola name = "Robust Softdrinks" desc = "A softdrink vendor provided by Robust Industries, LLC." - icon_state = "Cola_Machine" //VOREStation Edit - icon_vend = "Cola_Machine-purchase" //VOREStation Edit + icon_state = "Cola_Machine" product_slogans = "Robust Softdrinks: More robust than a toolbox to the head!" product_ads = "Refreshing!;Hope you're thirsty!;Over 1 million drinks sold!;Thirsty? Why not cola?;Please, have a drink!;Drink up!;The best drinks in space." products = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola = 10,/obj/item/weapon/reagent_containers/food/drinks/cans/space_mountain_wind = 10, @@ -789,6 +791,7 @@ /obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea = 1,/obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice = 1, /obj/item/weapon/reagent_containers/food/drinks/cans/gingerale = 1) idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan. + vending_sound = "machines/vending/vending_cans.ogg" /obj/machinery/vending/fitness name = "SweatMAX" @@ -823,7 +826,6 @@ desc = "Cartridges for PDAs." product_slogans = "Carts to go!" icon_state = "cart" - icon_deny = "cart-deny" req_access = list(access_hop) products = list(/obj/item/weapon/cartridge/medical = 10,/obj/item/weapon/cartridge/engineering = 10,/obj/item/weapon/cartridge/security = 10, /obj/item/weapon/cartridge/janitor = 10,/obj/item/weapon/cartridge/signal/science = 10,/obj/item/device/pda/heads = 10, @@ -865,7 +867,6 @@ name = "NanoMed Plus" desc = "Medical drug dispenser." icon_state = "med" - icon_deny = "med-deny" product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!" req_access = list(access_medical) products = list(/obj/item/weapon/reagent_containers/glass/bottle/antitoxin = 4,/obj/item/weapon/reagent_containers/glass/bottle/inaprovaline = 4, @@ -893,7 +894,6 @@ desc = "A wall-mounted version of the NanoMed." product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?" icon_state = "wallmed" - icon_deny = "wallmed-deny" density = 0 //It is wall-mounted, and thus, not dense. --Superxpdude products = list(/obj/item/stack/medical/bruise_pack = 2,/obj/item/stack/medical/ointment = 2,/obj/item/weapon/reagent_containers/hypospray/autoinjector = 4,/obj/item/device/healthanalyzer = 1) contraband = list(/obj/item/weapon/reagent_containers/syringe/antitoxin = 4,/obj/item/weapon/reagent_containers/syringe/antiviral = 4,/obj/item/weapon/reagent_containers/pill/tox = 1) @@ -904,7 +904,6 @@ name = "NanoMed" desc = "A wall-mounted version of the NanoMed, containing only vital first aid equipment." icon_state = "wallmed" - icon_deny = "wallmed-deny" density = 0 //It is wall-mounted, and thus, not dense. --Superxpdude products = list(/obj/item/weapon/reagent_containers/hypospray/autoinjector = 5,/obj/item/weapon/reagent_containers/syringe/antitoxin = 3,/obj/item/stack/medical/bruise_pack = 3, /obj/item/stack/medical/ointment =3,/obj/item/device/healthanalyzer = 3) @@ -917,7 +916,6 @@ desc = "A security equipment vendor." product_ads = "Crack capitalist skulls!;Beat some heads in!;Don't forget - harm is good!;Your weapons are right here.;Handcuffs!;Freeze, scumbag!;Don't tase me bro!;Tase them, bro.;Why not have a donut?" icon_state = "sec" - icon_deny = "sec-deny" req_access = list(access_security) products = list(/obj/item/weapon/handcuffs = 8,/obj/item/weapon/grenade/flashbang = 4,/obj/item/device/flash = 5, /obj/item/weapon/reagent_containers/food/snacks/donut/normal = 12,/obj/item/weapon/storage/box/evidence = 6) @@ -930,8 +928,7 @@ desc = "A plant nutrients vendor." product_slogans = "Aren't you glad you don't have to fertilize the natural way?;Now with 50% less stink!;Plants are people too!" product_ads = "We like plants!;Don't you want some?;The greenest thumbs ever.;We like big plants.;Soft soil..." - icon_state = "nutri" - icon_deny = "nutri-deny" + icon_state = "nutri_generic" products = list(/obj/item/weapon/reagent_containers/glass/bottle/eznutrient = 6,/obj/item/weapon/reagent_containers/glass/bottle/left4zed = 4,/obj/item/weapon/reagent_containers/glass/bottle/robustharvest = 3,/obj/item/weapon/plantspray/pests = 20, /obj/item/weapon/reagent_containers/syringe = 5,/obj/item/weapon/reagent_containers/glass/beaker = 4,/obj/item/weapon/storage/bag/plants = 5) premium = list(/obj/item/weapon/reagent_containers/glass/bottle/ammonia = 10,/obj/item/weapon/reagent_containers/glass/bottle/diethylamine = 5) @@ -942,7 +939,7 @@ desc = "When you need seeds fast!" product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!" product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!" - icon_state = "seeds" + icon_state = "seeds_generic" products = list(/obj/item/seeds/bananaseed = 3,/obj/item/seeds/berryseed = 3,/obj/item/seeds/carrotseed = 3,/obj/item/seeds/chantermycelium = 3,/obj/item/seeds/chiliseed = 3, /obj/item/seeds/cornseed = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/replicapod = 3,/obj/item/seeds/soyaseed = 3, @@ -996,6 +993,8 @@ product_ads = "Mm, food stuffs!;Food and food accessories.;Get your plates!;You like forks?;I like forks.;Woo, utensils.;You don't really need these..." icon_state = "dinnerware" products = list( + /obj/item/weapon/reagent_containers/food/condiment/yeast = 5, + /obj/item/weapon/reagent_containers/food/condiment/cornoil = 5, /obj/item/weapon/tray = 8, /obj/item/weapon/material/kitchen/utensil/fork = 6, /obj/item/weapon/material/knife = 6, @@ -1026,12 +1025,12 @@ products = list(/obj/item/weapon/reagent_containers/food/drinks/bottle/space_up = 30) // TODO Russian soda can contraband = list(/obj/item/weapon/reagent_containers/food/drinks/bottle/cola = 20) // TODO Russian cola can idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan. + vending_sound = "machines/vending/vending_cans.ogg" /obj/machinery/vending/tool name = "YouTool" desc = "Tools for tools." icon_state = "tool" - icon_deny = "tool-deny" //req_access = list(access_maint_tunnels) //Maintenance access products = list(/obj/item/stack/cable_coil/random = 10,/obj/item/weapon/tool/crowbar = 5,/obj/item/weapon/weldingtool = 3,/obj/item/weapon/tool/wirecutters = 5, /obj/item/weapon/tool/wrench = 5,/obj/item/device/analyzer = 5,/obj/item/device/t_scanner = 5,/obj/item/weapon/tool/screwdriver = 5, @@ -1046,7 +1045,6 @@ name = "Engi-Vend" desc = "Spare tool vending. What? Did you expect some witty description?" icon_state = "engivend" - icon_deny = "engivend-deny" req_access = list(access_engine_equip) products = list(/obj/item/device/geiger = 4,/obj/item/clothing/glasses/meson = 2,/obj/item/device/multitool = 4,/obj/item/weapon/cell/high = 10, /obj/item/weapon/airlock_electronics = 10,/obj/item/weapon/module/power_control = 10, @@ -1069,7 +1067,6 @@ name = "Robco Tool Maker" desc = "Everything you need for do-it-yourself station repair." icon_state = "engi" - icon_deny = "engi-deny" req_access = list(access_engine_equip) products = list(/obj/item/clothing/under/rank/chief_engineer = 4,/obj/item/clothing/under/rank/engineer = 4,/obj/item/clothing/shoes/orange = 4,/obj/item/clothing/head/hardhat = 4, /obj/item/weapon/storage/belt/utility = 4,/obj/item/clothing/glasses/meson = 4,/obj/item/clothing/gloves/yellow = 4, /obj/item/weapon/tool/screwdriver = 12, @@ -1087,7 +1084,6 @@ name = "Robotech Deluxe" desc = "All the tools you need to create your own robot army." icon_state = "robotics" - icon_deny = "robotics-deny" req_access = list(access_robotics) products = list(/obj/item/clothing/suit/storage/toggle/labcoat = 4,/obj/item/clothing/under/rank/roboticist = 4,/obj/item/stack/cable_coil = 4,/obj/item/device/flash = 4, /obj/item/weapon/cell/high = 12, /obj/item/device/assembly/prox_sensor = 3,/obj/item/device/assembly/signaler = 3,/obj/item/device/healthanalyzer = 3, @@ -1127,7 +1123,8 @@ /obj/item/toy/plushie/face_hugger = 1, /obj/item/toy/plushie/carp = 1, /obj/item/toy/plushie/deer = 1, - /obj/item/toy/plushie/tabby_cat = 1) + /obj/item/toy/plushie/tabby_cat = 1, + /obj/item/device/threadneedle = 3) premium = list(/obj/item/weapon/reagent_containers/food/drinks/bottle/champagne = 1, /obj/item/weapon/storage/trinketbox = 2) prices = list(/obj/item/weapon/storage/fancy/heartbox = 15, @@ -1155,7 +1152,8 @@ /obj/item/toy/plushie/face_hugger = 50, /obj/item/toy/plushie/carp = 50, /obj/item/toy/plushie/deer = 50, - /obj/item/toy/plushie/tabby_cat = 50) + /obj/item/toy/plushie/tabby_cat = 50, + /obj/item/device/threadneedle = 2) /obj/machinery/vending/fishing name = "Loot Trawler" diff --git a/code/game/machinery/vending_vr.dm b/code/game/machinery/vending_vr.dm index 788b050013..ed06132e7f 100644 --- a/code/game/machinery/vending_vr.dm +++ b/code/game/machinery/vending_vr.dm @@ -55,8 +55,7 @@ /obj/machinery/vending/food name = "Food-O-Mat" desc = "A technological marvel, supposedly able to cook or mix a large variety of food or drink." - icon_state = "boozeomat" - icon_deny = "boozeomat-deny" + icon_state = "hotfood" products = list(/obj/item/weapon/tray = 8, /obj/item/weapon/material/kitchen/utensil/fork = 6, /obj/item/weapon/material/knife/plastic = 6, @@ -89,8 +88,6 @@ /obj/machinery/vending/food/arojoan //Fluff vendor for the lewd houseboat. name = "Custom Food-O-Mat" desc = "Do you think Joan cooks? Of course not. Lazy squirrel!" - icon_state = "boozeomat" - icon_deny = "boozeomat-deny" products = list(/obj/item/weapon/tray = 6, /obj/item/weapon/material/kitchen/utensil/fork = 6, /obj/item/weapon/material/knife/plastic = 6, @@ -121,6 +118,7 @@ desc = "A self-defense equipment vending machine. When you need to take care of that clown." product_slogans = "The best defense is good offense!;Buy for your whole family today!;Nobody can outsmart bullet!;God created man - Frozen Star made them EQUAL!;Nobody can outsmart bullet!;Stupidity can be cured! By LEAD.;Dead kids can't bully your children!" product_ads = "Stunning!;Take justice in your own hands!;LEADearship!" + icon = 'icons/obj/vending_vr.dmi' icon_state = "weapon" products = list(/obj/item/device/flash = 6,/obj/item/weapon/reagent_containers/spray/pepper = 6, /obj/item/weapon/gun/projectile/olivaw = 5, /obj/item/weapon/gun/projectile/giskard = 5, /obj/item/ammo_magazine/mg/cl32/rubber = 20) contraband = list(/obj/item/weapon/reagent_containers/food/snacks/syndicake = 6) @@ -136,6 +134,7 @@ name = "Blood-Onator" desc = "Freezer-vendor for storage and quick dispensing of blood packs" product_ads = "The true life juice!;Vampire's choice!;Home-grown blood only!;Donate today, be saved tomorrow!;Approved by Zeng-Hu Pharmaceuticals Incorporated!; Curse you, Vey-Med artificial blood!" + icon = 'icons/obj/vending_vr.dmi' icon_state = "blood" idle_power_usage = 211 req_access = list(access_medical) @@ -151,6 +150,7 @@ name = "Fingers and Toes" desc = "A special vendor for gloves and shoes!" product_ads = "Do you have fingers and toes? COVER THEM UP!;Show me your toes! Wait. NO DON'T! BUY NEW SHOES!;Don't leave prints, BUY SOME GLOVES!;Remember to check your shoes for micros! You don't have to let them out, but just check for them!;Fingers and Toes is not liable for micro entrapment or abuse under the feet of our patrons.!;This little piggy went WE WE WE all the way down to FINGERS AND TOES to pick up some sweet new gloves and shoes." + icon = 'icons/obj/vending_vr.dmi' icon_state = "glovesnshoes" products = list(/obj/item/clothing/gloves/evening = 5, /obj/item/clothing/gloves/fingerless = 5, @@ -278,7 +278,6 @@ desc = "A vendor using compressed matter cartridges to store large amounts of basic station uniforms." product_ads = "Don't get caught naked!;Pick up your uniform!;Using compressed matter cartridges and VERY ETHICAL labor practices, we bring you the uniforms you need!;No uniform? No problem!;We've got your covered!;The Basics is not responsible for being crushed under the amount of things inside our machines. DO NOT VEND IN EXCESS!!" icon_state = "loadout" - icon_vend = "loadout-purchase" vend_delay = 16 products = list(/obj/item/device/pda = 50, /obj/item/device/radio/headset = 50, @@ -300,13 +299,13 @@ /obj/item/clothing/under/color/yellow = 5, /obj/item/clothing/shoes/black = 20, /obj/item/clothing/shoes/white = 20) + prices = list() /obj/machinery/vending/loadout/accessory name = "Looty Inc." desc = "A special vendor for accessories." product_ads = "Want shinies? We have the shinies.;Need that special something to complete your outfit? We have what you need!;Ditch that old dull dangly something you've got and pick up one of our shinies!;Bracelets, collars, scarfs rings and more! We have the fancy things you need!;Does your pet need a collar? We don't judge! Keep them in line with one of one of ours!;Top of the line materials! 'Hand crafted' goods!" icon_state = "accessory" - icon_vend = "accessory-purchase" vend_delay = 6 products = list(/obj/item/clothing/accessory = 5, /obj/item/clothing/accessory/armband/med/color = 10, @@ -536,7 +535,6 @@ desc = "A special vendor using compressed matter cartridges to store large amounts of clothing." product_ads = "Tired of your grey jumpsuit? Spruce yourself up!;We have the outfit for you!;Don't let that grey jumpsuit get you down, get a ROBUST outfit right now!;Using compressed matter catridges and VERY ETHICAL labor practices to bring YOU the clothing you crave!;Are you sure you want to go to work in THAT?;All of our wares have a whole TWO pockets!" icon_state = "clothing" - icon_vend = "clothing-purchase" vend_delay = 16 products = list(/obj/item/clothing/under/bathrobe = 5, /obj/item/clothing/under/dress/black_corset = 5, @@ -880,7 +878,6 @@ desc = "A special vendor for devices and gadgets." product_ads = "You can't RESIST our great deals!;Feeling disconnected? We have a gadget for you!;You know you have the capacity to buy our capacitors!;FILL THAT HOLE IN YOUR HEART WITH OUR PLASTIC DISTRACTIONS!!!;Devices for everyone! Chips Co.!;ROBUST INVENTORY, GREAT PRICES! ;DON'T FORGET THE oyPAD 13s PRO! ON SALE NOW, ONLY ONE THOUSAND THALERS!" icon_state = "gadgets" - icon_vend = "gadgets-purchase" vend_delay = 11 products = list(/obj/item/clothing/suit/circuitry = 1, /obj/item/clothing/head/circuitry = 1, @@ -960,7 +957,6 @@ desc = "A special vendor using compressed matter cartridges to store large amounts of overwear!" product_ads = "Dress your best! It's what big D would want.;Overwear for all occasions!;Big D has what you need if what you need is some form of jacket!;Need a new hoodie? Bid D has you covered.;Big D says you need a new suit!;Big D smiles when he sees you in one of his coats!" icon_state = "suit" - icon_vend = "suit-purchase" vend_delay = 16 products = list(/obj/item/clothing/suit/storage/apron = 5, /obj/item/clothing/suit/storage/flannel/aqua = 5, @@ -1143,7 +1139,8 @@ name = "Thespian's Delight" desc = "Sometimes nerds need costumes!" product_ads = "Don't let your art be stifled!;Remember, practice makes perfect!;Break a leg!;Don't make me get the cane!;Thespian's Delight entering stage right!;Costumes for your acting needs!" - icon_state = "Theater_b" + icon = 'icons/obj/vending.dmi' + icon_state = "theater" products = list(/obj/item/clothing/suit/storage/hooded/carp_costume = 3, /obj/item/clothing/suit/storage/hooded/carp_costume = 3, /obj/item/clothing/suit/chickensuit = 3, @@ -1307,14 +1304,9 @@ /obj/item/seeds/whitebeetseed = 3, /obj/item/seeds/wabback = 2) -//TFF 19/12/19 - Brig version of a Nutrimax /obj/machinery/vending/hydronutrients/brig name = "Brig NutriMax" desc = "A plant nutrients vendor. Seems some items aren't included." - product_slogans = "Aren't you glad you don't have to fertilize the natural way?;Now with 50% less stink!;Plants are people too!" - product_ads = "We like plants!;Don't you want some?;The greenest thumbs ever.;We like big plants.;Soft soil..." - icon_state = "nutri" - icon_deny = "nutri-deny" products = list(/obj/item/weapon/reagent_containers/glass/bottle/eznutrient = 6,/obj/item/weapon/reagent_containers/glass/bottle/left4zed = 4,/obj/item/weapon/reagent_containers/glass/bottle/robustharvest = 3,/obj/item/weapon/plantspray/pests = 20, /obj/item/weapon/reagent_containers/glass/beaker = 4,/obj/item/weapon/storage/bag/plants = 5) premium = list(/obj/item/weapon/reagent_containers/glass/bottle/ammonia = 10,/obj/item/weapon/reagent_containers/glass/bottle/diethylamine = 5) @@ -1331,4 +1323,11 @@ /obj/machinery/vending/emergencyfood/filled products = list(/obj/item/weapon/storage/box/wings/tray = 40) - contraband = list(/obj/item/weapon/storage/box/wings/tray = 20) \ No newline at end of file + contraband = list(/obj/item/weapon/storage/box/wings/tray = 20) + +/obj/machinery/vending/cola + icon_state = "Soda_Machine" + +/obj/machinery/vending/cola/soft + icon = 'icons/obj/vending_vr.dmi' + icon_state = "Cola_Machine" diff --git a/code/game/machinery/vending_yw.dm b/code/game/machinery/vending_yw.dm index 095ac26e27..cec6c9abcf 100644 --- a/code/game/machinery/vending_yw.dm +++ b/code/game/machinery/vending_yw.dm @@ -3,11 +3,10 @@ prices += list(/obj/item/weapon/storage/fancy/cigarettes/yw/mauser = 18) ..() -/obj/machinery/vending/food/prison //Fluff vendor for the lewd houseboat. +/obj/machinery/vending/food/prison name = "Prison Nutriment Vendor" desc = "Delicious, probably not." - icon_state = "boozeomat" - icon_deny = "boozeomat-deny" + icon_state = "fridge_dark" products = list(/obj/item/weapon/tray = 6, /obj/item/weapon/material/kitchen/utensil/fork = 6, /obj/item/weapon/material/knife/plastic = 6, @@ -27,7 +26,6 @@ desc = "A security equipment vendor." product_ads = "Crack capitalist skulls!;Beat some heads in!;Don't forget - harm is good!;Your weapons are right here.;Handcuffs!;Freeze, scumbag!;Don't tase me bro!;Tase them, bro.;Why not have a donut?" icon_state = "sec" - icon_deny = "sec-deny" req_access = list(access_security) products = list(/obj/item/weapon/handcuffs = 8,/obj/item/weapon/grenade/flashbang = 4,/obj/item/device/flash = 5, /obj/item/weapon/reagent_containers/food/snacks/donut/normal = 12,/obj/item/weapon/storage/box/evidence = 6) diff --git a/code/game/machinery/virtual_reality/ar_console.dm b/code/game/machinery/virtual_reality/ar_console.dm index 1f4c8c16fe..f4c6049487 100644 --- a/code/game/machinery/virtual_reality/ar_console.dm +++ b/code/game/machinery/virtual_reality/ar_console.dm @@ -73,7 +73,7 @@ if(A in component_parts) continue A.loc = src.loc - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() /obj/machinery/vr_sleeper/alien/enter_vr() diff --git a/code/game/machinery/virtual_reality/vr_console.dm b/code/game/machinery/virtual_reality/vr_console.dm index dcd6e555b9..8a417a98bc 100644 --- a/code/game/machinery/virtual_reality/vr_console.dm +++ b/code/game/machinery/virtual_reality/vr_console.dm @@ -18,7 +18,7 @@ var/mirror_first_occupant = TRUE // Do we force the newly produced body to look like the occupant? - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 15 active_power_usage = 200 light_color = "#FF0000" @@ -174,7 +174,7 @@ M.client.perspective = EYE_PERSPECTIVE M.client.eye = src M.loc = src - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) occupant = M update_icon() @@ -203,7 +203,7 @@ if(A in component_parts) continue A.loc = src.loc - update_use_power(1) + update_use_power(USE_POWER_IDLE) update_icon() /obj/machinery/vr_sleeper/proc/enter_vr() diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index 480c124235..fd46233390 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -5,6 +5,9 @@ icon_state = "wm_10" density = 1 anchored = 1.0 + clicksound = "button" + clickvol = 40 + circuit = /obj/item/weapon/circuitboard/washing var/state = 1 //1 = empty, open door @@ -43,7 +46,7 @@ return if(state != 4) - usr << "The washing machine cannot run in this state." + to_chat(usr, "The washing machine cannot run in this state.") return if(locate(/mob,washing)) @@ -94,7 +97,7 @@ return /*if(W.is_screwdriver()) panel = !panel - user << "You [panel ? "open" : "close"] the [src]'s maintenance panel"*/ + to_chat(user, "You [panel ? "open" : "close"] the [src]'s maintenance panel")*/ if(istype(W,/obj/item/weapon/pen/crayon) || istype(W,/obj/item/weapon/stamp)) if(state in list( 1, 3, 6)) if(!crayon) @@ -116,7 +119,7 @@ ..() else if(is_type_in_list(W, disallowed_types)) - user << "You can't fit \the [W] inside." + to_chat(user, "You can't fit \the [W] inside.") return else if(istype(W, /obj/item/clothing) || istype(W, /obj/item/weapon/bedsheet)) @@ -127,9 +130,9 @@ washing += W state = 3 else - user << "You can't put the item in right now." + to_chat(user, "You can't put the item in right now.") else - user << "The washing machine is full." + to_chat(user, "The washing machine is full.") else ..() update_icon() @@ -153,7 +156,7 @@ washing.Cut() state = 1 if(5) - user << "The [src] is busy." + to_chat(user, "The [src] is busy.") if(6) state = 7 if(7) diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm index e36b577e9e..81c720bacd 100644 --- a/code/game/machinery/wishgranter.dm +++ b/code/game/machinery/wishgranter.dm @@ -3,7 +3,7 @@ desc = "You're not so sure about this, anymore..." icon = 'icons/obj/device.dmi' icon_state = "syndbeacon" - use_power = 0 + use_power = USE_POWER_OFF anchored = 1 density = 1 var/charges = 1 @@ -13,23 +13,24 @@ usr.set_machine(src) if(charges <= 0) - user << "The Wish Granter lies silent." + to_chat(user, "The Wish Granter lies silent.") return else if(!istype(user, /mob/living/carbon/human)) - user << "You feel a dark stirring inside of the Wish Granter, something you want nothing of. Your instincts are better than any man's." + to_chat(user, "You feel a dark stirring inside of the Wish Granter, something you want nothing of. Your instincts are better than any man's.") return else if(is_special_character(user)) - user << "Even to a heart as dark as yours, you know nothing good will come of this. Something instinctual makes you pull away." + to_chat(user, "Even to a heart as dark as yours, you know nothing good will come of this. Something instinctual makes you pull away.") else if(!insisting) - user << "Your first touch makes the Wish Granter stir, listening to you. Are you really sure you want to do this?" + to_chat(user, "Your first touch makes the Wish Granter stir, listening to you. Are you really sure you want to do this?") insisting++ else - user << "You speak. [pick("I want the station to disappear","Humanity is corrupt, mankind must be destroyed","I want to be rich", "I want to rule the world","I want immortality.")]. The Wish Granter answers." - user << "Your head pounds for a moment, before your vision clears. You are the avatar of the Wish Granter, and your power is LIMITLESS! And it's all yours. You need to make sure no one can take it from you. No one can know, first." + var/message = "You speak. [pick("I want the station to disappear","Humanity is corrupt, mankind must be destroyed","I want to be rich", "I want to rule the world","I want immortality.")]. The Wish Granter answers." + to_chat(user, message) + to_chat(user, "Your head pounds for a moment, before your vision clears. You are the avatar of the Wish Granter, and your power is LIMITLESS! And it's all yours. You need to make sure no one can take it from you. No one can know, first.") charges-- insisting = 0 @@ -63,6 +64,6 @@ user.mind.objectives += silence show_objectives(user.mind) - user << "You have a very bad feeling about this." + to_chat(user, "You have a very bad feeling about this.") return \ No newline at end of file diff --git a/code/game/magic/archived_book.dm b/code/game/magic/archived_book.dm index b2af5f3f73..a283247996 100644 --- a/code/game/magic/archived_book.dm +++ b/code/game/magic/archived_book.dm @@ -52,7 +52,7 @@ datum/book_manager/proc/freeid() else var/DBQuery/query = dbcon.NewQuery("DELETE FROM library WHERE id=[isbn]") if(!query.Execute()) - usr << query.ErrorMsg() + to_chat(usr,query.ErrorMsg()) dbcon.Disconnect() else book_mgr.remove(isbn) @@ -85,7 +85,7 @@ datum/archived_book/New(var/path) if (isnull(version) || version < BOOK_VERSION_MIN || version > BOOK_VERSION_MAX) fdel(path) - usr << "What book?" + to_chat(usr, "What book?") return 0 F["author"] >> author diff --git a/code/game/mecha/combat/fighter.dm b/code/game/mecha/combat/fighter.dm new file mode 100644 index 0000000000..171c96395e --- /dev/null +++ b/code/game/mecha/combat/fighter.dm @@ -0,0 +1,452 @@ +#define NOGRAV_FIGHTER_DAMAGE 20 + +/obj/mecha/combat/fighter + name = "Delete me, nerd!!" + desc = "The base type of fightercraft. Don't spawn this one!" + + var/datum/effect/effect/system/ion_trail_follow/ion_trail + var/stabilization_enabled = TRUE //If our anti-space-drift is on + var/ground_capable = FALSE //If we can fly over normal turfs and not just space + + icon = 'icons/mecha/fighters64x64.dmi' + + icon_state = "" + initial_icon = "" + + step_in = 2 //Fast + + health = 400 + maxhealth = 400 + + infra_luminosity = 6 + + opacity = FALSE + + wreckage = /obj/effect/decal/mecha_wreckage/gunpod + + stomp_sound = 'sound/machines/generator/generator_end.ogg' + swivel_sound = 'sound/machines/hiss.ogg' + + bound_height = 64 + bound_width = 64 + + max_hull_equip = 2 + max_weapon_equip = 2 + max_utility_equip = 1 + max_universal_equip = 1 + max_special_equip = 1 + +/obj/mecha/combat/fighter/Initialize() + . = ..() + ion_trail = new /datum/effect/effect/system/ion_trail_follow() + ion_trail.set_up(src) + ion_trail.stop() + +/obj/mecha/combat/fighter/moved_inside(var/mob/living/carbon/human/H) + . = ..() + consider_gravity() + +/obj/mecha/combat/fighter/go_out() + . = ..() + consider_gravity() + +//We don't get lost quite as easy. +/obj/mecha/combat/fighter/touch_map_edge() + //No overmap enabled or no driver to choose + if(!using_map.use_overmap || !occupant || !can_ztravel()) + return ..() + + var/obj/effect/overmap/visitable/our_ship = get_overmap_sector(z) + + //We're not on the overmap + if(!our_ship) + return ..() + + //Stored for safety checking after user input + var/this_x = x + var/this_y = y + var/this_z = z + var/this_occupant = occupant + + var/what_edge + + var/new_x + var/new_y + var/new_z + + if(x <= TRANSITIONEDGE) + what_edge = WEST + new_x = world.maxx - TRANSITIONEDGE - 2 + new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (x >= (world.maxx - TRANSITIONEDGE + 1)) + what_edge = EAST + new_x = TRANSITIONEDGE + 1 + new_y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (y <= TRANSITIONEDGE) + what_edge = SOUTH + new_y = world.maxy - TRANSITIONEDGE -2 + new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + else if (y >= (world.maxy - TRANSITIONEDGE + 1)) + what_edge = NORTH + new_y = TRANSITIONEDGE + 1 + new_x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + var/list/choices = list() + for(var/obj/effect/overmap/visitable/V in range(1, our_ship)) + choices[V.name] = V + + var/choice = input("Choose an overmap destination:", "Destination", null) as null|anything in choices + if(!choice) + var/backwards = turn(what_edge, 180) + forceMove(get_step(src,backwards)) //Move them back a step, then. + set_dir(backwards) + return + else + var/obj/effect/overmap/visitable/V = choices[choice] + if(occupant != this_occupant || this_x != x || this_y != y || this_z != z || get_dist(V,our_ship) > 1) //Sanity after user input + to_chat(occupant, "You or they appear to have moved!") + return + var/list/levels = V.get_space_zlevels() + if(!levels.len) + to_chat(occupant, "You don't appear to be able to get there from here!") + return + new_z = pick(levels) + var/turf/destination = locate(new_x, new_y, new_z) + if(!destination || destination.density) + to_chat(occupant, "You don't appear to be able to get there from here! Is it blocked?") + return + else + forceMove(destination) + +//Modified phazon code +/obj/mecha/combat/fighter/Topic(href, href_list) + ..() + if (href_list["toggle_stabilization"]) + stabilization_enabled = !stabilization_enabled + send_byjax(src.occupant,"exosuit.browser","stabilization_command","[stabilization_enabled?"Dis":"En"]able thruster stabilization") + src.occupant_message("Thruster stabilization [stabilization_enabled? "enabled" : "disabled"].") + return + +/obj/mecha/combat/fighter/get_commands() + var/output = {" + "} + output += ..() + return output + +/obj/mecha/combat/fighter/can_ztravel() + return (stabilization_enabled && has_charge(step_energy_drain)) + +// No space drifting +/obj/mecha/combat/fighter/check_for_support() + if (stabilization_enabled) + return 1 + + return ..() + +// No falling if we've got our boosters on +/obj/mecha/combat/fighter/can_fall() + return (stabilization_enabled && has_charge(step_energy_drain)) + +/obj/mecha/combat/fighter/proc/consider_gravity(var/moved = FALSE) + var/gravity = has_gravity() + if(gravity && ground_capable && occupant) + start_hover() + else if((!gravity && ground_capable) || !occupant) + stop_hover() + else if(moved && gravity && !ground_capable) + occupant_message("Collision alert! Vehicle not rated for use in gravity!") + take_damage(NOGRAV_FIGHTER_DAMAGE, "brute") + playsound(loc, 'sound/effects/grillehit.ogg', 50, 1) + +/obj/mecha/combat/fighter/handle_equipment_movement() + . = ..() + consider_gravity(TRUE) + +/obj/mecha/combat/fighter/proc/start_hover() + if(!ion_trail.on) //We'll just use this to store if we're floating or not + ion_trail.start() + var/amplitude = 2 //maximum displacement from original position + var/period = 36 //time taken for the mob to go up >> down >> original position, in deciseconds. Should be multiple of 4 + + var/top = old_y + amplitude + var/bottom = old_y - amplitude + var/half_period = period / 2 + var/quarter_period = period / 4 + + animate(src, pixel_y = top, time = quarter_period, easing = SINE_EASING | EASE_OUT, loop = -1) //up + animate(pixel_y = bottom, time = half_period, easing = SINE_EASING, loop = -1) //down + animate(pixel_y = old_y, time = quarter_period, easing = SINE_EASING | EASE_IN, loop = -1) //back + +/obj/mecha/combat/fighter/proc/stop_hover() + if(ion_trail.on) + ion_trail.stop() + animate(src, pixel_y = old_y, time = 5, easing = SINE_EASING | EASE_IN) //halt animation + +/obj/mecha/combat/fighter/check_for_support() + if (has_charge(step_energy_drain) && stabilization_enabled) + return 1 + + var/list/things = orange(1, src) + + if(locate(/obj/structure/grille) in things || locate(/obj/structure/lattice) in things || locate(/turf/simulated) in things || locate(/turf/unsimulated) in things) + return 1 + else + return 0 + + +/obj/mecha/combat/fighter/play_entered_noise(var/mob/who) + if(hasInternalDamage()) + who << sound('sound/mecha/fighter_entered_bad.ogg',volume=50) + else + who << sound('sound/mecha/fighter_entered.ogg',volume=50) + +////////////// Equipment ////////////// + +// For 64x64 fighters +/obj/item/mecha_parts/mecha_equipment/omni_shield/fighter64 + shield_type = /obj/item/shield_projector/rectangle/mecha/fighter64 +/obj/item/shield_projector/rectangle/mecha/fighter64 + shift_x = 16 + shift_y = 16 + + +////////////// Gunpod ////////////// + +/obj/mecha/combat/fighter/gunpod + name = "Gunpod" + desc = "Small mounted weapons platform capable of space and surface combat. More like a flying tank than a dedicated fightercraft." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "gunpod" + initial_icon = "gunpod" + + catalogue_data = list(/datum/category_item/catalogue/technology/gunpod) + wreckage = /obj/effect/decal/mecha_wreckage/gunpod + + step_in = 3 //Slightly slower than others + + ground_capable = TRUE + + // Paint colors! Null if not set. + var/stripe1_color + var/stripe2_color + var/image/stripe1_overlay + var/image/stripe2_overlay + +/obj/mecha/combat/fighter/gunpod/loaded/Initialize() //Loaded version with gans + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive + ME.attach(src) + +/obj/mecha/combat/fighter/gunpod/recon/Initialize() //Blinky + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/teleporter(src) + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay(src) + ME.attach(src) + +/obj/mecha/combat/fighter/gunpod/update_icon() + cut_overlays() + ..() + + if(stripe1_color) + stripe1_overlay = image("gunpod_stripes1") + stripe1_overlay.color = stripe1_color + add_overlay(stripe1_overlay) + if(stripe2_color) + stripe2_overlay = image("gunpod_stripes2") + stripe2_overlay.color = stripe2_color + add_overlay(stripe2_overlay) + +/obj/mecha/combat/fighter/gunpod/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/device/multitool) && state == 1) + var/new_paint_location = input("Please select a target zone.", "Paint Zone", null) as null|anything in list("Fore Stripe", "Aft Stripe", "CANCEL") + if(new_paint_location && new_paint_location != "CANCEL") + var/new_paint_color = input("Please select a paint color.", "Paint Color", null) as color|null + if(new_paint_color) + switch(new_paint_location) + if("Fore Stripe") + stripe1_color = new_paint_color + if("Aft Stripe") + stripe2_color = new_paint_color + + update_icon() + else ..() + +/obj/effect/decal/mecha_wreckage/gunpod + name = "Gunpod wreckage" + desc = "Remains of some unfortunate gunpod. Completely unrepairable." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "gunpod-broken" + bound_width = 64 + bound_height = 64 + +/datum/category_item/catalogue/technology/gunpod + name = "Voidcraft - Gunpod" + desc = "This is a small space-capable fightercraft that has an arrowhead design. Can hold up to one pilot, \ + and sometimes one or two passengers, with the right modifications made. \ + Typically used as small fighter craft, the gunpod can't carry much of a payload, though it's still capable of holding it's own." + value = CATALOGUER_REWARD_MEDIUM + + +////////////// Baron ////////////// + +/obj/mecha/combat/fighter/baron + name = "Baron" + desc = "A conventional space superiority fighter, one-seater. Not capable of ground operations." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "baron" + initial_icon = "baron" + + catalogue_data = list(/datum/category_item/catalogue/technology/baron) + wreckage = /obj/effect/decal/mecha_wreckage/baron + + ground_capable = FALSE + +/obj/mecha/combat/fighter/baron/loaded/Initialize() //Loaded version with gans + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/omni_shield/fighter64 + ME.attach(src) + +/obj/effect/decal/mecha_wreckage/baron + name = "Baron wreckage" + desc = "Remains of some unfortunate fighter. Completely unrepairable." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "baron-broken" + bound_width = 64 + bound_height = 64 + +/datum/category_item/catalogue/technology/baron + name = "Voidcraft - Baron" + desc = "This is a small space fightercraft that has an arrowhead design. Can hold up to one pilot. \ + Unlike some fighters, this one is not designed for atmospheric operation, and is only capable of performing \ + maneuvers in the vacuum of space. Attempting to operate it in an atmosphere is not recommended." + value = CATALOGUER_REWARD_MEDIUM + + +////////////// Scoralis ////////////// + +/obj/mecha/combat/fighter/scoralis + name = "scoralis" + desc = "An imported space fighter with integral cloaking device. Beware the power consumption, though. Not capable of ground operations." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "scoralis" + initial_icon = "scoralis" + + catalogue_data = list(/datum/category_item/catalogue/technology/scoralis) + wreckage = /obj/effect/decal/mecha_wreckage/scoralis + + ground_capable = FALSE + +/obj/mecha/combat/fighter/scoralis/loaded/Initialize() //Loaded version with gans + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg + ME.attach(src) + ME = new /obj/item/mecha_parts/mecha_equipment/cloak + ME.attach(src) + +/obj/effect/decal/mecha_wreckage/scoralis + name = "scoralis wreckage" + desc = "Remains of some unfortunate fighter. Completely unrepairable." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "scoralis-broken" + bound_width = 64 + bound_height = 64 + +/datum/category_item/catalogue/technology/scoralis + name = "Voidcraft - Scoralis" + desc = "An import model fightercraft, this one contains an integral cloaking device that renders the fighter invisible \ + to the naked eye. Still detectable on thermal sensors, the craft can maneuver in close to ill-equipped foes and strike unseen. \ + Not rated for atmospheric travel, this craft excels at hit and run tactics, as it will likely need to recharge batteries between each 'hit'." + value = CATALOGUER_REWARD_MEDIUM + +////////////// Allure ////////////// + +/obj/mecha/combat/fighter/allure + name = "allure" + desc = "A fighter of Zorren design, it's blocky appearance is made up for by it's stout armor and finely decorated hull paint." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "allure" + initial_icon = "allure" + + catalogue_data = list(/datum/category_item/catalogue/technology/allure) + wreckage = /obj/effect/decal/mecha_wreckage/allure + + ground_capable = FALSE + + health = 500 + maxhealth = 500 + +/obj/mecha/combat/fighter/allure/loaded/Initialize() //Loaded version with gans + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/cloak + ME.attach(src) + +/obj/effect/decal/mecha_wreckage/allure + name = "allure wreckage" + desc = "Remains of some unfortunate fighter. Completely unrepairable." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "allure-broken" + bound_width = 64 + bound_height = 64 + +/datum/category_item/catalogue/technology/allure + name = "Voidcraft - Allure" + desc = "A space superiority fighter of zorren design, many would comment that the blocky shape hinders aesthetic appeal. However, Zorren are \ + often found painting their hulls in intricate designs of purple and gold, and this craft is no exception to the rule. Some individual seems to have \ + decorated it finely. Import craft like this one often ship with no weapons, though the Zorren saw fit to integrate a cloaking device." + value = CATALOGUER_REWARD_MEDIUM + +////////////// Pinnace ////////////// + +/obj/mecha/combat/fighter/pinnace + name = "pinnace" + desc = "A cramped ship's boat, capable of atmospheric and space flight. Not capable of mounting weapons. Capable of fitting one pilot and one passenger." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "pinnace" + initial_icon = "pinnace" + + max_hull_equip = 1 + max_weapon_equip = 0 + max_utility_equip = 0 + max_universal_equip = 0 + max_special_equip = 1 + + catalogue_data = list(/datum/category_item/catalogue/technology/pinnace) + wreckage = /obj/effect/decal/mecha_wreckage/pinnace + + ground_capable = TRUE + +/obj/mecha/combat/fighter/pinnace/loaded/Initialize() //Loaded version with gans + . = ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger + ME.attach(src) + +/obj/effect/decal/mecha_wreckage/pinnace + name = "pinnace wreckage" + desc = "Remains of some unfortunate ship's boat. Completely unrepairable." + icon = 'icons/mecha/fighters64x64.dmi' + icon_state = "pinnace-broken" + bound_width = 64 + bound_height = 64 + +/datum/category_item/catalogue/technology/pinnace + name = "Voidcraft - Pinnace" + desc = "A very small boat, usually used as a tender at very close ranges. The lack of a bluespace \ + drive means that it can't get too far from it's parent ship. Though the pinnace is typically unarmed, \ + it is capable of atmospheric flight and escaping most pursuing fighters by diving into the atmosphere of \ + nearby planets to seek cover." + value = CATALOGUER_REWARD_MEDIUM + +#undef NOGRAV_FIGHTER_DAMAGE \ No newline at end of file diff --git a/code/game/mecha/combat/gorilla.dm b/code/game/mecha/combat/gorilla.dm index 13e8040e65..2d5456953f 100644 --- a/code/game/mecha/combat/gorilla.dm +++ b/code/game/mecha/combat/gorilla.dm @@ -42,7 +42,7 @@ HONK Blaster and a pulse cannon protected by projectile armor and powered by a b /obj/effect/decal/mecha_wreckage/gorilla name = "Gorilla wreckage" desc = "... Blitzkrieg?" - icon = 'icons/mecha/NaziMech.dmi' + icon = 'icons/mecha/AxisMech.dmi' icon_state = "pzrwreck" plane = MOB_PLANE pixel_x = -16 @@ -51,7 +51,7 @@ HONK Blaster and a pulse cannon protected by projectile armor and powered by a b /obj/mecha/combat/gorilla desc = "BLITZKRIEEEEEEEG!" name = "Sd.Kfz. 269 Mechakampfwagen Gorilla Ausf. A" - icon = 'icons/mecha/NaziMech.dmi' + icon = 'icons/mecha/AxisMech.dmi' icon_state = "pzrmech" initial_icon = "pzrmech" pixel_x = -16 @@ -191,10 +191,10 @@ HONK Blaster and a pulse cannon protected by projectile armor and powered by a b src.log_message("Toggled zoom mode.") src.occupant_message("Zoom mode [zoom?"en":"dis"]abled.") if(zoom) - src.occupant.client.view = 12 + src.occupant.set_viewsize(12) src.occupant << sound('sound/mecha/imag_enh.ogg',volume=50) else - src.occupant.client.view = world.view//world.view - default mob view size + src.occupant.set_viewsize() // Reset to default return @@ -230,4 +230,4 @@ HONK Blaster and a pulse cannon protected by projectile armor and powered by a b src.smoke() if (href_list["toggle_zoom"]) src.zoom() - return \ No newline at end of file + return diff --git a/code/game/mecha/combat/marauder.dm b/code/game/mecha/combat/marauder.dm index 9521147089..2b383379ad 100644 --- a/code/game/mecha/combat/marauder.dm +++ b/code/game/mecha/combat/marauder.dm @@ -95,7 +95,7 @@ /obj/mecha/combat/marauder/relaymove(mob/user,direction) if(user != src.occupant) //While not "realistic", this piece is player friendly. user.loc = get_turf(src) - user << "You climb out from [src]" + to_chat(user, "You climb out from [src]") return 0 if(!can_move) return 0 @@ -180,10 +180,10 @@ src.log_message("Toggled zoom mode.") src.occupant_message("Zoom mode [zoom?"en":"dis"]abled.") if(zoom) - src.occupant.client.view = 12 + src.occupant.set_viewsize(12) src.occupant << sound('sound/mecha/imag_enh.ogg',volume=50) else - src.occupant.client.view = world.view//world.view - default mob view size + src.occupant.set_viewsize() // Reset to default return diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 8f02187132..c031e0941a 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -145,9 +145,6 @@ return 0 return 1 -/obj/item/mecha_parts/mecha_equipment/proc/handle_movement_action() //Any modules that have special effects or needs when taking a step or floating through space. - return - /obj/item/mecha_parts/mecha_equipment/proc/action(atom/target) return @@ -266,7 +263,7 @@ /obj/item/mecha_parts/mecha_equipment/proc/occupant_message(message) if(chassis) - chassis.occupant_message("\icon[src] [message]") + chassis.occupant_message("[bicon(src)] [message]") return /obj/item/mecha_parts/mecha_equipment/proc/log_message(message) diff --git a/code/game/mecha/equipment/mecha_equipment_dynamicprocs.dm b/code/game/mecha/equipment/mecha_equipment_dynamicprocs.dm new file mode 100644 index 0000000000..9166e6a3de --- /dev/null +++ b/code/game/mecha/equipment/mecha_equipment_dynamicprocs.dm @@ -0,0 +1,19 @@ +/* + * File containing all the default 'handlers' for Exosuit equipment, hopefully to make all dynX calls extinct. + */ + +// Used for impacting (thrown) objects, and damage value. +/obj/item/mecha_parts/mecha_equipment/proc/handle_ranged_contact(var/obj/A, var/inc_damage = 0) + return max(0, inc_damage) + +// Used for melee strikes with an object, and a mob, and damage value. +/obj/item/mecha_parts/mecha_equipment/proc/handle_melee_contact(var/obj/item/weapon/W, var/mob/living/user, var/inc_damage = 0) + return max(0, inc_damage) + +// Used for projectile impacts from bullet_act. +/obj/item/mecha_parts/mecha_equipment/proc/handle_projectile_contact(var/obj/item/projectile/Proj, var/inc_damage = 0) + return max(0, inc_damage) + +// Used for on-movement actions. +/obj/item/mecha_parts/mecha_equipment/proc/handle_movement_action() //Any modules that have special effects or needs when taking a step or floating through space. + return diff --git a/code/game/mecha/equipment/tools/armor_melee.dm b/code/game/mecha/equipment/tools/armor_melee.dm index 83c8228131..8390a2cc52 100644 --- a/code/game/mecha/equipment/tools/armor_melee.dm +++ b/code/game/mecha/equipment/tools/armor_melee.dm @@ -11,6 +11,30 @@ equip_type = EQUIP_HULL +/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/get_equip_info() + if(!chassis) return + return "* [src.name]" + +/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/handle_melee_contact(var/obj/item/weapon/W, var/mob/living/user, var/inc_damage = null) + if(!action_checks(user)) + return inc_damage + chassis.log_message("Attacked by [W]. Attacker - [user]") + if(prob(chassis.deflect_chance*deflect_coeff)) + to_chat(user, "\The [W] bounces off \the [chassis]'s armor.") + chassis.log_append_to_last("Armor saved.") + inc_damage = 0 + else + chassis.occupant_message("\The [user] hits [chassis] with [W].") + user.visible_message("\The [user] hits [chassis] with [W].", "You hit [src] with [W].") + inc_damage *= damage_coeff + set_ready_state(0) + chassis.use_power(energy_drain) + spawn() + do_after_cooldown() + return max(0, inc_damage) + +/* + /obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/can_attach(obj/mecha/M as obj) if(..()) if(!M.proc_res["dynattackby"]) @@ -27,16 +51,12 @@ ..() return -/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/get_equip_info() - if(!chassis) return - return "* [src.name]" - /obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob) if(!action_checks(user)) return chassis.dynattackby(W,user) chassis.log_message("Attacked by [W]. Attacker - [user]") if(prob(chassis.deflect_chance*deflect_coeff)) - user << "\The [W] bounces off [chassis] armor." + to_chat(user, "\The [W] bounces off [chassis] armor.") chassis.log_append_to_last("Armor saved.") else chassis.occupant_message("\The [user] hits [chassis] with [W].") @@ -46,4 +66,5 @@ set_ready_state(0) chassis.use_power(energy_drain) do_after_cooldown() - return \ No newline at end of file + return +*/ diff --git a/code/game/mecha/equipment/tools/armor_ranged.dm b/code/game/mecha/equipment/tools/armor_ranged.dm index 2e3523275f..4fb3aac32b 100644 --- a/code/game/mecha/equipment/tools/armor_ranged.dm +++ b/code/game/mecha/equipment/tools/armor_ranged.dm @@ -11,6 +11,45 @@ equip_type = EQUIP_HULL +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/handle_projectile_contact(var/obj/item/projectile/Proj, var/inc_damage) + if(istype(Proj, /obj/item/projectile/test)) + return inc_damage// Don't care about test projectiles, just what comes after them + if(!action_checks(src)) + return inc_damage + if(prob(chassis.deflect_chance*deflect_coeff)) + chassis.occupant_message("The armor deflects incoming projectile.") + chassis.visible_message("The [chassis.name] armor deflects the projectile.") + chassis.log_append_to_last("Armor saved.") + inc_damage = 0 + else + inc_damage *= src.damage_coeff + set_ready_state(0) + chassis.use_power(energy_drain) + spawn() + do_after_cooldown() + return max(0, inc_damage) + +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/handle_ranged_contact(var/obj/A, var/inc_damage = 0) + if(!action_checks(A)) + return inc_damage + if(prob(chassis.deflect_chance*deflect_coeff)) + chassis.occupant_message("The [A] bounces off the armor.") + chassis.visible_message("The [A] bounces off \the [chassis]'s armor") + chassis.log_append_to_last("Armor saved.") + inc_damage = 0 + else if(istype(A, /obj)) + inc_damage *= damage_coeff + set_ready_state(0) + chassis.use_power(energy_drain) + spawn() + do_after_cooldown() + return max(0, inc_damage) + +/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/get_equip_info() + if(!chassis) return + return "* [src.name]" + +/* /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/can_attach(obj/mecha/M as obj) if(..()) if(!M.proc_res["dynbulletdamage"] && !M.proc_res["dynhitby"]) @@ -29,10 +68,6 @@ ..() return -/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/get_equip_info() - if(!chassis) return - return "* [src.name]" - /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/proc/dynbulletdamage(var/obj/item/projectile/Proj) if(istype(Proj, /obj/item/projectile/test)) return // Don't care about test projectiles, just what comes after them @@ -69,4 +104,5 @@ set_ready_state(0) chassis.use_power(energy_drain) do_after_cooldown() - return \ No newline at end of file + return +*/ diff --git a/code/game/mecha/equipment/tools/cloak.dm b/code/game/mecha/equipment/tools/cloak.dm new file mode 100644 index 0000000000..6f6ddd1c8f --- /dev/null +++ b/code/game/mecha/equipment/tools/cloak.dm @@ -0,0 +1,70 @@ +/obj/item/mecha_parts/mecha_equipment/cloak + name = "cloaking device" + desc = "Integrated cloaking system. High power usage, but does render you invisible to the naked eye. Doesn't prevent noise, however." + icon_state = "tesla" + origin_tech = list(TECH_MAGNET = 5, TECH_DATA = 5) + equip_cooldown = 2 SECONDS + energy_drain = 300 + range = 0 + + equip_type = EQUIP_SPECIAL + + var/datum/global_iterator/mecha_cloak/cloak_iterator + +/obj/item/mecha_parts/mecha_equipment/cloak/Initialize() + . = ..() + cloak_iterator = new /datum/global_iterator/mecha_cloak(list(src),0) + cloak_iterator.set_delay(equip_cooldown) + +/obj/item/mecha_parts/mecha_equipment/cloak/Destroy() + qdel_null(cloak_iterator) + return ..() + +/obj/item/mecha_parts/mecha_equipment/cloak/detach() + qdel_null(cloak_iterator) + if(!equip_ready) //We were running + stop_cloak() + return ..() + +/obj/item/mecha_parts/mecha_equipment/cloak/get_equip_info() + if(!chassis) + return + return "* [src.name] - [equip_ready ? "A" : "Dea"]ctivate" + +/obj/item/mecha_parts/mecha_equipment/cloak/Topic(href, href_list) + ..() + if(href_list["toggle_cloak"]) + if(equip_ready) + start_cloak() + else + stop_cloak() + return + +/obj/item/mecha_parts/mecha_equipment/cloak/proc/start_cloak() + if(chassis) + chassis.cloak() + log_message("Activated.") + cloak_iterator.start() + set_ready_state(0) + playsound(get_turf(src), 'sound/effects/EMPulse.ogg', 100, 1) + +/obj/item/mecha_parts/mecha_equipment/cloak/proc/stop_cloak() + if(chassis) + chassis.uncloak() + log_message("Deactivated.") + cloak_iterator.stop() + set_ready_state(1) + playsound(get_turf(src), 'sound/effects/EMPulse.ogg', 100, 1) + +// These things are so silly +/datum/global_iterator/mecha_cloak/process(var/obj/item/mecha_parts/mecha_equipment/cloak/cloak) + //Removed from chassis + if(!cloak.chassis) + stop() + cloak.stop_cloak() + return + //Ran out of power + if(!cloak.chassis.use_power(cloak.energy_drain)) + stop() + cloak.stop_cloak() + return diff --git a/code/game/mecha/equipment/tools/generator.dm b/code/game/mecha/equipment/tools/generator.dm index b4896045c5..396215131d 100644 --- a/code/game/mecha/equipment/tools/generator.dm +++ b/code/game/mecha/equipment/tools/generator.dm @@ -88,7 +88,7 @@ if(isnull(result)) user.visible_message("[user] tries to shove [weapon] into [src]. What a dumb-ass.","[fuel] traces minimal. [weapon] cannot be used as fuel.") else if(!result) - user << "Unit is full." + to_chat(user, "Unit is full.") else user.visible_message("[user] loads [src] with [fuel].","[result] unit\s of [fuel] successfully loaded.") return diff --git a/code/game/mecha/equipment/tools/hardpoint_actuator.dm b/code/game/mecha/equipment/tools/hardpoint_actuator.dm new file mode 100644 index 0000000000..735fec6f96 --- /dev/null +++ b/code/game/mecha/equipment/tools/hardpoint_actuator.dm @@ -0,0 +1,34 @@ +/* + * A special device used to pick up and equip other exosuit components on the fly, without leaving an Exosuit. Costly. + */ + +/obj/item/mecha_parts/mecha_equipment/hardpoint_actuator + name = "hardpoint actuator clamp" + icon_state = "mecha_clamp" + equip_cooldown = 10 SECONDS + energy_drain = 600 + equip_type = EQUIP_HULL + origin_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_POWER = 4, TECH_COMBAT = 1, TECH_MAGNET = 4) + +/obj/item/mecha_parts/mecha_equipment/hardpoint_actuator/action(atom/target) + if(!action_checks(target)) + return + + if(istype(target,/obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/ME = target + if(ME.can_attach(chassis)) + occupant_message("[ME] can be integrated. Stand by.") + if(do_after(chassis.occupant, 3 SECONDS, target)) + if(ME.can_attach(chassis) && action_checks(target)) + ME.attach(chassis) + occupant_message("[ME] successfully integrated.") + else + occupant_message("[ME] cannot be integrated due to lack of free hardpoints.") + + else + occupant_message("[target] is not compatible with any present hardpoints.") + + set_ready_state(0) + chassis.use_power(energy_drain) + do_after_cooldown() + return diff --git a/code/game/mecha/equipment/tools/inflatables.dm b/code/game/mecha/equipment/tools/inflatables.dm new file mode 100644 index 0000000000..b18ba2eae1 --- /dev/null +++ b/code/game/mecha/equipment/tools/inflatables.dm @@ -0,0 +1,47 @@ +/obj/item/mecha_parts/mecha_equipment/tool/powertool/inflatables + name = "inflatable deployment mechanism" + desc = "An exosuit-mounted inflatable barrier deployer. Useful!" + icon_state = "mecha_inflatables" + origin_tech = list(TECH_MATERIAL = 5, TECH_MAGNET = 3) + equip_cooldown = 3 + energy_drain = 30 + range = MELEE + equip_type = EQUIP_UTILITY + ready_sound = 'sound/effects/spray.ogg' + required_type = list(/obj/mecha/working/ripley) + + tooltype = /obj/item/weapon/inflatable_dispenser/robot + var/obj/item/weapon/inflatable_dispenser/my_deployer = null + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/inflatables/Initialize() + ..() + my_deployer = my_tool + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/inflatables/Topic(href, href_list) + ..() + if(href_list["toggle_deployable_mode"]) + my_deployer.attack_self() + update_chassis_page() + return + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/inflatables/get_equip_info() + if(!chassis) return + var/data_return = "* [chassis.selected==src?"":""][src.name][chassis.selected==src?"":""] - Deploy [my_deployer.mode?"Door":"Wall"]
\ +   - Doors left: [my_deployer.stored_doors]/[my_deployer.max_doors]
\ +   - Walls left: [my_deployer.stored_walls]/[my_deployer.max_walls]" + + return data_return + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/inflatables/action(atom/target, params) + if(!action_checks(target)) + return + + if(istype(target, /turf)) + my_deployer.try_deploy_inflatable(target, chassis.occupant) + if(istype(target, /obj/item/inflatable) || istype(target, /obj/structure/inflatable)) + my_deployer.pick_up(target, chassis.occupant) + + set_ready_state(0) + chassis.use_power(energy_drain) + do_after_cooldown() + return diff --git a/code/game/mecha/equipment/tools/medigun_vr.dm b/code/game/mecha/equipment/tools/medigun_vr.dm index 19efa0a5fd..7b06a30ffd 100644 --- a/code/game/mecha/equipment/tools/medigun_vr.dm +++ b/code/game/mecha/equipment/tools/medigun_vr.dm @@ -2,7 +2,8 @@ equip_cooldown = 6 name = "\improper BL-3 \"Phoenix\" directed restoration system" desc = "The BL-3 'Phoenix' is a portable medical system used to treat external injuries from afar." - icon_state = "mecha_medbeam" + icon_state = "medbeam" + icon = 'icons/mecha/mecha_equipment_vr.dmi' energy_drain = 1000 projectile = /obj/item/projectile/beam/medigun fire_sound = 'sound/weapons/eluger.ogg' diff --git a/code/game/mecha/equipment/tools/passenger.dm b/code/game/mecha/equipment/tools/passenger.dm index 1e40da2f30..8ce9d21aa9 100644 --- a/code/game/mecha/equipment/tools/passenger.dm +++ b/code/game/mecha/equipment/tools/passenger.dm @@ -1,7 +1,7 @@ /obj/item/mecha_parts/mecha_equipment/tool/passenger name = "passenger compartment" desc = "A mountable passenger compartment for exosuits. Rather cramped." - icon_state = "mecha_abooster_ccw" + icon_state = "mecha_passenger" origin_tech = list(TECH_ENGINEERING = 1, TECH_BIO = 1) energy_drain = 10 range = MELEE @@ -16,7 +16,7 @@ /obj/item/mecha_parts/mecha_equipment/tool/passenger/destroy() for(var/atom/movable/AM in src) AM.forceMove(get_turf(src)) - AM << "You tumble out of the destroyed [src.name]!" + to_chat(AM, "You tumble out of the destroyed [src.name]!") return ..() /obj/item/mecha_parts/mecha_equipment/tool/passenger/Exit(atom/movable/O) @@ -107,13 +107,13 @@ return if (!isturf(usr.loc)) - usr << "You can't reach the passenger compartment from here." + to_chat(usr, "You can't reach the passenger compartment from here.") return if(iscarbon(usr)) var/mob/living/carbon/C = usr if(C.handcuffed) - usr << "Kinda hard to climb in while handcuffed don't you think?" + to_chat(usr, "Kinda hard to climb in while handcuffed don't you think?") return if(isliving(usr)) @@ -139,13 +139,13 @@ //didn't find anything switch (feedback) if (OCCUPIED) - usr << "The passenger compartment is already occupied!" + to_chat(usr, "The passenger compartment is already occupied!") if (LOCKED) - usr << "The passenger compartment hatch is locked!" + to_chat(usr, "The passenger compartment hatch is locked!") if (OCCUPIED|LOCKED) - usr << "All of the passenger compartments are already occupied or locked!" + to_chat(usr, "All of the passenger compartments are already occupied or locked!") if (0) - usr << "\The [src] doesn't have a passenger compartment." + to_chat(usr, "\The [src] doesn't have a passenger compartment.") #undef LOCKED #undef OCCUPIED \ No newline at end of file diff --git a/code/game/mecha/equipment/tools/wrench.dm b/code/game/mecha/equipment/tools/powertool.dm similarity index 66% rename from code/game/mecha/equipment/tools/wrench.dm rename to code/game/mecha/equipment/tools/powertool.dm index 605c2ee33d..0adcb98929 100644 --- a/code/game/mecha/equipment/tools/wrench.dm +++ b/code/game/mecha/equipment/tools/powertool.dm @@ -38,4 +38,18 @@ desc = "An exosuit-mounted pneumatic prybar." icon_state = "mecha_crowbar" tooltype = /obj/item/weapon/tool/crowbar/power - ready_sound = 'sound/mecha/gasdisconnected.ogg' \ No newline at end of file + ready_sound = 'sound/mecha/gasdisconnected.ogg' + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/cutter + name = "pneumatic cablecutter" + desc = "An exosuit-mounted pneumatic cablecutter." + icon_state = "mecha_cablecutter" + tooltype = /obj/item/weapon/tool/wirecutters/power + ready_sound = 'sound/mecha/gasdisconnected.ogg' + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/screwdriver + name = "pneumatic screwdriver" + desc = "An exosuit-mounted pneumatic screwdriver." + icon_state = "mecha_screwdriver" + tooltype = /obj/item/weapon/tool/screwdriver/power + ready_sound = 'sound/mecha/gasdisconnected.ogg' diff --git a/code/game/mecha/equipment/tools/repair_droid.dm b/code/game/mecha/equipment/tools/repair_droid.dm index f76eeed6f0..f4f9696aa5 100644 --- a/code/game/mecha/equipment/tools/repair_droid.dm +++ b/code/game/mecha/equipment/tools/repair_droid.dm @@ -27,16 +27,16 @@ /obj/item/mecha_parts/mecha_equipment/repair_droid/attach(obj/mecha/M as obj) ..() droid_overlay = new(src.icon, icon_state = "repair_droid") - M.overlays += droid_overlay + M.add_overlay(droid_overlay) return /obj/item/mecha_parts/mecha_equipment/repair_droid/destroy() - chassis.overlays -= droid_overlay + chassis.cut_overlay(droid_overlay) ..() return /obj/item/mecha_parts/mecha_equipment/repair_droid/detach() - chassis.overlays -= droid_overlay + chassis.cut_overlay(droid_overlay) pr_repair_droid.stop() ..() return @@ -49,7 +49,7 @@ /obj/item/mecha_parts/mecha_equipment/repair_droid/Topic(href, href_list) ..() if(href_list["toggle_repairs"]) - chassis.overlays -= droid_overlay + chassis.cut_overlay(droid_overlay) if(pr_repair_droid.toggle()) droid_overlay = new(src.icon, icon_state = "repair_droid_a") log_message("Activated.") @@ -57,7 +57,7 @@ droid_overlay = new(src.icon, icon_state = "repair_droid") log_message("Deactivated.") set_ready_state(1) - chassis.overlays += droid_overlay + chassis.add_overlay(droid_overlay) send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",src.get_equip_info()) return diff --git a/code/game/mecha/equipment/tools/shield_omni.dm b/code/game/mecha/equipment/tools/shield_omni.dm new file mode 100644 index 0000000000..8088cf296a --- /dev/null +++ b/code/game/mecha/equipment/tools/shield_omni.dm @@ -0,0 +1,97 @@ +#define OMNI_SHIELD_DRAIN 30 + +/obj/item/mecha_parts/mecha_equipment/omni_shield + name = "omni shield" + desc = "A shield generator that forms an ennlosing, omnidirectional shield around the exosuit." + icon_state = "shield" + origin_tech = list(TECH_PHORON = 3, TECH_MAGNET = 6, TECH_ILLEGAL = 4) + equip_cooldown = 5 + energy_drain = OMNI_SHIELD_DRAIN + range = 0 + + var/obj/item/shield_projector/shields = null + var/shield_type = /obj/item/shield_projector/rectangle/mecha + + equip_type = EQUIP_HULL + +/obj/item/mecha_parts/mecha_equipment/omni_shield/critfail() + ..() + shields.adjust_health(-200) + +/obj/item/mecha_parts/mecha_equipment/omni_shield/Destroy() + QDEL_NULL(shields) + ..() + +/obj/item/mecha_parts/mecha_equipment/omni_shield/attach(obj/mecha/M as obj) + . = ..() + if(chassis) + shields = new shield_type(chassis) + +/obj/item/mecha_parts/mecha_equipment/omni_shield/detach() + if(chassis) + QDEL_NULL(shields) + . = ..() + +/obj/item/mecha_parts/mecha_equipment/omni_shield/handle_movement_action() + if(chassis && shields) + shields.update_shield_positions() + +/obj/item/mecha_parts/mecha_equipment/omni_shield/proc/toggle_shield() + ..() + if(shields) + shields.set_on(!shields.active) + if(shields.active) + set_ready_state(0) + log_message("Activated.") + else + set_ready_state(1) + log_message("Deactivated.") + +/obj/item/mecha_parts/mecha_equipment/omni_shield/Topic(href, href_list) + ..() + if(href_list["toggle_omnishield"]) + toggle_shield() + +/obj/item/mecha_parts/mecha_equipment/omni_shield/get_equip_info() + if(!chassis) return + return "* [src.name] - [shields?.active?"Dea":"A"]ctivate" + + +////// The shield projector object +/obj/item/shield_projector/rectangle/mecha + shield_health = 200 + max_shield_health = 200 + shield_regen_delay = 10 SECONDS + shield_regen_amount = 10 + size_x = 1 + size_y = 1 + + var/shift_x = 0 + var/shift_y = 0 + + var/obj/mecha/my_mech = null + +/obj/item/shield_projector/rectangle/mecha/Initialize() + . = ..() + my_mech = loc + GLOB.moved_event.register(my_mech, src, /obj/item/shield_projector/proc/update_shield_positions) + +/obj/item/shield_projector/rectangle/mecha/Destroy() + GLOB.moved_event.unregister(my_mech, src, /obj/item/shield_projector/proc/update_shield_positions) + my_mech = null + ..() + +/obj/item/shield_projector/rectangle/mecha/create_shield() + . = ..() + if(shift_x || shift_y) + var/obj/effect/directional_shield/newshield = active_shields[active_shields.len] + newshield.pixel_x = shift_x + newshield.pixel_y = shift_y + +/obj/item/shield_projector/rectangle/mecha/adjust_health(amount) + . = ..() + my_mech.use_power(OMNI_SHIELD_DRAIN) + if(!active && shield_health < shield_regen_amount) + my_mech.use_power(OMNI_SHIELD_DRAIN * 4) + +#undef OMNI_SHIELD_DRAIN diff --git a/code/game/mecha/equipment/tools/weldinglaser.dm b/code/game/mecha/equipment/tools/weldinglaser.dm new file mode 100644 index 0000000000..ff1c5733a5 --- /dev/null +++ b/code/game/mecha/equipment/tools/weldinglaser.dm @@ -0,0 +1,34 @@ +/obj/item/mecha_parts/mecha_equipment/tool/powertool/welding + name = "welding laser" + desc = "An exosuit-mounted welding laser." + icon_state = "mecha_laser-rig" + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_POWER = 4, TECH_PHORON = 2) + equip_cooldown = 3 + energy_drain = 15 + range = MELEE + equip_type = EQUIP_UTILITY + ready_sound = 'sound/items/Ratchet.ogg' + required_type = list(/obj/mecha/working/ripley) + + tooltype = /obj/item/weapon/weldingtool/electric/mounted/exosuit + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/welding/action(var/atom/target) + ..() + + var/datum/beam/weld_beam = null + if(is_ranged()) + var/atom/movable/beam_origin = chassis + weld_beam = beam_origin.Beam(target, icon_state = "solar_beam", time = 0.3 SECONDS) + + if(!do_after(chassis.occupant, 0.3 SECONDS, target)) + qdel(weld_beam) + +/obj/item/mecha_parts/mecha_equipment/tool/powertool/welding/attach(obj/mecha/M as obj) + ..() + + if(enable_special) + range = MELEE|RANGED + my_tool.reach = 7 + else + range = MELEE + my_tool.reach = 1 diff --git a/code/game/mecha/equipment/weapons/defense/shocker.dm b/code/game/mecha/equipment/weapons/defense/shocker.dm index af120167d2..58dccec562 100644 --- a/code/game/mecha/equipment/weapons/defense/shocker.dm +++ b/code/game/mecha/equipment/weapons/defense/shocker.dm @@ -15,6 +15,7 @@ equip_type = EQUIP_HULL +/* /obj/item/mecha_parts/mecha_equipment/shocker/can_attach(obj/mecha/M as obj) if(..()) if(!M.proc_res["dynattackby"] && !M.proc_res["dynattackhand"] && !M.proc_res["dynattackalien"]) @@ -25,9 +26,11 @@ ..() chassis.proc_res["dynattackby"] = src return +*/ -/obj/item/mecha_parts/mecha_equipment/shocker/proc/dynattackby(obj/item/weapon/W, mob/living/user) +/obj/item/mecha_parts/mecha_equipment/shocker/handle_melee_contact(var/obj/item/weapon/W, var/mob/living/user, var/inc_damage = null) if(!action_checks(user) || !active) return + user.electrocute_act(shock_damage, src) - return chassis.dynattackby(W,user) + return inc_damage diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index cbbc702adf..5d01416785 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -72,7 +72,7 @@ var/obj/item/projectile/P = A P.dispersion = deviation process_accuracy(P, chassis.occupant, target) - P.launch_projectile_from_turf(target, chassis.occupant.zone_sel.selecting, chassis.occupant, params) + P.launch_projectile_from_turf(target, chassis.get_pilot_zone_sel(), chassis.occupant, params) else if(istype(A, /atom/movable)) var/atom/movable/AM = A AM.throw_at(target, 7, 1, chassis) diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 0b23214a03..a881cdb720 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -5,7 +5,7 @@ desc = "A machine used for construction of mechas." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 5000 req_access = list(access_robotics) @@ -13,8 +13,8 @@ var/speed = 1 var/mat_efficiency = 1 - var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, MAT_PLASTEEL = 0, "gold" = 0, "silver" = 0, MAT_LEAD = 0, "osmium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, "phoron" = 0, "uranium" = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0) - var/list/hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER) + var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, MAT_GRAPHITE = 0, MAT_PLASTEEL = 0, "gold" = 0, "silver" = 0, MAT_LEAD = 0, "osmium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, "phoron" = 0, "uranium" = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0) + var/list/hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_GRAPHITE, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER) var/res_max_amount = 200000 var/datum/research/files @@ -48,11 +48,11 @@ if(stat) return if(busy) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) progress += speed check_build() else - use_power = 1 + update_use_power(USE_POWER_IDLE) update_icon() /obj/machinery/mecha_part_fabricator/update_icon() @@ -177,20 +177,20 @@ switch(emagged) if(0) emagged = 0.5 - visible_message("\icon[src] [src] beeps: \"DB error \[Code 0x00F1\]\"") + visible_message("[bicon(src)] [src] beeps: \"DB error \[Code 0x00F1\]\"") sleep(10) - visible_message("\icon[src] [src] beeps: \"Attempting auto-repair\"") + visible_message("[bicon(src)] [src] beeps: \"Attempting auto-repair\"") sleep(15) - visible_message("\icon[src] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") + visible_message("[bicon(src)] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") sleep(30) - visible_message("\icon[src] [src] beeps: \"User DB truncated. Please contact your [using_map.company_name] system operator for future assistance.\"") + visible_message("[bicon(src)] [src] beeps: \"User DB truncated. Please contact your [using_map.company_name] system operator for future assistance.\"") req_access = null emagged = 1 return 1 if(0.5) - visible_message("\icon[src] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") + visible_message("[bicon(src)] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") if(1) - visible_message("\icon[src] [src] beeps: \"No records in User DB\"") + visible_message("[bicon(src)] [src] beeps: \"No records in User DB\"") /obj/machinery/mecha_part_fabricator/proc/update_busy() if(queue.len) diff --git a/code/game/mecha/mech_prosthetics.dm b/code/game/mecha/mech_prosthetics.dm index 5382cabb46..5e93378a0c 100644 --- a/code/game/mecha/mech_prosthetics.dm +++ b/code/game/mecha/mech_prosthetics.dm @@ -5,7 +5,7 @@ desc = "A machine used for construction of prosthetics." density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 5000 req_access = list(access_robotics) @@ -13,8 +13,8 @@ var/speed = 1 var/mat_efficiency = 1 - var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, MAT_PLASTEEL = 0, "gold" = 0, "silver" = 0, MAT_LEAD = 0, "osmium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, "phoron" = 0, "uranium" = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0) - var/list/hidden_materials = list(MAT_DURASTEEL, MAT_VERDANTIUM, MAT_MORPHIUM) + var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, MAT_GRAPHITE = 0, MAT_PLASTEEL = 0, "gold" = 0, "silver" = 0, MAT_LEAD = 0, "osmium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, "phoron" = 0, "uranium" = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0) + var/list/hidden_materials = list(MAT_DURASTEEL, MAT_GRAPHITE, MAT_VERDANTIUM, MAT_MORPHIUM) var/res_max_amount = 200000 var/datum/research/files @@ -25,6 +25,8 @@ var/list/categories = list() var/category = null var/manufacturer = null + var/species_types = list("Human") + var/species = "Human" var/sync_message = "" /obj/machinery/pros_fabricator/New() @@ -50,11 +52,11 @@ if(stat) return if(busy) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) progress += speed check_build() else - use_power = 1 + update_use_power(USE_POWER_IDLE) update_icon() /obj/machinery/pros_fabricator/update_icon() @@ -100,11 +102,14 @@ data["buildable"] = get_build_options() data["category"] = category data["categories"] = categories + data["species_types"] = species_types + data["species"] = species if(all_robolimbs) var/list/T = list() for(var/A in all_robolimbs) var/datum/robolimb/R = all_robolimbs[A] if(R.unavailable_to_build) continue + if(species in R.species_cannot_use) continue T += list(list("id" = A, "company" = R.company)) data["manufacturers"] = T data["manufacturer"] = manufacturer @@ -135,6 +140,10 @@ if(href_list["category"] in categories) category = href_list["category"] + if(href_list["species"]) + if(href_list["species"] in species_types) + species = href_list["species"] + if(href_list["manufacturer"]) if(href_list["manufacturer"] in all_robolimbs) manufacturer = href_list["manufacturer"] @@ -173,6 +182,18 @@ qdel(I) return + if(istype(I,/obj/item/weapon/disk/species)) + var/obj/item/weapon/disk/species/D = I + if(!D.species || !(D.species in GLOB.all_species)) + to_chat(user, "This disk seems to be corrupted!") + else + to_chat(user, "Uploading modification files for [D.species]...") + if(do_after(user,50,src)) + species_types |= D.species + to_chat(user, "Uploaded [D.species] files!") + qdel(I) + return + if(istype(I,/obj/item/stack/material)) var/obj/item/stack/material/S = I if(!(S.material.name in materials)) @@ -204,20 +225,20 @@ switch(emagged) if(0) emagged = 0.5 - visible_message("\icon[src] [src] beeps: \"DB error \[Code 0x00F1\]\"") + visible_message("[bicon(src)] [src] beeps: \"DB error \[Code 0x00F1\]\"") sleep(10) - visible_message("\icon[src] [src] beeps: \"Attempting auto-repair\"") + visible_message("[bicon(src)] [src] beeps: \"Attempting auto-repair\"") sleep(15) - visible_message("\icon[src] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") + visible_message("[bicon(src)] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") sleep(30) - visible_message("\icon[src] [src] beeps: \"User DB truncated. Please contact your [using_map.company_name] system operator for future assistance.\"") + visible_message("[bicon(src)] [src] beeps: \"User DB truncated. Please contact your [using_map.company_name] system operator for future assistance.\"") req_access = null emagged = 1 return 1 if(0.5) - visible_message("\icon[src] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") + visible_message("[bicon(src)] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") if(1) - visible_message("\icon[src] [src] beeps: \"No records in User DB\"") + visible_message("[bicon(src)] [src] beeps: \"No records in User DB\"") /obj/machinery/pros_fabricator/proc/update_busy() if(queue.len) diff --git a/code/game/mecha/mech_sensor.dm b/code/game/mecha/mech_sensor.dm index aa598d1b0d..33147e0ab1 100644 --- a/code/game/mecha/mech_sensor.dm +++ b/code/game/mecha/mech_sensor.dm @@ -6,7 +6,7 @@ anchored = 1 density = 1 throwpass = 1 - use_power = 1 + use_power = USE_POWER_IDLE layer = ON_WINDOW_LAYER power_channel = EQUIP var/on = 0 @@ -44,11 +44,11 @@ if(istype(O, /obj/mecha)) var/obj/mecha/R = O if(R && R.occupant) - R.occupant << block_message + to_chat(R.occupant,block_message) else if(istype(O, /obj/vehicle/train/engine)) var/obj/vehicle/train/engine/E = O if(E && E.load && E.is_train_head()) - E.load << block_message + to_chat(E.load,block_message) feedback_timer = 1 spawn(50) //Without this timer the feedback becomes horribly spamy diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 2a6f08df03..d9198cc6e7 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -259,7 +259,9 @@ /obj/mecha/proc/check_for_support() - if(locate(/obj/structure/grille, orange(1, src)) || locate(/obj/structure/lattice, orange(1, src)) || locate(/turf/simulated, orange(1, src)) || locate(/turf/unsimulated, orange(1, src))) + var/list/things = orange(1, src) + + if(locate(/obj/structure/grille) in things || locate(/obj/structure/lattice) in things || locate(/turf/simulated) in things || locate(/turf/unsimulated) in things) return 1 else return 0 @@ -269,19 +271,19 @@ var/integrity = health/initial(health)*100 switch(integrity) if(85 to 100) - user << "It's fully intact." + to_chat(user, "It's fully intact.") if(65 to 85) - user << "It's slightly damaged." + to_chat(user, "It's slightly damaged.") if(45 to 65) - user << "It's badly damaged." + to_chat(user, "It's badly damaged.") if(25 to 45) - user << "It's heavily damaged." + to_chat(user, "It's heavily damaged.") else - user << "It's falling apart." + to_chat(user, "It's falling apart.") if(equipment && equipment.len) - user << "It's equipped with:" + to_chat(user, "It's equipped with:") for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - user << "\icon[ME] [ME]" + to_chat(user, "[bicon(ME)] [ME]") return @@ -397,21 +399,26 @@ /obj/mecha/relaymove(mob/user,direction) if(user != src.occupant) //While not "realistic", this piece is player friendly. if(istype(user,/mob/living/carbon/brain)) - to_chat(user,"You try to move, but you are not the pilot! The exosuit doesn't respond.") + to_chat(user, "You try to move, but you are not the pilot! The exosuit doesn't respond.") return 0 user.forceMove(get_turf(src)) - to_chat(user,"You climb out from [src]") + to_chat(user, "You climb out from [src]") return 0 if(connected_port) if(world.time - last_message > 20) - src.occupant_message("Unable to move while connected to the air system port") + src.occupant_message("Unable to move while connected to the air system port") last_message = world.time return 0 if(state) - occupant_message("Maintenance protocols in effect") + occupant_message("Maintenance protocols in effect") return return domove(direction) +/obj/mecha/proc/can_ztravel() + for(var/obj/item/mecha_parts/mecha_equipment/tool/jetpack/jp in equipment) + return jp.equip_ready + return FALSE + /obj/mecha/proc/domove(direction) return call((proc_res["dyndomove"]||src), "dyndomove")(direction) @@ -423,20 +430,51 @@ return 0 if(!has_charge(step_energy_drain)) return 0 + var/move_result = 0 + if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) move_result = mechsteprand() - else if(src.dir!=direction) + //Up/down zmove + else if(direction & UP || direction & DOWN) + if(!can_ztravel()) + occupant_message("Your vehicle lacks the capacity to move in that direction!") + return FALSE + + //We're using locs because some mecha are 2x2 turfs. So thicc! + var/result = TRUE + + for(var/turf/T in locs) + if(!T.CanZPass(src,direction)) + occupant_message("You can't move that direction from here!") + result = FALSE + break + var/turf/dest = direction & UP ? GetAbove(T) : GetBelow(T) + if(!dest) + occupant_message("There is nothing of interest in this direction.") + result = FALSE + break + if(!dest.CanZPass(src,direction)) + occupant_message("There's something blocking your movement in that direction!") + result = FALSE + break + if(result) + move_result = mechstep(direction) + //Turning + else if(src.dir != direction) move_result = mechturn(direction) + //Stepping else move_result = mechstep(direction) + + if(move_result) can_move = 0 use_power(step_energy_drain) if(istype(src.loc, /turf/space)) if(!src.check_for_support()) src.pr_inertial_movement.start(list(src,direction)) - src.log_message("Movement control lost. Inertial movement started.") + src.log_message("Movement control lost. Inertial movement started.") if(do_after(step_in)) can_move = 1 return 1 @@ -580,12 +618,12 @@ src.take_damage(15) src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1) - user << "You slash at the armored suit!" + to_chat(user, "You slash at the armored suit!") visible_message("\The [user] slashes at [src.name]'s armor!") else src.log_append_to_last("Armor saved.") playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1) - user << "Your claws had no effect!" + to_chat(user, "Your claws had no effect!") src.occupant_message("\The [user]'s claws are stopped by the armor.") visible_message("\The [user] rebounds off [src.name]'s armor!") else @@ -622,7 +660,12 @@ else if(istype(A, /obj)) var/obj/O = A if(O.throwforce) - src.take_damage(O.throwforce) + + var/pass_damage = O.throwforce + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_ranged_contact(A, pass_damage) + + src.take_damage(pass_damage) src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) return @@ -652,7 +695,12 @@ var/ignore_threshold if(istype(Proj, /obj/item/projectile/beam/pulse)) ignore_threshold = 1 - src.take_damage(Proj.damage, Proj.check_armour) + + var/pass_damage = Proj.damage + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_projectile_contact(Proj, pass_damage) + + src.take_damage(pass_damage, Proj.check_armour) if(prob(25)) spark_system.start() src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) @@ -704,14 +752,14 @@ src.take_damage(6) src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) playsound(src.loc, 'sound/effects/blobattack.ogg', 50, 1, -1) - user << "You smash at the armored suit!" + to_chat(user, "You smash at the armored suit!") for (var/mob/V in viewers(src)) if(V.client && !(V.blinded)) V.show_message("\The [user] smashes against [src.name]'s armor!", 1) else src.log_append_to_last("Armor saved.") playsound(src.loc, 'sound/effects/blobattack.ogg', 50, 1, -1) - user << "Your attack had no effect!" + to_chat(user, "Your attack had no effect!") src.occupant_message("\The [user]'s attack is stopped by the armor.") for (var/mob/V in viewers(src)) if(V.client && !(V.blinded)) @@ -738,7 +786,7 @@ user.setClickCooldown(user.get_attack_speed(W)) src.log_message("Attacked by [W]. Attacker - [user]") if(prob(src.deflect_chance)) - user << "\The [W] bounces off [src.name]." + to_chat(user, "\The [W] bounces off [src.name].") src.log_append_to_last("Armor saved.") /* for (var/mob/V in viewers(src)) @@ -748,7 +796,11 @@ else src.occupant_message("[user] hits [src] with [W].") user.visible_message("[user] hits [src] with [W].", "You hit [src] with [W].") - src.take_damage(W.force,W.damtype) + + var/pass_damage = W.force + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + pass_damage = ME.handle_projectile_contact(W, pass_damage) + src.take_damage(pass_damage,W.damtype) src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) return @@ -760,9 +812,9 @@ if(istype(W, /obj/item/device/mmi)) if(mmi_move_inside(W,user)) - to_chat(user,"[src]-MMI interface initialized successfuly") + to_chat(user, "[src]-MMI interface initialized successfuly") else - to_chat(user,"[src]-MMI interface initialization failed.") + to_chat(user, "[src]-MMI interface initialization failed.") return if(istype(W, /obj/item/mecha_parts/mecha_equipment)) @@ -773,7 +825,7 @@ E.attach(src) user.visible_message("[user] attaches [W] to [src]", "You attach [W] to [src]") else - user << "You were unable to attach [W] to [src]" + to_chat(user, "You were unable to attach [W] to [src]") return if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) if(add_req_access || maint_access) @@ -787,57 +839,57 @@ output_maintenance_dialog(id_card, user) return else - user << "Invalid ID: Access denied." + to_chat(user, "Invalid ID: Access denied.") else - user << "Maintenance protocols disabled by operator." + to_chat(user, "Maintenance protocols disabled by operator.") else if(W.is_wrench()) if(state==1) state = 2 - user << "You undo the securing bolts." + to_chat(user, "You undo the securing bolts.") else if(state==2) state = 1 - user << "You tighten the securing bolts." + to_chat(user, "You tighten the securing bolts.") return else if(W.is_crowbar()) if(state==2) state = 3 - user << "You open the hatch to the power unit" + to_chat(user, "You open the hatch to the power unit") else if(state==3) state=2 - user << "You close the hatch to the power unit" + to_chat(user, "You close the hatch to the power unit") return else if(istype(W, /obj/item/stack/cable_coil)) if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) var/obj/item/stack/cable_coil/CC = W if(CC.use(2)) clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) - user << "You replace the fused wires." + to_chat(user, "You replace the fused wires.") else - user << "There's not enough wire to finish the task." + to_chat(user, "There's not enough wire to finish the task.") return else if(W.is_screwdriver()) if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) clearInternalDamage(MECHA_INT_TEMP_CONTROL) - user << "You repair the damaged temperature controller." + to_chat(user, "You repair the damaged temperature controller.") else if(state==3 && src.cell) src.cell.forceMove(src.loc) src.cell = null state = 4 - user << "You unscrew and pry out the powercell." + to_chat(user, "You unscrew and pry out the powercell.") src.log_message("Powercell removed") else if(state==4 && src.cell) state=3 - user << "You screw the cell in place" + to_chat(user, "You screw the cell in place") return else if(istype(W, /obj/item/device/multitool)) if(state>=3 && src.occupant) - user << "You attempt to eject the pilot using the maintenance controls." + to_chat(user, "You attempt to eject the pilot using the maintenance controls.") if(src.occupant.stat) src.go_out() src.log_message("[src.occupant] was ejected using the maintenance controls.") else - user << "Your attempt is rejected." + to_chat(user, "Your attempt is rejected.") src.occupant_message("An attempt to eject you was made using the maintenance controls.") src.log_message("Eject attempt made using maintenance controls - rejected.") return @@ -845,13 +897,13 @@ else if(istype(W, /obj/item/weapon/cell)) if(state==4) if(!src.cell) - user << "You install the powercell" + to_chat(user, "You install the powercell") user.drop_item() W.forceMove(src) src.cell = W src.log_message("Powercell installed") else - user << "There's already a powercell installed." + to_chat(user, "There's already a powercell installed.") return else if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != I_HURT) @@ -859,14 +911,14 @@ if (WT.remove_fuel(0,user)) if (hasInternalDamage(MECHA_INT_TANK_BREACH)) clearInternalDamage(MECHA_INT_TANK_BREACH) - user << "You repair the damaged gas tank." + to_chat(user, "You repair the damaged gas tank.") else return if(src.healthYou repair some damage to [src.name].
" + to_chat(user, "You repair some damage to [src.name].") src.health += min(10, initial(src.health)-src.health) else - user << "The [src.name] is at full integrity" + to_chat(user, "The [src.name] is at full integrity") return else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) @@ -880,7 +932,7 @@ /* src.log_message("Attacked by [W]. Attacker - [user]") if(prob(src.deflect_chance)) - user << "\The [W] bounces off [src.name] armor." + to_chat(user, "\The [W] bounces off [src.name] armor.") src.log_append_to_last("Armor saved.") /* for (var/mob/V in viewers(src)) @@ -914,19 +966,19 @@ /obj/mecha/proc/mmi_move_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user,"Consciousness matrix not detected.") + to_chat(user, "Consciousness matrix not detected.") return 0 else if(mmi_as_oc.brainmob.stat) - to_chat(user,"Brain activity below acceptable level.") + to_chat(user, "Brain activity below acceptable level.") return 0 else if(occupant) - to_chat(user,"Occupant detected.") + to_chat(user, "Occupant detected.") return 0 else if(dna && dna!=mmi_as_oc.brainmob.dna.unique_enzymes) - to_chat(user,"Genetic sequence or serial number incompatible with locking mechanism.") + to_chat(user, "Genetic sequence or serial number incompatible with locking mechanism.") return 0 //Added a message here since people assume their first click failed or something./N -// user << "Installing MMI, please stand by." +// to_chat(user, "Installing MMI, please stand by.") visible_message("[usr] starts to insert a brain into [src.name]") @@ -934,18 +986,18 @@ if(!occupant) return mmi_moved_inside(mmi_as_oc,user) else - to_chat(user,"Occupant detected.") + to_chat(user, "Occupant detected.") else - to_chat(user,"You stop attempting to install the brain.") + to_chat(user, "You stop attempting to install the brain.") return 0 /obj/mecha/proc/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) if(mmi_as_oc && user in range(1)) if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user,"Consciousness matrix not detected.") + to_chat(user, "Consciousness matrix not detected.") return 0 else if(mmi_as_oc.brainmob.stat) - to_chat(user,"Beta-rhythm below acceptable level.") + to_chat(user, "Beta-rhythm below acceptable level.") return 0 user.drop_from_inventory(mmi_as_oc) var/mob/brainmob = mmi_as_oc.brainmob @@ -1023,7 +1075,7 @@ return 0 //Make sure are close enough for a valid connection - if(new_port.loc != src.loc) + if(!(new_port.loc in locs)) return 0 //Perform the connection @@ -1064,21 +1116,26 @@ set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 - if(!src.occupant) return - if(usr!=src.occupant) + + if(!occupant) return - var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector/) in loc - if(possible_port) - if(connect(possible_port)) - src.occupant_message("\The [name] connects to the port.") - src.verbs += /obj/mecha/verb/disconnect_from_port - src.verbs -= /obj/mecha/verb/connect_to_port - return + + if(usr != occupant) + return + + for(var/turf/T in locs) + var/obj/machinery/atmospherics/portables_connector/possible_port = locate(/obj/machinery/atmospherics/portables_connector) in T + if(possible_port) + if(connect(possible_port)) + occupant_message("\The [name] connects to the port.") + verbs += /obj/mecha/verb/disconnect_from_port + verbs -= /obj/mecha/verb/connect_to_port + return + else + occupant_message("\The [name] failed to connect to the port.") + return else - src.occupant_message("\The [name] failed to connect to the port.") - return - else - src.occupant_message("Nothing happens") + occupant_message("Nothing happens") /obj/mecha/verb/disconnect_from_port() @@ -1086,15 +1143,19 @@ set category = "Exosuit Interface" set src = usr.loc set popup_menu = 0 - if(!src.occupant) return - if(usr!=src.occupant) + + if(!occupant) return + + if(usr != occupant) + return + if(disconnect()) - src.occupant_message("[name] disconnects from the port.") - src.verbs -= /obj/mecha/verb/disconnect_from_port - src.verbs += /obj/mecha/verb/connect_to_port + occupant_message("[name] disconnects from the port.") + verbs -= /obj/mecha/verb/disconnect_from_port + verbs += /obj/mecha/verb/connect_to_port else - src.occupant_message("[name] is not connected to the port at the moment.") + occupant_message("[name] is not connected to the port at the moment.") /obj/mecha/verb/toggle_lights() set name = "Toggle Lights" @@ -1144,22 +1205,22 @@ return if (usr.buckled) - to_chat(usr,"You can't climb into the exosuit while buckled!") + to_chat(usr, "You can't climb into the exosuit while buckled!") return src.log_message("[usr] tries to move in.") if(iscarbon(usr)) var/mob/living/carbon/C = usr if(C.handcuffed) - to_chat(usr,"Kinda hard to climb in while handcuffed don't you think?") + to_chat(usr, "Kinda hard to climb in while handcuffed don't you think?") return if (src.occupant) - to_chat(usr,"The [src.name] is already occupied!") + to_chat(usr, "The [src.name] is already occupied!") src.log_append_to_last("Permission denied.") return /* if (usr.abiotic()) - usr << "Subject cannot have abiotic items on." + to_chat(usr, "Subject cannot have abiotic items on.") return */ var/passed @@ -1169,7 +1230,7 @@ else if(src.operation_allowed(usr)) passed = 1 if(!passed) - to_chat(usr,"Access denied") + to_chat(usr, "Access denied") src.log_append_to_last("Permission denied.") return if(isliving(usr)) @@ -1178,7 +1239,7 @@ to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) return -// usr << "You start climbing into [src.name]" +// to_chat(usr, "You start climbing into [src.name]") visible_message("\The [usr] starts to climb into [src.name]") @@ -1186,9 +1247,9 @@ if(!src.occupant) moved_inside(usr) else if(src.occupant!=usr) - to_chat(usr,"[src.occupant] was faster. Try better next time, loser.") + to_chat(usr, "[src.occupant] was faster. Try better next time, loser.") else - to_chat(usr,"You stop entering the exosuit.") + to_chat(usr, "You stop entering the exosuit.") return /obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) @@ -1208,26 +1269,32 @@ src.icon_state = src.reset_icon() set_dir(dir_in) playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!hasInternalDamage()) //Otherwise it's not nominal! - switch(mech_faction) - if(MECH_FACTION_NT)//The good guys category - if(firstactivation)//First time = long activation sound - firstactivation = 1 - src.occupant << sound('sound/mecha/LongNanoActivation.ogg',volume=50) - else - src.occupant << sound('sound/mecha/nominalnano.ogg',volume=50) - if(MECH_FACTION_SYNDI)//Bad guys - if(firstactivation) - firstactivation = 1 - src.occupant << sound('sound/mecha/LongSyndiActivation.ogg',volume=50) - else - src.occupant << sound('sound/mecha/nominalsyndi.ogg',volume=50) - else//Everyone else gets the normal noise - src.occupant << sound('sound/mecha/nominal.ogg',volume=50) + if(occupant.client && cloaked_selfimage) + occupant.client.images += cloaked_selfimage + play_entered_noise(occupant) return 1 else return 0 +/obj/mecha/proc/play_entered_noise(var/mob/who) + if(!hasInternalDamage()) //Otherwise it's not nominal! + switch(mech_faction) + if(MECH_FACTION_NT)//The good guys category + if(firstactivation)//First time = long activation sound + firstactivation = 1 + who << sound('sound/mecha/LongNanoActivation.ogg',volume=50) + else + who << sound('sound/mecha/nominalnano.ogg',volume=50) + if(MECH_FACTION_SYNDI)//Bad guys + if(firstactivation) + firstactivation = 1 + who << sound('sound/mecha/LongSyndiActivation.ogg',volume=50) + else + who << sound('sound/mecha/nominalsyndi.ogg',volume=50) + else//Everyone else gets the normal noise + who << sound('sound/mecha/nominal.ogg',volume=50) + + /obj/mecha/verb/view_stats() set name = "View Stats" set category = "Exosuit Interface" @@ -1271,45 +1338,21 @@ else return if(mob_container.forceMove(src.loc))//ejecting mob container - /* - if(ishuman(occupant) && (return_pressure() > HAZARD_HIGH_PRESSURE)) - use_internal_tank = 0 - var/datum/gas_mixture/environment = get_turf_air() - if(environment) - var/env_pressure = environment.return_pressure() - var/pressure_delta = (cabin.return_pressure() - env_pressure) - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(pressure_delta > 0) - transfer_moles = pressure_delta*environment.volume/(cabin.return_temperature() * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = cabin.air_contents.remove(transfer_moles) - loc.assume_air(removed) - - occupant.SetStunned(5) - occupant.SetWeakened(5) - occupant << "You were blown out of the mech!" - */ - src.log_message("[mob_container] moved out.") + log_message("[mob_container] moved out.") occupant.reset_view() - /* - if(src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - */ - src.occupant << browse(null, "window=exosuit") + occupant << browse(null, "window=exosuit") + if(occupant.client && cloaked_selfimage) + occupant.client.images -= cloaked_selfimage if(istype(mob_container, /obj/item/device/mmi)) var/obj/item/device/mmi/mmi = mob_container if(mmi.brainmob) occupant.loc = mmi mmi.mecha = null - src.occupant.canmove = 0 - src.occupant = null - src.icon_state = src.reset_icon()+"-open" - src.set_dir(dir_in) - src.verbs -= /obj/mecha/verb/eject + occupant.canmove = 0 + occupant = null + icon_state = src.reset_icon()+"-open" + set_dir(dir_in) + verbs -= /obj/mecha/verb/eject return ///////////////////////// @@ -1596,7 +1639,7 @@ /obj/mecha/proc/occupant_message(message as text) if(message) if(src.occupant && src.occupant.client) - src.occupant << "\icon[src] [message]" + to_chat(src.occupant, "[bicon(src)] [message]") return /obj/mecha/proc/log_message(message as text,red=null) @@ -1708,10 +1751,10 @@ if(user) if(state==0) state = 1 - user << "The securing bolts are now exposed." + to_chat(user, "The securing bolts are now exposed.") else if(state==1) state = 0 - user << "The securing bolts are now hidden." + to_chat(user, "The securing bolts are now hidden.") output_maintenance_dialog(top_filter.getObj("id_card"),user) return if(href_list["set_internal_tank_valve"] && state >=1) @@ -1721,7 +1764,7 @@ var/new_pressure = input(user,"Input new output pressure","Pressure setting",internal_tank_valve) as num if(new_pressure) internal_tank_valve = new_pressure - user << "The internal pressure valve has been set to [internal_tank_valve]kPa." + to_chat(user, "The internal pressure valve has been set to [internal_tank_valve]kPa.") if(href_list["remove_passenger"] && state >= 1) var/mob/user = top_filter.getMob("user") var/list/passengers = list() @@ -1730,7 +1773,7 @@ passengers["[P.occupant]"] = P if (!passengers) - user << "There are no passengers to remove." + to_chat(user, "There are no passengers to remove.") return var/pname = input(user, "Choose a passenger to forcibly remove.", "Forcibly Remove Passenger") as null|anything in passengers @@ -1829,7 +1872,7 @@ O.aiRestorePowerRoutine = 0 O.control_disabled = 1 // Can't control things remotely if you're stuck in a card! O.laws = AI.laws - O.stat = AI.stat + O.set_stat(AI.stat) O.oxyloss = AI.getOxyLoss() O.fireloss = AI.getFireLoss() O.bruteloss = AI.getBruteLoss() @@ -2018,6 +2061,17 @@ ///////////// +/obj/mecha/cloak() + . = ..() + if(occupant && occupant.client && cloaked_selfimage) + occupant.client.images += cloaked_selfimage + +/obj/mecha/uncloak() + if(occupant && occupant.client && cloaked_selfimage) + occupant.client.images -= cloaked_selfimage + return ..() + + //debug /* /obj/mecha/verb/test_int_damage() diff --git a/code/game/mecha/mecha_helpers.dm b/code/game/mecha/mecha_helpers.dm new file mode 100644 index 0000000000..f916e19f6f --- /dev/null +++ b/code/game/mecha/mecha_helpers.dm @@ -0,0 +1,11 @@ +/* + * Helper file for Exosuit / Mecha code. + */ + +// Returns, at least, a usable target body position, for things like guns. + +/obj/mecha/proc/get_pilot_zone_sel() + if(!occupant || !occupant.zone_sel || occupant.stat) + return BP_TORSO + + return occupant.zone_sel.selecting diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm index 1f90a45d61..ce72a16119 100644 --- a/code/game/mecha/medical/odysseus.dm +++ b/code/game/mecha/medical/odysseus.dm @@ -49,7 +49,7 @@ var/perspective = input("Select a perspective type.", "Client perspective", occupant.client.perspective) in list(MOB_PERSPECTIVE,EYE_PERSPECTIVE) - world << "[perspective]" + to_world("[perspective]") occupant.client.perspective = perspective return @@ -61,7 +61,7 @@ occupant.client.eye = src else occupant.client.eye = occupant - world << "[occupant.client.eye]" + to_world("[occupant.client.eye]") return */ @@ -72,15 +72,15 @@ // process_hud(var/mob/M) //TODO VIS /* - world<< "view(M)" + to_world("view(M)") for(var/mob/mob in view(M)) - world << "[mob]" - world<< "view(M.client)" + to_world("[mob]") + to_world("view(M.client)") for(var/mob/mob in view(M.client)) - world << "[mob]" - world<< "view(M.loc)" + to_world("[mob]") + to_world("view(M.loc)") for(var/mob/mob in view(M.loc)) - world << "[mob]" + to_world("[mob]") if(!M || M.stat || !(M in view(M))) return diff --git a/code/game/mecha/micro/micro.dm b/code/game/mecha/micro/micro.dm index 9c8a44899d..0b1e340487 100644 --- a/code/game/mecha/micro/micro.dm +++ b/code/game/mecha/micro/micro.dm @@ -121,7 +121,7 @@ /obj/mecha/micro/move_inside() var/mob/living/carbon/C = usr if (C.size_multiplier >= 0.5) - C << "You can't fit in this suit!" + to_chat(C, "You can't fit in this suit!") return else ..() @@ -129,7 +129,7 @@ /obj/mecha/micro/move_inside_passenger() var/mob/living/carbon/C = usr if (C.size_multiplier >= 0.5) - C << "You can't fit in this suit!" + to_chat(C, "You can't fit in this suit!") return else ..() diff --git a/code/game/mecha/space/hoverpod.dm b/code/game/mecha/space/hoverpod.dm index 31558458d9..fc5fc1e739 100644 --- a/code/game/mecha/space/hoverpod.dm +++ b/code/game/mecha/space/hoverpod.dm @@ -26,11 +26,20 @@ max_universal_equip = 1 max_special_equip = 1 -/obj/mecha/working/hoverpod/New() - ..() +/obj/mecha/working/hoverpod/Initialize() + . = ..() ion_trail = new /datum/effect/effect/system/ion_trail_follow() ion_trail.set_up(src) - ion_trail.start() + +/obj/mecha/working/hoverpod/moved_inside(var/mob/living/carbon/human/H as mob) + . = ..(H) + if(.) + ion_trail.start() + +/obj/mecha/working/hoverpod/go_out() + . = ..() + if(!occupant) + ion_trail.stop() //Modified phazon code /obj/mecha/working/hoverpod/Topic(href, href_list) @@ -52,6 +61,9 @@ output += ..() return output +/obj/mecha/working/hoverpod/can_ztravel() + return (stabilization_enabled && has_charge(step_energy_drain)) + // No space drifting /obj/mecha/working/hoverpod/check_for_support() //does the hoverpod have enough charge left to stabilize itself? @@ -106,7 +118,7 @@ max_special_equip = 1 /obj/mecha/working/hoverpod/combatpod/Initialize() - ..() + . = ..() var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/explosive @@ -117,7 +129,7 @@ desc = "Who knew a tiny ball could fit three people?" /obj/mecha/working/hoverpod/shuttlepod/Initialize() - ..() + . = ..() var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger ME.attach(src) ME = new /obj/item/mecha_parts/mecha_equipment/tool/passenger diff --git a/code/game/mecha/space/shuttle.dm b/code/game/mecha/space/shuttle.dm index 7e361ece0c..bf9aec3476 100644 --- a/code/game/mecha/space/shuttle.dm +++ b/code/game/mecha/space/shuttle.dm @@ -41,44 +41,30 @@ max_universal_equip = 1 max_special_equip = 1 -/obj/mecha/working/hoverpod/Initialize() - ..() - ion_trail.stop() - -/obj/mecha/working/hoverpod/shuttlecraft/moved_inside(var/mob/living/carbon/human/H as mob) - . = ..(H) - if(.) - ion_trail.start() - -/obj/mecha/working/hoverpod/shuttlecraft/go_out() - . = ..() - if(!occupant) - ion_trail.stop() - /obj/mecha/working/hoverpod/shuttlecraft/update_icon() - overlays.Cut() + cut_overlays() ..() if(base_paint) if(!base_paint_mask) base_paint_mask = image(icon, "[initial_icon]-mask+base", src.layer + 1) base_paint_mask.color = base_paint - overlays |= base_paint_mask + add_overlay(base_paint_mask) if(front_paint) if(!front_paint_mask) front_paint_mask = image(icon, "[initial_icon]-mask+front", src.layer + 1) front_paint_mask.color = front_paint - overlays |= front_paint_mask + add_overlay(front_paint_mask) if(engine_paint) if(!engine_paint_mask) engine_paint_mask = image(icon, "[initial_icon]-mask+engine", src.layer + 1) engine_paint_mask.color = engine_paint - overlays |= engine_paint_mask + add_overlay(engine_paint_mask) if(central_paint) if(!engine_paint_mask) central_paint_mask = image(icon, "[initial_icon]-mask+central", src.layer + 2) central_paint_mask.color = central_paint - overlays |= central_paint_mask + add_overlay(central_paint_mask) /obj/mecha/working/hoverpod/shuttlecraft/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W,/obj/item/device/multitool) && state == 1) diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index a679f5e1b6..cc1e327478 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -113,7 +113,7 @@ //Wrapper procs that handle sanity and user feedback /atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, var/forced = FALSE, var/silent = FALSE) if(!ticker) - user << "You can't buckle anyone in before the game starts." + to_chat(user, "You can't buckle anyone in before the game starts.") return FALSE // Is this really needed? if(!user.Adjacent(M) || user.restrained() || user.stat || istype(user, /mob/living/silicon/pai)) return FALSE @@ -133,6 +133,7 @@ // step_towards(M, src) . = buckle_mob(M, forced) + playsound(src.loc, 'sound/effects/seatbelt.ogg', 50, 1) if(.) var/reveal_message = list("buckled_mob" = null, "buckled_to" = null) //VORE EDIT: This being a list and messages existing for the buckle target atom. if(!silent) @@ -160,6 +161,7 @@ /atom/movable/proc/user_unbuckle_mob(mob/living/buckled_mob, mob/user) var/mob/living/M = unbuckle_mob(buckled_mob) + playsound(src.loc, 'sound/effects/seatbelt.ogg', 50, 1) if(M) if(M != user) M.visible_message(\ diff --git a/code/game/objects/effects/alien/aliens.dm b/code/game/objects/effects/alien/aliens.dm index bdcbe95e7f..d8b0821a4d 100644 --- a/code/game/objects/effects/alien/aliens.dm +++ b/code/game/objects/effects/alien/aliens.dm @@ -109,7 +109,7 @@ /obj/effect/alien/resin/attack_hand() usr.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if (HULK in usr.mutations) - usr << "You easily destroy the [name]." + to_chat(usr, "You easily destroy the [name].") for(var/mob/O in oviewers(src)) O.show_message("[usr] destroys the [name]!", 1) health = 0 @@ -125,7 +125,7 @@ healthcheck() return - usr << "You claw at the [name]." + to_chat(usr, "You claw at the [name].") for(var/mob/O in oviewers(src)) O.show_message("[usr] claws at the [name]!", 1) health -= rand(5,10) @@ -476,14 +476,14 @@ Alien plants should do something if theres a lot of poison switch(status) if(BURST) - user << "You clear the hatched egg." + to_chat(user, "You clear the hatched egg.") qdel(src) return /* if(GROWING) - user << "The child is not developed yet." + to_chat(user, "The child is not developed yet.") return if(GROWN) - user << "You retrieve the child." + to_chat(user, "You retrieve the child.") Burst(0) return diff --git a/code/game/objects/effects/chem/chemsmoke.dm b/code/game/objects/effects/chem/chemsmoke.dm index 43b42cbac9..5538755d24 100644 --- a/code/game/objects/effects/chem/chemsmoke.dm +++ b/code/game/objects/effects/chem/chemsmoke.dm @@ -3,7 +3,7 @@ ///////////////////////////////////////////// /obj/effect/effect/smoke/chem icon = 'icons/effects/chemsmoke.dmi' - opacity = 0 + opacity = TRUE time_to_live = 300 pass_flags = PASSTABLE | PASSGRILLE | PASSGLASS //PASSGLASS is fine here, it's just so the visual effect can "flow" around glass @@ -16,6 +16,9 @@ walk(src, 0) // Because we might have called walk_to, we must stop the walk loop or BYOND keeps an internal reference to us forever. return ..() +/obj/effect/effect/smoke/chem/transparent + opacity = FALSE + /datum/effect/effect/system/smoke_spread/chem smoke_type = /obj/effect/effect/smoke/chem var/obj/chemholder @@ -29,13 +32,16 @@ show_log = 0 var/datum/seed/seed -/datum/effect/effect/system/smoke_spread/chem/spores/New(seed_name) - if(seed_name && plant_controller) - seed = plant_controller.seeds[seed_name] - if(!seed) - qdel(src) +/datum/effect/effect/system/smoke_spread/chem/spores/New(_seed) + seed = _seed + if(!istype(seed)) + CRASH("Invalid seed datum passed! [seed] ([seed?.type])") ..() +/datum/effect/effect/system/smoke_spread/chem/blob + show_log = 0 + smoke_type = /obj/effect/effect/smoke/chem/transparent + /datum/effect/effect/system/smoke_spread/chem/New() ..() chemholder = new/obj() @@ -156,7 +162,7 @@ if(passed_smoke) smoke = passed_smoke else - smoke = new /obj/effect/effect/smoke/chem(location) + smoke = new smoke_type(location) if(chemholder.reagents.reagent_list.len) chemholder.reagents.trans_to_obj(smoke, chemholder.reagents.total_volume / dist, copy = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents @@ -166,11 +172,13 @@ smoke.pixel_x = -32 + rand(-8, 8) smoke.pixel_y = -32 + rand(-8, 8) walk_to(smoke, T) - smoke.set_opacity(1) //switching opacity on after the smoke has spawned, and then - sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner - smoke.set_opacity(0) // lighting and view range updates - fadeOut(smoke) - qdel(smoke) + if(initial(smoke.opacity)) + smoke.set_opacity(1) //switching opacity on after the smoke has spawned, and then + spawn() + sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner + smoke.set_opacity(0) // lighting and view range updates + fadeOut(smoke) + qdel(smoke) /datum/effect/effect/system/smoke_spread/chem/spores/spawnSmoke(var/turf/T, var/icon/I, var/dist = 1) var/obj/effect/effect/smoke/chem/spores = new /obj/effect/effect/smoke/chem(location) diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm index d3ee736251..5a461e1525 100644 --- a/code/game/objects/effects/chem/foam.dm +++ b/code/game/objects/effects/chem/foam.dm @@ -15,25 +15,30 @@ var/expand = 1 var/metal = 0 -/obj/effect/effect/foam/New(var/loc, var/ismetal = 0) - ..(loc) +/obj/effect/effect/foam/Initialize(var/mapload, var/ismetal = 0) + . = ..() icon_state = "[ismetal? "m" : ""]foam" metal = ismetal playsound(src, 'sound/effects/bubbles2.ogg', 80, 1, -3) - spawn(3 + metal * 3) - process() - checkReagents() - spawn(120) - STOP_PROCESSING(SSobj, src) - sleep(30) - if(metal) - var/obj/structure/foamedmetal/M = new(src.loc) - M.metal = metal - M.updateicon() - flick("[icon_state]-disolve", src) - sleep(5) - qdel(src) - return + + addtimer(CALLBACK(src, .proc/post_spread), 3 + metal * 3) + addtimer(CALLBACK(src, .proc/pre_harden), 12 SECONDS) + addtimer(CALLBACK(src, .proc/harden), 15 SECONDS) + +/obj/effect/effect/foam/proc/post_spread() + process() + checkReagents() + +/obj/effect/effect/foam/proc/pre_harden() + STOP_PROCESSING(SSobj, src) + +/obj/effect/effect/foam/proc/harden() + if(metal) + var/obj/structure/foamedmetal/M = new(src.loc) + M.metal = metal + M.updateicon() + flick("[icon_state]-disolve", src) + QDEL_IN(src, 5) /obj/effect/effect/foam/proc/checkReagents() // transfer any reagents to the floor if(!metal && reagents) @@ -74,12 +79,8 @@ qdel(src) /obj/effect/effect/foam/Crossed(var/atom/movable/AM) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(AM.is_incorporeal()) + return if(metal) return if(istype(AM, /mob/living)) @@ -168,7 +169,7 @@ user.visible_message("[user] smashes through the foamed metal.", "You smash through the metal foam wall.") qdel(src) else - user << "You hit the metal foam but bounce off it." + to_chat(user, "You hit the metal foam but bounce off it.") return /obj/structure/foamedmetal/attackby(var/obj/item/I, var/mob/user) @@ -184,4 +185,4 @@ user.visible_message("[user] smashes through the foamed metal.", "You smash through the foamed metal with \the [I].") qdel(src) else - user << "You hit the metal foam to no effect." + to_chat(user, "You hit the metal foam to no effect.") diff --git a/code/game/objects/effects/chem/water.dm b/code/game/objects/effects/chem/water.dm index ee1c75980c..7de9b9f8ff 100644 --- a/code/game/objects/effects/chem/water.dm +++ b/code/game/objects/effects/chem/water.dm @@ -5,11 +5,9 @@ mouse_opacity = 0 pass_flags = PASSTABLE | PASSGRILLE | PASSBLOB -/obj/effect/effect/water/New(loc) - ..() - spawn(150) // In case whatever made it forgets to delete it - if(src) - qdel(src) +/obj/effect/effect/water/Initialize() + . = ..() + QDEL_IN(src, 15 SECONDS) /obj/effect/effect/water/proc/set_color() // Call it after you move reagents to it icon += reagents.get_color() diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index cfde668d5c..fba219e924 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -20,7 +20,6 @@ var/global/list/image/splatter_cache=list() var/synthblood = 0 var/list/datum/disease2/disease/virus2 = list() var/amount = 5 - var/drytime /obj/effect/decal/cleanable/blood/reveal_blood() if(!fluorescent) @@ -33,13 +32,8 @@ var/global/list/image/splatter_cache=list() if(invisibility != 100) invisibility = 100 amount = 0 - STOP_PROCESSING(SSobj, src) ..(ignore=1) -/obj/effect/decal/cleanable/blood/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - /obj/effect/decal/cleanable/blood/New() ..() update_icon() @@ -52,12 +46,7 @@ var/global/list/image/splatter_cache=list() if (B.blood_DNA) blood_DNA |= B.blood_DNA.Copy() qdel(B) - drytime = world.time + DRYING_TIME * (amount+1) - START_PROCESSING(SSobj, src) - -/obj/effect/decal/cleanable/blood/process() - if(world.time > drytime) - dry() + addtimer(CALLBACK(src, .proc/dry), DRYING_TIME * (amount+1)) /obj/effect/decal/cleanable/blood/update_icon() if(basecolor == "rainbow") basecolor = "#[get_random_colour(1)]" @@ -70,12 +59,8 @@ var/global/list/image/splatter_cache=list() desc = initial(desc) /obj/effect/decal/cleanable/blood/Crossed(mob/living/carbon/human/perp) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = perp - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(perp.is_incorporeal()) + return if (!istype(perp)) return if(amount < 1) @@ -120,7 +105,6 @@ var/global/list/image/splatter_cache=list() desc = drydesc color = adjust_brightness(color, -50) amount = 0 - STOP_PROCESSING(SSobj, src) /obj/effect/decal/cleanable/blood/attack_hand(mob/living/carbon/human/user) ..() @@ -130,7 +114,7 @@ var/global/list/image/splatter_cache=list() return var/taken = rand(1,amount) amount -= taken - user << "You get some of \the [src] on your hands." + to_chat(user, "You get some of \the [src] on your hands.") if (!user.blood_DNA) user.blood_DNA = list() user.blood_DNA |= blood_DNA.Copy() @@ -176,7 +160,7 @@ var/global/list/image/splatter_cache=list() /obj/effect/decal/cleanable/blood/writing/examine(mob/user) ..(user) - user << "It reads: \"[message]\"" + to_chat(user, "It reads: \"[message]\"") /obj/effect/decal/cleanable/blood/gibs name = "gibs" @@ -245,14 +229,18 @@ var/global/list/image/splatter_cache=list() random_icon_states = list("mucus") var/list/datum/disease2/disease/virus2 = list() - var/dry=0 // Keeps the lag down + var/dry = 0 // Keeps the lag down -/obj/effect/decal/cleanable/mucus/New() - spawn(DRYING_TIME * 2) - dry=1 +/obj/effect/decal/cleanable/mucus/Initialize() + . = ..() + VARSET_IN(src, dry, TRUE, DRYING_TIME * 2) //This version should be used for admin spawns and pre-mapped virus vectors (e.g. in PoIs), this version does not dry -/obj/effect/decal/cleanable/mucus/mapped/New() - ..() +/obj/effect/decal/cleanable/mucus/mapped/Initialize() + . = ..() virus2 |= new /datum/disease2/disease virus2[1].makerandom() + +/obj/effect/decal/cleanable/mucus/mapped/Destroy() + virus2.Cut() + return ..() diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 1ef83bc722..7c2e274644 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -16,7 +16,7 @@ anchored = 1 /obj/effect/decal/cleanable/ash/attack_hand(mob/user as mob) - user << "[src] sifts through your fingers." + to_chat(user, "[src] sifts through your fingers.") var/turf/simulated/floor/F = get_turf(src) if (istype(F)) F.dirt += 4 diff --git a/code/game/objects/effects/decals/contraband.dm b/code/game/objects/effects/decals/contraband.dm index d765c479fb..b0a28aa490 100644 --- a/code/game/objects/effects/decals/contraband.dm +++ b/code/game/objects/effects/decals/contraband.dm @@ -4,7 +4,7 @@ /obj/item/weapon/contraband name = "contraband item" desc = "You probably shouldn't be holding this." - icon = 'icons/obj/contraband.dmi' + icon = 'icons/obj/contraband_vr.dmi' //VOREStation Edit force = 0 @@ -33,12 +33,12 @@ //must place on a wall and user must not be inside a closet/mecha/whatever var/turf/W = A if (!iswall(W) || !isturf(user.loc)) - user << "You can't place this here!" + to_chat(user, "You can't place this here!") return var/placement_dir = get_dir(user, W) if (!(placement_dir in cardinal)) - user << "You must stand directly in front of the wall you wish to place that on." + to_chat(user, "You must stand directly in front of the wall you wish to place that on.") return //just check if there is a poster on or adjacent to the wall @@ -54,10 +54,10 @@ break if (stuff_on_wall) - user << "There is already a poster there!" + to_chat(user, "There is already a poster there!") return - user << "You start placing the poster on the wall..." //Looks like it's uncluttered enough. Place the poster. + to_chat(user, "You start placing the poster on the wall...") //Looks like it's uncluttered enough. Place the poster. var/obj/structure/sign/poster/P = new poster_type(user.loc, placement_dir=get_dir(user, W), serial=serial_number, itemtype = src.type) @@ -70,7 +70,7 @@ if(!P) return if(iswall(W) && user && P.loc == user.loc) //Let's check if everything is still there - user << "You place the poster!" + to_chat(user, "You place the poster!") else P.roll_and_drop(P.loc) @@ -93,7 +93,7 @@ /obj/structure/sign/poster name = "poster" desc = "A large piece of space-resistant printed paper. " - icon = 'icons/obj/contraband.dmi' + icon = 'icons/obj/contraband_vr.dmi' //VOREStation Edit anchored = 1 var/serial_number //Will hold the value of src.loc if nobody initialises it var/poster_type //So mappers can specify a desired poster @@ -148,10 +148,10 @@ if(W.is_wirecutter()) playsound(src.loc, W.usesound, 100, 1) if(ruined) - user << "You remove the remnants of the poster." + to_chat(user, "You remove the remnants of the poster.") qdel(src) else - user << "You carefully remove the poster from the wall." + to_chat(user, "You carefully remove the poster from the wall.") roll_and_drop(user.loc) return diff --git a/code/game/objects/effects/decals/posters/voreposters_vr.dm b/code/game/objects/effects/decals/posters/voreposters_vr.dm new file mode 100644 index 0000000000..bd951f88b3 --- /dev/null +++ b/code/game/objects/effects/decals/posters/voreposters_vr.dm @@ -0,0 +1,44 @@ +/datum/poster/vore_1 + icon_state = "sbsposter1" + name = "Best Girl Pip" + desc = "A poster of the official NT Best Girl Mascot, Pip" +/datum/poster/vore_2 + icon_state = "sbsposter2" + name = "AroButt" + desc = "A lewd view of KHI's cutest fops." +/datum/poster/vore_3 + icon_state = "sbsposter3" + name = "Poster This Dog" + desc = "Post this dog." +/datum/poster/vore_4 + icon_state = "sbsposter4" + name = "PAT" + desc = "A poster containing very obvious PAT propoganda." +/datum/poster/vore_5 + icon_state = "sbsposter5" + name = "The Inn" + desc = "A poster of a certain black-eyed shadekin." +/datum/poster/vore_6 + icon_state = "sbsposter6" + name = "Mawletta" + desc = "The best view in the system." +/datum/poster/vore_7 + icon_state = "sbsposter7" + name = "Sam FEAR" + desc = "A poster of the most dangerous cat working for NT." +/datum/poster/vore_8 + icon_state = "sbsposter8" + name = "Shitty Timbs" + desc = "A special edition, brand deal, Shitty Tim Concert Poster." +/datum/poster/vore_9 + icon_state = "sbsposter9" + name = "Shitty Tim" + desc = "A Shitty Tim concert poster." +/datum/poster/vore_10 + icon_state = "sbsposter10" + name = "Kitty Kisuke" + desc = "A poster of famous redspace researcher, Kisuke Gema." +/datum/poster/vore_11 + icon_state = "sbsposter11" + name = "Fear" + desc = "Depicted on this poster is a monster crackling with crimson energy that seems to drip off its body. The word 'RUN' is printed across the bottom." diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm index d35da75324..7452586543 100644 --- a/code/game/objects/effects/decals/remains.dm +++ b/code/game/objects/effects/decals/remains.dm @@ -56,7 +56,7 @@ icon_state = "mummified2" /obj/effect/decal/remains/attack_hand(mob/user as mob) - user << "[src] sinks together into a pile of ash." + to_chat(user, "[src] sinks together into a pile of ash.") var/turf/simulated/floor/F = get_turf(src) if (istype(F)) new /obj/effect/decal/cleanable/ash(F) diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index d1de900bae..00896e7107 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -185,12 +185,8 @@ steam.start() -- spawns the effect qdel(src) /obj/effect/effect/smoke/Crossed(mob/living/carbon/M as mob ) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = M - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(M.is_incorporeal()) + return ..() if(istype(M)) affect(M) @@ -242,6 +238,15 @@ steam.start() -- spawns the effect if(prob(25)) L.emote("cough") +/obj/effect/effect/smoke/bad/noxious + opacity = 0 + +/obj/effect/effect/smoke/bad/noxious/affect(var/mob/living/L) + if (!..()) + return 0 + if(L.needs_to_breathe()) + L.adjustToxLoss(1) + /* Not feasile until a later date /obj/effect/effect/smoke/bad/Crossed(atom/movable/M as mob|obj) ..() @@ -251,7 +256,7 @@ steam.start() -- spawns the effect B.damage = (B.damage/2) projectiles += B destroyed_event.register(B, src, /obj/effect/effect/smoke/bad/proc/on_projectile_delete) - world << "Damage is: [B.damage]" + to_world("Damage is: [B.damage]") return 1 /obj/effect/effect/smoke/bad/proc/on_projectile_delete(obj/item/projectile/beam/proj) @@ -373,6 +378,9 @@ steam.start() -- spawns the effect /datum/effect/effect/system/smoke_spread/bad smoke_type = /obj/effect/effect/smoke/bad +/datum/effect/effect/system/smoke_spread/noxious + smoke_type = /obj/effect/effect/smoke/bad/noxious + /datum/effect/effect/system/smoke_spread/fire smoke_type = /obj/effect/effect/smoke/elemental/fire @@ -511,10 +519,10 @@ steam.start() -- spawns the effect s.start() for(var/mob/M in viewers(5, location)) - M << "The solution violently explodes." + to_chat(M, "The solution violently explodes.") for(var/mob/M in viewers(1, location)) if (prob (50 * amount)) - M << "The explosion knocks you down." + to_chat(M, "The explosion knocks you down.") M.Weaken(rand(1,5)) return else @@ -537,7 +545,7 @@ steam.start() -- spawns the effect flash = (amount/4) * flashing_factor for(var/mob/M in viewers(8, location)) - M << "The solution violently explodes." + to_chat(M, "The solution violently explodes.") explosion( location, diff --git a/code/game/objects/effects/gibs.dm b/code/game/objects/effects/gibs.dm index 16db09edb6..e33da567f3 100644 --- a/code/game/objects/effects/gibs.dm +++ b/code/game/objects/effects/gibs.dm @@ -18,7 +18,7 @@ proc/Gib(atom/location, var/datum/dna/MobDNA = null) if(gibtypes.len != gibamounts.len || gibamounts.len != gibdirections.len) - world << "Gib list length mismatch!" + to_world("Gib list length mismatch!") return var/obj/effect/decal/cleanable/blood/gibs/gib = null diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 193d24d4ab..68c23322f7 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -24,6 +24,7 @@ return if("JoinLate") // Bit difference, since we need the spawn point to move. latejoin += src + simulated = 1 // delete_me = 1 return if("JoinLateGateway") diff --git a/code/game/objects/effects/landmarks_vr.dm b/code/game/objects/effects/landmarks_vr.dm new file mode 100644 index 0000000000..1111b71724 --- /dev/null +++ b/code/game/objects/effects/landmarks_vr.dm @@ -0,0 +1,39 @@ +/obj/effect/landmark/late_antag + name = "Antag Latespawn" + var/antag_id + +/obj/effect/landmark/late_antag/Initialize() + . = ..() + var/datum/antagonist/A = all_antag_types[antag_id] + if(istype(A)) + A.starting_locations |= get_turf(src) + var/list/allpoints = all_antag_spawnpoints[A.landmark_id] + allpoints |= get_turf(src) + +/obj/effect/landmark/late_antag/ert + name = "Response Team - Lateload" + antag_id = MODE_ERT + +/obj/effect/landmark/late_antag/trader + name = "Trader - Lateload" + antag_id = MODE_TRADE + +/obj/effect/landmark/late_antag/wizard + name = "Wizard - Lateload" + antag_id = MODE_WIZARD + +/obj/effect/landmark/late_antag/technomancer + name = "Technomancer - Lateload" + antag_id = MODE_TECHNOMANCER + +/obj/effect/landmark/late_antag/mercenary + name = "Mercenary - Lateload" + antag_id = MODE_MERCENARY + +/obj/effect/landmark/late_antag/ninja + name = "Ninja - Lateload" + antag_id = MODE_NINJA + +/obj/effect/landmark/late_antag/raider + name = "Raider - Lateload" + antag_id = MODE_RAIDER \ No newline at end of file diff --git a/code/game/objects/effects/map_effects/beam_point.dm b/code/game/objects/effects/map_effects/beam_point.dm index 524686874c..c10e8b0315 100644 --- a/code/game/objects/effects/map_effects/beam_point.dm +++ b/code/game/objects/effects/map_effects/beam_point.dm @@ -36,8 +36,7 @@ GLOBAL_LIST_EMPTY(all_beam_points) if(make_beams_on_init) create_beams() if(use_timer) - spawn(initial_delay) - handle_beam_timer() + addtimer(CALLBACK(src, .proc/handle_beam_timer), initial_delay) return ..() /obj/effect/map_effect/beam_point/Destroy() diff --git a/code/game/objects/effects/map_effects/map_effects.dm b/code/game/objects/effects/map_effects/map_effects.dm index 9e2986072d..d5bad86662 100644 --- a/code/game/objects/effects/map_effects/map_effects.dm +++ b/code/game/objects/effects/map_effects/map_effects.dm @@ -1,72 +1,69 @@ -// These are objects you can use inside special maps (like PoIs), or for adminbuse. -// Players cannot see or interact with these. -/obj/effect/map_effect - anchored = TRUE - invisibility = 99 // So a badmin can go view these by changing their see_invisible. - icon = 'icons/effects/map_effects.dmi' - - // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects. - var/always_run = FALSE // If true, the game will not try to suppress this from firing if nobody is around to see it. - var/proximity_needed = 12 // How many tiles a mob with a client must be for this to run. - var/ignore_ghosts = FALSE // If true, ghosts won't satisfy the above requirement. - var/ignore_afk = TRUE // If true, AFK people (5 minutes) won't satisfy it as well. - var/retry_delay = 3 SECONDS // How long until we check for players again. - -/obj/effect/map_effect/ex_act() - return - -/obj/effect/map_effect/singularity_pull() - return - -/obj/effect/map_effect/singularity_act() - return - -// Base type for effects that run on variable intervals. -/obj/effect/map_effect/interval - var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger. - var/interval_upper_bound = 5 SECONDS // Higher number for above. - var/halt = FALSE // Set to true to stop the loop when it reaches the next iteration. - -/obj/effect/map_effect/interval/Initialize() - handle_interval_delay() - return ..() - -/obj/effect/map_effect/interval/Destroy() - halt = TRUE // Shouldn't need it to GC but just in case. - return ..() - -// Override this for the specific thing to do. Be sure to call parent to keep looping. -/obj/effect/map_effect/interval/proc/trigger() - handle_interval_delay() - -// Handles the delay and making sure it doesn't run when it would be bad. -/obj/effect/map_effect/interval/proc/handle_interval_delay() - // Check to see if we're useful first. - if(halt) - return // Do not pass .(), do not recursively collect 200 thaler. - - if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) - spawn(retry_delay) // Maybe someday we'll have fancy TG timers/schedulers. - if(!QDELETED(src)) - .() - return - - var/next_interval = rand(interval_lower_bound, interval_upper_bound) - spawn(next_interval) - if(!QDELETED(src)) - trigger() - -// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it. -/proc/check_for_player_proximity(var/atom/proximity_to, var/radius = 12, var/ignore_ghosts = FALSE, var/ignore_afk = TRUE) - if(!proximity_to) - return FALSE - - for(var/thing in player_list) - var/mob/M = thing // Avoiding typechecks for more speed, player_list will only contain mobs anyways. - if(ignore_ghosts && isobserver(M)) - continue - if(ignore_afk && M.client && M.client.is_afk(5 MINUTES)) - continue - if(M.z == proximity_to.z && get_dist(M, proximity_to) <= radius) - return TRUE - return FALSE \ No newline at end of file +// These are objects you can use inside special maps (like PoIs), or for adminbuse. +// Players cannot see or interact with these. +/obj/effect/map_effect + anchored = TRUE + invisibility = 99 // So a badmin can go view these by changing their see_invisible. + icon = 'icons/effects/map_effects.dmi' + + // Below vars concern check_for_player_proximity() and is used to not waste effort if nobody is around to appreciate the effects. + var/always_run = FALSE // If true, the game will not try to suppress this from firing if nobody is around to see it. + var/proximity_needed = 12 // How many tiles a mob with a client must be for this to run. + var/ignore_ghosts = FALSE // If true, ghosts won't satisfy the above requirement. + var/ignore_afk = TRUE // If true, AFK people (5 minutes) won't satisfy it as well. + var/retry_delay = 5 SECONDS // How long until we check for players again. + var/next_attempt = 0 // Next time we're going to do ACTUAL WORK + +/obj/effect/map_effect/ex_act() + return + +/obj/effect/map_effect/singularity_pull() + return + +/obj/effect/map_effect/singularity_act() + return + +// Base type for effects that run on variable intervals. +/obj/effect/map_effect/interval + var/interval_lower_bound = 5 SECONDS // Lower number for how often the map_effect will trigger. + var/interval_upper_bound = 5 SECONDS // Higher number for above. + +/obj/effect/map_effect/interval/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/effect/map_effect/interval/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +// Override this for the specific thing to do. +/obj/effect/map_effect/interval/proc/trigger() + return + +// Handles the delay and making sure it doesn't run when it would be bad. +/obj/effect/map_effect/interval/process() + //Not yet! + if(world.time < next_attempt) + return + + // Check to see if we're useful first. + if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) + next_attempt = world.time + retry_delay + // Hey there's someone nearby. + else + next_attempt = world.time + rand(interval_lower_bound, interval_upper_bound) + trigger() + +// Helper proc to optimize the use of effects by making sure they do not run if nobody is around to perceive it. +/proc/check_for_player_proximity(var/atom/proximity_to, var/radius = 12, var/ignore_ghosts = FALSE, var/ignore_afk = TRUE) + if(!proximity_to) + return FALSE + + for(var/thing in player_list) + var/mob/M = thing // Avoiding typechecks for more speed, player_list will only contain mobs anyways. + if(ignore_ghosts && isobserver(M)) + continue + if(ignore_afk && M.client && M.client.is_afk(5 MINUTES)) + continue + if(M.z == proximity_to.z && get_dist(M, proximity_to) <= radius) + return TRUE + return FALSE diff --git a/code/game/objects/effects/map_effects/perma_light.dm b/code/game/objects/effects/map_effects/perma_light.dm index 3a82dc7e69..281c128fe6 100644 --- a/code/game/objects/effects/map_effects/perma_light.dm +++ b/code/game/objects/effects/map_effects/perma_light.dm @@ -6,4 +6,12 @@ light_range = 3 light_power = 1 + light_color = "#FFFFFF" + +/obj/effect/map_effect/perma_light/brighter + name = "permanent light (bright)" + icon_state = "permalight" + + light_range = 5 + light_power = 3 light_color = "#FFFFFF" \ No newline at end of file diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index fb4117ef44..fb82e6515a 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -16,6 +16,10 @@ icon_state = "uglyminearmed" wires = new(src) +/obj/effect/mine/Destroy() + qdel_null(wires) + return ..() + /obj/effect/mine/proc/explode(var/mob/living/M) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread() triggered = 1 @@ -35,13 +39,9 @@ explode() ..() -/obj/effect/mine/Crossed(AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/effect/mine/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return Bumped(AM) /obj/effect/mine/Bumped(mob/M as mob|obj) diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index 5fca6fbecf..d7ab1ebfa6 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -14,11 +14,10 @@ invisibility = 0 var/time_to_die = 10 SECONDS // Afer which, it will delete itself. -/obj/effect/temporary_effect/New() - ..() +/obj/effect/temporary_effect/Initialize() + . = ..() if(time_to_die) - spawn(time_to_die) - qdel(src) + QDEL_IN(src, time_to_die) // Shown really briefly when attacking with axes. /obj/effect/temporary_effect/cleave_attack @@ -41,12 +40,9 @@ /obj/effect/temporary_effect/shuttle_landing name = "shuttle landing" desc = "You better move if you don't want to go splat!" - icon_state = "shuttle_warning_still" - time_to_die = 4.9 SECONDS - -/obj/effect/temporary_effect/shuttle_landing/Initialize() - flick("shuttle_warning", src) // flick() forces the animation to always begin at the start. - . = ..() + icon = 'icons/goonstation/featherzone.dmi' + icon_state = "hazard-corners" + time_to_die = 5 SECONDS // The manifestation of Zeus's might. Or just a really unlucky day. // This is purely a visual effect, this isn't the part of the code that hurts things. diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index cce6dfc4b7..b1c00bb79a 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -67,7 +67,7 @@ if (istype(W, /obj/item/weapon/shovel)) user.visible_message("[user] begins to shovel away \the [src].") if(do_after(user, 40)) - user << "You have finished shoveling!" + to_chat(user, "You have finished shoveling!") qdel(src) return diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 5ba58dc4e0..c549a7e3d1 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -20,13 +20,9 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) return return -/obj/effect/portal/Crossed(AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/effect/portal/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return if(istype(AM,/mob) && !(istype(AM,/mob/living))) return //do not send ghosts, zshadows, ai eyes, etc spawn(0) @@ -42,12 +38,9 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) return return -/obj/effect/portal/New() - ..() // Necessary for the list boilerplate to work - spawn(300) - qdel(src) - return - return +/obj/effect/portal/Initialize() + . = ..() + QDEL_IN(src, 30 SECONDS) /obj/effect/portal/proc/teleport(atom/movable/M as mob|obj) if(istype(M, /obj/effect)) //sparks don't teleport @@ -60,6 +53,14 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) qdel(src) return if (istype(M, /atom/movable)) + //VOREStation Addition Start: Prevent taurriding abuse + if(istype(M, /mob/living)) + var/mob/living/L = M + if(LAZYLEN(L.buckled_mobs)) + var/datum/riding/R = L.riding_datum + for(var/rider in L.buckled_mobs) + R.force_dismount(rider) + //VOREStation Addition End: Prevent taurriding abuse if(prob(failchance)) //oh dear a problem, put em in deep space src.icon_state = "portal1" do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0) diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 90d9ae1820..f6422259d3 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -40,6 +40,18 @@ health -= damage healthcheck() +/obj/effect/spider/spiderling/attack_hand(mob/living/user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(src) + if(prob(20)) + visible_message("\The [user] tries to stomp on \the [src], but misses!") + var/list/nearby = oview(2, src) + if(length(nearby)) + walk_to(src, pick(nearby), 2) + return + visible_message("\The [user] stomps \the [src] dead!") + die() + /obj/effect/spider/bullet_act(var/obj/item/projectile/Proj) ..() health -= Proj.get_structure_damage() @@ -238,7 +250,7 @@ O.owner.apply_damage(1, TOX, O.organ_tag) if(world.time > last_itch + 30 SECONDS) last_itch = world.time - O.owner << "Your [O.name] itches..." + to_chat(O.owner, "Your [O.name] itches...") else if(prob(1)) src.visible_message("\The [src] skitters.") @@ -274,6 +286,9 @@ grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/hunter) +/obj/effect/spider/spiderling/non_growing + amount_grown = -1 + /obj/effect/decal/cleanable/spiderling_remains name = "spiderling remains" desc = "Green squishy mess." diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm index 870656d92e..a3c3c242c2 100644 --- a/code/game/objects/effects/step_triggers.dm +++ b/code/game/objects/effects/step_triggers.dm @@ -12,13 +12,9 @@ /obj/effect/step_trigger/proc/Trigger(var/atom/movable/A) return 0 -/obj/effect/step_trigger/Crossed(H as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = H - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/effect/step_trigger/Crossed(atom/movable/H as mob|obj) + if(H.is_incorporeal()) + return ..() if(!H) return diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 04046985c6..eb2aefeb5c 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -24,7 +24,54 @@ icon_state = "smoke" duration = 50 -// VOREStation Add - Used by Kinetic Accelerator +/obj/effect/temp_visual/impact_effect + icon_state = "impact_bullet" + plane = PLANE_LIGHTING_ABOVE // So they're visible even in a shootout in maint. + duration = 5 + +/obj/effect/temp_visual/impact_effect/Initialize(mapload, obj/item/projectile/P, x, y) + pixel_x = x + pixel_y = y + return ..() + +/obj/effect/temp_visual/impact_effect/red_laser + icon_state = "impact_laser" + duration = 4 + +/obj/effect/temp_visual/impact_effect/red_laser/wall + icon_state = "impact_laser_wall" + duration = 10 + +/obj/effect/temp_visual/impact_effect/blue_laser + icon_state = "impact_laser_blue" + duration = 4 + +/obj/effect/temp_visual/impact_effect/green_laser + icon_state = "impact_laser_green" + duration = 4 + +/obj/effect/temp_visual/impact_effect/purple_laser + icon_state = "impact_laser_purple" + duration = 4 + +// Colors itself based on the projectile. +// Checks light_color and color. +/obj/effect/temp_visual/impact_effect/monochrome_laser + icon_state = "impact_laser_monochrome" + duration = 4 + +/obj/effect/temp_visual/impact_effect/monochrome_laser/Initialize(mapload, obj/item/projectile/P, x, y) + if(P.light_color) + color = P.light_color + else if(P.color) + color = P.color + return ..() + +/obj/effect/temp_visual/impact_effect/ion + icon_state = "shieldsparkles" + duration = 6 + +// VOREStation Add - Kinetic Accelerator/Medigun /obj/effect/temp_visual/kinetic_blast name = "kinetic explosion" icon = 'icons/obj/projectiles.dmi' @@ -43,9 +90,7 @@ /obj/effect/temp_visual/explosion/fast icon_state = "explosionfast" duration = 4 -// VOREStation Add End -//VOREStation edit: medigun /obj/effect/temp_visual/heal name = "healing glow" icon_state = "heal" @@ -54,4 +99,4 @@ /obj/effect/temp_visual/heal/Initialize(mapload) pixel_x = rand(-12, 12) pixel_y = rand(-9, 0) -//VOREStation edit ends \ No newline at end of file +// VOREStation Add End diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index bd1d9cbd12..438bb1188d 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -94,7 +94,7 @@ proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impa var/took = (world.timeofday-start)/10 //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare - if(Debug2) world.log << "## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds." + if(Debug2) to_world_log("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") //Machines which report explosions. for(var/i,i<=doppler_arrays.len,i++) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index b651e2bfe2..f6fb3c94cf 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -4,6 +4,7 @@ w_class = ITEMSIZE_NORMAL var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite + var/randpixel = 6 var/abstract = 0 var/r_speed = 1.0 var/health = null @@ -91,6 +92,8 @@ var/icon/default_worn_icon //Default on-mob icon var/worn_layer //Default on-mob layer + var/drop_sound = 'sound/items/drop/device.ogg' // drop sound - this is the default + /obj/item/New() ..() if(embed_chance < 0) @@ -220,10 +223,10 @@ if (user.hand) temp = H.organs_by_name["l_hand"] if(temp && !temp.is_usable()) - user << "You try to move your [temp.name], but cannot!" + to_chat(user, "You try to move your [temp.name], but cannot!") return if(!temp) - user << "You try to use your hand, but realize it is no longer attached!" + to_chat(user, "You try to use your hand, but realize it is no longer attached!") return var/old_loc = src.loc @@ -273,6 +276,11 @@ /obj/item/proc/moved(mob/user as mob, old_loc as turf) return +/obj/item/throw_impact(atom/hit_atom) + ..() + if(drop_sound) + playsound(src, drop_sound, 50, 0, preference = /datum/client_preference/drop_sounds) + // apparently called whenever an item is removed from a slot, container, or anything else. /obj/item/proc/dropped(mob/user as mob) ..() @@ -282,6 +290,8 @@ // called just as an item is picked up (loc is not yet changed) /obj/item/proc/pickup(mob/user) + pixel_x = 0 + pixel_y = 0 return // called when this item is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called. @@ -369,12 +379,12 @@ var/list/global/slot_flags_enumeration = list( if(slot_wear_id) if(!H.w_uniform && (slot_w_uniform in mob_equip)) if(!disable_warning) - H << "You need a jumpsuit before you can attach this [name]." + to_chat(H, "You need a jumpsuit before you can attach this [name].") return 0 if(slot_l_store, slot_r_store) if(!H.w_uniform && (slot_w_uniform in mob_equip)) if(!disable_warning) - H << "You need a jumpsuit before you can attach this [name]." + to_chat(H, "You need a jumpsuit before you can attach this [name].") return 0 if(slot_flags & SLOT_DENYPOCKET) return 0 @@ -383,11 +393,11 @@ var/list/global/slot_flags_enumeration = list( if(slot_s_store) if(!H.wear_suit && (slot_wear_suit in mob_equip)) if(!disable_warning) - H << "You need a suit before you can attach this [name]." + to_chat(H, "You need a suit before you can attach this [name].") return 0 if(!H.wear_suit.allowed) if(!disable_warning) - usr << "You somehow have a suit with no defined allowed items for suit storage, stop that." + to_chat(usr, "You somehow have a suit with no defined allowed items for suit storage, stop that.") return 0 if( !(istype(src, /obj/item/device/pda) || istype(src, /obj/item/weapon/pen) || is_type_in_list(src, H.wear_suit.allowed)) ) return 0 @@ -413,7 +423,7 @@ var/list/global/slot_flags_enumeration = list( break if(!allow) if(!disable_warning) - H << "You're not wearing anything you can attach this [name] to." + to_chat(H, "You're not wearing anything you can attach this [name] to.") return 0 return 1 @@ -437,20 +447,20 @@ var/list/global/slot_flags_enumeration = list( if(!usr.canmove || usr.stat || usr.restrained() || !Adjacent(usr)) return if((!istype(usr, /mob/living/carbon)) || (istype(usr, /mob/living/carbon/brain)))//Is humanoid, and is not a brain - usr << "You can't pick things up!" + to_chat(usr, "You can't pick things up!") return var/mob/living/carbon/C = usr if( usr.stat || usr.restrained() )//Is not asleep/dead and is not restrained - usr << "You can't pick things up!" + to_chat(usr, "You can't pick things up!") return if(src.anchored) //Object isn't anchored - usr << "You can't pick that up!" + to_chat(usr, "You can't pick that up!") return if(C.get_active_hand()) //Hand is not full - usr << "Your hand is full." + to_chat(usr, "Your hand is full.") return if(!istype(src.loc, /turf)) //Object is on a turf - usr << "You can't pick that up!" + to_chat(usr, "You can't pick that up!") return //All checks are done, time to pick it up! usr.UnarmedAttack(src) @@ -485,11 +495,11 @@ var/list/global/slot_flags_enumeration = list( for(var/obj/item/protection in list(H.head, H.wear_mask, H.glasses)) if(protection && (protection.body_parts_covered & EYES)) // you can't stab someone in the eyes wearing a mask! - user << "You're going to need to remove the eye covering first." + to_chat(user, "You're going to need to remove the eye covering first.") return if(!M.has_eyes()) - user << "You cannot locate any eyes on [M]!" + to_chat(user, "You cannot locate any eyes on [M]!") return //this should absolutely trigger even if not aim-impaired in some way @@ -511,7 +521,7 @@ var/list/global/slot_flags_enumeration = list( //if((CLUMSY in user.mutations) && prob(50)) // M = user /* - M << "You stab yourself in the eye." + to_chat(M, "You stab yourself in the eye.") M.sdisabilities |= BLIND M.weakened += 4 M.adjustBruteLoss(10) @@ -524,8 +534,8 @@ var/list/global/slot_flags_enumeration = list( if(H != user) for(var/mob/O in (viewers(M) - user - M)) O.show_message("[M] has been stabbed in the eye with [src] by [user].", 1) - M << "[user] stabs you in the eye with [src]!" - user << "You stab [M] in the eye with [src]!" + to_chat(M, "[user] stabs you in the eye with [src]!") + to_chat(user, "You stab [M] in the eye with [src]!") else user.visible_message( \ "[user] has stabbed themself with [src]!", \ @@ -536,17 +546,17 @@ var/list/global/slot_flags_enumeration = list( if(eyes.damage >= eyes.min_bruised_damage) if(M.stat != 2) if(!(eyes.robotic >= ORGAN_ROBOT)) //robot eyes bleeding might be a bit silly - M << "Your eyes start to bleed profusely!" + to_chat(M, "Your eyes start to bleed profusely!") if(prob(50)) if(M.stat != 2) - M << "You drop what you're holding and clutch at your eyes!" + to_chat(M, "You drop what you're holding and clutch at your eyes!") M.drop_item() M.eye_blurry += 10 M.Paralyse(1) M.Weaken(4) if (eyes.damage >= eyes.min_broken_damage) if(M.stat != 2) - M << "You go blind!" + to_chat(M, "You go blind!") var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) if(affecting.take_damage(7)) M:UpdateDamageIcon() @@ -624,6 +634,8 @@ modules/mob/mob_movement.dm if you move you will be zoomed out modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. */ //Looking through a scope or binoculars should /not/ improve your periphereal vision. Still, increase viewsize a tiny bit so that sniping isn't as restricted to NSEW +/obj/item/var/ignore_visor_zoom_restriction = FALSE + /obj/item/proc/zoom(var/tileoffset = 14,var/viewsize = 9) //tileoffset is client view offset in the direction the user is facing. viewsize is how far out this thing zooms. 7 is normal view var/devicename @@ -636,13 +648,13 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. var/cannotzoom if((usr.stat && !zoom) || !(istype(usr,/mob/living/carbon/human))) - usr << "You are unable to focus through the [devicename]" + to_chat(usr, "You are unable to focus through the [devicename]") cannotzoom = 1 else if(!zoom && global_hud.darkMask[1] in usr.client.screen) - usr << "Your visor gets in the way of looking through the [devicename]" + to_chat(usr, "Your visor gets in the way of looking through the [devicename]") cannotzoom = 1 else if(!zoom && usr.get_active_hand() != src) - usr << "You are too distracted to look through the [devicename], perhaps if it was in your active hand this might work better" + to_chat(usr, "You are too distracted to look through the [devicename], perhaps if it was in your active hand this might work better") cannotzoom = 1 //We checked above if they are a human and returned already if they weren't. @@ -651,7 +663,7 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. if(!zoom && !cannotzoom) if(H.hud_used.hud_shown) H.toggle_zoom_hud() // If the user has already limited their HUD this avoids them having a HUD when they zoom in - H.client.view = viewsize + H.set_viewsize(viewsize) zoom = 1 var/tilesize = 32 @@ -672,11 +684,12 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. H.client.pixel_y = 0 H.visible_message("[usr] peers through the [zoomdevicename ? "[zoomdevicename] of the [src.name]" : "[src.name]"].") - H.looking_elsewhere = TRUE + if(!ignore_visor_zoom_restriction) + H.looking_elsewhere = TRUE H.handle_vision() else - H.client.view = world.view + H.set_viewsize() // Reset to default if(!H.hud_used.hud_shown) H.toggle_zoom_hud() zoom = 0 @@ -711,6 +724,16 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/proc/in_inactive_hand(mob/user) return +//Used for selecting a random pixel placement, usually on initialize. Checks for pixel_x/y to not interfere with mapped in items. +/obj/item/proc/randpixel_xy() + if(!pixel_x && !pixel_y) + pixel_x = rand(-randpixel, randpixel) + pixel_y = rand(-randpixel, randpixel) + return TRUE + else + return FALSE + + // My best guess as to why this is here would be that it does so little. Still, keep it under all the procs, for sanity's sake. /obj/item/device icon = 'icons/obj/device.dmi' diff --git a/code/game/objects/items/antag_spawners.dm b/code/game/objects/items/antag_spawners.dm index 0d8e18373a..afa7ae66f9 100644 --- a/code/game/objects/items/antag_spawners.dm +++ b/code/game/objects/items/antag_spawners.dm @@ -49,7 +49,7 @@ ghost_query_type = /datum/ghost_query/apprentice /obj/item/weapon/antag_spawner/technomancer_apprentice/attack_self(mob/user) - user << "Teleporter attempting to lock on to your apprentice." + to_chat(user, "Teleporter attempting to lock on to your apprentice.") request_player() /obj/item/weapon/antag_spawner/technomancer_apprentice/request_player() @@ -68,10 +68,10 @@ C.prefs.copy_to(H) H.key = C.key - H << "You are the Technomancer's apprentice! Your goal is to assist them in their mission at the [station_name()]." - H << "Your service has not gone unrewarded, however. Studying under them, you have learned how to use a Manipulation Core \ - of your own. You also have a catalog, to purchase your own functions and equipment as you see fit." - H << "It would be wise to speak to your master, and learn what their plans are for today." + to_chat(H, "You are the Technomancer's apprentice! Your goal is to assist them in their mission at the [station_name()].") + to_chat(H, "Your service has not gone unrewarded, however. Studying under them, you have learned how to use a Manipulation Core \ + of your own. You also have a catalog, to purchase your own functions and equipment as you see fit.") + to_chat(H, "It would be wise to speak to your master, and learn what their plans are for today.") spawn(1) technomancers.add_antagonist(H.mind, 0, 1, 0, 0, 0) diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index 5a326aa482..d6825967c2 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -48,7 +48,7 @@ /obj/item/blueprints/attack_self(mob/M as mob) if (!istype(M,/mob/living/carbon/human)) - M << "This stack of blue paper means nothing to you." //monkeys cannot into projecting + to_chat(M, "This stack of blue paper means nothing to you.") //monkeys cannot into projecting return interact() return @@ -62,19 +62,19 @@ switch(href_list["action"]) if ("create_area") if (!(get_area_type() & can_create_areas_in)) - usr << "You can't make a new area here." + to_chat(usr, "You can't make a new area here.") interact() return create_area() if ("edit_area") if (!(get_area_type() & can_rename_areas_in)) - usr << "You can't rename this area." + to_chat(usr, "You can't rename this area.") interact() return edit_area() if ("expand_area") if (!(get_area_type() & can_expand_areas_in)) - usr << "You can't expand this area." + to_chat(usr, "You can't expand this area.") interact() return expand_area() @@ -132,20 +132,20 @@ if(!istype(res,/list)) switch(res) if(ROOM_ERR_SPACE) - usr << "The new area must be completely airtight!" + to_chat(usr, "The new area must be completely airtight!") return if(ROOM_ERR_TOOLARGE) - usr << "The new area too large!" + to_chat(usr, "The new area too large!") return else - usr << "Error! Please notify administration!" + to_chat(usr, "Error! Please notify administration!") return var/list/turf/turfs = res var/str = sanitizeSafe(input("New area name:","Blueprint Editing", ""), MAX_NAME_LEN) if(!str || !length(str)) //cancel return if(length(str) > 50) - usr << "Name too long." + to_chat(usr, "Name too long.") return var/area/A = new A.name = str @@ -170,13 +170,13 @@ if(!istype(res,/list)) switch(res) if(ROOM_ERR_SPACE) - usr << "The new area must be completely airtight!" + to_chat(usr, "The new area must be completely airtight!") return if(ROOM_ERR_TOOLARGE) - usr << "The new area too large!" + to_chat(usr, "The new area too large!") return else - usr << "Error! Please notify administration!" + to_chat(usr, "Error! Please notify administration!") return var/list/turf/turfs = res @@ -184,11 +184,11 @@ for(var/turf/T in A.contents) turfs -= T // Don't add turfs already in A to A if(turfs.len == 0) - usr << "\The [A] already covers the entire room." + to_chat(usr, "\The [A] already covers the entire room.") return move_turfs_to_area(turfs, A) - usr << "Expanded \the [A] by [turfs.len] turfs" + to_chat(usr, "Expanded \the [A] by [turfs.len] turfs") spawn(5) interact() return @@ -206,11 +206,11 @@ if(!str || !length(str) || str==prevname) //cancel return if(length(str) > 50) - usr << "Text too long." + to_chat(usr, "Text too long.") return set_area_machinery_title(A,str,prevname) A.name = str - usr << "You set the area '[prevname]' title to '[str]'." + to_chat(usr, "You set the area '[prevname]' title to '[str]'.") interact() return @@ -286,13 +286,13 @@ // Remove any existing seeAreaColors_remove() - usr << "\The [src] shows nearby areas in different colors." + to_chat(usr, "\The [src] shows nearby areas in different colors.") var/i = 0 for(var/area/A in range(usr)) if(get_area_type(A) == AREA_SPACE) continue // Don't overlay all of space! var/icon/areaColor = new('icons/misc/debug_rebuild.dmi', "[++i]") - usr << "- [A] as [i]" + to_chat(usr, "- [A] as [i]") for(var/turf/T in A.contents) usr << image(areaColor, T, "blueprints", TURF_LAYER) areaColor_turfs += T @@ -308,13 +308,13 @@ if(!istype(res, /list)) switch(res) if(ROOM_ERR_SPACE) - usr << "The new area must be completely airtight!" + to_chat(usr, "The new area must be completely airtight!") return if(ROOM_ERR_TOOLARGE) - usr << "The new area too large!" + to_chat(usr, "The new area too large!") return else - usr << "Error! Please notify administration!" + to_chat(usr, "Error! Please notify administration!") return // Okay we got a room, lets color it seeAreaColors_remove() @@ -322,7 +322,7 @@ for(var/turf/T in res) usr << image(green, T, "blueprints", TURF_LAYER) areaColor_turfs += T - usr << "The space covered by the new area is highlighted in green." + to_chat(usr, "The space covered by the new area is highlighted in green.") /obj/item/blueprints/verb/seeAreaColors_remove() set src in usr diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index 59504b35c5..1105cf8388 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -177,6 +177,15 @@ QDEL_NULL(tank) return ..() +/obj/structure/closet/body_bag/cryobag/attack_hand(mob/living/user) + if(used) + var/confirm = alert(user, "Are you sure you want to open \the [src]? \ + \The [src] will expire upon opening it.", "Confirm Opening", "No", "Yes") + if(confirm == "Yes") + ..() // Will call `toggle()` and open the bag. + else + ..() + /obj/structure/closet/body_bag/cryobag/open() . = ..() if(used) diff --git a/code/game/objects/items/contraband_vr.dm b/code/game/objects/items/contraband_vr.dm index 399be72e30..a7b0c48aa9 100644 --- a/code/game/objects/items/contraband_vr.dm +++ b/code/game/objects/items/contraband_vr.dm @@ -12,13 +12,13 @@ switch(spawn_chance) if(0 to 49) new /obj/random/gun/guarenteed(usr.loc) - usr << "You got a thing!" + to_chat(usr, "You got a thing!") if(50 to 99) new /obj/item/weapon/bikehorn/rubberducky(usr.loc) new /obj/item/weapon/bikehorn(usr.loc) - usr << "You got two things!" + to_chat(usr, "You got two things!") if(100) - usr << "The box contained nothing!" + to_chat(usr, "The box contained nothing!") return */ var/loot = pick(/obj/effect/landmark/costume, @@ -99,3 +99,13 @@ storage_slots = 7 can_hold = list(/obj/item/clothing/mask/smokable/cigarette/cigar/havana) icon_type = "cigar" + +/obj/item/weapon/miscdisc + name = "wah thing" + desc = "wahwahwah wahwahwa" + icon_state = "wahdisc" + icon = 'icons/obj/contraband_vr.dmi' + w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/miscdisc/attack_self(mob/living/user as mob) + to_chat(user, "LORE.") \ No newline at end of file diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 1e31744e26..45ad1fa5b8 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -95,6 +95,12 @@ if(instant || do_after(user, 50)) new /obj/effect/decal/cleanable/crayon(target,colour,shadeColour,drawtype) to_chat(user, "You finish drawing.") + + if(config.log_graffiti) + var/msg = "[user.client.key] ([user]) has drawn [drawtype] (with [src]) at [target.x],[target.y],[target.z]." + message_admins(msg) + log_game(msg) + target.add_fingerprint(user) // Adds their fingerprints to the floor the crayon is drawn on. if(uses) uses-- @@ -105,13 +111,13 @@ /obj/item/weapon/pen/crayon/attack(mob/M as mob, mob/user as mob) if(M == user) - user << "You take a bite of the crayon and swallow it." + to_chat(user, "You take a bite of the crayon and swallow it.") user.nutrition += 1 user.reagents.add_reagent("crayon_dust",min(5,uses)/3) if(uses) uses -= 5 if(uses <= 0) - to_chat(user,"You ate your crayon!") + to_chat(user, "You ate your crayon!") qdel(src) else ..() @@ -197,7 +203,10 @@ if(uses) uses -= 5 if(uses <= 0) - to_chat(user,"You ate the marker!") + to_chat(user, "You ate the marker!") qdel(src) else - ..() \ No newline at end of file + ..() + +/obj/item/weapon/pen/crayon/attack_self(var/mob/user) + return \ No newline at end of file diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 6f44d6446d..9488be9e82 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -48,6 +48,7 @@ var/global/list/obj/item/device/pda/PDAs = list() var/list/conversations = list() // For keeping up with who we have PDA messsages from. var/new_message = 0 //To remove hackish overlay check var/new_news = 0 + var/touch_silent = 0 //If 1, no beeps on interacting. var/active_feed // The selected feed var/list/warrant // The warrant as we last knew it @@ -64,6 +65,8 @@ var/global/list/obj/item/device/pda/PDAs = list() var/obj/item/device/paicard/pai = null // A slot for a personal AI device + var/spam_proof = FALSE // If true, it can't be spammed by random events. + /obj/item/device/pda/examine(mob/user) if(..(user, 1)) to_chat(user, "The time [stationtime2text()] is displayed in the corner of the screen.") @@ -71,7 +74,7 @@ var/global/list/obj/item/device/pda/PDAs = list() /obj/item/device/pda/CtrlClick() if(issilicon(usr)) return - + if(can_use(usr)) remove_pen() return @@ -87,6 +90,11 @@ var/global/list/obj/item/device/pda/PDAs = list() else to_chat(usr, "This PDA does not have an ID in it.") +//Bloop when using: +/obj/item/device/pda/CouldUseTopic(var/mob/user) + ..() + if(iscarbon(user) && !touch_silent) + playsound(src, 'sound/machines/pda_click.ogg', 20) /obj/item/device/pda/medical default_cartridge = /obj/item/weapon/cartridge/medical @@ -328,6 +336,8 @@ var/global/list/obj/item/device/pda/PDAs = list() /obj/item/device/pda/ai/pai ttone = "assist" +/obj/item/device/pda/ai/shell + spam_proof = TRUE // Since empty shells get a functional PDA. // Used for the PDA multicaster, which mirrors messages sent to it to a specific department, /obj/item/device/pda/multicaster @@ -336,6 +346,7 @@ var/global/list/obj/item/device/pda/PDAs = list() ttone = "data" detonate = 0 news_silent = 1 + spam_proof = TRUE // Spam messages don't actually work and its difficult to disable these. var/list/cartridges_to_send_to = list() // This is what actually mirrors the message, @@ -439,6 +450,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if(2) icon = 'icons/obj/pda_slim.dmi' if(3) icon = 'icons/obj/pda_old.dmi' if(4) icon = 'icons/obj/pda_rugged.dmi' + if(5) icon = 'icons/obj/pda_holo.dmi' else icon = 'icons/obj/pda_old.dmi' log_debug("Invalid switch for PDA, defaulting to old PDA icons. [pdachoice] chosen.") @@ -498,6 +510,7 @@ var/global/list/obj/item/device/pda/PDAs = list() data["note"] = note // current pda notes data["message_silent"] = message_silent // does the pda make noise when it receives a message? data["news_silent"] = news_silent // does the pda make noise when it receives news? + data["touch_silent"] = touch_silent // does the pda make noise when it receives news? data["toff"] = toff // is the messenger function turned off? data["active_conversation"] = active_conversation // Which conversation are we following right now? @@ -647,8 +660,9 @@ var/global/list/obj/item/device/pda/PDAs = list() // auto update every Master Controller tick ui.set_auto_update(auto_update) -//NOTE: graphic resources are loaded on client login /obj/item/device/pda/attack_self(mob/user as mob) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/pda) + assets.send(user) user.set_machine(src) @@ -766,6 +780,8 @@ var/global/list/obj/item/device/pda/PDAs = list() scanmode = 0 else if((!isnull(cartridge)) && (cartridge.access_atmos)) scanmode = 5 + if("Toggle Beeping") + touch_silent = !touch_silent //MESSENGER/NOTE FUNCTIONS=================================== @@ -1042,6 +1058,7 @@ var/global/list/obj/item/device/pda/PDAs = list() var/mob/M = loc M.put_in_hands(id) to_chat(usr, "You remove the ID from the [name].") + playsound(loc, 'sound/machines/id_swipe.ogg', 100, 1) else id.loc = get_turf(src) id = null @@ -1130,7 +1147,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if (!beep_silent) playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src] *[message_tone]*")) + O.show_message(text("[bicon(src)] *[message_tone]*")) //Search for holder of the PDA. var/mob/living/L = null if(loc && isliving(loc)) @@ -1141,11 +1158,11 @@ var/global/list/obj/item/device/pda/PDAs = list() if(L) if(reception_message) - L << reception_message + to_chat(L,reception_message) SSnanoui.update_user_uis(L, src) // Update the receiving user's PDA UI so that they can see the new message /obj/item/device/pda/proc/new_news(var/message) - new_info(news_silent, newstone, news_silent ? "" : "\icon[src] [message]") + new_info(news_silent, newstone, news_silent ? "" : "[bicon(src)] [message]") if(!news_silent) new_news = 1 @@ -1160,7 +1177,7 @@ var/global/list/obj/item/device/pda/PDAs = list() new_message(sending_device, sending_device.owner, sending_device.ownjob, message) /obj/item/device/pda/proc/new_message(var/sending_unit, var/sender, var/sender_job, var/message, var/reply = 1) - var/reception_message = "\icon[src] Message from [sender] ([sender_job]), \"[message]\" ([reply ? "Reply" : "Unable to Reply"])" + var/reception_message = "[bicon(src)] Message from [sender] ([sender_job]), \"[message]\" ([reply ? "Reply" : "Unable to Reply"])" new_info(message_silent, ttone, reception_message) log_pda("(PDA: [sending_unit]) sent \"[message]\" to [name]", usr) @@ -1172,12 +1189,21 @@ var/global/list/obj/item/device/pda/PDAs = list() if(ismob(sending_unit.loc) && isAI(loc)) track = "(Follow)" - var/reception_message = "\icon[src] Message from [sender] ([sender_job]), \"[message]\" (Reply) [track]" + var/reception_message = "[bicon(src)] Message from [sender] ([sender_job]), \"[message]\" (Reply) [track]" new_info(message_silent, newstone, reception_message) log_pda("(PDA: [sending_unit]) sent \"[message]\" to [name]",usr) new_message = 1 +/obj/item/device/pda/proc/spam_message(sender, message) + var/reception_message = "\icon[src] Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)" + new_info(message_silent, ttone, reception_message) + + if(prob(50)) // Give the AI an increased chance to intercept the message + for(var/mob/living/silicon/ai/ai in mob_list) + if(ai.aiPDA != src) + ai.show_message("Intercepted message from [sender] (Unknown / spam?) to [owner]: [message]") + /obj/item/device/pda/verb/verb_reset_pda() set category = "Object" set name = "Reset PDA" @@ -1248,6 +1274,7 @@ var/global/list/obj/item/device/pda/PDAs = list() if (cartridge.radio) cartridge.radio.hostpda = null to_chat(usr, "You remove \the [cartridge] from the [name].") + playsound(loc, 'sound/machines/id_swipe.ogg', 100, 1) cartridge = null /obj/item/device/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use. @@ -1384,11 +1411,11 @@ var/global/list/obj/item/device/pda/PDAs = list() var/reagents_length = A.reagents.reagent_list.len to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.") for (var/re in A.reagents.reagent_list) - to_chat(user," [re]") + to_chat(user, " [re]") else - to_chat(user,"No active chemical agents found in [A].") + to_chat(user, "No active chemical agents found in [A].") else - to_chat(user,"No significantchemical agents found in [A].") + to_chat(user, "No significantchemical agents found in [A].") if(5) analyze_gases(A, user) @@ -1440,7 +1467,7 @@ var/global/list/obj/item/device/pda/PDAs = list() // feature to the PDA, which would better convey the availability of the feature, but this will work for now. // Inform the user - to_chat(user,"Paper scanned and OCRed to notekeeper.") //concept of scanning paper copyright brainoblivion 2009 + to_chat(user, "Paper scanned and OCRed to notekeeper.") //concept of scanning paper copyright brainoblivion 2009 /obj/item/device/pda/proc/explode() //This needs tuning. //Sure did. @@ -1453,7 +1480,7 @@ var/global/list/obj/item/device/pda/PDAs = list() /obj/item/device/pda/Destroy() PDAs -= src - if (src.id && prob(100)) //IDs are kept in 90% of the cases //VOREStation Edit - 100% of the cases + if (src.id && prob(100) && !delete_id) //IDs are kept in 90% of the cases //VOREStation Edit - 100% of the cases, excpet when specified otherwise src.id.forceMove(get_turf(src.loc)) else QDEL_NULL(src.id) @@ -1461,13 +1488,9 @@ var/global/list/obj/item/device/pda/PDAs = list() QDEL_NULL(src.pai) return ..() -/obj/item/device/pda/clown/Crossed(AM as mob|obj) //Clown PDA is slippery. - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/item/device/pda/clown/Crossed(atom/movable/AM as mob|obj) //Clown PDA is slippery. + if(AM.is_incorporeal()) + return if (istype(AM, /mob/living)) var/mob/living/M = AM diff --git a/code/game/objects/items/devices/PDA/PDA_vr.dm b/code/game/objects/items/devices/PDA/PDA_vr.dm index 20558c965c..6a6da399eb 100644 --- a/code/game/objects/items/devices/PDA/PDA_vr.dm +++ b/code/game/objects/items/devices/PDA/PDA_vr.dm @@ -1,3 +1,6 @@ +/obj/item/device/pda + var/delete_id = FALSE //Guaranteed deletion of ID upon deletion of PDA + /obj/item/device/pda/centcom default_cartridge = /obj/item/weapon/cartridge/captain icon_state = "pda-h" diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index e34d1180c9..ad4e1d7f3e 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -256,7 +256,7 @@ var/list/civilian_cartridges = list( var/datum/signal/status_signal = new status_signal.source = src - status_signal.transmission_method = 1 + status_signal.transmission_method = TRANSMISSION_RADIO status_signal.data["command"] = command switch(command) @@ -309,8 +309,12 @@ var/list/civilian_cartridges = list( if(mode==43 || mode==433) var/list/sensors = list() var/obj/machinery/power/sensor/MS = null + var/my_z = get_z(user) + var/list/levels = using_map.get_map_levels(my_z) for(var/obj/machinery/power/sensor/S in machines) + if(!(get_z(S) in levels)) + continue sensors.Add(list(list("name_tag" = S.name_tag))) if(S.name_tag == selected_sensor) MS = S @@ -425,14 +429,14 @@ var/list/civilian_cartridges = list( if(mode==47) var/supplyData[0] - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle if (shuttle) supplyData["shuttle_moving"] = shuttle.has_arrive_time() supplyData["shuttle_eta"] = shuttle.eta_minutes() supplyData["shuttle_loc"] = shuttle.at_station() ? "Station" : "Dock" var/supplyOrderCount = 0 var/supplyOrderData[0] - for(var/S in supply_controller.shoppinglist) + for(var/S in SSsupply.shoppinglist) var/datum/supply_order/SO = S supplyOrderData[++supplyOrderData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "ApprovedBy" = SO.ordered_by, "Comment" = html_encode(SO.comment)) @@ -444,7 +448,7 @@ var/list/civilian_cartridges = list( var/requestCount = 0 var/requestData[0] - for(var/S in supply_controller.order_history) + for(var/S in SSsupply.order_history) var/datum/supply_order/SO = S if(SO.status != SUP_ORDER_REQUESTED) continue diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm index a5a76dd993..133798e4b1 100644 --- a/code/game/objects/items/devices/PDA/radio.dm +++ b/code/game/objects/items/devices/PDA/radio.dm @@ -15,14 +15,14 @@ proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3, s_filter) - //world << "Post: [freq]: [key]=[value], [key2]=[value2]" + //to_world("Post: [freq]: [key]=[value], [key2]=[value2]") var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) if(!frequency) return var/datum/signal/signal = new() signal.source = src - signal.transmission_method = 1 + signal.transmission_method = TRANSMISSION_RADIO signal.data[key] = value if(key2) signal.data[key2] = value2 @@ -57,9 +57,9 @@ // var/obj/item/device/pda/P = src.loc /* - world << "recvd:[P] : [signal.source]" + to_world("recvd:[P] : [signal.source]") for(var/d in signal.data) - world << "- [d] = [signal.data[d]]" + to_world("- [d] = [signal.data[d]]") */ if (signal.data["type"] == "secbot") if(!botlist) diff --git a/code/game/objects/items/devices/advnifrepair.dm b/code/game/objects/items/devices/advnifrepair.dm index 8a7c0b1da4..ab71b97324 100644 --- a/code/game/objects/items/devices/advnifrepair.dm +++ b/code/game/objects/items/devices/advnifrepair.dm @@ -25,11 +25,11 @@ if(istype(W,/obj/item/stack/nanopaste)) var/obj/item/stack/nanopaste/np = W if(np.use(1) && supply.get_free_space() >= efficiency) - to_chat(user,"You convert some nanopaste into programmed nanites inside \the [src].") + to_chat(user, "You convert some nanopaste into programmed nanites inside \the [src].") supply.add_reagent(id = "nifrepairnanites", amount = efficiency) update_icon() else if(supply.get_free_space() < efficiency) - to_chat(user,"\The [src] is too full. Empty it into a container first.") + to_chat(user, "\The [src] is too full. Empty it into a container first.") return /obj/item/device/nifrepairer/update_icon() @@ -43,21 +43,21 @@ return 0 if(!supply || !supply.total_volume) - to_chat(user,"[src] is empty. Feed it nanopaste.") + to_chat(user, "[src] is empty. Feed it nanopaste.") return 1 if(!target.reagents.get_free_space()) - user << "[target] is already full." + to_chat(user, "[target] is already full.") return 1 var/trans = supply.trans_to(target, 15) - to_chat(user,"You transfer [trans] units of the programmed nanites to [target].") + to_chat(user, "You transfer [trans] units of the programmed nanites to [target].") update_icon() return 1 /obj/item/device/nifrepairer/examine(mob/user) if(..(user, 1)) if(supply.total_volume) - to_chat(user,"\The [src] contains [supply.total_volume] units of programmed nanites, ready for dispensing.") + to_chat(user, "\The [src] contains [supply.total_volume] units of programmed nanites, ready for dispensing.") else - to_chat(user,"\The [src] is empty and ready to accept nanopaste.") + to_chat(user, "\The [src] is empty and ready to accept nanopaste.") diff --git a/code/game/objects/items/devices/ai_detector.dm b/code/game/objects/items/devices/ai_detector.dm index fde112706f..94d3a35912 100644 --- a/code/game/objects/items/devices/ai_detector.dm +++ b/code/game/objects/items/devices/ai_detector.dm @@ -94,22 +94,22 @@ if(new_state != old_state) switch(new_state) if(PROXIMITY_OFF_CAMERANET) - to_chat(carrier, "\icon[src] Now outside of camera network.") + to_chat(carrier, "[bicon(src)] Now outside of camera network.") carrier << 'sound/machines/defib_failed.ogg' if(PROXIMITY_NONE) - to_chat(carrier, "\icon[src] Now within camera network, AI and cameras unfocused.") + to_chat(carrier, "[bicon(src)] Now within camera network, AI and cameras unfocused.") carrier << 'sound/machines/defib_safetyOff.ogg' if(PROXIMITY_NEAR) - to_chat(carrier, "\icon[src] Warning: AI focus at nearby location.") + to_chat(carrier, "[bicon(src)] Warning: AI focus at nearby location.") carrier << 'sound/machines/defib_SafetyOn.ogg' if(PROXIMITY_ON_SCREEN) - to_chat(carrier, "\icon[src] Alert: AI or camera focused at current location!") + to_chat(carrier, "[bicon(src)] Alert: AI or camera focused at current location!") carrier <<'sound/machines/defib_ready.ogg' if(PROXIMITY_TRACKING) - to_chat(carrier, "\icon[src] Danger: AI is actively tracking you!") + to_chat(carrier, "[bicon(src)] Danger: AI is actively tracking you!") carrier << 'sound/machines/defib_success.ogg' if(PROXIMITY_TRACKING_FAIL) - to_chat(carrier, "\icon[src] Danger: AI is attempting to actively track you, but you are outside of the camera network!") + to_chat(carrier, "[bicon(src)] Danger: AI is attempting to actively track you, but you are outside of the camera network!") carrier <<'sound/machines/defib_ready.ogg' diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index cc7ac0e4a4..05260e3d2f 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -19,7 +19,7 @@ return ..() else M.death() - user << "ERROR ERROR ERROR" + to_chat(user, "ERROR ERROR ERROR") /obj/item/device/aicard/attack_self(mob/user) diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index d0ae461edd..627145a38f 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -32,7 +32,7 @@ if(!active_dummy) if(istype(target,/obj/item) && !istype(target, /obj/item/weapon/disk/nuclear)) playsound(get_turf(src), 'sound/weapons/flash.ogg', 100, 1, -6) - user << "Scanned [target]." + to_chat(user, "Scanned [target].") saved_item = target.type saved_icon = target.icon saved_icon_state = target.icon_state @@ -45,7 +45,7 @@ playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6) qdel(active_dummy) active_dummy = null - usr << "You deactivate the [src]." + to_chat(usr, "You deactivate the [src].") var/obj/effect/overlay/T = new /obj/effect/overlay(get_turf(src)) T.icon = 'icons/effects/effects.dmi' flick("emppulse",T) @@ -57,7 +57,7 @@ var/obj/effect/dummy/chameleon/C = new /obj/effect/dummy/chameleon(usr.loc) C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, src) qdel(O) - usr << "You activate the [src]." + to_chat(usr, "You activate the [src].") var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) T.icon = 'icons/effects/effects.dmi' flick("emppulse",T) @@ -104,22 +104,22 @@ /obj/effect/dummy/chameleon/attackby() for(var/mob/M in src) - M << "Your chameleon-projector deactivates." + to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() /obj/effect/dummy/chameleon/attack_hand() for(var/mob/M in src) - M << "Your chameleon-projector deactivates." + to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() /obj/effect/dummy/chameleon/ex_act() for(var/mob/M in src) - M << "Your chameleon-projector deactivates." + to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() /obj/effect/dummy/chameleon/bullet_act() for(var/mob/M in src) - M << "Your chameleon-projector deactivates." + to_chat(M, "Your chameleon-projector deactivates.") ..() master.disrupt() diff --git a/code/game/objects/items/devices/communicator/UI.dm b/code/game/objects/items/devices/communicator/UI.dm index e972cd1210..a4686e39de 100644 --- a/code/game/objects/items/devices/communicator/UI.dm +++ b/code/game/objects/items/devices/communicator/UI.dm @@ -121,7 +121,9 @@ data["flashlight"] = fon data["manifest"] = PDA_Manifest data["feeds"] = compile_news() - //data["latest_news"] = get_recent_news() //VOREStation Edit, bandaid for catastrophic runtime lag in helper.dm + data["latest_news"] = get_recent_news() + if(newsfeed_channel) + data["target_feed"] = data["feeds"][newsfeed_channel] if(cartridge) // If there's a cartridge, we need to grab the information from it data["cart_devices"] = cartridge.get_device_status() data["cart_templates"] = cartridge.ui_templates @@ -280,6 +282,9 @@ var/obj/O = cartridge.internal_devices[text2num(href_list["toggle_device"])] cartridge.active_devices ^= list(O) // Exclusive or, will toggle its presence + if(href_list["newsfeed"]) + newsfeed_channel = text2num(href_list["newsfeed"]) + if(href_list["cartridge_topic"] && cartridge) // Has to have a cartridge to perform these functions cartridge.Topic(href, href_list) diff --git a/code/game/objects/items/devices/communicator/cartridge.dm b/code/game/objects/items/devices/communicator/cartridge.dm index b5a46b4ebe..120df593e9 100644 --- a/code/game/objects/items/devices/communicator/cartridge.dm +++ b/code/game/objects/items/devices/communicator/cartridge.dm @@ -140,7 +140,7 @@ if(!reason) return - supply_controller.create_order(S, user, reason) + SSsupply.create_order(S, user, reason) internal_data["supply_reqtime"] = (world.time + 5) % 1e5 if(href_list["order_ref"]) @@ -189,20 +189,20 @@ O.approved_at = new_val if(href_list["approve"]) - supply_controller.approve_order(O, user) + SSsupply.approve_order(O, user) if(href_list["deny"]) - supply_controller.deny_order(O, user) + SSsupply.deny_order(O, user) if(href_list["delete"]) - supply_controller.delete_order(O, user) + SSsupply.delete_order(O, user) if(href_list["clear_all_requests"]) var/mob/user = locate(href_list["user"]) if(!istype(user)) // Invalid ref return - supply_controller.deny_all_pending(user) + SSsupply.deny_all_pending(user) if(href_list["export_ref"]) var/datum/exported_crate/E = locate(href_list["export_ref"]) @@ -258,29 +258,29 @@ E.value = num else if(href_list["delete"]) - supply_controller.delete_export(E, user) + SSsupply.delete_export(E, user) else if(href_list["add_item"]) - supply_controller.add_export_item(E, user) + SSsupply.add_export_item(E, user) - if(supply_controller && supply_controller.shuttle) + if(SSsupply && SSsupply.shuttle) switch(href_list["send_shuttle"]) if("send_away") - if(supply_controller.shuttle.forbidden_atoms_check()) + if(SSsupply.shuttle.forbidden_atoms_check()) to_chat(usr, "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.") else - supply_controller.shuttle.launch(src) + SSsupply.shuttle.launch(src) to_chat(usr, "Initiating launch sequence.") if("send_to_station") - supply_controller.shuttle.launch(src) - to_chat(usr, "The supply shuttle has been called and will arrive in approximately [round(supply_controller.movetime/600,1)] minutes.") + SSsupply.shuttle.launch(src) + to_chat(usr, "The supply shuttle has been called and will arrive in approximately [round(SSsupply.movetime/600,1)] minutes.") if("cancel_shuttle") - supply_controller.shuttle.cancel_launch(src) + SSsupply.shuttle.cancel_launch(src) if("force_shuttle") - supply_controller.shuttle.force_launch(src) + SSsupply.shuttle.force_launch(src) // Status display switch(href_list["stat_display"]) @@ -329,7 +329,7 @@ var/datum/signal/status_signal = new status_signal.source = src - status_signal.transmission_method = 1 + status_signal.transmission_method = TRANSMISSION_RADIO status_signal.data["command"] = command switch(command) diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm index b9a8eff8a4..fae2d4586f 100644 --- a/code/game/objects/items/devices/communicator/communicator.dm +++ b/code/game/objects/items/devices/communicator/communicator.dm @@ -72,13 +72,14 @@ var/global/list/obj/item/device/communicator/all_communicators = list() var/datum/exonet_protocol/exonet = null var/list/communicating = list() var/update_ticks = 0 + var/newsfeed_channel = 0 // Proc: New() // Parameters: None // Description: Adds the new communicator to the global list of all communicators, sorts the list, obtains a reference to the Exonet node, then tries to // assign the device to the holder's name automatically in a spectacularly shitty way. -/obj/item/device/communicator/New() - ..() +/obj/item/device/communicator/Initialize() + . = ..() all_communicators += src all_communicators = sortAtom(all_communicators) node = get_exonet_node() @@ -86,16 +87,22 @@ var/global/list/obj/item/device/communicator/all_communicators = list() camera = new(src) camera.name = "[src] #[rand(100,999)]" camera.c_tag = camera.name + //This is a pretty terrible way of doing this. - spawn(5 SECONDS) //Wait for our mob to finish spawning. - if(ismob(loc)) - register_device(loc.name) - initialize_exonet(loc) - else if(istype(loc, /obj/item/weapon/storage)) - var/obj/item/weapon/storage/S = loc - if(ismob(S.loc)) - register_device(S.loc.name) - initialize_exonet(S.loc) + addtimer(CALLBACK(src, .proc/register_to_holder), 5 SECONDS) + +// Proc: register_to_holder() +// Parameters: None +// Description: Tries to register ourselves to the mob that we've presumably spawned in. Not the most amazing way of doing this. +/obj/item/device/communicator/proc/register_to_holder() + if(ismob(loc)) + register_device(loc.name) + initialize_exonet(loc) + else if(istype(loc, /obj/item/weapon/storage)) + var/obj/item/weapon/storage/S = loc + if(ismob(S.loc)) + register_device(S.loc.name) + initialize_exonet(S.loc) // Proc: examine() // Parameters: user - the user doing the examining @@ -274,14 +281,13 @@ var/global/list/obj/item/device/communicator/all_communicators = list() // Proc: New() // Parameters: None // Description: Gives ghosts an exonet address based on their key and ghost name. -/mob/observer/dead/New() +/mob/observer/dead/Initialize() . = ..() - spawn(20) - exonet = new(src) - if(client) - exonet.make_address("communicator-[src.client]-[src.client.prefs.real_name]") - else - exonet.make_address("communicator-[key]-[src.real_name]") + exonet = new(src) + if(client) + exonet.make_address("communicator-[src.client]-[src.client.prefs.real_name]") + else + exonet.make_address("communicator-[key]-[src.real_name]") // Proc: Destroy() // Parameters: None @@ -312,7 +318,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list() /obj/item/device/communicator/Destroy() for(var/mob/living/voice/voice in contents) voice_mobs.Remove(voice) - to_chat(voice, "\icon[src] Connection timed out with remote host.") + to_chat(voice, "[bicon(src)] Connection timed out with remote host.") qdel(voice) close_connection(reason = "Connection timed out") diff --git a/code/game/objects/items/devices/communicator/helper.dm b/code/game/objects/items/devices/communicator/helper.dm index 273686fb46..ec67686668 100644 --- a/code/game/objects/items/devices/communicator/helper.dm +++ b/code/game/objects/items/devices/communicator/helper.dm @@ -46,23 +46,24 @@ index++ if(FM.img) usr << browse_rsc(FM.img, "pda_news_tmp_photo_[feeds["channel"]]_[index].png") - // News stories are HTML-stripped but require newline replacement to be properly displayed in NanoUI - var/body = replacetext(FM.body, "\n", "
") - messages[++messages.len] = list( - "author" = FM.author, - "body" = body, - "message_type" = FM.message_type, - "time_stamp" = FM.time_stamp, - "has_image" = (FM.img != null), - "caption" = FM.caption, - "index" = index - ) + // News stories are HTML-stripped but require newline replacement to be properly displayed in NanoUI + var/body = replacetext(FM.body, "\n", "
") + messages[++messages.len] = list( + "author" = FM.author, + "body" = body, + "message_type" = FM.message_type, + "time_stamp" = FM.time_stamp, + "has_image" = (FM.img != null), + "caption" = FM.caption, + "index" = index + ) feeds[++feeds.len] = list( "name" = channel.channel_name, "censored" = channel.censored, "author" = channel.author, - "messages" = messages + "messages" = messages, + "index" = feeds.len + 1 // actually align them, since I guess the population of the list doesn't occur until after the evaluation of the new entry's contents ) return feeds @@ -85,14 +86,14 @@ "time_stamp" = FM.time_stamp, "has_image" = (FM.img != null), "caption" = FM.caption, + "time" = FM.post_time ) // Cut out all but the youngest three - while(news.len > 3) - var/oldest = min(news[0]["time_stamp"], news[1]["time_stamp"], news[2]["time_stamp"], news[3]["time_stamp"]) - for(var/i = 0, i < 4, i++) - if(news[i]["time_stamp"] == oldest) - news.Remove(news[i]) + if(news.len > 3) + sortByKey(news, "time") + news.Cut(1, news.len - 2) // Last three have largest timestamps, youngest posts + news.Swap(1, 3) // List is sorted in ascending order of timestamp, we want descending return news @@ -393,7 +394,7 @@ // code\game\machinery\computer\supply.dm, starting at line 55 /obj/item/weapon/commcard/proc/get_supply_shuttle_status() var/shuttle_status[0] - var/datum/shuttle/ferry/supply/shuttle = supply_controller.shuttle + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle if(shuttle) if(shuttle.has_arrive_time()) shuttle_status["location"] = "In transit" @@ -403,8 +404,8 @@ else shuttle_status["time"] = 0 if(shuttle.at_station()) - if(shuttle.docking_controller) - switch(shuttle.docking_controller.get_docking_status()) + if(shuttle.shuttle_docking_controller) + switch(shuttle.shuttle_docking_controller.get_docking_status()) if("docked") shuttle_status["location"] = "Docked" shuttle_status["mode"] = SUP_SHUTTLE_DOCKED @@ -453,7 +454,7 @@ // code\game\machinery\computer\supply.dm, starting at line 130 /obj/item/weapon/commcard/proc/get_supply_orders() var/orders[0] - for(var/datum/supply_order/S in supply_controller.order_history) + for(var/datum/supply_order/S in SSsupply.order_history) orders[++orders.len] = list( "ref" = "\ref[S]", "status" = S.status, @@ -476,7 +477,7 @@ // code\game\machinery\computer\supply.dm, starting at line 147 /obj/item/weapon/commcard/proc/get_supply_receipts() var/receipts[0] - for(var/datum/exported_crate/E in supply_controller.exported_crates) + for(var/datum/exported_crate/E in SSsupply.exported_crates) receipts[++receipts.len] = list( "ref" = "\ref[E]", "contents" = E.contents, @@ -494,8 +495,8 @@ // code\game\machinery\computer\supply.dm, starting at line 147 /obj/item/weapon/commcard/proc/get_supply_pack_list() var/supply_packs[0] - for(var/pack_name in supply_controller.supply_pack) - var/datum/supply_pack/P = supply_controller.supply_pack[pack_name] + for(var/pack_name in SSsupply.supply_pack) + var/datum/supply_pack/P = SSsupply.supply_pack[pack_name] if(P.group == internal_data["supply_category"]) var/list/pack = list( "name" = P.name, @@ -520,7 +521,7 @@ return list( "shuttle_auth" = (internal_data["supply_controls"] & SUP_SEND_SHUTTLE), "order_auth" = (internal_data["supply_controls"] & SUP_ACCEPT_ORDERS), - "supply_points" = supply_controller.points, + "supply_points" = SSsupply.points, "supply_categories" = all_supply_groups ) diff --git a/code/game/objects/items/devices/communicator/messaging.dm b/code/game/objects/items/devices/communicator/messaging.dm index e50540acb0..57e480957f 100644 --- a/code/game/objects/items/devices/communicator/messaging.dm +++ b/code/game/objects/items/devices/communicator/messaging.dm @@ -34,7 +34,7 @@ if(src in comm.voice_invites) comm.open_connection(src) return - to_chat(src, "\icon[origin_atom] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \ + to_chat(src, "[bicon(origin_atom)] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \ verb, and select that name to answer the call.") src << 'sound/machines/defib_SafetyOn.ogg' comm.voice_invites |= src @@ -44,7 +44,7 @@ random = random / 10 exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms") if(message == "text") - to_chat(src, "\icon[origin_atom] Received text message from [origin_atom]: \"[text]\"") + to_chat(src, "[bicon(origin_atom)] Received text message from [origin_atom]: \"[text]\"") src << 'sound/machines/defib_safetyOff.ogg' exonet_messages.Add("From [origin_atom]:
[text]") return @@ -78,7 +78,7 @@ if(ringer) playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src] *beep*")) + O.show_message(text("[bicon(src)] *beep*")) alert_called = 1 update_icon() @@ -89,7 +89,7 @@ L = loc if(L) - to_chat(L, "\icon[src] Message from [who].") + to_chat(L, "[bicon(src)] Message from [who].") // Verb: text_communicator() // Parameters: None diff --git a/code/game/objects/items/devices/communicator/phone.dm b/code/game/objects/items/devices/communicator/phone.dm index 66a2b49f69..3042b06fa9 100644 --- a/code/game/objects/items/devices/communicator/phone.dm +++ b/code/game/objects/items/devices/communicator/phone.dm @@ -39,15 +39,15 @@ comm.voice_requests.Remove(src) if(user) - comm.visible_message("\icon[src] Connecting to [src].") - to_chat(user, "\icon[src] Attempting to call [comm].") + comm.visible_message("[bicon(src)] Connecting to [src].") + to_chat(user, "[bicon(src)] Attempting to call [comm].") sleep(10) - to_chat(user, "\icon[src] Dialing internally from [station_name()], [system_name()].") + to_chat(user, "[bicon(src)] Dialing internally from [station_name()], [system_name()].") sleep(20) //If they don't have an exonet something is very wrong and we want a runtime. - to_chat(user, "\icon[src] Connection re-routed to [comm] at [comm.exonet.address].") + to_chat(user, "[bicon(src)] Connection re-routed to [comm] at [comm.exonet.address].") sleep(40) - to_chat(user, "\icon[src] Connection to [comm] at [comm.exonet.address] established.") - comm.visible_message("\icon[src] Connection to [src] at [exonet.address] established.") + to_chat(user, "[bicon(src)] Connection to [comm] at [comm.exonet.address] established.") + comm.visible_message("[bicon(src)] Connection to [src] at [exonet.address] established.") sleep(20) src.add_communicating(comm) @@ -86,28 +86,28 @@ //Now for some connection fluff. if(user) - to_chat(user, "\icon[src] Connecting to [candidate].") - to_chat(new_voice, "\icon[src] Attempting to call [src].") + to_chat(user, "[bicon(src)] Connecting to [candidate].") + to_chat(new_voice, "[bicon(src)] Attempting to call [src].") sleep(10) - to_chat(new_voice, "\icon[src] Dialing to [station_name()], Kara Subsystem, [system_name()].") + to_chat(new_voice, "[bicon(src)] Dialing to [station_name()], Kara Subsystem, [system_name()].") sleep(20) - to_chat(new_voice, "\icon[src] Connecting to [station_name()] telecommunications array.") + to_chat(new_voice, "[bicon(src)] Connecting to [station_name()] telecommunications array.") sleep(40) - to_chat(new_voice, "\icon[src] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].") + to_chat(new_voice, "[bicon(src)] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].") sleep(20) //We're connected, no need to hide everything. new_voice.client.screen.Remove(blackness) qdel(blackness) - to_chat(new_voice, "\icon[src] Connection to [src] established.") + to_chat(new_voice, "[bicon(src)] Connection to [src] established.") to_chat(new_voice, "To talk to the person on the other end of the call, just talk normally.") to_chat(new_voice, "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.") to_chat(new_voice, "Remember, your character does not know anything you've learned from observing!") if(new_voice.mind) new_voice.mind.assigned_role = "Disembodied Voice" if(user) - to_chat(user, "\icon[src] Your communicator is now connected to [candidate]'s communicator.") + to_chat(user, "[bicon(src)] Your communicator is now connected to [candidate]'s communicator.") // Proc: close_connection() // Parameters: 3 (user - the user who initiated the disconnect, target - the mob or device being disconnected, reason - string shown when disconnected) @@ -120,8 +120,8 @@ for(var/mob/living/voice/voice in voice_mobs) //Handle ghost-callers if(target && voice != target) //If no target is inputted, it deletes all of them. continue - to_chat(voice, "\icon[src] [reason].") - visible_message("\icon[src] [reason].") + to_chat(voice, "[bicon(src)] [reason].") + visible_message("[bicon(src)] [reason].") voice_mobs.Remove(voice) qdel(voice) update_icon() @@ -131,8 +131,8 @@ continue src.del_communicating(comm) comm.del_communicating(src) - comm.visible_message("\icon[src] [reason].") - visible_message("\icon[src] [reason].") + comm.visible_message("[bicon(src)] [reason].") + visible_message("[bicon(src)] [reason].") if(comm.camera && video_source == comm.camera) //We hung up on the person on video end_video() if(camera && comm.video_source == camera) //We hung up on them while they were watching us @@ -163,7 +163,7 @@ if(ringer) playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src] *beep*")) + O.show_message(text("[bicon(src)] *beep*")) alert_called = 1 update_icon() @@ -174,7 +174,7 @@ L = loc if(L) - to_chat(L, "\icon[src] Communications request from [who].") + to_chat(L, "[bicon(src)] Communications request from [who].") // Proc: del_request() // Parameters: 1 (candidate - the ghost or communicator to be declined) @@ -197,24 +197,24 @@ us = loc if(us) - to_chat(us, "\icon[src] Declined request.") + to_chat(us, "[bicon(src)] Declined request.") // Proc: see_emote() // Parameters: 2 (M - the mob the emote originated from, text - the emote's contents) // Description: Relays the emote to all linked communicators. /obj/item/device/communicator/see_emote(mob/living/M, text) - var/rendered = "\icon[src] [text]" + var/rendered = "[bicon(src)] [text]" for(var/obj/item/device/communicator/comm in communicating) var/turf/T = get_turf(comm) - if(!T) return - //VOREStation Edit Start for commlinks - var/list/mobs_to_relay - if(istype(comm,/obj/item/device/communicator/commlink)) - var/obj/item/device/communicator/commlink/CL = comm - mobs_to_relay = list(CL.nif.human) - else - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display - mobs_to_relay = in_range["mobs"] + if(!T) return + //VOREStation Edit Start for commlinks + var/list/mobs_to_relay + if(istype(comm,/obj/item/device/communicator/commlink)) + var/obj/item/device/communicator/commlink/CL = comm + mobs_to_relay = list(CL.nif.human) + else + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display + mobs_to_relay = in_range["mobs"] //VOREStation Edit End for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other. @@ -235,14 +235,14 @@ var/turf/T = get_turf(comm) if(!T) return - //VOREStation Edit Start for commlinks - var/list/mobs_to_relay - if(istype(comm,/obj/item/device/communicator/commlink)) - var/obj/item/device/communicator/commlink/CL = comm - mobs_to_relay = list(CL.nif.human) - else - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display - mobs_to_relay = in_range["mobs"] + //VOREStation Edit Start for commlinks + var/list/mobs_to_relay + if(istype(comm,/obj/item/device/communicator/commlink)) + var/obj/item/device/communicator/commlink/CL = comm + mobs_to_relay = list(CL.nif.human) + else + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display + mobs_to_relay = in_range["mobs"] //VOREStation Edit End for(var/mob/mob in mobs_to_relay) @@ -255,16 +255,16 @@ var/name_used = M.GetVoice() var/rendered = null if(speaking) //Language being used - rendered = "\icon[src] [name_used] [speaking.format_message(text, verb)]" + rendered = "[bicon(src)] [name_used] [speaking.format_message(text, verb)]" else - rendered = "\icon[src] [name_used] [verb], \"[text]\"" + rendered = "[bicon(src)] [name_used] [verb], \"[text]\"" mob.show_message(rendered, 2) // Proc: show_message() // Parameters: 4 (msg - the message, type - number to determine if message is visible or audible, alt - unknown, alt_type - unknown) // Description: Relays the message to all linked communicators. /obj/item/device/communicator/show_message(msg, type, alt, alt_type) - var/rendered = "\icon[src] [msg]" + var/rendered = "[bicon(src)] [msg]" for(var/obj/item/device/communicator/comm in communicating) var/turf/T = get_turf(comm) if(!T) return @@ -345,14 +345,14 @@ to_chat(user, "You cannot see well enough to do that!") if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something - to_chat(user, "\icon[src]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.") + to_chat(user, "[bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.") - to_chat(user, "\icon[src] Attempting to start video over existing call.") + to_chat(user, "[bicon(src)] Attempting to start video over existing call.") sleep(30) - to_chat(user, "\icon[src] Please wait...") + to_chat(user, "[bicon(src)] Please wait...") video_source = comm.camera - comm.visible_message("\icon[src] New video connection from [comm].") + comm.visible_message("[bicon(src)] New video connection from [comm].") watch_video(user) update_icon() @@ -363,13 +363,13 @@ if(!Adjacent(user) || !video_source) return user.set_machine(video_source) user.reset_view(video_source) - to_chat(user,"Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View") - to_chat(user,"To return to an active video session, use the communicator in your hand.") + to_chat(user, "Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View") + to_chat(user, "To return to an active video session, use the communicator in your hand.") spawn(0) while(user.machine == video_source && Adjacent(user)) var/turf/T = get_turf(video_source) if(!T || !is_on_same_plane_or_station(T.z, user.z) || !video_source.can_use()) - user << "The screen bursts into static, then goes black." + to_chat(user, "The screen bursts into static, then goes black.") video_cleanup(user) return sleep(10) @@ -391,8 +391,8 @@ /obj/item/device/communicator/proc/end_video(var/reason) video_source = null - . = "\icon[src] [reason ? reason : "Video session ended"]." + . = "[bicon(src)] [reason ? reason : "Video session ended"]." visible_message(.) update_icon() - + diff --git a/code/game/objects/items/devices/debugger.dm b/code/game/objects/items/devices/debugger.dm index a0d41a6124..8c200977af 100644 --- a/code/game/objects/items/devices/debugger.dm +++ b/code/game/objects/items/devices/debugger.dm @@ -25,21 +25,21 @@ if(istype(O, /obj/machinery/power/apc)) var/obj/machinery/power/apc/A = O if(A.emagged || A.hacker) - user << "There is a software error with the device." + to_chat(user, "There is a software error with the device.") else - user << "The device's software appears to be fine." + to_chat(user, "The device's software appears to be fine.") return 1 if(istype(O, /obj/machinery/door)) var/obj/machinery/door/D = O if(D.operating == -1) - user << "There is a software error with the device." + to_chat(user, "There is a software error with the device.") else - user << "The device's software appears to be fine." + to_chat(user, "The device's software appears to be fine.") return 1 else if(istype(O, /obj/machinery)) var/obj/machinery/A = O if(A.emagged) - user << "There is a software error with the device." + to_chat(user, "There is a software error with the device.") else - user << "The device's software appears to be fine." + to_chat(user, "The device's software appears to be fine.") return 1 diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index b66c4a3776..80d9dc081b 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -19,6 +19,9 @@ var/obj/item/weapon/shockpaddles/linked/paddles var/obj/item/weapon/cell/bcell = null +/obj/item/device/defib_kit/get_cell() + return bcell + /obj/item/device/defib_kit/New() //starts without a cell for rnd ..() if(ispath(paddles)) @@ -142,6 +145,12 @@ return 1 if((slot_flags & SLOT_BELT) && M.get_equipped_item(slot_belt) == src) return 1 + //VOREStation Add Start - RIGSuit compatability + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_s_store) == src) + return 1 + if((slot_flags & SLOT_BELT) && M.get_equipped_item(slot_s_store) == src) + return 1 + //VOREStation Add End return 0 @@ -278,9 +287,8 @@ return null /obj/item/weapon/shockpaddles/proc/can_revive(mob/living/carbon/human/H) //This is checked right before attempting to revive - - var/deadtime = world.time - H.timeofdeath - if (deadtime > DEFIB_TIME_LIMIT && !H.isSynthetic()) + var/obj/item/organ/internal/brain/brain = H.internal_organs_by_name[O_BRAIN] + if(H.should_have_organ(O_BRAIN) && (!brain || brain.defib_timer <= 0 ) ) return "buzzes, \"Resuscitation failed - Excessive neural degeneration. Further attempts futile.\"" H.updatehealth() @@ -381,7 +389,7 @@ if(!H.client && !H.teleop) for(var/mob/observer/dead/ghost in player_list) if(ghost.mind == H.mind) - to_chat(ghost, "Someone is attempting to resuscitate you. Re-enter your body if you want to be revived! (Verbs -> Ghost -> Re-enter corpse)") + ghost.notify_revive("Someone is trying to resuscitate you. Re-enter your body if you want to be revived!", 'sound/effects/genetics.ogg') break //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process @@ -478,32 +486,46 @@ add_attack_logs(user,H,"Shocked using [name]") /obj/item/weapon/shockpaddles/proc/make_alive(mob/living/carbon/human/M) //This revives the mob - var/deadtime = world.time - M.timeofdeath - dead_mob_list.Remove(M) if((M in living_mob_list) || (M in dead_mob_list)) WARNING("Mob [M] was defibbed but already in the living or dead list still!") living_mob_list += M M.timeofdeath = 0 - M.stat = UNCONSCIOUS //Life() can bring them back to consciousness if it needs to. + M.set_stat(UNCONSCIOUS) //Life() can bring them back to consciousness if it needs to. M.failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath. M.reload_fullscreen() M.emote("gasp") M.Weaken(rand(10,25)) M.updatehealth() - apply_brain_damage(M, deadtime) + apply_brain_damage(M) + // SSgame_master.adjust_danger(-20) // VOREStation Edit - We don't use SSgame_master yet. -/obj/item/weapon/shockpaddles/proc/apply_brain_damage(mob/living/carbon/human/H, var/deadtime) - if(deadtime < DEFIB_TIME_LOSS) return - - if(!H.should_have_organ(O_BRAIN)) return //no brain +/obj/item/weapon/shockpaddles/proc/apply_brain_damage(mob/living/carbon/human/H) + if(!H.should_have_organ(O_BRAIN)) + return // No brain. var/obj/item/organ/internal/brain/brain = H.internal_organs_by_name[O_BRAIN] - if(!brain) return //no brain + if(!brain) + return // Still no brain. + + // If the brain'd `defib_timer` var gets below this number, brain damage will happen at a linear rate. + // This is measures in `Life()` ticks. E.g. 10 minute defib timer = 6000 world.time units = 3000 `Life()` ticks. + var/brain_damage_timer = ((config.defib_timer MINUTES) / 2) - ((config.defib_braindamage_timer MINUTES) / 2) + + if(brain.defib_timer > brain_damage_timer) + return // They got revived before brain damage got a chance to set in. + + // As the brain decays, this will be between 0 and 1, with 1 being the most fresh. + var/brain_death_scale = brain.defib_timer / brain_damage_timer + + // This is backwards from what you might expect, since 1 = fresh and 0 = rip. + var/damage_calc = LERP(brain.max_damage, H.getBrainLoss(), brain_death_scale) + + // A bit of sanity. + var/brain_damage = between(H.getBrainLoss(), damage_calc, brain.max_damage) - var/brain_damage = CLAMP((deadtime - DEFIB_TIME_LOSS)/(DEFIB_TIME_LIMIT - DEFIB_TIME_LOSS)*brain.max_damage, H.getBrainLoss(), brain.max_damage) H.setBrainLoss(brain_damage) /obj/item/weapon/shockpaddles/proc/make_announcement(var/message, var/msg_class) diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index acc89b39dc..bd7a4687ae 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -78,8 +78,8 @@ var/obj/item/rig_module/module = src.loc if(module.holder && module.holder.wearer) var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.back) - var/obj/item/weapon/rig/suit = H.back + if(istype(H) && H.get_rig()) + var/obj/item/weapon/rig/suit = H.get_rig() if(istype(suit)) return suit.cell return null diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 150c2e1fe5..6930e5f26d 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -53,7 +53,7 @@ if(choice) brightness_level = choice power_usage = brightness_levels[choice] - user << "You set the brightness level on \the [src] to [brightness_level]." + to_chat(user, "You set the brightness level on \the [src] to [brightness_level].") update_icon() /obj/item/device/flashlight/process() @@ -102,15 +102,15 @@ else if(cell.charge > cell.maxcharge*0.75 && cell.charge <= cell.maxcharge) tempdesc += "It appears to have a high amount of power remaining." - user << "[tempdesc]" + to_chat(user, "[tempdesc]") /obj/item/device/flashlight/attack_self(mob/user) if(power_use) if(!isturf(user.loc)) - user << "You cannot turn the light on while in this [user.loc]." //To prevent some lighting anomalities. + to_chat(user, "You cannot turn the light on while in this [user.loc].") //To prevent some lighting anomalities. return 0 if(!cell || cell.charge == 0) - user << "You flick the switch on [src], but nothing happens." + to_chat(user, "You flick the switch on [src], but nothing happens.") return 0 on = !on playsound(src.loc, 'sound/weapons/empty.ogg', 15, 1, -3) @@ -134,38 +134,38 @@ if(istype(H)) for(var/obj/item/clothing/C in list(H.head,H.wear_mask,H.glasses)) if(istype(C) && (C.body_parts_covered & EYES)) - user << "You're going to need to remove [C.name] first." + to_chat(user, "You're going to need to remove [C.name] first.") return var/obj/item/organ/vision if(H.species.vision_organ) vision = H.internal_organs_by_name[H.species.vision_organ] if(!vision) - user << "You can't find any [H.species.vision_organ ? H.species.vision_organ : "eyes"] on [H]!" + to_chat(user, "You can't find any [H.species.vision_organ ? H.species.vision_organ : "eyes"] on [H]!") user.visible_message("\The [user] directs [src] to [M]'s eyes.", \ "You direct [src] to [M]'s eyes.") if(H != user) //can't look into your own eyes buster if(M.stat == DEAD || M.blinded) //mob is dead or fully blind - user << "\The [M]'s pupils do not react to the light!" + to_chat(user, "\The [M]'s pupils do not react to the light!") return if(XRAY in M.mutations) - user << "\The [M] pupils give an eerie glow!" + to_chat(user, "\The [M] pupils give an eerie glow!") if(vision.is_bruised()) - user << "There's visible damage to [M]'s [vision.name]!" + to_chat(user, "There's visible damage to [M]'s [vision.name]!") else if(M.eye_blurry) - user << "\The [M]'s pupils react slower than normally." + to_chat(user, "\The [M]'s pupils react slower than normally.") if(M.getBrainLoss() > 15) - user << "There's visible lag between left and right pupils' reactions." + to_chat(user, "There's visible lag between left and right pupils' reactions.") var/list/pinpoint = list("oxycodone"=1,"tramadol"=5) var/list/dilating = list("space_drugs"=5,"mindbreaker"=1) if(M.reagents.has_any_reagent(pinpoint) || H.ingested.has_any_reagent(pinpoint)) - user << "\The [M]'s pupils are already pinpoint and cannot narrow any more." + to_chat(user, "\The [M]'s pupils are already pinpoint and cannot narrow any more.") else if(M.reagents.has_any_reagent(dilating) || H.ingested.has_any_reagent(dilating)) - user << "\The [M]'s pupils narrow slightly, but are still very dilated." + to_chat(user, "\The [M]'s pupils narrow slightly, but are still very dilated.") else - user << "\The [M]'s pupils narrow." + to_chat(user, "\The [M]'s pupils narrow.") user.setClickCooldown(user.get_attack_speed(src)) //can be used offensively M.flash_eyes() @@ -178,7 +178,7 @@ cell.update_icon() user.put_in_hands(cell) cell = null - user << "You remove the cell from the [src]." + to_chat(user, "You remove the cell from the [src].") playsound(src, 'sound/machines/button.ogg', 30, 1, 0) on = 0 update_icon() @@ -227,13 +227,13 @@ user.drop_item() W.loc = src cell = W - user << "You install a cell in \the [src]." + to_chat(user, "You install a cell in \the [src].") playsound(src, 'sound/machines/button.ogg', 30, 1, 0) update_icon() else - user << "\The [src] already has a cell." + to_chat(user, "\The [src] already has a cell.") else - user << "\The [src] cannot use that type of cell." + to_chat(user, "\The [src] cannot use that type of cell.") else ..() @@ -295,6 +295,7 @@ desc = "A desk lamp with an adjustable mount." icon_state = "lamp" force = 10 + center_of_mass = list("x" = 13,"y" = 11) brightness_on = 10 //TFF 27/11/19 - post refactor fix for intensity levels. w_class = ITEMSIZE_LARGE power_use = 0 @@ -305,6 +306,7 @@ /obj/item/device/flashlight/lamp/green desc = "A classic green-shaded desk lamp." icon_state = "lampgreen" + center_of_mass = list("x" = 15,"y" = 11) brightness_on = 5 flashlight_colour = "#FFC58F" @@ -358,7 +360,7 @@ // Usual checks if(!fuel) - user << "It's out of fuel." + to_chat(user, "It's out of fuel.") return if(on) return @@ -412,7 +414,7 @@ /obj/item/device/flashlight/glowstick/attack_self(mob/user) if(!fuel) - user << "The glowstick has already been turned on." + to_chat(user, "The glowstick has already been turned on.") return if(on) return diff --git a/code/game/objects/items/devices/floor_painter.dm b/code/game/objects/items/devices/floor_painter.dm index 6cf3545524..72e6d2ced7 100644 --- a/code/game/objects/items/devices/floor_painter.dm +++ b/code/game/objects/items/devices/floor_painter.dm @@ -45,11 +45,11 @@ var/turf/simulated/floor/F = A if(!istype(F)) - user << "\The [src] can only be used on station flooring." + to_chat(user, "\The [src] can only be used on station flooring.") return if(!F.flooring || !F.flooring.can_paint || F.broken || F.burnt) - user << "\The [src] cannot paint broken or missing tiles." + to_chat(user, "\The [src] cannot paint broken or missing tiles.") return var/list/decal_data = decals[decal] @@ -63,11 +63,11 @@ config_error = 1 if(config_error) - user << "\The [src] flashes an error light. You might need to reconfigure it." + to_chat(user, "\The [src] flashes an error light. You might need to reconfigure it.") return if(F.decals && F.decals.len > 5 && painting_decal != /obj/effect/floor_decal/reset) - user << "\The [F] has been painted too much; you need to clear it off." + to_chat(user, "\The [F] has been painted too much; you need to clear it off.") return var/painting_dir = 0 @@ -111,7 +111,7 @@ /obj/item/device/floor_painter/examine(mob/user) ..(user) - user << "It is configured to produce the '[decal]' decal with a direction of '[paint_dir]' using [paint_colour] paint." + to_chat(user, "It is configured to produce the '[decal]' decal with a direction of '[paint_dir]' using [paint_colour] paint.") /obj/item/device/floor_painter/verb/choose_colour() set name = "Choose Colour" @@ -124,7 +124,7 @@ var/new_colour = input(usr, "Choose a colour.", "Floor painter", paint_colour) as color|null if(new_colour && new_colour != paint_colour) paint_colour = new_colour - usr << "You set \the [src] to paint with a new colour." + to_chat(usr, "You set \the [src] to paint with a new colour.") /obj/item/device/floor_painter/verb/choose_decal() set name = "Choose Decal" @@ -138,7 +138,7 @@ var/new_decal = input("Select a decal.") as null|anything in decals if(new_decal && !isnull(decals[new_decal])) decal = new_decal - usr << "You set \the [src] decal to '[decal]'." + to_chat(usr, "You set \the [src] decal to '[decal]'.") /obj/item/device/floor_painter/verb/choose_direction() set name = "Choose Direction" @@ -152,4 +152,4 @@ var/new_dir = input("Select a direction.") as null|anything in paint_dirs if(new_dir && !isnull(paint_dirs[new_dir])) paint_dir = new_dir - usr << "You set \the [src] direction to '[paint_dir]'." + to_chat(usr, "You set \the [src] direction to '[paint_dir]'.") diff --git a/code/game/objects/items/devices/geiger.dm b/code/game/objects/items/devices/geiger.dm index 92ff449855..c6f7c3f9f1 100644 --- a/code/game/objects/items/devices/geiger.dm +++ b/code/game/objects/items/devices/geiger.dm @@ -62,7 +62,7 @@ scanning = !scanning update_icon() update_sound() - to_chat(user, "\icon[src] You switch [scanning ? "on" : "off"] \the [src].") + to_chat(user, "[bicon(src)] You switch [scanning ? "on" : "off"] \the [src].") /obj/item/device/geiger/update_icon() if(!scanning) diff --git a/code/game/objects/items/devices/geiger_yw.dm b/code/game/objects/items/devices/geiger_yw.dm new file mode 100644 index 0000000000..eb89f3f4dd --- /dev/null +++ b/code/game/objects/items/devices/geiger_yw.dm @@ -0,0 +1,112 @@ +/obj/item/device/geiger/wall + name = "mounted geiger counter" + desc = "A wall mounted device used for detecting and measuring radiation in an area." + icon = 'icons/obj/devices_yw.dmi' + icon_state = "geiger_wall" + item_state = "geiger_wall" + anchored = 1 + scanning = 1 + radiation_count = 0 + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + w_class = ITEMSIZE_LARGE + flags = NOBLOODY + var/circuit = /obj/item/weapon/circuitboard/geiger + var/number = 0 + var/last_tick //used to delay the powercheck + var/wiresexposed = 0 + +/obj/item/device/geiger/wall/Initialize() + START_PROCESSING(SSobj, src) + soundloop = new(list(src), FALSE) + return ..() + +/obj/item/device/geiger/wall/Destroy() + STOP_PROCESSING(SSobj, src) + QDEL_NULL(soundloop) + return ..() +/* +/obj/item/device/geiger/wall/process() + get_radiation() + +/obj/item/device/geiger/wall/proc/get_radiation() + if(!scanning) + return + radiation_count = SSradiation.get_rads_at_turf(get_turf(src)) + update_icon() + update_sound() + +/obj/item/device/geiger/wall/examine(mob/user) + ..(user) + get_radiation() + to_chat(user, "[scanning ? "Ambient" : "Stored"] radiation level: [radiation_count ? radiation_count : "0"]Bq.") + +/obj/item/device/geiger/wall/rad_act(amount) + if(!amount || !scanning) + return FALSE + + if(amount > radiation_count) + radiation_count = amount + + update_icon() + update_sound() +*/ + +/obj/item/device/geiger/wall/attack_self(var/mob/user) + scanning = !scanning + update_icon() + update_sound() + to_chat(user, "\icon[src] You switch [scanning ? "on" : "off"] \the [src].") + +/obj/item/device/geiger/wall/update_icon() + if(!scanning) + icon_state = "geiger_wall-p" + return 1 + + switch(radiation_count) + if(null) + icon_state = "geiger_level_1" + if(-INFINITY to RAD_LEVEL_LOW) + icon_state = "geiger_level_1" + if(RAD_LEVEL_LOW to RAD_LEVEL_MODERATE) + icon_state = "geiger_level_2" + if(RAD_LEVEL_MODERATE to RAD_LEVEL_HIGH) + icon_state = "geiger_level_3" + if(RAD_LEVEL_HIGH to RAD_LEVEL_VERY_HIGH) + icon_state = "geiger_level_4" + if(RAD_LEVEL_VERY_HIGH to INFINITY) + icon_state = "geiger_level_5" +/* +/obj/item/device/geiger/wall/process() + if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0)) + last_tick = world.timeofday + + if(!src.loc) + scanning = 0 + else + var/area/A = get_area(src) + if(!A) + scanning = 0 + else + scanning = A.powered(EQUIP) // set "on" to the power status + + if(!scanning) + if(wiresexposed) + icon_state = "geiger_wall-p_open" + else + icon_state = "geiger_wall-p" + else + if(wiresexposed) + icon_state = "geiger_wall-p_open" + else + icon_state = initial(icon_state) */ + +/obj/item/device/geiger/wall/attack_ai(mob/user as mob) + src.add_fingerprint(user) + spawn (0) + attack_self(user) + +/obj/item/device/geiger/wall/attack_hand(mob/user as mob) + src.add_fingerprint(user) + spawn (0) + attack_self(user) diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index 830fa803e4..562ed56990 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -83,7 +83,7 @@ var/list/GPS_list = list() dat["curr_z_name"] = using_map.get_zlevel_name(curr.z) dat["gps_list"] = list() dat["z_level_detection"] = using_map.get_map_levels(curr.z, long_range) - + for(var/obj/item/device/gps/G in GPS_list - src) if(!G.tracking || G.emped || G.hide_signal) continue @@ -99,9 +99,7 @@ var/list/GPS_list = list() gps_data["gps_tag"] = G.gps_tag var/area/A = get_area(G) - gps_data["area_name"] = A.name - if(istype(A, /area/submap)) - gps_data["area_name"] = "Unknown Area" // Avoid spoilers. + gps_data["area_name"] = A.get_name() gps_data["z_name"] = using_map.get_zlevel_name(T.z) gps_data["direction"] = get_adir(curr, T) diff --git a/code/game/objects/items/devices/hacktool.dm b/code/game/objects/items/devices/hacktool.dm index fb3d8d9bcc..0b6d25f0c9 100644 --- a/code/game/objects/items/devices/hacktool.dm +++ b/code/game/objects/items/devices/hacktool.dm @@ -44,27 +44,27 @@ /obj/item/device/multitool/hacktool/proc/attempt_hack(var/mob/user, var/atom/target) if(is_hacking) - user << "You are already hacking!" + to_chat(user, "You are already hacking!") return 0 if(!is_type_in_list(target, supported_types)) - user << "\icon[src] Unable to hack this target!" + to_chat(user, "[bicon(src)] Unable to hack this target!") return 0 var/found = known_targets.Find(target) if(found) known_targets.Swap(1, found) // Move the last hacked item first return 1 - user << "You begin hacking \the [target]..." + to_chat(user, "You begin hacking \the [target]...") is_hacking = 1 // On average hackin takes ~30 seconds. Fairly small random span to avoid people simply aborting and trying again var/hack_result = do_after(user, (20 SECONDS + rand(0, 10 SECONDS) + rand(0, 10 SECONDS))) is_hacking = 0 if(hack_result && in_hack_mode) - user << "Your hacking attempt was succesful!" + to_chat(user, "Your hacking attempt was succesful!") user.playsound_local(get_turf(src), 'sound/instruments/piano/An6.ogg', 50) else - user << "Your hacking attempt failed!" + to_chat(user, "Your hacking attempt failed!") return 0 known_targets.Insert(1, target) // Insert the newly hacked target first, diff --git a/code/game/objects/items/devices/holowarrant.dm b/code/game/objects/items/devices/holowarrant.dm index 36a13a6bec..bcf456126e 100644 --- a/code/game/objects/items/devices/holowarrant.dm +++ b/code/game/objects/items/devices/holowarrant.dm @@ -39,13 +39,15 @@ /obj/item/device/holowarrant/attackby(obj/item/weapon/W, mob/user) if(active) var/obj/item/weapon/card/id/I = W.GetIdCard() - if(I) + if(access_hos in I.access) // VOREStation edit var/choice = alert(user, "Would you like to authorize this warrant?","Warrant authorization","Yes","No") if(choice == "Yes") active.fields["auth"] = "[I.registered_name] - [I.assignment ? I.assignment : "(Unknown)"]" user.visible_message("You swipe \the [I] through the [src].", \ "[user] swipes \the [I] through the [src].") return 1 + to_chat(user, "You don't have the access to do this!") // VOREStation edit + return 1 ..() //hit other people with it @@ -107,4 +109,13 @@ Vessel or habitat: _[using_map.station_name]____
"} - show_browser(user, output, "window=Search warrant for [active.fields["namewarrant"]]") \ No newline at end of file + show_browser(user, output, "window=Search warrant for [active.fields["namewarrant"]]") + +/obj/item/weapon/storage/box/holowarrants // VOREStation addition starts + name = "holowarrant devices" + desc = "A box of holowarrant diplays for security use." + +/obj/item/weapon/storage/box/holowarrants/New() + ..() + for(var/i = 0 to 3) + new /obj/item/device/holowarrant(src) // VOREStation addition ends \ No newline at end of file diff --git a/code/game/objects/items/devices/locker_painter.dm b/code/game/objects/items/devices/locker_painter.dm index 0d264b6a5b..816c9790ff 100644 --- a/code/game/objects/items/devices/locker_painter.dm +++ b/code/game/objects/items/devices/locker_painter.dm @@ -81,7 +81,7 @@ if(istype(A,ctype)) non_closet = 1 if(non_closet) - user << "\The [src] can only be used on closets." + to_chat(user, "\The [src] can only be used on closets.") return var/config_error @@ -89,7 +89,7 @@ if(istype(A,/obj/structure/closet/secure_closet)) var/obj/structure/closet/secure_closet/F = A if(F.broken) - user << "\The [src] cannot paint broken closets." + to_chat(user, "\The [src] cannot paint broken closets.") return var/list/colour_data = colours_secure[colour_secure] @@ -114,7 +114,7 @@ F.update_icon() if(config_error) - user << "\The [src] flashes an error light. You might need to reconfigure it." + to_chat(user, "\The [src] flashes an error light. You might need to reconfigure it.") return /obj/item/device/closet_painter/attack_self(var/mob/user) @@ -126,7 +126,7 @@ /obj/item/device/closet_painter/examine(mob/user) ..(user) - user << "It is configured to produce the '[colour]' paint scheme or the '[colour_secure]' secure closet paint scheme." + to_chat(user, "It is configured to produce the '[colour]' paint scheme or the '[colour_secure]' secure closet paint scheme.") /obj/item/device/closet_painter/verb/choose_colour() set name = "Choose Colour" @@ -140,7 +140,7 @@ var/new_colour = input("Select a colour.") as null|anything in colours if(new_colour && !isnull(colours[new_colour])) colour = new_colour - usr << "You set \the [src] regular closet colour to '[colour]'." + to_chat(usr, "You set \the [src] regular closet colour to '[colour]'.") /obj/item/device/closet_painter/verb/choose_colour_secure() set name = "Choose Secure Colour" @@ -154,4 +154,4 @@ var/new_colour_secure = input("Select a colour.") as null|anything in colours_secure if(new_colour_secure && !isnull(colours_secure[new_colour_secure])) colour_secure = new_colour_secure - usr << "You set \the [src] secure closet colour to '[colour_secure]'." + to_chat(usr, "You set \the [src] secure closet colour to '[colour_secure]'.") diff --git a/code/game/objects/items/devices/modkit.dm b/code/game/objects/items/devices/modkit.dm index 6ff95fe17d..1ceaa3e9d1 100644 --- a/code/game/objects/items/devices/modkit.dm +++ b/code/game/objects/items/devices/modkit.dm @@ -22,7 +22,7 @@ return //it shouldn't be null, okay? if(!parts) - user << "This kit has no parts for this modification left." + to_chat(user, "This kit has no parts for this modification left.") user.drop_from_inventory(src) qdel(src) return @@ -34,17 +34,17 @@ var/obj/item/clothing/I = O if (!istype(I) || !allowed) - user << "[src] is unable to modify that." + to_chat(user, "[src] is unable to modify that.") return var/excluding = ("exclude" in I.species_restricted) var/in_list = (target_species in I.species_restricted) if (excluding ^ in_list) - user << "[I] is already modified." + to_chat(user, "[I] is already modified.") return if(!isturf(O.loc)) - user << "[O] must be safely placed on the ground for modification." + to_chat(user, "[O] must be safely placed on the ground for modification.") return playsound(src.loc, O.usesound, 100, 1) @@ -64,7 +64,7 @@ /obj/item/device/modkit/examine(mob/user) ..(user) - user << "It looks as though it modifies hardsuits to fit [target_species] users." + to_chat(user, "It looks as though it modifies hardsuits to fit [target_species] users.") /obj/item/device/modkit/tajaran name = "tajaran hardsuit modification kit" diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index fc3f69dd22..a289e66e82 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -63,6 +63,9 @@ return +/obj/item/device/multitool/is_multitool() + return TRUE + /obj/item/device/multitool/cyborg name = "multitool" desc = "Optimised and stripped-down version of a regular multitool." diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 2ffebcb81a..8b7f55e0a9 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -239,12 +239,12 @@ GLOBAL_LIST_BOILERPLATE(all_pai_cards, /obj/item/device/paicard) return var/mob/M = usr if(!istype(M, /mob/living/carbon)) - usr << "You don't have any DNA, or your DNA is incompatible with this device." + to_chat(usr, "You don't have any DNA, or your DNA is incompatible with this device.") else var/datum/dna/dna = usr.dna pai.master = M.real_name pai.master_dna = dna.unique_enzymes - pai << "

You have been bound to a new master.

" + to_chat(pai, "

You have been bound to a new master.

") if(href_list["request"]) src.looking_for_personality = 1 paiController.findPAI(src, usr) @@ -252,10 +252,10 @@ GLOBAL_LIST_BOILERPLATE(all_pai_cards, /obj/item/device/paicard) var/confirm = input("Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe") in list("Yes", "No") if(confirm == "Yes") for(var/mob/M in src) - M << "

You feel yourself slipping away from reality.

" - M << "

Byte by byte you lose your sense of self.

" - M << "

Your mental faculties leave you.

" - M << "
oblivion...
" + to_chat(M, "

You feel yourself slipping away from reality.

") + to_chat(M, "

Byte by byte you lose your sense of self.

") + to_chat(M, "

Your mental faculties leave you.

") + to_chat(M, "
oblivion...
") M.death(0) removePersonality() if(href_list["wires"]) @@ -269,9 +269,9 @@ GLOBAL_LIST_BOILERPLATE(all_pai_cards, /obj/item/device/paicard) var/newlaws = sanitize(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws) as message) if(newlaws) pai.pai_laws = newlaws - pai << "Your supplemental directives have been updated. Your new directives are:" - pai << "Prime Directive:
[pai.pai_law0]" - pai << "Supplemental Directives:
[pai.pai_laws]" + to_chat(pai, "Your supplemental directives have been updated. Your new directives are:") + to_chat(pai, "Prime Directive:
[pai.pai_law0]") + to_chat(pai, "Supplemental Directives:
[pai.pai_laws]") attack_self(usr) // WIRE_SIGNAL = 1 diff --git a/code/game/objects/items/devices/pipe_painter.dm b/code/game/objects/items/devices/pipe_painter.dm index 54c9a9539c..8a32988fb6 100644 --- a/code/game/objects/items/devices/pipe_painter.dm +++ b/code/game/objects/items/devices/pipe_painter.dm @@ -28,4 +28,4 @@ /obj/item/device/pipe_painter/examine(mob/user) ..(user) - user << "It is in [mode] mode." + to_chat(user, "It is in [mode] mode.") diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm index 5bd91e81b6..d49de71ae1 100644 --- a/code/game/objects/items/devices/radio/beacon.dm +++ b/code/game/objects/items/devices/radio/beacon.dm @@ -44,7 +44,7 @@ GLOBAL_LIST_BOILERPLATE(all_beacons, /obj/item/device/radio/beacon) /obj/item/device/radio/beacon/syndicate/attack_self(mob/user as mob) if(user) - user << "Locked In" + to_chat(user, "Locked In") new /obj/machinery/power/singularity_beacon/syndicate( user.loc ) playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) qdel(src) diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index 617b314600..cc555f8073 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -17,7 +17,7 @@ /obj/item/device/radio/electropack/attack_hand(mob/living/user as mob) if(src == user.back) - user << "You need help taking this off!" + to_chat(user, "You need help taking this off!") return ..() @@ -25,7 +25,7 @@ ..() if(istype(W, /obj/item/clothing/head/helmet)) if(!b_stat) - user << "[src] is not ready to be attached!" + to_chat(user, "[src] is not ready to be attached!") return var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit( user ) A.icon = 'icons/obj/assemblies.dmi' @@ -95,7 +95,7 @@ sleep(50) if(M) M.moved_recently = 0 - M << "You feel a sharp shock!" + to_chat(M, "You feel a sharp shock!") var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, M) s.start() diff --git a/code/game/objects/items/devices/radio/headset_vr.dm b/code/game/objects/items/devices/radio/headset_vr.dm index 49783eaca4..14398d9f59 100644 --- a/code/game/objects/items/devices/radio/headset_vr.dm +++ b/code/game/objects/items/devices/radio/headset_vr.dm @@ -17,6 +17,10 @@ centComm = 1 ks2type = /obj/item/device/encryptionkey/ert +/obj/item/device/radio/headset/nanotrasen/alt + name = "\improper NT bowman headset" + icon_state = "nt_headset_alt" + /obj/item/device/radio/headset sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/seromi/ears.dmi', SPECIES_WEREBEAST = 'icons/mob/species/werebeast/ears.dmi') diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 6d823f217f..795355a6e3 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -22,6 +22,12 @@ /obj/item/device/radio/intercom/interrogation name = "station intercom (Interrogation)" frequency = 1449 + broadcasting = 1 //yw edit starts + listening = 0 + +/obj/item/device/radio/intercom/interrogation/observation + listening = 1 + broadcasting = 0 //yw edit ends /obj/item/device/radio/intercom/private name = "station intercom (Private)" diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 40dc7bb1f7..69b5192abb 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -52,6 +52,13 @@ var/global/list/default_medbay_channels = list( w_class = ITEMSIZE_SMALL show_messages = 1 + // Bluespace radios talk directly to telecomms equipment + var/bluespace_radio = FALSE + var/weakref/bs_tx_weakref //Maybe misleading, this is the device to TRANSMIT TO + // For mappers or subtypes, to start them prelinked to these devices + var/bs_tx_preload_id + var/bs_rx_preload_id + matter = list("glass" = 25,DEFAULT_WALL_MATERIAL = 75) var/const/FREQ_LISTENING = 1 var/list/internal_channels @@ -90,6 +97,43 @@ var/global/list/default_medbay_channels = list( for (var/ch_name in channels) secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + if(bluespace_radio) + if(bs_tx_preload_id) + //Try to find a receiver + for(var/obj/machinery/telecomms/receiver/RX in telecomms_list) + if(RX.id == bs_tx_preload_id) //Again, bs_tx is the thing to TRANSMIT TO, so a receiver. + bs_tx_weakref = weakref(RX) + RX.link_radio(src) + break + //Hmm, howabout an AIO machine + if(!bs_tx_weakref) + for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) + if(AIO.id == bs_tx_preload_id) + bs_tx_weakref = weakref(AIO) + AIO.link_radio(src) + break + if(!bs_tx_weakref) + testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") + + if(bs_rx_preload_id) + var/found = 0 + //Try to find a transmitter + for(var/obj/machinery/telecomms/broadcaster/TX in telecomms_list) + if(TX.id == bs_rx_preload_id) //Again, bs_rx is the thing to RECEIVE FROM, so a transmitter. + TX.link_radio(src) + found = 1 + break + //Hmm, howabout an AIO machine + if(!found) + for(var/obj/machinery/telecomms/allinone/AIO in telecomms_list) + if(AIO.id == bs_rx_preload_id) + AIO.link_radio(src) + found = 1 + break + if(!found) + testing("A radio [src] at [x],[y],[z] specified bluespace prelink IDs, but the machines with corresponding IDs ([bs_tx_preload_id], [bs_rx_preload_id]) couldn't be found.") + + /obj/item/device/radio/attack_self(mob/user as mob) user.set_machine(src) interact(user) @@ -128,6 +172,11 @@ var/global/list/default_medbay_channels = list( ui.set_initial_data(data) ui.open() +/obj/item/device/radio/CouldUseTopic(var/mob/user) + ..() + if(iscarbon(user)) + playsound(src, "button", 10) + /obj/item/device/radio/proc/list_channels(var/mob/user) return list_internal_channels(user) @@ -235,11 +284,10 @@ var/global/list/default_medbay_channels = list( if(.) SSnanoui.update_uis(src) -/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel) //BS12 EDIT +/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/list/zlevels) //BS12 EDIT var/datum/radio_frequency/connection = null if(channel && channels && channels.len > 0) if (channel == "department") - //world << "DEBUG: channel=\"[channel]\" switching to \"[channels[1]]\"" channel = channels[1] connection = secure_radio_connections[channel] else @@ -248,12 +296,15 @@ var/global/list/default_medbay_channels = list( if (!istype(connection)) return + if(!LAZYLEN(zlevels)) + zlevels = list(0) + var/static/mob/living/silicon/ai/announcer/A = new /mob/living/silicon/ai/announcer(src, null, null, 1) A.SetName(from) Broadcast_Message(connection, A, 0, "*garbled automated announcement*", src, message, from, "Automated Announcement", from, "synthesized voice", - 4, 0, list(0), connection.frequency, "states") + 4, 0, zlevels, connection.frequency, "states") // Interprets the message mode when talking into a radio, possibly returning a connection datum /obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, message, message_mode) @@ -305,7 +356,7 @@ var/global/list/default_medbay_channels = list( if (!istype(connection)) return FALSE - var/turf/position = get_turf(src) + var/pos_z = get_z(src) //#### Tagging the signal with all appropriate identity values ####// @@ -354,54 +405,73 @@ var/global/list/default_medbay_channels = list( jobname = "Unknown" voicemask = 1 + // First, we want to generate a new radio signal + var/datum/signal/signal = new + // --- Finally, tag the actual signal with the appropriate values --- + signal.data = list( + // Identity-associated tags: + "mob" = M, // store a reference to the mob + "mobtype" = M.type, // the mob's type + "realname" = real_name, // the mob's real name + "name" = displayname, // the mob's display name + "job" = jobname, // the mob's job + "key" = mobkey, // the mob's key + "vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood + "vname" = M.voice_name, // the name to display if the voice wasn't understood + "vmask" = voicemask, // 1 if the mob is using a voice gas mask - /* ###### Radio headsets can only broadcast through subspace ###### */ - if(subspace_transmission) + // We store things that would otherwise be kept in the actual mob + // so that they can be logged even AFTER the mob is deleted or something + + // Other tags: + "compression" = rand(45,50), // compressed radio signal + "message" = message, // the actual sent message + "connection" = connection, // the radio connection to use + "radio" = src, // stores the radio used for transmission + "slow" = 0, // how much to sleep() before broadcasting - simulates net lag + "traffic" = 0, // dictates the total traffic sum that the signal went through + "type" = SIGNAL_NORMAL, // determines what type of radio input it is: normal broadcast + "server" = null, // the last server to log this signal + "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery + "level" = pos_z, // The source's z level + "language" = speaking, + "verb" = verb + ) + signal.frequency = connection.frequency // Quick frequency set + + var/filter_type = DATA_LOCAL //If we end up having to send it the old fashioned way, it's with this data var. + + /* ###### Bluespace radios talk directly to receivers (and only directly to receivers) ###### */ + if(bluespace_radio) + //Nothing to transmit to + if(!bs_tx_weakref) + to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") + return FALSE + + var/obj/machinery/telecomms/tx_to = bs_tx_weakref.resolve() + //Was linked, now destroyed or something + if(!tx_to) + bs_tx_weakref = null + to_chat(loc, "\The [src] buzzes to inform you of the lack of a functioning connection.") + return FALSE + + //Transmitted in the blind. If we get a message back, cool. If not, oh well. + signal.transmission_method = TRANSMISSION_BLUESPACE + return tx_to.receive_signal(signal) + + /* ###### Radios with subspace_transmission can only broadcast through subspace (unless they have adhoc_fallback) ###### */ + else if(subspace_transmission) var/list/jamming = is_jammed(src) if(jamming) var/distance = jamming["distance"] - to_chat(M,"\icon[src] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.") + to_chat(M, "[bicon(src)] You hear the [distance <= 2 ? "loud hiss" : "soft hiss"] of static.") return FALSE // First, we want to generate a new radio signal - var/datum/signal/signal = new - signal.transmission_method = 2 // 2 would be a subspace transmission. - // transmission_method could probably be enumerated through #define. Would be neater. + signal.transmission_method = TRANSMISSION_SUBSPACE - // --- Finally, tag the actual signal with the appropriate values --- - signal.data = list( - // Identity-associated tags: - "mob" = M, // store a reference to the mob - "mobtype" = M.type, // the mob's type - "realname" = real_name, // the mob's real name - "name" = displayname, // the mob's display name - "job" = jobname, // the mob's job - "key" = mobkey, // the mob's key - "vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood - "vname" = M.voice_name, // the name to display if the voice wasn't understood - "vmask" = voicemask, // 1 if the mob is using a voice gas mask - - // We store things that would otherwise be kept in the actual mob - // so that they can be logged even AFTER the mob is deleted or something - - // Other tags: - "compression" = rand(45,50), // compressed radio signal - "message" = message, // the actual sent message - "connection" = connection, // the radio connection to use - "radio" = src, // stores the radio used for transmission - "slow" = 0, // how much to sleep() before broadcasting - simulates net lag - "traffic" = 0, // dictates the total traffic sum that the signal went through - "type" = 0, // determines what type of radio input it is: normal broadcast - "server" = null, // the last server to log this signal - "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery - "level" = position.z, // The source's z level - "language" = speaking, - "verb" = verb - ) - signal.frequency = connection.frequency // Quick frequency set - - //#### Sending the signal to all subspace receivers ####// + //#### Sending the signal to all subspace receivers ####// for(var/obj/machinery/telecomms/receiver/R in telecomms_list) R.receive_signal(signal) @@ -411,84 +481,47 @@ var/global/list/default_medbay_channels = list( R.receive_signal(signal) // Receiving code can be located in Telecommunications.dm - if(signal.data["done"] && position.z in signal.data["level"]) + if(signal.data["done"] && (pos_z in signal.data["level"])) return TRUE //Huzzah, sent via subspace else if(adhoc_fallback) //Less huzzah, we have to fallback - to_chat(loc,"\The [src] pings as it falls back to local radio transmission.") + to_chat(loc, "\The [src] pings as it falls back to local radio transmission.") subspace_transmission = FALSE - return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), - src, message, displayname, jobname, real_name, M.voice_name, - signal.transmission_method, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency,verb,speaking) - /* ###### Intercoms and station-bounced radios ###### */ + else //Oh well + return FALSE - var/filter_type = 2 + /* ###### Intercoms and station-bounced radios ###### */ + else + /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ + if(istype(src, /obj/item/device/radio/intercom)) + filter_type = DATA_INTERCOM - /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ - if(istype(src, /obj/item/device/radio/intercom)) - filter_type = 1 + /* --- Try to send a normal subspace broadcast first */ + signal.transmission_method = TRANSMISSION_SUBSPACE + signal.data["compression"] = 0 + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) - var/datum/signal/signal = new - signal.transmission_method = 2 - - - /* --- Try to send a normal subspace broadcast first */ - - signal.data = list( - - "mob" = M, // store a reference to the mob - "mobtype" = M.type, // the mob's type - "realname" = real_name, // the mob's real name - "name" = displayname, // the mob's display name - "job" = jobname, // the mob's job - "key" = mobkey, // the mob's key - "vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood - "vname" = M.voice_name, // the name to display if the voice wasn't understood - "vmask" = voicemask, // 1 if the mob is using a voice gas mas - - "compression" = 0, // uncompressed radio signal - "message" = message, // the actual sent message - "connection" = connection, // the radio connection to use - "radio" = src, // stores the radio used for transmission - "slow" = 0, - "traffic" = 0, - "type" = 0, - "server" = null, - "reject" = 0, - "level" = position.z, - "language" = speaking, - "verb" = verb - ) - signal.frequency = connection.frequency // Quick frequency set + // Allinone can act as receivers. + for(var/obj/machinery/telecomms/allinone/R in telecomms_list) + R.receive_signal(signal) for(var/obj/machinery/telecomms/receiver/R in telecomms_list) R.receive_signal(signal) - if(signal.data["done"] && position.z in signal.data["level"]) - if(adhoc_fallback) - to_chat(loc,"\The [src] pings as it reestablishes subspace communications.") - subspace_transmission = TRUE - // we're done here. - return TRUE - - // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. - // Send a mundane broadcast with limited targets: - - //THIS IS TEMPORARY. YEAH RIGHT - if(!connection) return FALSE //~Carn - -//VOREStation Add Start - if(bluespace_radio) - return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), - src, message, displayname, jobname, real_name, M.voice_name, - 0, signal.data["compression"], list(0), connection.frequency,verb,speaking) -//VOREStation Add End + if(signal.data["done"] && pos_z in signal.data["level"]) + if(adhoc_fallback) + to_chat(loc, "\The [src] pings as it reestablishes subspace communications.") + subspace_transmission = TRUE + // we're done here. + return TRUE + //Nothing handled any sort of remote radio-ing and returned before now, just squawk on this zlevel. return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), - src, message, displayname, jobname, real_name, M.voice_name, - filter_type, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency,verb,speaking) + src, message, displayname, jobname, real_name, M.voice_name, + filter_type, signal.data["compression"], using_map.get_map_levels(pos_z), connection.frequency, verb, speaking) /obj/item/device/radio/hear_talk(mob/M as mob, msg, var/verb = "says", var/datum/language/speaking = null) @@ -497,33 +530,20 @@ var/global/list/default_medbay_channels = list( talk_into(M, msg,null,verb,speaking) -/* -/obj/item/device/radio/proc/accept_rad(obj/item/device/radio/R as obj, message) - - if ((R.frequency == frequency && message)) - return TRUE - else if - - else - return null - return -*/ - /obj/item/device/radio/proc/receive_range(freq, level) // check if this radio can receive on the given frequency, and if so, // what the range is in which mobs will hear the radio // returns: -1 if can't receive, range otherwise - - if (wires.IsIndexCut(WIRE_RECEIVE)) + if(wires.IsIndexCut(WIRE_RECEIVE)) return -1 if(!listening) return -1 if(is_jammed(src)) return -1 if(!(0 in level)) - var/turf/position = get_turf(src) - if((!position || !(position.z in level)) && !bluespace_radio) //VOREStation Edit + var/pos_z = get_z(src) + if(!(pos_z in level)) return -1 if(freq in ANTAG_FREQS) if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys diff --git a/code/game/objects/items/devices/radio/radio_vr.dm b/code/game/objects/items/devices/radio/radio_vr.dm index b70917752b..aa12444576 100644 --- a/code/game/objects/items/devices/radio/radio_vr.dm +++ b/code/game/objects/items/devices/radio/radio_vr.dm @@ -1,6 +1,3 @@ -/obj/item/device/radio - var/bluespace_radio = FALSE - /obj/item/device/radio/phone subspace_transmission = 1 canhear_range = 0 @@ -17,132 +14,20 @@ ..() internal_channels = default_medbay_channels.Copy() -//Pathfinder's Subspace Radio -/obj/item/device/subspaceradio - name = "subspace radio" - desc = "A powerful new radio recently gifted to Nanotrasen from Xion Manufacturing Group, this communications device has the ability to send and recieve transmissions from anywhere." - catalogue_data = list(/datum/category_item/catalogue/information/organization/xion) - icon = 'icons/vore/custom_items_vr.dmi' - icon_override = 'icons/mob/back_vr.dmi' - icon_state = "radiopack" - item_state = "radiopack" - slot_flags = SLOT_BACK - force = 5 - throwforce = 6 - preserve_item = 1 - w_class = ITEMSIZE_LARGE - action_button_name = "Remove/Replace Handset" - var/obj/item/device/radio/subspacehandset/linked/handset -/obj/item/device/subspaceradio/New() //starts without a cell for rnd - ..() - if(ispath(handset)) - handset = new handset(src, src) - else - handset = new(src, src) +/obj/item/device/bluespaceradio/tether_prelinked + name = "bluespace radio (tether)" + handset = /obj/item/device/radio/bluespacehandset/linked/tether_prelinked -/obj/item/device/subspaceradio/Destroy() - . = ..() - QDEL_NULL(handset) +/obj/item/device/radio/bluespacehandset/linked/tether_prelinked + bs_tx_preload_id = "tether_rx" //Transmit to a receiver + bs_rx_preload_id = "tether_tx" //Recveive from a transmitter -/obj/item/device/subspaceradio/ui_action_click() - toggle_handset() +/obj/item/device/bluespaceradio/talon_prelinked + name = "bluespace radio (talon)" + handset = /obj/item/device/radio/bluespacehandset/linked/talon_prelinked -/obj/item/device/subspaceradio/attack_hand(mob/user) - if(loc == user) - toggle_handset() - else - ..() - -/obj/item/device/subspaceradio/MouseDrop() - if(ismob(loc)) - if(!CanMouseDrop(src)) - return - var/mob/M = loc - if(!M.unEquip(src)) - return - add_fingerprint(usr) - M.put_in_any_hand_if_possible(src) - -/obj/item/device/subspaceradio/attackby(obj/item/weapon/W, mob/user, params) - if(W == handset) - reattach_handset(user) - else - return ..() - -/obj/item/device/subspaceradio/verb/toggle_handset() - set name = "Toggle Handset" - set category = "Object" - - var/mob/living/carbon/human/user = usr - if(!handset) - to_chat(user, "The handset is missing!") - return - - if(handset.loc != src) - reattach_handset(user) //Remove from their hands and back onto the defib unit - return - - if(!slot_check()) - to_chat(user, "You need to equip [src] before taking out [handset].") - else - if(!usr.put_in_hands(handset)) //Detach the handset into the user's hands - to_chat(user, "You need a free hand to hold the handset!") - update_icon() //success - -//checks that the base unit is in the correct slot to be used -/obj/item/device/subspaceradio/proc/slot_check() - var/mob/M = loc - if(!istype(M)) - return 0 //not equipped - - if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src) - return 1 - if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_s_store) == src) - return 1 - - return 0 - -/obj/item/device/subspaceradio/dropped(mob/user) - ..() - reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit - -/obj/item/device/subspaceradio/proc/reattach_handset(mob/user) - if(!handset) return - - if(ismob(handset.loc)) - var/mob/M = handset.loc - if(M.drop_from_inventory(handset, src)) - to_chat(user, "\The [handset] snaps back into the main unit.") - else - handset.forceMove(src) - -//Subspace Radio Handset -/obj/item/device/radio/subspacehandset - name = "subspace radio handset" - desc = "A large walkie talkie attached to the subspace radio by a retractable cord. It sits comfortably on a slot in the radio when not in use." - bluespace_radio = TRUE - icon_state = "signaller" - slot_flags = null - w_class = ITEMSIZE_LARGE - -/obj/item/device/radio/subspacehandset/linked - var/obj/item/device/subspaceradio/base_unit - -/obj/item/device/radio/subspacehandset/linked/New(newloc, obj/item/device/subspaceradio/radio) - base_unit = radio - ..(newloc) - -/obj/item/device/radio/subspacehandset/linked/Destroy() - if(base_unit) - //ensure the base unit's icon updates - if(base_unit.handset == src) - base_unit.handset = null - base_unit = null - return ..() - -/obj/item/device/radio/subspacehandset/linked/dropped(mob/user) - ..() //update twohanding - if(base_unit) - base_unit.reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit +/obj/item/device/radio/bluespacehandset/linked/talon_prelinked + bs_tx_preload_id = "talon_aio" //Transmit to a receiver + bs_rx_preload_id = "talon_aio" //Recveive from a transmitter \ No newline at end of file diff --git a/code/game/objects/items/devices/radio/radiopack.dm b/code/game/objects/items/devices/radio/radiopack.dm new file mode 100644 index 0000000000..c2ba25ecc2 --- /dev/null +++ b/code/game/objects/items/devices/radio/radiopack.dm @@ -0,0 +1,153 @@ +/obj/item/device/bluespaceradio + name = "bluespace radio" + desc = "A powerful radio that uses a tiny bluespace wormhole to send signals directly to subspace receivers and transmitters, bypassing the limitations of subspace." + icon = 'icons/obj/radio.dmi' + icon_state = "radiopack" + item_state = "radiopack" + slot_flags = SLOT_BACK + force = 5 + throwforce = 6 + preserve_item = 1 + w_class = ITEMSIZE_LARGE + action_button_name = "Remove/Replace Handset" + + var/obj/item/device/radio/bluespacehandset/linked/handset = /obj/item/device/radio/bluespacehandset/linked + +/obj/item/device/bluespaceradio/Initialize() + . = ..() + if(ispath(handset)) + handset = new handset(src, src) + +/obj/item/device/bluespaceradio/Destroy() + . = ..() + QDEL_NULL(handset) + +/obj/item/device/bluespaceradio/ui_action_click() + toggle_handset() + +/obj/item/device/bluespaceradio/attack_hand(var/mob/user) + if(loc == user) + toggle_handset() + else + ..() + +/obj/item/device/bluespaceradio/MouseDrop() + if(ismob(loc)) + if(!CanMouseDrop(src)) + return + var/mob/M = loc + if(!M.unEquip(src)) + return + add_fingerprint(usr) + M.put_in_any_hand_if_possible(src) + +/obj/item/device/bluespaceradio/attackby(var/obj/item/weapon/W, var/mob/user, var/params) + if(W == handset) + reattach_handset(user) + else + return ..() + +/obj/item/device/bluespaceradio/verb/toggle_handset() + set name = "Toggle Handset" + set category = "Object" + + var/mob/living/carbon/human/user = usr + if(!handset) + to_chat(user, "The handset is missing!") + return + + if(handset.loc != src) + reattach_handset(user) //Remove from their hands and back onto the defib unit + return + + if(!slot_check()) + to_chat(user, "You need to equip [src] before taking out [handset].") + else + if(!usr.put_in_hands(handset)) //Detach the handset into the user's hands + to_chat(user, "You need a free hand to hold the handset!") + update_icon() //success + +//checks that the base unit is in the correct slot to be used +/obj/item/device/bluespaceradio/proc/slot_check() + var/mob/M = loc + if(!istype(M)) + return 0 //not equipped + + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src) + return 1 + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_s_store) == src) + return 1 + + return 0 + +/obj/item/device/bluespaceradio/dropped(var/mob/user) + ..() + reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit + +/obj/item/device/bluespaceradio/proc/reattach_handset(var/mob/user) + if(!handset) return + + if(ismob(handset.loc)) + var/mob/M = handset.loc + if(M.drop_from_inventory(handset, src)) + to_chat(user, "\The [handset] snaps back into the main unit.") + else + handset.forceMove(src) + +//Subspace Radio Handset +/obj/item/device/radio/bluespacehandset + name = "bluespace radio handset" + desc = "A large walkie talkie attached to the bluespace radio by a retractable cord. It sits comfortably on a slot in the radio when not in use." + bluespace_radio = TRUE + icon_state = "signaller" + slot_flags = null + w_class = ITEMSIZE_LARGE + canhear_range = 1 + +/obj/item/device/radio/bluespacehandset/linked + var/obj/item/device/bluespaceradio/base_unit + +/obj/item/device/radio/bluespacehandset/linked/Initialize(mapload, var/obj/item/device/bluespaceradio/radio) + base_unit = radio + . = ..() + +/obj/item/device/radio/bluespacehandset/linked/Destroy() + if(base_unit) + //ensure the base unit's icon updates + if(base_unit.handset == src) + base_unit.handset = null + base_unit = null + return ..() + +/obj/item/device/radio/bluespacehandset/linked/dropped(var/mob/user) + ..() //update twohanding + if(base_unit) + base_unit.reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit + +/obj/item/device/radio/bluespacehandset/linked/receive_range(var/freq, var/list/level) + //Only care about megabroadcasts or things that are targeted at us + if(!(0 in level)) + return -1 + if(wires.IsIndexCut(WIRE_RECEIVE)) + return -1 + if(!listening) + return -1 + if(is_jammed(src)) + return -1 + if (!on) + return -1 + if (!freq) //recieved on main frequency + if (!listening) + return -1 + else + var/accept = (freq==frequency && listening) + if (!accept) + for (var/ch_name in channels) + var/datum/radio_frequency/RF = secure_radio_connections[ch_name] + if (RF && RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) + accept = 1 + break + if (!accept) + return -1 + + return canhear_range diff --git a/code/game/objects/items/devices/scanners_vr.dm b/code/game/objects/items/devices/scanners_vr.dm index 72a0d9e7b1..8a7bb15c32 100644 --- a/code/game/objects/items/devices/scanners_vr.dm +++ b/code/game/objects/items/devices/scanners_vr.dm @@ -17,6 +17,32 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob var/datum/mind/stored_mind + var/ooc_notes = null //For holding prefs + + + +//These don't perform any checks and need to be wrapped by checks +/obj/item/device/sleevemate/proc/clear_mind() + stored_mind = null + ooc_notes = null + update_icon() + +/obj/item/device/sleevemate/proc/get_mind(mob/living/M) + ASSERT(M.mind) + ooc_notes = M.ooc_notes + stored_mind = M.mind + M.ghostize() + stored_mind.current = null + update_icon() + +/obj/item/device/sleevemate/proc/put_mind(mob/living/M) + stored_mind.active = TRUE + stored_mind.transfer_to(M) + M.ooc_notes = ooc_notes + clear_mind() + + + /obj/item/device/sleevemate/attack(mob/living/M, mob/living/user) if(ishuman(M)) scan_mob(M, user) @@ -34,8 +60,7 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob switch(choice) if("Delete") to_chat(user,"Internal copy of [stored_mind.name] deleted.") - stored_mind = null - update_icon() + clear_mind() if("Backup") to_chat(user,"Internal copy of [stored_mind.name] backed up to database.") SStranscore.m_backup(stored_mind,null,one_time = TRUE) @@ -177,16 +202,13 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob to_chat(usr,"There is already someone's mind stored inside") return - var/choice = alert(usr,"This will remove the target's mind from their body. The only way to put it back is via a resleeving pod. Continue?","Confirmation","Continue","Cancel") + var/choice = alert(usr,"This will remove the target's mind from their body (and from the game as long as they're in the sleevemate). You can put them into a (mindless) body, a NIF, or back them up for normal resleeving, but you should probably have a plan in advance so you don't leave them unable to interact for too long. Continue?","Confirmation","Continue","Cancel") if(choice == "Continue" && usr.get_active_hand() == src && usr.Adjacent(target)) usr.visible_message("[usr] begins downloading [target]'s mind!","You begin downloading [target]'s mind!") if(do_after(usr,35 SECONDS,target)) //This is powerful, yo. if(!stored_mind && target.mind) - stored_mind = target.mind - target.ghostize() - stored_mind.current = null - update_icon() + get_mind(target) to_chat(usr,"Mind downloaded!") return @@ -212,12 +234,9 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob if(!sleevemate_mob) sleevemate_mob = new() - stored_mind.active = TRUE //Setting this causes transfer_to, to key them into the mob - stored_mind.transfer_to(sleevemate_mob) + put_mind(sleevemate_mob) SC.catch_mob(sleevemate_mob) - stored_mind = null to_chat(usr,"Mind transferred into Soulcatcher!") - update_icon() if(href_list["mindupload"]) if(!stored_mind) @@ -229,7 +248,7 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob if(istype(target, /mob/living/carbon/human)) var/mob/living/carbon/human/H = target - if(H.resleeve_lock && stored_mind.loaded_from_ckey != H.resleeve_lock) + if(H.resleeve_lock && stored_mind.loaded_from_ckey != H.resleeve_lock) to_chat(usr,"\[H] is protected from impersonation!") return @@ -238,11 +257,8 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob if(!stored_mind) to_chat(usr,"\The [src] no longer has a stored mind.") return - stored_mind.active = TRUE - stored_mind.transfer_to(target) - stored_mind = null + put_mind(target) to_chat(usr,"Mind transferred into [target]!") - update_icon() if(href_list["mindrelease"]) if(stored_mind) @@ -254,16 +270,12 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob return for(var/mob/living/carbon/brain/caught_soul/soul in SC.brainmobs) if(soul.name == href_list["mindrelease"]) - stored_mind = soul.mind - stored_mind.current = null - soul.Destroy() - update_icon() + get_mind(soul) + qdel(soul) to_chat(usr,"Mind downloaded!") return to_chat(usr,"Unable to find that mind in Soulcatcher!") - - /obj/item/device/sleevemate/update_icon() if(stored_mind) icon_state = "[initial(icon_state)]_on" diff --git a/code/game/objects/items/devices/text_to_speech.dm b/code/game/objects/items/devices/text_to_speech.dm index 47dfc256fc..f3feacd86e 100644 --- a/code/game/objects/items/devices/text_to_speech.dm +++ b/code/game/objects/items/devices/text_to_speech.dm @@ -25,4 +25,4 @@ var/message = sanitize(input(user,"Choose a message to relay to those around you.") as text|null) if(message) var/obj/item/device/text_to_speech/O = src - audible_message("\icon[O] \The [O.name] states, \"[message]\"") + audible_message("[bicon(O)] \The [O.name] states, \"[message]\"") diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index 655e6efe31..7a9b07b999 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -31,7 +31,7 @@ effective or pretty fucking useless. /obj/item/device/batterer/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) if(!user) return if(times_used >= max_uses) - user << "The mind batterer has been burnt out!" + to_chat(user, "The mind batterer has been burnt out!") return var/list/affected = list() @@ -43,15 +43,15 @@ effective or pretty fucking useless. M.Weaken(rand(10,20)) if(prob(25)) M.Stun(rand(5,10)) - M << "You feel a tremendous, paralyzing wave flood your mind." + to_chat(M, "You feel a tremendous, paralyzing wave flood your mind.") else - M << "You feel a sudden, electric jolt travel through your head." + to_chat(M, "You feel a sudden, electric jolt travel through your head.") add_attack_logs(user,affected,"Used a [name]") playsound(src.loc, 'sound/misc/interference.ogg', 50, 1) - user << "You trigger [src]." + to_chat(user, "You trigger [src].") times_used += 1 if(times_used >= max_uses) icon_state = "battererburnt" diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index ccd339e180..4169dfc947 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -11,28 +11,23 @@ var/toggle = 1 flags = PROXMOVE -/obj/item/device/transfer_valve/proc/process_activation(var/obj/item/device/D) - -/obj/item/device/transfer_valve/IsAssemblyHolder() - return 1 - /obj/item/device/transfer_valve/attackby(obj/item/item, mob/user) var/turf/location = get_turf(src) // For admin logs if(istype(item, /obj/item/weapon/tank)) if(tank_one && tank_two) - user << "There are already two tanks attached, remove one first." + to_chat(user, "There are already two tanks attached, remove one first.") return if(!tank_one) tank_one = item user.drop_item() item.loc = src - user << "You attach the tank to the transfer valve." + to_chat(user, "You attach the tank to the transfer valve.") else if(!tank_two) tank_two = item user.drop_item() item.loc = src - user << "You attach the tank to the transfer valve." + to_chat(user, "You attach the tank to the transfer valve.") message_admins("[key_name_admin(user)] attached both tanks to a transfer valve. (JMP)") log_game("[key_name_admin(user)] attached both tanks to a transfer valve.") @@ -42,15 +37,15 @@ else if(isassembly(item)) var/obj/item/device/assembly/A = item if(A.secured) - user << "The device is secured." + to_chat(user, "The device is secured.") return if(attached_device) - user << "There is already an device attached to the valve, remove it first." + to_chat(user, "There is already an device attached to the valve, remove it first.") return user.remove_from_mob(item) attached_device = A A.loc = src - user << "You attach the [item] to the valve controls and secure it." + to_chat(user, "You attach the [item] to the valve controls and secure it.") A.holder = src A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb). @@ -116,12 +111,11 @@ src.add_fingerprint(usr) return 1 // Returning 1 sends an update to attached UIs -/obj/item/device/transfer_valve/process_activation(var/obj/item/device/D) +/obj/item/device/transfer_valve/proc/process_activation(var/obj/item/device/D) if(toggle) - toggle = 0 + toggle = FALSE toggle_valve() - spawn(50) // To stop a signal being spammed from a proxy sensor constantly going off or whatever - toggle = 1 + VARSET_IN(src, toggle, TRUE, 5 SECONDS) /obj/item/device/transfer_valve/update_icon() overlays.Cut() diff --git a/code/game/objects/items/devices/translocator_vr.dm b/code/game/objects/items/devices/translocator_vr.dm index ef0ec42b51..f171f147c3 100644 --- a/code/game/objects/items/devices/translocator_vr.dm +++ b/code/game/objects/items/devices/translocator_vr.dm @@ -16,6 +16,7 @@ var/abductor = 0 //Can be used on teleportation blocking turfs var/list/beacons = list() + var/loc_network = null //Used if you want to create pre-made beacons on the maps var/ready = 1 var/beacons_left = 3 var/failure_chance = 5 //Percent @@ -24,8 +25,9 @@ var/list/warned_users = list() var/list/logged_events = list() -/obj/item/device/perfect_tele/New() - ..() + +/obj/item/device/perfect_tele/Initialize() + . = ..() flags |= NOBLUDGEON if(cell_type) power_source = new cell_type(src) @@ -73,6 +75,12 @@ to_chat(user,"[src] does not have a power cell.") /obj/item/device/perfect_tele/attack_self(mob/user) + if(loc_network) + for(var/obj/item/device/perfect_tele_beacon/stationary/nb in premade_tele_beacons) + if(nb.tele_network == loc_network) + beacons[nb.tele_name] = nb + loc_network = null //Consumed + if(!(user.ckey in warned_users)) warned_users |= user.ckey alert(user,"This device can be easily used to break ERP preferences due to the nature of teleporting \ @@ -209,6 +217,14 @@ ready = 0 power_source.use(charge_cost) + //Unbuckle taur riders + if(istype(target, /mob/living)) + var/mob/living/L = target + if(LAZYLEN(L.buckled_mobs)) + var/datum/riding/R = L.riding_datum + for(var/rider in L.buckled_mobs) + R.force_dismount(rider) + //Failure chance if(prob(failure_chance) && beacons.len >= 2) var/list/wrong_choices = beacons - destination.tele_name @@ -372,6 +388,7 @@ GLOBAL_LIST_BOILERPLATE(premade_tele_beacons, /obj/item/device/perfect_tele_beac /obj/item/device/perfect_tele/alien name = "alien translocator" desc = "This strange device allows one to teleport people and objects across large distances." + icon_state = "alientele" cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien charge_cost = 400 @@ -380,13 +397,26 @@ GLOBAL_LIST_BOILERPLATE(premade_tele_beacons, /obj/item/device/perfect_tele_beac longrange = 1 abductor = 1 +/obj/item/device/perfect_tele/alien/bluefo + name = "hybrid translocator" + desc = "This strange device allows one to teleport people and objects across large distances. It has only a single preprogrammed destination, though." + icon_state = "alientele" + + cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien + charge_cost = 400 + beacons_left = 0 + failure_chance = 0 + longrange = 1 + abductor = 1 + loc_network = "hybridshuttle" + /obj/item/device/perfect_tele/frontier - icon_state = "minitrans" + icon_state = "frontiertrans" beacons_left = 1 //Just one battery_lock = 1 unacidable = 1 failure_chance = 0 //Percent - var/loc_network = null + var/phase_power = 75 var/recharging = 0 @@ -421,12 +451,6 @@ GLOBAL_LIST_BOILERPLATE(premade_tele_beacons, /obj/item/device/perfect_tele_beac loc_network = "centcom" longrange = 1 -/obj/item/device/perfect_tele/frontier/staff/New() - ..() - for(var/obj/item/device/perfect_tele_beacon/stationary/nb in premade_tele_beacons) - if(nb.tele_network == loc_network) - beacons[nb.tele_name] = nb - /obj/item/device/perfect_tele/frontier/unknown name = "modified translocator" desc = "This crank-charged translocator has only one beacon, but it already has a destination preprogrammed into it." @@ -434,12 +458,6 @@ GLOBAL_LIST_BOILERPLATE(premade_tele_beacons, /obj/item/device/perfect_tele_beac longrange = 1 abductor = 1 -/obj/item/device/perfect_tele/frontier/unknown/New() - ..() - for(var/obj/item/device/perfect_tele_beacon/stationary/nb in premade_tele_beacons) - if(nb.tele_network == loc_network) - beacons[nb.tele_name] = nb - /obj/item/device/perfect_tele/frontier/unknown/one loc_network = "unkone" /obj/item/device/perfect_tele/frontier/unknown/two diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm index ea7006a73b..5a19c72742 100644 --- a/code/game/objects/items/devices/uplink.dm +++ b/code/game/objects/items/devices/uplink.dm @@ -1,3 +1,5 @@ +GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink) + /obj/item/device/uplink var/welcome = "Welcome, Operative" // Welcoming menu message var/uses // Numbers of crystals @@ -12,32 +14,29 @@ var/datum/mind/uplink_owner = null var/used_TC = 0 var/offer_time = 10 MINUTES //The time increment per discount offered - var/next_offer_time //The time a discount will next be offered + var/next_offer_time var/datum/uplink_item/discount_item //The item to be discounted var/discount_amount //The amount as a percent the item will be discounted by /obj/item/device/uplink/nano_host() return loc -/obj/item/device/uplink/New(var/location, var/datum/mind/owner = null, var/telecrystals = DEFAULT_TELECRYSTAL_AMOUNT) - ..() - src.uplink_owner = owner +/obj/item/device/uplink/Initialize(var/mapload, var/datum/mind/owner = null, var/telecrystals = DEFAULT_TELECRYSTAL_AMOUNT) + . = ..() + uplink_owner = owner purchase_log = list() - world_uplinks += src if(owner) uses = owner.tcrystals else uses = telecrystals - START_PROCESSING(SSobj, src) - -/obj/item/device/uplink/Destroy() - world_uplinks -= src - STOP_PROCESSING(SSobj, src) - return ..() + addtimer(CALLBACK(src, .proc/next_offer), offer_time) //It seems like only the /hidden type actually makes use of this... /obj/item/device/uplink/get_item_cost(var/item_type, var/item_cost) return (discount_item && (item_type == discount_item)) ? max(1, round(item_cost*discount_amount)) : item_cost +/obj/item/device/uplink/proc/next_offer() + return //Stub, used on children. + // HIDDEN UPLINK - Can be stored in anything but the host item has to have a trigger for it. /* How to create an uplink in 3 easy steps! @@ -58,21 +57,20 @@ var/exploit_id // Id of the current exploit record we are viewing // The hidden uplink MUST be inside an obj/item's contents. -/obj/item/device/uplink/hidden/New() - spawn(2) - if(!istype(src.loc, /obj/item)) - qdel(src) - ..() +/obj/item/device/uplink/hidden/Initialize() + . = ..() + if(!isitem(loc)) + return INITIALIZE_HINT_QDEL nanoui_data = list() update_nano_data() -/obj/item/device/uplink/hidden/process() - if(world.time > next_offer_time) - discount_item = default_uplink_selection.get_random_item(INFINITY) - discount_amount = pick(90;0.9, 80;0.8, 70;0.7, 60;0.6, 50;0.5, 40;0.4, 30;0.3, 20;0.2, 10;0.1) - next_offer_time = world.time + offer_time - update_nano_data() - SSnanoui.update_uis(src) +/obj/item/device/uplink/hidden/next_offer() + discount_item = default_uplink_selection.get_random_item(INFINITY) + discount_amount = pick(90;0.9, 80;0.8, 70;0.7, 60;0.6, 50;0.5, 40;0.4, 30;0.3, 20;0.2, 10;0.1) + update_nano_data() + SSnanoui.update_uis(src) + next_offer_time = world.time + offer_time + addtimer(CALLBACK(src, .proc/next_offer), offer_time) // Toggles the uplink on and off. Normally this will bypass the item's normal functions and go to the uplink menu, if activated. /obj/item/device/uplink/hidden/proc/toggle() @@ -112,7 +110,7 @@ // update the ui if it exists, returns null if no ui is passed/found ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) // No auto-refresh - ui = new(user, src, ui_key, "uplink.tmpl", title, 450, 600, state = inventory_state) + ui = new(user, src, ui_key, "uplink.tmpl", title, 630, 700, state = inventory_state) data["menu"] = 0 ui.set_initial_data(data) ui.open() @@ -146,8 +144,8 @@ nanoui_menu = text2num(href_list["menu"]) if(href_list["id"]) exploit_id = href_list["id"] - if(href_list["category"]) - category = locate(href_list["category"]) in uplink.categories + else if(href_list["category"]) + category = locate(href_list["category"]) in uplink.categories update_nano_data() return 1 @@ -162,14 +160,17 @@ nanoui_data["discount_name"] = discount_item ? discount_item.name : "" nanoui_data["discount_amount"] = (1-discount_amount)*100 nanoui_data["offer_expiry"] = worldtime2stationtime(next_offer_time) - else if(nanoui_menu == 1) - var/items[0] - for(var/datum/uplink_item/item in category.items) - if(item.can_view(src)) - var/cost = item.cost(uses, src) - if(!cost) cost = "???" - items[++items.len] = list("name" = item.name, "description" = replacetext(item.description(), "\n", "
"), "can_buy" = item.can_buy(src), "cost" = cost, "ref" = "\ref[item]") - nanoui_data["items"] = items + + if(category) + nanoui_data["current_category"] = category.name + var/items[0] + for(var/datum/uplink_item/item in category.items) + if(item.can_view(src)) + var/cost = item.cost(uses, src) + if(!cost) cost = "???" + items[++items.len] = list("name" = item.name, "description" = replacetext(item.description(), "\n", "
"), "can_buy" = item.can_buy(src), "cost" = cost, "ref" = "\ref[item]") + nanoui_data["items"] = items + else if(nanoui_menu == 2) var/permanentData[0] for(var/datum/data/record/L in sortRecord(data_core.locked)) diff --git a/code/game/objects/items/devices/uplink_random_lists.dm b/code/game/objects/items/devices/uplink_random_lists.dm index 23dc77d143..de05f91013 100644 --- a/code/game/objects/items/devices/uplink_random_lists.dm +++ b/code/game/objects/items/devices/uplink_random_lists.dm @@ -114,5 +114,5 @@ var/datum/uplink_random_selection/all_uplink_selection = new/datum/uplink_random /proc/debug_uplink_item_assoc_list() for(var/key in uplink.items_assoc) - world << "[key] - [uplink.items_assoc[key]]" + to_world("[key] - [uplink.items_assoc[key]]") #endif diff --git a/code/game/objects/items/devices/whistle.dm b/code/game/objects/items/devices/whistle.dm index 52a830f811..c26d3f80aa 100644 --- a/code/game/objects/items/devices/whistle.dm +++ b/code/game/objects/items/devices/whistle.dm @@ -16,7 +16,7 @@ set desc = "Alter the message shouted by your hailer." if(!isnull(insults)) - usr << "The hailer is fried. The tiny input screen just shows a waving ASCII penis." + to_chat(usr, "The hailer is fried. The tiny input screen just shows a waving ASCII penis.") return var/new_message = input(usr, "Please enter new message (leave blank to reset).") as text @@ -25,7 +25,7 @@ else use_message = capitalize(copytext(sanitize(new_message), 1, MAX_MESSAGE_LEN)) - usr << "You configure the hailer to shout \"[use_message]\"." + to_chat(usr, "You configure the hailer to shout \"[use_message]\".") /obj/item/device/hailer/attack_self(mob/living/carbon/user as mob) if (spamcheck) @@ -41,7 +41,7 @@ user.audible_message("[user]'s [name] gurgles something indecipherable and deeply offensive.", "\The [user] holds up \the [name].") insults-- else - user << "*BZZZZZZZZT*" + to_chat(user, "*BZZZZZZZZT*") spamcheck = 1 spawn(20) @@ -49,8 +49,8 @@ /obj/item/device/hailer/emag_act(var/remaining_charges, var/mob/user) if(isnull(insults)) - user << "You overload \the [src]'s voice synthesizer." + to_chat(user, "You overload \the [src]'s voice synthesizer.") insults = rand(1, 3)//to prevent dickflooding return 1 else - user << "The hailer is fried. You can't even fit the sequencer into the input slot." + to_chat(user, "The hailer is fried. You can't even fit the sequencer into the input slot.") diff --git a/code/game/objects/items/falling_object_vr.dm b/code/game/objects/items/falling_object_vr.dm index 3c8c6e39aa..038fc51025 100644 --- a/code/game/objects/items/falling_object_vr.dm +++ b/code/game/objects/items/falling_object_vr.dm @@ -51,16 +51,3 @@ /obj/effect/falling_effect/ex_act() return - - -/obj/effect/falling_effect/pizza_delivery - name = "PIZZA PIE POWER!" - crushing = FALSE - -/obj/effect/falling_effect/pizza_delivery/Initialize(mapload) - ..() - falling_type = pick(prob(25);/obj/item/pizzabox/meat, - prob(25);/obj/item/pizzabox/margherita, - prob(25);/obj/item/pizzabox/vegetable, - prob(25);/obj/item/pizzabox/mushroom) - return INITIALIZE_HINT_LATELOAD diff --git a/code/game/objects/items/glassjar.dm b/code/game/objects/items/glassjar.dm index ce5bfbe91e..60fd8b19eb 100644 --- a/code/game/objects/items/glassjar.dm +++ b/code/game/objects/items/glassjar.dm @@ -22,7 +22,7 @@ if(istype(A, D)) accept = 1 if(!accept) - user << "[A] doesn't fit into \the [src]." + to_chat(user, "[A] doesn't fit into \the [src].") return var/mob/L = A user.visible_message("[user] scoops [L] into \the [src].", "You scoop [L] into \the [src].") @@ -44,7 +44,7 @@ if(1) for(var/obj/O in src) O.loc = user.loc - user << "You take money out of \the [src]." + to_chat(user, "You take money out of \the [src].") contains = 0 update_icon() return diff --git a/code/game/objects/items/godfigures.dm b/code/game/objects/items/godfigures.dm index b1b1e7a4aa..195081aae5 100644 --- a/code/game/objects/items/godfigures.dm +++ b/code/game/objects/items/godfigures.dm @@ -110,7 +110,7 @@ else if(options[choice] == "catrobe") desc = "A painted holy figure of a plain looking Tajaran in a robe." - M << "The religious icon is now a [choice]. All hail!" + to_chat(M, "The religious icon is now a [choice]. All hail!") return 1 @@ -127,5 +127,5 @@ if(src && input && !M.stat && in_range(M,src)) name = "icon of " + input - M << "You name the figure. Glory to [input]!." + to_chat(M, "You name the figure. Glory to [input]!.") return 1 \ No newline at end of file diff --git a/code/game/objects/items/pizza_voucher_vr.dm b/code/game/objects/items/pizza_voucher_vr.dm index c7e75680e1..e2efd9c6b6 100644 --- a/code/game/objects/items/pizza_voucher_vr.dm +++ b/code/game/objects/items/pizza_voucher_vr.dm @@ -25,19 +25,11 @@ user.visible_message("[user] presses a button on [src]!") desc = desc + " This one seems to be used-up." spent = TRUE + user.visible_message("A small bluespace rift opens just above your head and spits out a pizza box!") if(special_delivery) - var/delivery = pick(prob(25);/obj/item/pizzabox/meat, - prob(25);/obj/item/pizzabox/margherita, - prob(25);/obj/item/pizzabox/vegetable, - prob(25);/obj/item/pizzabox/mushroom) - command_announcement.Announce("SPECIAL DELIVERY PIZZA ORDER #[rand(1000,9999)]-[rand(100,999)] HAS BEEN RECIEVED. SHIPMENT DISPATCHED VIA BALLISTIC SUPPLY POD FOR IMMEDIATE DELIVERY! THANK YOU AND ENJOY YOUR PIZZA!", "WE ALWAYS DELIVER!") - var/crash_x = user.x - var/crash_y = user.y - var/crash_z = user.z - spawn(rand(30, 75)) - new /datum/random_map/droppod/pizza(null, crash_x, crash_y, crash_z, automated = TRUE, supplied_drop = delivery) // Splat. + command_announcement.Announce("SPECIAL DELIVERY PIZZA ORDER #[rand(1000,9999)]-[rand(100,999)] HAS BEEN RECIEVED. SHIPMENT DISPATCHED VIA EXTRA-POWERFUL BALLISTIC LAUNCHERS FOR IMMEDIATE DELIVERY! THANK YOU AND ENJOY YOUR PIZZA!", "WE ALWAYS DELIVER!") + new /obj/effect/falling_effect/pizza_delivery/special(user.loc) else - user.visible_message("A small bluespace rift opens just above your head and spits out a pizza box!") new /obj/effect/falling_effect/pizza_delivery(user.loc) else to_chat(user, "The [src] is spent!") @@ -53,9 +45,18 @@ else to_chat(user, "The [src] is already in special delivery mode!") +/obj/effect/falling_effect/pizza_delivery + name = "PIZZA PIE POWER!" + crushing = FALSE -/datum/random_map/droppod/pizza - placement_explosion_dev = 0 - placement_explosion_heavy = 1 - placement_explosion_light = 2 - placement_explosion_flash = 4 \ No newline at end of file +/obj/effect/falling_effect/pizza_delivery/Initialize(mapload) + ..() + falling_type = pick(prob(20);/obj/item/pizzabox/meat, + prob(20);/obj/item/pizzabox/margherita, + prob(20);/obj/item/pizzabox/vegetable, + prob(20);/obj/item/pizzabox/mushroom, + prob(20);/obj/item/pizzabox/pineapple) + return INITIALIZE_HINT_LATELOAD + +/obj/effect/falling_effect/pizza_delivery/special + crushing = TRUE \ No newline at end of file diff --git a/code/game/objects/items/poi_items.dm b/code/game/objects/items/poi_items.dm index c12a3a655a..b86d93aec8 100644 --- a/code/game/objects/items/poi_items.dm +++ b/code/game/objects/items/poi_items.dm @@ -3,10 +3,30 @@ icon = 'icons/obj/objects.dmi' desc = "This is definitely something cool." +/datum/category_item/catalogue/information/objects/pascalb + name = "Object - Pascal B Steel Shaft Cap" + desc = "In the year 1957, the United States of America - an Earth nation - performed a series \ + of earth nuclear weapons tests codenamed 'Operation Plumbbob', which remain the largest and \ + longest running nuclear test series performed on the American continent. Test data included \ + various altitude detonations, effects on several materials and structures at various \ + distances, and the effects of radiation on military hardware and the human body. \ +

\ + On the 27th of August that year, in a test named 'Pascal-B' a 300t nuclear payload \ + was buried in a shaft capped by a 900kg steel plate cap. The test was intended to \ + verify the safety of underground detonation, but the shaft was not sufficient to \ + contain the shockwave. According to experiment designer Robert Brownlee, the steel \ + cap was propelled upwards at a velocity of 240,000km/h - over six times Earth's \ + escape velocity. The cap appeared in only one frame of high-speed camera recording. \ +

\ + It had been theorized that the cap had exited earth's atmosphere and entered orbit. \ + It would seem the cap traveled farther than had been possibly imagined." + value = CATALOGUER_REWARD_MEDIUM + /obj/item/poi/pascalb icon_state = "pascalb" name = "misshapen manhole cover" desc = "The top of this twisted chunk of metal is faintly stamped with a five pointed star. 'Property of US Army, Pascal B - 1957'." + catalogue_data = list(/datum/category_item/catalogue/information/objects/pascalb) /obj/item/poi/pascalb/New() START_PROCESSING(SSobj, src) @@ -19,6 +39,25 @@ STOP_PROCESSING(SSobj, src) return ..() +/datum/category_item/catalogue/information/objects/oldreactor + name = "Object - 24th Century Fission Reactor Rack" + desc = "Prior to the discovery of Phoron in 2380, and the development of the hydrophoron \ + supermatter reactor, most spacecraft operated on nuclear fission reactors, using processed \ + radioactive material as fuel. While the design had been near-perfected by the 24th century, \ + with some models capable of holding hundreds of fuel rods at one time and operating almost \ + unsupervised for weeks at a time.\ +

\ + However, as accidents were not uncommon due to the inherent dangers of space travel and the \ + nature of reactor racks such as this one fully containing the unstable fuel material, many \ + fission vessels were built capable of jettisoning their entire engine sections as it was seen \ + as preferable to evacuating a ship's crew and potentially losing the entire craft and its cargo. \ +

\ + VifGov records indicate that the colony ship ICV Kauai declared a major onboard emergency in Sif orbit \ + on the 14th April 2353, citing major systems malfunction following a fire in the engine compartment. \ + Due to the relatively sparse population of the planet, it was deemed safe to jettison both engine \ + blocks, and the colonists were safely towed to port with no hands lost." + value = CATALOGUER_REWARD_MEDIUM + /obj/structure/closet/crate/oldreactor name = "fission reactor rack" desc = "Used in older models of nuclear reactors, essentially a cooling rack for high volumes of radioactive material." @@ -26,6 +65,7 @@ icon_state = "poireactor" icon_opened = "poireactor_open" icon_closed = "poireactor" + catalogue_data = list(/datum/category_item/catalogue/information/objects/oldreactor) climbable = 0 starts_with = list( @@ -35,6 +75,7 @@ icon_state = "poireactor_broken" name = "ruptured fission reactor rack" desc = "This broken hunk of machinery looks extremely dangerous." + catalogue_data = list(/datum/category_item/catalogue/information/objects/oldreactor) /obj/item/poi/brokenoldreactor/New() START_PROCESSING(SSobj, src) @@ -47,3 +88,29 @@ STOP_PROCESSING(SSobj, src) return ..() +/datum/category_item/catalogue/information/objects/growthcanister + name = "Object - Growth Inhibitor 78-1" + desc = "The production of Vatborn humans is a process which involves the synthesis of over two hundred \ + distinct chemical compounds. While most Vatborn are 'produced' as infants and merely genetically modified \ + to encourage rapid early maturation, the specific development of the controversial 'Expedited' Vatborn calls for \ + a far more intensive process.\ +

\ + Growth Inhibitor Type 78-1 is used in the rapid artificial maturation process to prevent the 'overdevelopment' of\ + particular cell structures in the Vatborn's body, halting the otherwise inevitable development of aggressive cancerous\ + growths which would be detrimental or lethal to the subject. Exposure to the compound in its pure form can cause\ + devastating damage to living tissue, ceasing all regenerative activity in an organism's cells. While immediate effects\ + can be halted by recent medical innovations, exposure can severely shorten a sapient's life expectancy.\ +

\ + In early 2564, the NanoTrasen corporation was implicated in the accidental spillage of over a dozen full cargo containers\ + of Growth Inhibitor 78-1 in the Ullran Expanse of Sif, and were charged by the Sif Environmental Agency with extreme \ + environmental damage and neglect." + value = CATALOGUER_REWARD_MEDIUM + +/obj/structure/prop/poicanister + name = "Ruptured Chemical Canister" + desc = "A cracked open chemical canister labelled 'Growth Inhibitor 78-1'" + icon = 'icons/obj/atmos.dmi' + icon_state = "yellow-1" + catalogue_data = list(/datum/category_item/catalogue/information/objects/growthcanister) + anchored = 0 + density = 1 \ No newline at end of file diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index fe1685ec70..602f6e3a66 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -66,7 +66,7 @@ if(ghost.mind && ghost.mind.current == R) R.key = ghost.key - R.stat = CONSCIOUS + R.set_stat(CONSCIOUS) dead_mob_list -= R living_mob_list |= R R.notify_ai(ROBOT_NOTIFICATION_NEW_UNIT) diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index e24a727b99..c3bc83d3b9 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -37,7 +37,7 @@ var/obj/item/weapon/weldingtool/WT = W if(WT.remove_fuel(0, user)) overlays.Cut() - usr << "You slice off [src]'s uneven chunks of aluminum and scorch marks." + to_chat(usr, "You slice off [src]'s uneven chunks of aluminum and scorch marks.") return @@ -59,10 +59,10 @@ if(ishuman(user)) if(!user.get_active_hand()) user.put_in_hands(src) - user << "You take the target out of the stake." + to_chat(user, "You take the target out of the stake.") else src.loc = get_turf(user) - user << "You take the target out of the stake." + to_chat(user, "You take the target out of the stake.") stake.pinned_target = null return @@ -96,7 +96,7 @@ if(hp <= 0) for(var/mob/O in oviewers()) if ((O.client && !( O.blinded ))) - O << "\The [src] breaks into tiny pieces and collapses!" + to_chat(O, "\The [src] breaks into tiny pieces and collapses!") qdel(src) // Create a temporary object to represent the damage diff --git a/code/game/objects/items/stacks/fifty_spawner.dm b/code/game/objects/items/stacks/fifty_spawner.dm index 0be33e5829..6cd9051c6f 100644 --- a/code/game/objects/items/stacks/fifty_spawner.dm +++ b/code/game/objects/items/stacks/fifty_spawner.dm @@ -4,21 +4,15 @@ desc = "This item spawns stack of 50 of a given material." icon = 'icons/misc/mark.dmi' icon_state = "x4" -// var/material = "" var/obj/item/stack/type_to_spawn = null -/obj/fiftyspawner/New() - spawn() - //spawns the 50-stack and qdels self - ..() - if(istype(src.loc, /obj/structure/loot_pile)) //Spawning from a lootpile is weird, need to wait until we're out of it to do our work. - while(istype(src.loc, /obj/structure/loot_pile)) - sleep(1) - // var/obj_path = text2path("/obj/item/stack/[material]") - var/obj/item/stack/M = new type_to_spawn(src.loc) - M.amount = M.max_amount //some stuff spawns with 60, we're still calling it fifty - M.update_icon() // Some stacks have different sprites depending on how full they are. - qdel(src) +/obj/fiftyspawner/Initialize() + ..() //We're not returning . because we're going to ask to be deleted. + + var/obj/item/stack/M = new type_to_spawn(get_turf(src)) + M.amount = M.max_amount //some stuff spawns with 60, we're still calling it fifty + M.update_icon() // Some stacks have different sprites depending on how full they are. + return INITIALIZE_HINT_QDEL //Bye! /obj/fiftyspawner/rods name = "stack of rods" //this needs to be defined for cargo diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 51e2b8a03e..8887dd0053 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -10,17 +10,18 @@ var/heal_brute = 0 var/heal_burn = 0 var/apply_sounds + drop_sound = 'sound/items/drop/box.ogg' var/upgrade_to // The type path this stack can be upgraded to. /obj/item/stack/medical/attack(mob/living/carbon/M as mob, mob/user as mob) if (!istype(M)) - user << "\The [src] cannot be applied to [M]!" + to_chat(user, "\The [src] cannot be applied to [M]!") return 1 if ( ! (istype(user, /mob/living/carbon/human) || \ istype(user, /mob/living/silicon)) ) - user << "You don't have the dexterity to do this!" + to_chat(user, "You don't have the dexterity to do this!") return 1 if (istype(M, /mob/living/carbon/human)) @@ -28,24 +29,24 @@ var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) if(!affecting) - user << "No body part there to work on!" + to_chat(user, "No body part there to work on!") return 1 if(affecting.organ_tag == BP_HEAD) if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) - user << "You can't apply [src] through [H.head]!" + to_chat(user, "You can't apply [src] through [H.head]!") return 1 else if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) - user << "You can't apply [src] through [H.wear_suit]!" + to_chat(user, "You can't apply [src] through [H.wear_suit]!") return 1 if(affecting.robotic == ORGAN_ROBOT) - user << "This isn't useful at all on a robotic limb." + to_chat(user, "This isn't useful at all on a robotic limb.") return 1 if(affecting.robotic >= ORGAN_LIFELIKE) - user << "You apply the [src], but it seems to have no effect..." + to_chat(user, "You apply the [src], but it seems to have no effect...") use(1) return 1 @@ -143,6 +144,7 @@ origin_tech = list(TECH_BIO = 1) no_variants = FALSE apply_sounds = list('sound/effects/rip1.ogg','sound/effects/rip2.ogg') + drop_sound = 'sound/items/drop/gloves.ogg' upgrade_to = /obj/item/stack/medical/advanced/bruise_pack @@ -212,6 +214,7 @@ origin_tech = list(TECH_BIO = 1) no_variants = FALSE apply_sounds = list('sound/effects/ointment.ogg') + drop_sound = 'sound/items/drop/herb.ogg' /obj/item/stack/medical/ointment/attack(mob/living/carbon/M as mob, mob/user as mob) if(..()) @@ -354,6 +357,7 @@ icon_state = "splint" amount = 5 max_amount = 5 + drop_sound = 'sound/items/drop/hat.ogg' var/list/splintable_organs = list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO) //List of organs you can splint, natch. diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm index 1170fedc04..bc1f6486c1 100644 --- a/code/game/objects/items/stacks/nanopaste.dm +++ b/code/game/objects/items/stacks/nanopaste.dm @@ -24,11 +24,24 @@ user.visible_message("\The [user] applied some [src] on [R]'s damaged areas.",\ "You apply some [src] at [R]'s damaged areas.") else - user << "All [R]'s systems are nominal." + to_chat(user, "All [R]'s systems are nominal.") if (istype(M,/mob/living/carbon/human)) //Repairing robolimbs var/mob/living/carbon/human/H = M var/obj/item/organ/external/S = H.get_organ(user.zone_sel.selecting) + if(!S) + to_chat(user, "No body part there to work on!") + return 1 + + if(S.organ_tag == BP_HEAD) + if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) + to_chat(user, "You can't apply [src] through [H.head]!") + return 1 + else + if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) + to_chat(user, "You can't apply [src] through [H.wear_suit]!") + return 1 + //VOREStation Edit Start if (S && (S.robotic >= ORGAN_ROBOT)) if(!S.get_damage()) diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 53dc72faaa..f571123fc1 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -16,6 +16,7 @@ icon_state = "sheet-glass" var/is_reinforced = 0 default_type = "glass" + drop_sound = 'sound/items/drop/glass.ogg' /obj/item/stack/material/glass/attack_self(mob/user as mob) construct_window(user) @@ -26,17 +27,17 @@ if(istype(W,/obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/CC = W if (get_amount() < 1 || CC.get_amount() < 5) - user << "You need five lengths of coil and one sheet of glass to make wired glass." + to_chat(user, "You need five lengths of coil and one sheet of glass to make wired glass.") return CC.use(5) use(1) - user << "You attach wire to the [name]." + to_chat(user, "You attach wire to the [name].") new /obj/item/stack/light_w(user.loc) else if(istype(W, /obj/item/stack/rods)) var/obj/item/stack/rods/V = W if (V.get_amount() < 1 || get_amount() < 1) - user << "You need one rod and one sheet of glass to make reinforced glass." + to_chat(user, "You need one rod and one sheet of glass to make reinforced glass.") return var/obj/item/stack/material/glass/reinforced/RG = new (user.loc) diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm index 531fcbf144..255a739d84 100644 --- a/code/game/objects/items/stacks/sheets/leather.dm +++ b/code/game/objects/items/stacks/sheets/leather.dm @@ -4,6 +4,7 @@ singular_name = "human skin piece" icon_state = "sheet-hide" no_variants = FALSE + drop_sound = 'sound/items/drop/clothing.ogg' /obj/item/stack/material/animalhide/human amount = 50 @@ -107,7 +108,7 @@ //visible message on mobs is defined as visible_message(var/message, var/self_message, var/blind_message) usr.visible_message("\The [usr] starts cutting hair off \the [src]", "You start cutting the hair off \the [src]", "You hear the sound of a knife rubbing against flesh") if(do_after(user,50)) - usr << "You cut the hair from this [src.singular_name]" + to_chat(usr, "You cut the hair from this [src.singular_name]") //Try locating an exisitng stack on the tile and add to there if possible for(var/obj/item/stack/material/hairlesshide/HS in usr.loc) if(HS.amount < 50) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 984c5b97f6..324ade23ce 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -13,6 +13,8 @@ gender = PLURAL origin_tech = list(TECH_MATERIAL = 1) icon = 'icons/obj/stacks.dmi' + randpixel = 7 + center_of_mass = null var/list/datum/stack_recipe/recipes var/singular_name var/amount = 1 @@ -58,9 +60,9 @@ /obj/item/stack/examine(mob/user) if(..(user, 1)) if(!uses_charge) - user << "There are [src.amount] [src.singular_name]\s in the stack." + to_chat(user, "There are [src.amount] [src.singular_name]\s in the stack.") else - user << "There is enough charge for [get_amount()]." + to_chat(user, "There is enough charge for [get_amount()].") /obj/item/stack/attack_self(mob/user as mob) list_recipes(user) @@ -126,21 +128,21 @@ if (!can_use(required)) if (produced>1) - user << "You haven't got enough [src] to build \the [produced] [recipe.title]\s!" + to_chat(user, "You haven't got enough [src] to build \the [produced] [recipe.title]\s!") else - user << "You haven't got enough [src] to build \the [recipe.title]!" + to_chat(user, "You haven't got enough [src] to build \the [recipe.title]!") return if (recipe.one_per_turf && (locate(recipe.result_type) in user.loc)) - user << "There is another [recipe.title] here!" + to_chat(user, "There is another [recipe.title] here!") return if (recipe.on_floor && !isfloor(user.loc)) - user << "\The [recipe.title] must be constructed on the floor!" + to_chat(user, "\The [recipe.title] must be constructed on the floor!") return if (recipe.time) - user << "Building [recipe.title] ..." + to_chat(user, "Building [recipe.title] ...") if (!do_after(user, recipe.time)) return @@ -326,7 +328,7 @@ continue var/transfer = src.transfer_to(item) if (transfer) - user << "You add a new [item.singular_name] to the stack. It now contains [item.amount] [item.singular_name]\s." + to_chat(user, "You add a new [item.singular_name] to the stack. It now contains [item.amount] [item.singular_name]\s.") if(!amount) break diff --git a/code/game/objects/items/stacks/telecrystal.dm b/code/game/objects/items/stacks/telecrystal.dm index b0c48fffa2..0a6d47b3c0 100644 --- a/code/game/objects/items/stacks/telecrystal.dm +++ b/code/game/objects/items/stacks/telecrystal.dm @@ -16,11 +16,11 @@ safe_blink(target, 14) use(5) else - user << "There are not enough telecrystals to do that." + to_chat(user, "There are not enough telecrystals to do that.") /obj/item/stack/telecrystal/attack_self(mob/user as mob) if(user.mind.accept_tcrystals) //Checks to see if antag type allows for tcrystals - user << "You use \the [src], adding [src.amount] to your balance." + to_chat(user, "You use \the [src], adding [src.amount] to your balance.") user.mind.tcrystals += amount use(amount) return \ No newline at end of file diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 7c441e8169..b1e86987ac 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -14,13 +14,14 @@ name = "tile" singular_name = "tile" desc = "A non-descript floor tile" + randpixel = 7 w_class = ITEMSIZE_NORMAL max_amount = 60 + drop_sound = 'sound/items/drop/axe.ogg' /obj/item/stack/tile/New() ..() - pixel_x = rand(-7, 7) - pixel_y = rand(-7, 7) + randpixel_xy() /* * Grass @@ -37,6 +38,7 @@ flags = 0 origin_tech = list(TECH_BIO = 1) no_variants = FALSE + drop_sound = 'sound/items/drop/herb.ogg' /* * Wood */ @@ -51,6 +53,7 @@ throw_range = 20 flags = 0 no_variants = FALSE + drop_sound = 'sound/items/drop/wooden.ogg' /obj/item/stack/tile/wood/sif name = "alien wood tile" @@ -80,6 +83,7 @@ throw_range = 20 flags = 0 no_variants = FALSE + drop_sound = 'sound/items/drop/clothing.ogg' /obj/item/stack/tile/carpet/teal name = "teal carpet" diff --git a/code/game/objects/items/tailoring.dm b/code/game/objects/items/tailoring.dm new file mode 100644 index 0000000000..4dc359ba33 --- /dev/null +++ b/code/game/objects/items/tailoring.dm @@ -0,0 +1,7 @@ +// I like the idea of this item having more uses in future. + +/obj/item/device/threadneedle + name = "thread and needle" + icon = 'icons/obj/items.dmi' + icon_state = "needle_thread" + desc = "Used for most sewing and tailoring applications." diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 92269ead77..cb177e0080 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -26,6 +26,7 @@ throw_speed = 4 throw_range = 20 force = 0 + drop_sound = 'sound/items/drop/gloves.ogg' /* @@ -36,6 +37,7 @@ desc = "A translucent balloon. There's nothing in it." icon = 'icons/obj/toy.dmi' icon_state = "waterballoon-e" + drop_sound = 'sound/items/drop/rubber.ogg' /obj/item/toy/balloon/New() var/datum/reagents/R = new/datum/reagents(10) @@ -49,7 +51,7 @@ if(!proximity) return if (istype(A, /obj/structure/reagent_dispensers/watertank) && get_dist(src,A) <= 1) A.reagents.trans_to_obj(src, 10) - user << "You fill the balloon with the contents of [A]." + to_chat(user, "You fill the balloon with the contents of [A].") src.desc = "A translucent balloon with some form of liquid sloshing around in it." src.update_icon() return @@ -58,15 +60,15 @@ if(istype(O, /obj/item/weapon/reagent_containers/glass)) if(O.reagents) if(O.reagents.total_volume < 1) - user << "The [O] is empty." + to_chat(user, "The [O] is empty.") else if(O.reagents.total_volume >= 1) if(O.reagents.has_reagent("pacid", 1)) - user << "The acid chews through the balloon!" + to_chat(user, "The acid chews through the balloon!") O.reagents.splash(user, reagents.total_volume) qdel(src) else src.desc = "A translucent balloon with some form of liquid sloshing around in it." - user << "You fill the balloon with the contents of [O]." + to_chat(user, "You fill the balloon with the contents of [O].") O.reagents.trans_to_obj(src, 10) src.update_icon() return @@ -99,6 +101,7 @@ icon = 'icons/obj/weapons.dmi' icon_state = "syndballoon" w_class = ITEMSIZE_LARGE + drop_sound = 'sound/items/drop/rubber.ogg' /obj/item/toy/nanotrasenballoon name = "criminal balloon" @@ -110,6 +113,7 @@ icon = 'icons/obj/weapons.dmi' icon_state = "ntballoon" w_class = ITEMSIZE_LARGE + drop_sound = 'sound/items/drop/rubber.ogg' /* * Fake telebeacon @@ -147,10 +151,11 @@ w_class = ITEMSIZE_SMALL attack_verb = list("attacked", "struck", "hit") var/bullets = 5 + drop_sound = 'sound/items/drop/gun.ogg' examine(mob/user) if(..(user, 2) && bullets) - user << "It is loaded with [bullets] foam darts!" + to_chat(user, "It is loaded with [bullets] foam darts!") attackby(obj/item/I as obj, mob/user as mob) if(istype(I, /obj/item/toy/ammo/crossbow)) @@ -158,9 +163,9 @@ user.drop_item() qdel(I) bullets++ - user << "You load the foam dart into the crossbow." + to_chat(user, "You load the foam dart into the crossbow.") else - usr << "It's already fully loaded." + to_chat(usr, "It's already fully loaded.") afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) @@ -239,6 +244,7 @@ icon_state = "foamdart" w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS + drop_sound = 'sound/items/drop/food.ogg' /obj/effect/foam_dart_dummy name = "" @@ -256,6 +262,7 @@ desc = "A cheap, plastic replica of an energy sword. Realistic sounds! Ages 8 and up." icon = 'icons/obj/weapons.dmi' icon_state = "esword" + drop_sound = 'sound/items/drop/gun.ogg' var/lcolor var/rainbow = FALSE item_icons = list( @@ -269,12 +276,12 @@ attack_self(mob/user as mob) src.active = !( src.active ) if (src.active) - user << "You extend the plastic blade with a quick flick of your wrist." + to_chat(user, "You extend the plastic blade with a quick flick of your wrist.") playsound(user, 'sound/weapons/saberon.ogg', 50, 1) src.item_state = "[icon_state]_blade" src.w_class = ITEMSIZE_LARGE else - user << "You push the plastic blade back down into the handle." + to_chat(user, "You push the plastic blade back down into the handle.") playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) src.item_state = "[icon_state]" src.w_class = ITEMSIZE_SMALL @@ -344,6 +351,7 @@ icon = 'icons/obj/toy.dmi' icon_state = "snappop" w_class = ITEMSIZE_TINY + drop_sound = null throw_impact(atom/hit_atom) ..() @@ -355,17 +363,13 @@ playsound(src, 'sound/effects/snap.ogg', 50, 1) qdel(src) -/obj/item/toy/snappop/Crossed(H as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = H - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/item/toy/snappop/Crossed(atom/movable/H as mob|obj) + if(H.is_incorporeal()) + return if((ishuman(H))) //i guess carp and shit shouldn't set them off var/mob/living/carbon/M = H if(M.m_intent == "run") - M << "You step on the snap pop!" + to_chat(M, "You step on the snap pop!") var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(2, 0, src) @@ -382,6 +386,7 @@ name = "water flower" desc = "A seemingly innocent sunflower...with a twist." icon = 'icons/obj/device.dmi' + drop_sound = 'sound/items/drop/food.ogg' icon_state = "sunflower" item_state = "sunflower" var/empty = 0 @@ -405,13 +410,13 @@ return else if (istype(A, /obj/structure/reagent_dispensers/watertank) && get_dist(src,A) <= 1) - A.reagents.trans_to(src, 10) - user << "You refill your flower!" + A.reagents.trans_to_obj(src, 10) + to_chat(user, "You refill your flower!") return else if (src.reagents.total_volume < 1) src.empty = 1 - user << "Your flower has run dry!" + to_chat(user, "Your flower has run dry!") return else @@ -433,7 +438,7 @@ for(var/atom/T in get_turf(D)) D.reagents.touch(T) if(ismob(T) && T:client) - T:client << "\The [user] has sprayed you with water!" + to_chat(T:client, "\The [user] has sprayed you with water!") sleep(4) qdel(D) @@ -441,7 +446,7 @@ /obj/item/toy/waterflower/examine(mob/user) if(..(user, 0)) - user << text("\icon[] [] units of water left!", src, src.reagents.total_volume) + to_chat(user, "[bicon(src)] [src.reagents.total_volume] units of water left!") /* * Bosun's whistle @@ -452,13 +457,14 @@ desc = "A genuine Admiral Krush Bosun's Whistle, for the aspiring ship's captain! Suitable for ages 8 and up, do not swallow." icon = 'icons/obj/toy.dmi' icon_state = "bosunwhistle" + drop_sound = 'sound/items/drop/card.ogg' var/cooldown = 0 w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS | SLOT_HOLSTER /obj/item/toy/bosunwhistle/attack_self(mob/user as mob) if(cooldown < world.time - 35) - user << "You blow on [src], creating an ear-splitting noise!" + to_chat(user, "You blow on [src], creating an ear-splitting noise!") playsound(user, 'sound/misc/boatswain.ogg', 20, 1) cooldown = world.time @@ -469,18 +475,19 @@ icon = 'icons/obj/toy.dmi' icon_state = "ripleytoy" var/cooldown = 0 + drop_sound = 'sound/mecha/mechstep.ogg' //all credit to skasi for toy mech fun ideas /obj/item/toy/prize/attack_self(mob/user as mob) if(cooldown < world.time - 8) - user << "You play with [src]." + to_chat(user, "You play with [src].") playsound(user, 'sound/mecha/mechstep.ogg', 20, 1) cooldown = world.time /obj/item/toy/prize/attack_hand(mob/user as mob) if(loc == user) if(cooldown < world.time - 8) - user << "You play with [src]." + to_chat(user, "You play with [src].") playsound(user, 'sound/mecha/mechturn.ogg', 20, 1) cooldown = world.time return @@ -550,6 +557,7 @@ icon_state = "nuketoy" var/cooldown = 0 var/toysay = "What the fuck did you do?" + drop_sound = 'sound/items/drop/accessory.ogg' /obj/item/toy/figure/New() ..() @@ -880,9 +888,31 @@ anchored = 0 density = 1 var/phrase = "I don't want to exist anymore!" + var/searching = FALSE + var/opened = FALSE // has this been slit open? this will allow you to store an object in a plushie. + var/obj/item/stored_item // Note: Stored items can't be bigger than the plushie itself. + +/obj/structure/plushie/examine(mob/user) + ..() + if(opened) + to_chat(user, "You notice an incision has been made on [src].") + if(in_range(user, src) && stored_item) + to_chat(user, "You can see something in there...") /obj/structure/plushie/attack_hand(mob/user) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + + if(stored_item && opened && !searching) + searching = TRUE + if(do_after(user, 10)) + to_chat(user, "You find \icon[stored_item] [stored_item] in [src]!") + stored_item.forceMove(get_turf(src)) + stored_item = null + searching = FALSE + return + else + searching = FALSE + if(user.a_intent == I_HELP) user.visible_message("\The [user] hugs [src]!","You hug [src]!") else if (user.a_intent == I_HURT) @@ -894,6 +924,34 @@ visible_message("[src] says, \"[phrase]\"") +/obj/structure/plushie/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I, /obj/item/device/threadneedle) && opened) + to_chat(user, "You sew the hole in [src].") + opened = FALSE + return + + if(is_sharp(I) && !opened) + to_chat(user, "You open a small incision in [src]. You can place tiny items inside.") + opened = TRUE + return + + if(opened) + if(stored_item) + to_chat(user, "There is already something in here.") + return + + if(!(I.w_class > w_class)) + to_chat(user, "You place [I] inside [src].") + user.drop_from_inventory(I, src) + I.forceMove(src) + stored_item = I + return + else + to_chat(user, "You open a small incision in [src]. You can place tiny items inside.") + + + ..() + /obj/structure/plushie/ian name = "plush corgi" desc = "A plushie of an adorable corgi! Don't you just want to hug it and squeeze it and call it \"Ian\"?" @@ -924,11 +982,34 @@ desc = "A small toy plushie. It's very cute." icon = 'icons/obj/toy.dmi' icon_state = "nymphplushie" + drop_sound = 'sound/items/drop/plushie.ogg' w_class = ITEMSIZE_TINY var/last_message = 0 var/pokephrase = "Uww!" + var/searching = FALSE + var/opened = FALSE // has this been slit open? this will allow you to store an object in a plushie. + var/obj/item/stored_item // Note: Stored items can't be bigger than the plushie itself. + + +/obj/item/toy/plushie/examine(mob/user) + ..() + if(opened) + to_chat(user, "You notice an incision has been made on [src].") + if(in_range(user, src) && stored_item) + to_chat(user, "You can see something in there...") /obj/item/toy/plushie/attack_self(mob/user as mob) + if(stored_item && opened && !searching) + searching = TRUE + if(do_after(user, 10)) + to_chat(user, "You find \icon[stored_item] [stored_item] in [src]!") + stored_item.forceMove(get_turf(src)) + stored_item = null + searching = FALSE + return + else + searching = FALSE + if(world.time - last_message <= 1 SECOND) return if(user.a_intent == I_HELP) @@ -939,7 +1020,8 @@ user.visible_message("\The [user] attempts to strangle [src]!","You attempt to strangle [src]!") else user.visible_message("\The [user] pokes [src].","You poke [src].") - visible_message("[src] says, \"[pokephrase]\"") + playsound(src, 'sound/items/drop/plushie.ogg', 25, 0) + audible_message("[src] says, \"[pokephrase]\"") last_message = world.time /obj/item/toy/plushie/verb/rename_plushie() @@ -961,6 +1043,31 @@ if(istype(I, /obj/item/toy/plushie) || istype(I, /obj/item/organ/external/head)) user.visible_message("[user] makes \the [I] kiss \the [src]!.", \ "You make \the [I] kiss \the [src]!.") + return + + + if(istype(I, /obj/item/device/threadneedle) && opened) + to_chat(user, "You sew the hole underneath [src].") + opened = FALSE + return + + if(is_sharp(I) && !opened) + to_chat(user, "You open a small incision in [src]. You can place tiny items inside.") + opened = TRUE + return + + if( (!(I.w_class > w_class)) && opened) + if(stored_item) + to_chat(user, "There is already something in here.") + return + + to_chat(user, "You place [I] inside [src].") + user.drop_from_inventory(I, src) + I.forceMove(src) + stored_item = I + to_chat(user, "You placed [I] into [src].") + return + return ..() /obj/item/toy/plushie/nymph @@ -1395,6 +1502,7 @@ icon_state = "inflatable" icon = 'icons/obj/clothing/belts.dmi' slot_flags = SLOT_BELT + drop_sound = 'sound/items/drop/rubber.ogg' /obj/item/toy/xmastree name = "Miniature Christmas tree" @@ -1404,3 +1512,4 @@ w_class = ITEMSIZE_TINY force = 1 throwforce = 1 + drop_sound = 'sound/items/drop/box.ogg' diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 12315972a2..b72a73f34e 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -6,6 +6,7 @@ icon = 'icons/obj/trash.dmi' w_class = ITEMSIZE_SMALL desc = "This is rubbish." + drop_sound = 'sound/items/drop/wrapper.ogg' /obj/item/trash/raisins name = "\improper 4no raisins" @@ -38,6 +39,7 @@ /obj/item/trash/unajerky name = "Moghes Imported Sissalik Jerky" icon_state = "unathitinred" + drop_sound = 'sound/items/drop/soda.ogg' /obj/item/trash/syndi_cakes name = "syndi cakes" @@ -66,6 +68,7 @@ /obj/item/trash/tray name = "tray" icon_state = "tray" + drop_sound = 'sound/items/trayhit1.ogg' /obj/item/trash/candle name = "candle" diff --git a/code/game/objects/items/trash_material.dm b/code/game/objects/items/trash_material.dm new file mode 100644 index 0000000000..6ba36565d6 --- /dev/null +++ b/code/game/objects/items/trash_material.dm @@ -0,0 +1,72 @@ +/obj/item/trash/material + icon = 'icons/obj/material_trash.dmi' + matter = list() + var/matter_chances = list() //List of lists: list(mat_name, chance, amount) + + +/obj/item/trash/material/Initialize() + . = ..() + if(!matter) + matter = list() + + for(var/list/L in matter_chances) + if(prob(L[2])) + matter |= L[1] + matter[L[1]] += max(0, L[3] + rand(-2,2)) + + + + +/obj/item/trash/material/metal + name = "scrap metal" + desc = "A piece of metal that can be recycled in an autolathe." + icon_state = "metal0" + matter_chances = list( + list(MAT_STEEL, 100, 15), + list(MAT_STEEL, 50, 10), + list(MAT_STEEL, 10, 20), + list(MAT_PLASTEEL, 10, 5), + list(MAT_PLASTEEL, 5, 10) + ) + +/obj/item/trash/material/metal/Initialize() + . = ..() + icon_state = "metal[rand(4)]" + + +/obj/item/trash/material/circuit + name = "burnt circuit" + desc = "A burnt circuit that can be recycled in an autolathe." + w_class = ITEMSIZE_SMALL + icon_state = "circuit0" + matter_chances = list( + list(MAT_GLASS, 100, 4), + list(MAT_GLASS, 50, 3), + list(MAT_PLASTIC, 40, 3), + list(MAT_SILVER, 18, 3), + list(MAT_GOLD, 17, 3), + list(MAT_DIAMOND, 4, 2), + ) + +/obj/item/trash/material/circuit/Initialize() + . = ..() + icon_state = "circuit[rand(3)]" + + +/obj/item/trash/material/device + name = "broken device" + desc = "A broken device that can be recycled in an autolathe." + w_class = ITEMSIZE_SMALL + icon_state = "device0" + matter_chances = list( + list(MAT_STEEL, 100, 10), + list(MAT_GLASS, 90, 7), + list(MAT_PLASTIC, 100, 10), + list(MAT_SILVER, 16, 7), + list(MAT_GOLD, 15, 5), + list(MAT_DIAMOND, 5, 2), + ) + +/obj/item/trash/material/device/Initialize() + . = ..() + icon_state = "device[rand(3)]" diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 4d6b6eea03..4a160d05c8 100644 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -106,6 +106,7 @@ AI MODULES if(laws) laws.sync(target, 0) + target.notify_of_law_change() addAdditionalLaws(target, sender) to_chat(target, "\The [sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") diff --git a/code/game/objects/items/weapons/RPD.dm b/code/game/objects/items/weapons/RPD.dm index b53aa54cba..38806886a1 100644 --- a/code/game/objects/items/weapons/RPD.dm +++ b/code/game/objects/items/weapons/RPD.dm @@ -35,12 +35,14 @@ src.spark_system = new /datum/effect/effect/system/spark_spread spark_system.set_up(5, 0, src) spark_system.attach(src) + tool = new /obj/item/weapon/tool/wrench/cyborg(src) // RPDs have wrenches inside of them, so that they can wrench down spawned pipes without being used as superior wrenches themselves. + +/obj/item/weapon/pipe_dispenser/proc/SetupPipes() if(!first_atmos) first_atmos = atmos_pipe_recipes[atmos_pipe_recipes[1]][1] + recipe = first_atmos if(!first_disposal) first_disposal = disposal_pipe_recipes[disposal_pipe_recipes[1]][1] - recipe = first_atmos - tool = new /obj/item/weapon/tool/wrench/cyborg(src) // RPDs have wrenches inside of them, so that they can wrench down spawned pipes without being used as superior wrenches themselves. /obj/item/weapon/pipe_dispenser/Destroy() qdel(spark_system) @@ -58,6 +60,7 @@ src.interact(user) /obj/item/weapon/pipe_dispenser/interact(mob/user) + SetupPipes() var/list/lines = list() if(mode >= ATMOS_MODE) lines += "

Direction:

" @@ -169,6 +172,7 @@ popup.open() /obj/item/weapon/pipe_dispenser/Topic(href,href_list) + SetupPipes() if(..()) return if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) diff --git a/code/game/objects/items/weapons/autopsy.dm b/code/game/objects/items/weapons/autopsy.dm index e1c165bc2f..94a2636798 100644 --- a/code/game/objects/items/weapons/autopsy.dm +++ b/code/game/objects/items/weapons/autopsy.dm @@ -80,7 +80,7 @@ set src in view(usr, 1) set name = "Print Data" if(usr.stat || !(istype(usr,/mob/living/carbon/human))) - usr << "No." + to_chat(usr, "No.") return var/scan_data = "" diff --git a/code/game/objects/items/weapons/canes.dm b/code/game/objects/items/weapons/canes.dm new file mode 100644 index 0000000000..f7688fb2d1 --- /dev/null +++ b/code/game/objects/items/weapons/canes.dm @@ -0,0 +1,124 @@ +/obj/item/weapon/cane + name = "cane" + desc = "A cane used by a true gentleman." + icon = 'icons/obj/weapons.dmi' + icon_state = "cane" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + force = 5.0 + throwforce = 7.0 + w_class = ITEMSIZE_NORMAL + matter = list(DEFAULT_WALL_MATERIAL = 50) + attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") + +/obj/item/weapon/cane/crutch + name ="crutch" + desc = "A long stick with a crosspiece at the top, used to help with walking." + icon_state = "crutch" + item_state = "crutch" + +/obj/item/weapon/cane/concealed + var/concealed_blade + +/obj/item/weapon/cane/concealed/New() + ..() + var/obj/item/weapon/material/butterfly/switchblade/temp_blade = new(src) + concealed_blade = temp_blade + temp_blade.attack_self() + +/obj/item/weapon/cane/concealed/attack_self(var/mob/user) + var/datum/gender/T = gender_datums[user.get_visible_gender()] + if(concealed_blade) + user.visible_message("[user] has unsheathed \a [concealed_blade] from [T.his] [src]!", "You unsheathe \the [concealed_blade] from \the [src].") + // Calling drop/put in hands to properly call item drop/pickup procs + playsound(user.loc, 'sound/weapons/holster/sheathout.ogg', 50, 1) + user.drop_from_inventory(src) + user.put_in_hands(concealed_blade) + user.put_in_hands(src) + user.update_inv_l_hand(0) + user.update_inv_r_hand() + concealed_blade = null + else + ..() + +/obj/item/weapon/cane/concealed/attackby(var/obj/item/weapon/material/butterfly/W, var/mob/user) + if(!src.concealed_blade && istype(W)) + var/datum/gender/T = gender_datums[user.get_visible_gender()] + user.visible_message("[user] has sheathed \a [W] into [T.his] [src]!", "You sheathe \the [W] into \the [src].") + playsound(user.loc, 'sound/weapons/holster/sheathin.ogg', 50, 1) + user.drop_from_inventory(W) + W.loc = src + src.concealed_blade = W + update_icon() + else + ..() + +/obj/item/weapon/cane/concealed/update_icon() + if(concealed_blade) + name = initial(name) + icon_state = initial(icon_state) + item_state = initial(icon_state) + else + name = "cane shaft" + icon_state = "nullrod" + item_state = "foldcane" + +/obj/item/weapon/cane/white + name = "white cane" + desc = "A white cane. They are commonly used by the blind or visually impaired as a mobility tool or as a courtesy to others." + icon_state = "whitecane" + +/obj/item/weapon/cane/white/attack(mob/M as mob, mob/user as mob) + if(user.a_intent == I_HELP) + user.visible_message("\The [user] has lightly tapped [M] on the ankle with their white cane!") + return TRUE + else + . = ..() + + +//Code for Telescopic White Cane writen by Gozulio + +/obj/item/weapon/cane/white/collapsible + name = "telescopic white cane" + desc = "A telescopic white cane. They are commonly used by the blind or visually impaired as a mobility tool or as a courtesy to others." + icon_state = "whitecane1in" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + slot_flags = SLOT_BELT + w_class = ITEMSIZE_SMALL + force = 3 + var/on = 0 + +/obj/item/weapon/cane/white/collapsible/attack_self(mob/user as mob) + on = !on + if(on) + user.visible_message("\The [user] extends the white cane.",\ + "You extend the white cane.",\ + "You hear an ominous click.") + icon_state = "whitecane1out" + item_state_slots = list(slot_r_hand_str = "whitecane", slot_l_hand_str = "whitecane") + w_class = ITEMSIZE_NORMAL + force = 5 + attack_verb = list("smacked", "struck", "cracked", "beaten") + else + user.visible_message("\The [user] collapses the white cane.",\ + "You collapse the white cane.",\ + "You hear a click.") + icon_state = "whitecane1in" + item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null) + w_class = ITEMSIZE_SMALL + force = 3 + attack_verb = list("hit", "poked", "prodded") + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) + add_fingerprint(user) + return TRUE \ No newline at end of file diff --git a/code/game/objects/items/weapons/cigs_lighters.dm b/code/game/objects/items/weapons/cigs_lighters.dm index 39fff82a82..f3df3c914b 100644 --- a/code/game/objects/items/weapons/cigs_lighters.dm +++ b/code/game/objects/items/weapons/cigs_lighters.dm @@ -34,6 +34,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM origin_tech = list(TECH_MATERIAL = 1) slot_flags = SLOT_EARS attack_verb = list("burnt", "singed") + drop_sound = null /obj/item/weapon/flame/match/process() if(isliving(loc)) @@ -59,6 +60,15 @@ CIGARETTE PACKETS ARE IN FANCY.DM burn_out() return ..() +/obj/item/weapon/flame/match/proc/light(var/mob/user) + playsound(src, 'sound/items/cigs_lighters/matchstick_lit.ogg', 25, 0, -1) + lit = 1 + damtype = "burn" + icon_state = "match_lit" + name = "burning match" + desc = "A match. This one is presently on fire." + START_PROCESSING(SSobj, src) + /obj/item/weapon/flame/match/proc/burn_out() lit = 0 burnt = 1 @@ -90,6 +100,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM var/ignitermes = "USER lights NAME with FLAME" var/brand blood_sprite_state = null //Can't bloody these + drop_sound = 'sound/items/cigs_lighters/cig_snuff.ogg' /obj/item/clothing/mask/smokable/Initialize() . = ..() @@ -144,20 +155,21 @@ CIGARETTE PACKETS ARE IN FANCY.DM var/smoke_percent = round((smoketime / max_smoketime) * 100) switch(smoke_percent) if(90 to INFINITY) - user << "[src] is still fresh." + to_chat(user, "[src] is still fresh.") if(60 to 90) - user << "[src] has a good amount of burn time remaining." + to_chat(user, "[src] has a good amount of burn time remaining.") if(30 to 60) - user << "[src] is about half finished." + to_chat(user, "[src] is about half finished.") if(10 to 30) - user << "[src] is starting to burn low." + to_chat(user, "[src] is starting to burn low.") else - user << "[src] is nearly burnt out!" + to_chat(user, "[src] is nearly burnt out!") /obj/item/clothing/mask/smokable/proc/light(var/flavor_text = "[usr] lights the [name].") if(!src.lit) src.lit = 1 + playsound(src, 'sound/items/cigs_lighters/cig_light.ogg', 75, 1, -1) damtype = "fire" if(reagents.get_reagent_amount("phoron")) // the phoron explodes when exposed to fire var/datum/effect/effect/system/reagents_explosion/e = new() @@ -182,6 +194,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/clothing/mask/smokable/proc/die(var/nomessage = 0) var/turf/T = get_turf(src) set_light(0) + playsound(src.loc, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) STOP_PROCESSING(SSobj, src) if (type_butt) var/obj/item/butt = new type_butt(T) @@ -202,7 +215,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(ismob(loc)) var/mob/living/M = loc if (!nomessage) - M << "Your [name] goes out, and you empty the ash." + to_chat(M, "Your [name] goes out, and you empty the ash.") + playsound(src.loc, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) lit = 0 icon_state = initial(icon_state) item_state = initial(item_state) @@ -225,6 +239,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM to_chat(H, "\The [blocked] is in the way!") return 1 to_chat(H, "You take a drag on your [name].") + playsound(src, 'sound/items/cigs_lighters/inhale.ogg', 50, 0, -1) smoke(5) return 1 return ..() @@ -302,17 +317,18 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(istype(glass)) //you can dip cigarettes into beakers var/transfered = glass.reagents.trans_to_obj(src, chem_volume) if(transfered) //if reagents were transfered, show the message - user << "You dip \the [src] into \the [glass]." + 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) - user << "[glass] is empty." + to_chat(user, "[glass] is empty.") else - user << "[src] is full." + to_chat(user, "[src] is full.") /obj/item/clothing/mask/smokable/cigarette/attack_self(mob/user as mob) if(lit == 1) if(user.a_intent == I_HURT) user.visible_message("[user] drops and treads on the lit [src], putting it out instantly.") + playsound(src.loc, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) die(1) else user.visible_message("[user] puts out \the [src].") @@ -359,14 +375,14 @@ CIGARETTE PACKETS ARE IN FANCY.DM desc = "A manky old cigarette butt." icon = 'icons/obj/clothing/masks.dmi' icon_state = "cigbutt" + randpixel = 10 w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS throwforce = 1 /obj/item/weapon/cigbutt/Initialize() . = ..() - pixel_x = rand(-10,10) - pixel_y = rand(-10,10) + randpixel_xy() transform = turn(transform,rand(0,360)) /obj/item/weapon/cigbutt/cigarbutt @@ -406,6 +422,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(lit == 1) if(user.a_intent == I_HURT) user.visible_message("[user] empties the lit [src] on the floor!.") + playsound(src.loc, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1) die(1) else user.visible_message("[user] puts out \the [src].") @@ -420,10 +437,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W if (!G.dry) - user << "[G] must be dried before you stuff it into [src]." + to_chat(user, "[G] must be dried before you stuff it into [src].") return if (smoketime) - user << "[src] is already packed." + to_chat(user, "[src] is already packed.") return max_smoketime = 1000 smoketime = 1000 @@ -479,10 +496,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM if (istype(W, /obj/item/weapon/reagent_containers/food/snacks)) var/obj/item/weapon/reagent_containers/food/snacks/grown/G = W if (!G.dry) - user << "[G] must be dried before you roll it into [src]." + to_chat(user, "[G] must be dried before you roll it into [src].") return var/obj/item/clothing/mask/smokable/cigarette/joint/J = new /obj/item/clothing/mask/smokable/cigarette/joint(user.loc) - to_chat(usr,"You roll the [G.name] into a joint!") + to_chat(usr, "You roll the [G.name] into a joint!") J.add_fingerprint(user) if(G.reagents) G.reagents.trans_to_obj(J, G.reagents.total_volume) @@ -505,6 +522,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM slot_flags = SLOT_BELT attack_verb = list("burnt", "singed") var/base_state + var/activation_sound = 'sound/items/lighter_on.ogg' + var/deactivation_sound = 'sound/items/lighter_off.ogg' /obj/item/weapon/flame/lighter/zippo name = "\improper Zippo lighter" @@ -512,6 +531,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM icon = 'icons/obj/zippo.dmi' icon_state = "zippo" item_state = "zippo" + activation_sound = 'sound/items/zippo_on.ogg' + deactivation_sound = 'sound/items/zippo_off.ogg' /obj/item/weapon/flame/lighter/random New() @@ -526,13 +547,14 @@ CIGARETTE PACKETS ARE IN FANCY.DM lit = 1 icon_state = "[base_state]on" item_state = "[base_state]on" + playsound(src.loc, activation_sound, 75, 1) if(istype(src, /obj/item/weapon/flame/lighter/zippo) ) user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.") else if(prob(95)) user.visible_message("After a few attempts, [user] manages to light the [src].") else - user << "You burn yourself while lighting the lighter." + to_chat(user, "You burn yourself while lighting the lighter.") if (user.get_left_hand() == src) user.apply_damage(2,BURN,"l_hand") else @@ -545,6 +567,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM lit = 0 icon_state = "[base_state]" item_state = "[base_state]" + playsound(src.loc, deactivation_sound, 75, 1) if(istype(src, /obj/item/weapon/flame/lighter/zippo) ) user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what they're doing.") else diff --git a/code/game/objects/items/weapons/circuitboards/circuitboards_yw.dm b/code/game/objects/items/weapons/circuitboards/circuitboards_yw.dm new file mode 100644 index 0000000000..ed2d1c5da0 --- /dev/null +++ b/code/game/objects/items/weapons/circuitboards/circuitboards_yw.dm @@ -0,0 +1,5 @@ +/obj/item/weapon/circuitboard/geiger + name = T_BOARD("geiger counter") + build_path = /obj/item/device/geiger/wall + board_type = new /datum/frame/frame_types/geiger + matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) \ No newline at end of file diff --git a/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm b/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm index 6eb53a10f3..097f465a51 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm @@ -53,9 +53,9 @@ /obj/item/weapon/circuitboard/security/emag_act(var/remaining_charges, var/mob/user) if(emagged) - user << "Circuit lock is already removed." + to_chat(user, "Circuit lock is already removed.") return - user << "You override the circuit lock and open controls." + to_chat(user, "You override the circuit lock and open controls.") emagged = 1 locked = 0 return 1 @@ -63,26 +63,26 @@ /obj/item/weapon/circuitboard/security/attackby(obj/item/I as obj, mob/user as mob) if(istype(I,/obj/item/weapon/card/id)) if(emagged) - user << "Circuit lock does not respond." + to_chat(user, "Circuit lock does not respond.") return if(check_access(I)) locked = !locked - user << "You [locked ? "" : "un"]lock the circuit controls." + to_chat(user, "You [locked ? "" : "un"]lock the circuit controls.") else - user << "Access denied." + to_chat(user, "Access denied.") else if(istype(I,/obj/item/device/multitool)) if(locked) - user << "Circuit controls are locked." + to_chat(user, "Circuit controls are locked.") return var/existing_networks = jointext(network,",") var/input = sanitize(input(usr, "Which networks would you like to connect this camera console circuit to? Seperate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Multitool-Circuitboard interface", existing_networks)) if(!input) - usr << "No input found please hang up and try your call again." + to_chat(usr, "No input found please hang up and try your call again.") return var/list/tempnetwork = splittext(input, ",") tempnetwork = difflist(tempnetwork,restricted_camera_networks,1) if(tempnetwork.len < 1) - usr << "No network found please hang up and try your call again." + to_chat(usr, "No network found please hang up and try your call again.") return network = tempnetwork return diff --git a/code/game/objects/items/weapons/circuitboards/computer/computer.dm b/code/game/objects/items/weapons/circuitboards/computer/computer.dm index 2190650d67..91b09022a5 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/computer.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/computer.dm @@ -177,6 +177,30 @@ build_path = /obj/machinery/computer/aifixer origin_tech = list(TECH_DATA = 3, TECH_BIO = 2) + +/obj/item/weapon/circuitboard/helm + name = T_BOARD("helm control console") + build_path = /obj/machinery/computer/ship/helm + +/obj/item/weapon/circuitboard/engine + name = T_BOARD("engine control console") + build_path = /obj/machinery/computer/ship/engines + +/obj/item/weapon/circuitboard/nav + name = T_BOARD("navigation console") + build_path = /obj/machinery/computer/ship/navigation + +/obj/item/weapon/circuitboard/nav/tele + name = T_BOARD("navigation telescreen") + build_path = /obj/machinery/computer/ship/navigation/telescreen + +/obj/item/weapon/circuitboard/sensors + name = T_BOARD("sensors console") + build_path = /obj/machinery/computer/ship/sensors + + + + /obj/item/weapon/circuitboard/area_atmos name = T_BOARD("area air control console") build_path = /obj/machinery/computer/area_atmos diff --git a/code/game/objects/items/weapons/circuitboards/computer/shuttle.dm b/code/game/objects/items/weapons/circuitboards/computer/shuttle.dm new file mode 100644 index 0000000000..1c3c0ec25f --- /dev/null +++ b/code/game/objects/items/weapons/circuitboards/computer/shuttle.dm @@ -0,0 +1,54 @@ +#ifndef T_BOARD +#error T_BOARD macro is not defined but we need it! +#endif + +// +// Shuttle control console. Board tries to auto-link the computer if built on a shuttle. +// +/obj/item/weapon/circuitboard/shuttle_console + origin_tech = list(TECH_DATA = 3) + var/shuttle_category = null // Shuttle datum's category must exactly equal this to auto-detect + var/shuttle_tag = null // If set, link constructed console to this shuttle. If null, auto-detect. + +/obj/item/weapon/circuitboard/shuttle_console/deconstruct(obj/machinery/computer/shuttle_control/M) + shuttle_tag = M.shuttle_tag + if(shuttle_tag) + name = T_BOARD("[shuttle_tag] control console") + +// Try to auto-link the shuttle computer if it is constructed on a shuttle (and not pre-programmed) +/obj/item/weapon/circuitboard/shuttle_console/construct(obj/machinery/computer/shuttle_control/M) + if(!shuttle_tag) + shuttle_tag = auto_detect_shuttle(M) // We don't have a preset tag, so lets try to auto-link. + if(shuttle_tag && M.set_shuttle_tag(shuttle_tag)) + log_and_message_admins("[key_name_admin(usr)] built a [M] for [shuttle_tag] at [ADMIN_COORDJMP(M)]") + M.ping("[M] auto-links with shuttle [shuttle_tag]") + +// Return shuttle_tag of shuttle of current area +/obj/item/weapon/circuitboard/shuttle_console/proc/auto_detect_shuttle(obj/machinery/computer/shuttle_control/M) + var/area/A = get_area(M) + if(!A || !(A in SSshuttles.shuttle_areas)) + return // Definately not on a shuttle + for(var/shuttle_name in SSshuttles.shuttles) + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_name] + if(A in S.find_childfree_areas()) + // Found the owning shuttle! Return it if its a valid type + return (S.category == shuttle_category) ? S.name : null + +// Overmap shuttle console. +/obj/item/weapon/circuitboard/shuttle_console/explore + name = T_BOARD("long range shuttle control console") + build_path = /obj/machinery/computer/shuttle_control/explore + origin_tech = list(TECH_DATA = 3, TECH_BLUESPACE = 4) + shuttle_category = /datum/shuttle/autodock/overmap + +// Multi-shuttle console +/obj/item/weapon/circuitboard/shuttle_console/multi + name = T_BOARD("multi-route shuttle control console") + build_path = /obj/machinery/computer/shuttle_control/multi + shuttle_category = /datum/shuttle/autodock/multi + +// Basic "ferry" shuttle console +/obj/item/weapon/circuitboard/shuttle_console/ferry + name = T_BOARD("basic shuttle control console") + build_path = /obj/machinery/computer/shuttle_control + shuttle_category = /datum/shuttle/autodock/ferry diff --git a/code/game/objects/items/weapons/circuitboards/computer/supply.dm b/code/game/objects/items/weapons/circuitboards/computer/supply.dm index 324f1c1a06..a19ecd1d8e 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/supply.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/supply.dm @@ -39,5 +39,5 @@ if("Cancel") return else - user << "DERP! BUG! Report this (And what you were doing to cause it) to Agouri" + to_chat(user, "DERP! BUG! Report this (And what you were doing to cause it) to Agouri") return diff --git a/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm b/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm index 6d683cd9a8..f34f7a3c65 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm @@ -2,6 +2,37 @@ #error T_BOARD macro is not defined but we need it! #endif +// +// New shield generator +// + +/obj/item/weapon/circuitboard/shield_generator + name = T_BOARD("advanced shield generator") + board_type = new /datum/frame/frame_types/machine + build_path = /obj/machinery/power/shield_generator + origin_tech = list(TECH_MAGNET = 3, TECH_POWER = 4, TECH_BLUESPACE = 2, TECH_ENGINEERING = 3) + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 1, + /obj/item/weapon/stock_parts/micro_laser = 1, + /obj/item/weapon/smes_coil = 1, + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/weapon/stock_parts/subspace/amplifier = 1, + /obj/item/stack/cable_coil = 5) + +/obj/item/weapon/circuitboard/shield_diffuser + name = T_BOARD("shield diffuser") + board_type = new /datum/frame/frame_types/machine + build_path = /obj/machinery/shield_diffuser + origin_tech = list(TECH_MAGNET = 4, TECH_POWER = 2, TECH_ILLEGAL = 1) + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 1, + /obj/item/weapon/stock_parts/micro_laser = 1, + /obj/item/weapon/stock_parts/console_screen = 1) + +// +// Legacy shield generators +// + /obj/item/weapon/circuitboard/shield_gen_ex name = T_BOARD("hull shield generator") board_type = new /datum/frame/frame_types/machine diff --git a/code/game/objects/items/weapons/circuitboards/machinery/ships.dm b/code/game/objects/items/weapons/circuitboards/machinery/ships.dm new file mode 100644 index 0000000000..b71e7ce37b --- /dev/null +++ b/code/game/objects/items/weapons/circuitboards/machinery/ships.dm @@ -0,0 +1,23 @@ +#ifndef T_BOARD +#error T_BOARD macro is not defined but we need it! +#endif + +/obj/item/weapon/circuitboard/pointdefense + name = T_BOARD("point defense battery") + board_type = new /datum/frame/frame_types/machine + desc = "Control systems for a Kuiper pattern point defense battery. Aim away from vessel." + build_path = /obj/machinery/power/pointdefense + origin_tech = list(TECH_ENGINEERING = 3, TECH_COMBAT = 2, TECH_POWER = 2) + req_components = list( + /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser = 1, + /obj/item/weapon/stock_parts/manipulator = 2, + /obj/item/weapon/stock_parts/capacitor = 2, + ) + +/obj/item/weapon/circuitboard/pointdefense_control + name = T_BOARD("fire assist mainframe") + board_type = new /datum/frame/frame_types/machine + desc = "A control computer to synchronize point defense batteries." + build_path = /obj/machinery/pointdefense_control + origin_tech = list(TECH_ENGINEERING = 3, TECH_COMBAT = 2) + req_components = list() diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm index 890e557f50..1f53e64e77 100644 --- a/code/game/objects/items/weapons/clown_items.dm +++ b/code/game/objects/items/weapons/clown_items.dm @@ -8,13 +8,9 @@ /* * Banana Peals */ -/obj/item/weapon/bananapeel/Crossed(AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/item/weapon/bananapeel/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return if (istype(AM, /mob/living)) var/mob/living/M = AM M.slip("the [src.name]",4) @@ -29,13 +25,9 @@ /obj/item/weapon/soap/proc/wet() reagents.add_reagent("cleaner", 5) -/obj/item/weapon/soap/Crossed(AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/item/weapon/soap/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return if (istype(AM, /mob/living)) var/mob/living/M = AM M.slip("the [src.name]",3) diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm index 4533f41537..9435be7fb3 100644 --- a/code/game/objects/items/weapons/cosmetics.dm +++ b/code/game/objects/items/weapons/cosmetics.dm @@ -8,6 +8,7 @@ slot_flags = SLOT_EARS var/colour = "red" var/open = 0 + drop_sound = 'sound/items/drop/glass.ogg' /obj/item/weapon/lipstick/purple name = "purple lipstick" @@ -29,7 +30,7 @@ name = "[colour] lipstick" /obj/item/weapon/lipstick/attack_self(mob/user as mob) - user << "You twist \the [src] [open ? "closed" : "open"]." + to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") open = !open if(open) icon_state = "[initial(icon_state)]_[colour]" @@ -44,7 +45,7 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M if(H.lip_style) //if they already have lipstick on - user << "You need to wipe off the old lipstick first!" + to_chat(user, "You need to wipe off the old lipstick first!") return if(H == user) user.visible_message("[user] does their lips with \the [src].", \ @@ -60,7 +61,7 @@ H.lip_style = colour H.update_icons_body() else - user << "Where are the lips on that?" + to_chat(user, "Where are the lips on that?") //you can wipe off lipstick with paper! see code/modules/paperwork/paper.dm, paper/attack() diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm index ac0a93941b..9000ec4b1f 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/game/objects/items/weapons/dna_injector.dm @@ -122,7 +122,7 @@ var/mob/living/carbon/human/H = M if(!istype(H)) - user << "Apparently it didn't work..." + to_chat(user, "Apparently it didn't work...") return // Used by admin log. diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index d4a29d47f3..c4d0bf657e 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -31,7 +31,7 @@ /obj/item/weapon/plastique/attackby(var/obj/item/I, var/mob/user) if(I.is_screwdriver()) open_panel = !open_panel - user << "You [open_panel ? "open" : "close"] the wire panel." + to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") playsound(src, I.usesound, 50, 1) else if(I.is_wirecutter() || istype(I, /obj/item/device/multitool) || istype(I, /obj/item/device/assembly/signaler )) wires.Interact(user) @@ -43,14 +43,14 @@ if(user.get_active_hand() == src) newtime = CLAMP(newtime, 10, 60000) timer = newtime - user << "Timer set for [timer] seconds." + to_chat(user, "Timer set for [timer] seconds.") /obj/item/weapon/plastique/afterattack(atom/movable/target, mob/user, flag) if (!flag) return if (ismob(target) || istype(target, /turf/unsimulated) || istype(target, /turf/simulated/shuttle) || istype(target, /obj/item/weapon/storage/) || istype(target, /obj/item/clothing/accessory/storage/) || istype(target, /obj/item/clothing/under)) return - user << "Planting explosives..." + to_chat(user, "Planting explosives...") user.do_attack_animation(target) if(do_after(user, 50) && in_range(user, target)) @@ -66,7 +66,7 @@ log_game("[key_name(user)] planted [src.name] on [target.name] at ([target.x],[target.y],[target.z]) with [timer] second fuse") target.overlays += image_overlay - user << "Bomb has been planted. Timer counting down from [timer]." + to_chat(user, "Bomb has been planted. Timer counting down from [timer].") spawn(timer*10) explode(get_turf(target)) diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 44242719fd..f44abcb96b 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -12,6 +12,7 @@ force = 10 matter = list(DEFAULT_WALL_MATERIAL = 90) attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") + drop_sound = 'sound/items/drop/gascan.ogg' var/spray_particles = 3 var/spray_amount = 10 //units of liquid per particle @@ -40,7 +41,7 @@ /obj/item/weapon/extinguisher/examine(mob/user) if(..(user, 0)) - to_chat(user, text("\icon[] [] contains [] units of water left!", src, src.name, src.reagents.total_volume)) + to_chat(user, "[bicon(src)] [src.name] contains [src.reagents.total_volume] units of water left!") /obj/item/weapon/extinguisher/attack_self(mob/user as mob) safety = !safety @@ -72,13 +73,13 @@ if( istype(target, /obj/structure/reagent_dispensers/watertank) && flag) var/obj/o = target var/amount = o.reagents.trans_to_obj(src, 50) - user << "You fill [src] with [amount] units of the contents of [target]." + to_chat(user, "You fill [src] with [amount] units of the contents of [target].") playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6) return if (!safety) if (src.reagents.total_volume < 1) - usr << "\The [src] is empty." + to_chat(usr, "\The [src] is empty.") return if (world.time < src.last_use + 20) diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 498cad3f68..6cd2a65774 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -86,7 +86,7 @@ if(W.is_screwdriver() && igniter && !lit) status = !status - user << "[igniter] is now [status ? "secured" : "unsecured"]!" + to_chat(user, "[igniter] is now [status ? "secured" : "unsecured"]!") update_icon() return @@ -102,7 +102,7 @@ if(istype(W,/obj/item/weapon/tank/phoron)) if(ptank) - user << "There appears to already be a phoron tank loaded in [src]!" + to_chat(user, "There appears to already be a phoron tank loaded in [src]!") return user.drop_item() ptank = W @@ -122,7 +122,7 @@ if(user.stat || user.restrained() || user.lying) return user.set_machine(src) if(!ptank) - user << "Attach a phoron tank first!" + to_chat(user, "Attach a phoron tank first!") return var/dat = text("Flamethrower ([lit ? "Lit" : "Unlit"])
\n Tank Pressure: [ptank.air_contents.return_pressure()]
\nAmount to throw: - - - [throw_amount] + + +
\nRemove phorontank - Close
") user << browse(dat, "window=flamethrower;size=600x300") diff --git a/code/game/objects/items/weapons/gift_wrappaper.dm b/code/game/objects/items/weapons/gift_wrappaper.dm index db9748e6af..27c917df28 100644 --- a/code/game/objects/items/weapons/gift_wrappaper.dm +++ b/code/game/objects/items/weapons/gift_wrappaper.dm @@ -13,6 +13,7 @@ icon = 'icons/obj/items.dmi' icon_state = "gift1" item_state = "gift1" + drop_sound = 'sound/items/drop/box.ogg' /obj/item/weapon/a_gift/New() ..() @@ -26,11 +27,12 @@ /obj/item/weapon/gift/attack_self(mob/user as mob) user.drop_item() + playsound(src.loc, 'sound/items/package_unwrap.ogg', 50,1) if(src.gift) user.put_in_active_hand(gift) src.gift.add_fingerprint(user) else - user << "The gift was empty!" + to_chat(user, "The gift was empty!") qdel(src) return @@ -41,16 +43,16 @@ /obj/effect/spresent/relaymove(mob/user as mob) if (user.stat) return - user << "You can't move." + to_chat(user, "You can't move.") /obj/effect/spresent/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() if (!W.is_wirecutter()) - user << "I need wirecutters for that." + to_chat(user, "I need wirecutters for that.") return - user << "You cut open the present." + to_chat(user, "You cut open the present.") for(var/mob/M in src) //Should only be one but whatever. M.loc = src.loc @@ -127,13 +129,13 @@ /obj/item/weapon/wrapping_paper/attackby(obj/item/weapon/W as obj, mob/living/user as mob) ..() if (!( locate(/obj/structure/table, src.loc) )) - user << "You MUST put the paper on a table!" + to_chat(user, "You MUST put the paper on a table!") if (W.w_class < ITEMSIZE_LARGE) var/obj/item/I = user.get_inactive_hand() if(I.is_wirecutter()) var/a_used = 2 ** (src.w_class - 1) if (src.amount < a_used) - user << "You need more paper!" + to_chat(user, "You need more paper!") return else if(istype(W, /obj/item/smallDelivery) || istype(W, /obj/item/weapon/gift)) //No gift wrapping gifts! @@ -155,15 +157,15 @@ qdel(src) return else - user << "You need scissors!" + to_chat(user, "You need scissors!") else - user << "The object is FAR too large!" + to_chat(user, "The object is FAR too large!") return /obj/item/weapon/wrapping_paper/examine(mob/user) if(..(user, 1)) - user << text("There is about [] square units of paper left!", src.amount) + to_chat(user, "There is about [src.amount] square units of paper left!") /obj/item/weapon/wrapping_paper/attack(mob/target as mob, mob/user as mob) if (!istype(target, /mob/living/carbon/human)) return @@ -182,6 +184,6 @@ add_attack_logs(user,H,"Wrapped with [src]") else - user << "You need more paper." + to_chat(user, "You need more paper.") else - user << "They are moving around too much. A straightjacket would help." + to_chat(user, "They are moving around too much. A straightjacket would help.") diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index eaed5f9f4c..087265ca5b 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -80,10 +80,10 @@ if(ishuman(M)) var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] if (E && E.damage >= E.min_bruised_damage) - M << "Your eyes start to burn badly!" + to_chat(M, "Your eyes start to burn badly!") if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) if (E.damage >= E.min_broken_damage) - M << "You can't see anything!" + to_chat(M, "You can't see anything!") if (M.ear_damage >= 15) to_chat(M, "Your ears start to ring badly!") if(!banglet && !(istype(src , /obj/item/weapon/grenade/flashbang/clusterbang))) diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index d7512f54b1..ac24d42255 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -16,7 +16,7 @@ /obj/item/weapon/grenade/proc/clown_check(var/mob/living/user) if((CLUMSY in user.mutations) && prob(50)) - user << "Huh? How does this thing work?" + to_chat(user, "Huh? How does this thing work?") activate(user) add_fingerprint(user) @@ -30,7 +30,7 @@ if (istype(target, /obj/item/weapon/storage)) return ..() // Trying to put it in a full container if (istype(target, /obj/item/weapon/gun/grenadelauncher)) return ..() if((user.get_active_hand() == src) && (!active) && (clown_check(user)) && target.loc != src.loc) - user << "You prime the [name]! [det_time/10] seconds!" + to_chat(user, "You prime the [name]! [det_time/10] seconds!") active = 1 icon_state = initial(icon_state) + "_active" playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) @@ -47,17 +47,17 @@ /obj/item/weapon/grenade/examine(mob/user) if(..(user, 0)) if(det_time > 1) - user << "The timer is set to [det_time/10] seconds." + to_chat(user, "The timer is set to [det_time/10] seconds.") return if(det_time == null) return - user << "\The [src] is set for instant detonation." + to_chat(user, "\The [src] is set for instant detonation.") /obj/item/weapon/grenade/attack_self(mob/user as mob) if(!active) if(clown_check(user)) - user << "You prime \the [name]! [det_time/10] seconds!" + to_chat(user, "You prime \the [name]! [det_time/10] seconds!") activate(user) add_fingerprint(user) @@ -95,16 +95,16 @@ switch(det_time) if (1) det_time = 10 - user << "You set the [name] for 1 second detonation time." + to_chat(user, "You set the [name] for 1 second detonation time.") if (10) det_time = 30 - user << "You set the [name] for 3 second detonation time." + to_chat(user, "You set the [name] for 3 second detonation time.") if (30) det_time = 50 - user << "You set the [name] for 5 second detonation time." + to_chat(user, "You set the [name] for 5 second detonation time.") if (50) det_time = 1 - user << "You set the [name] for instant detonation." + to_chat(user, "You set the [name] for instant detonation.") add_fingerprint(user) ..() return diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 6c5f2ced83..763b89c353 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -11,6 +11,7 @@ throw_range = 5 origin_tech = list(TECH_MATERIAL = 1) matter = list(DEFAULT_WALL_MATERIAL = 500) + drop_sound = 'sound/items/drop/accessory.ogg' var/elastic var/dispenser = 0 var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes diff --git a/code/game/objects/items/weapons/hydroponics.dm b/code/game/objects/items/weapons/hydroponics.dm index 749f1a08b9..15c1478bf3 100644 --- a/code/game/objects/items/weapons/hydroponics.dm +++ b/code/game/objects/items/weapons/hydroponics.dm @@ -25,9 +25,9 @@ mode = !mode switch (mode) if(1) - usr << "The bag now picks up all seeds in a tile at once." + to_chat(usr, "The bag now picks up all seeds in a tile at once.") if(0) - usr << "The bag now picks up one seed pouch at a time." + to_chat(usr, "The bag now picks up one seed pouch at a time.") /obj/item/seeds/attackby(var/obj/item/O as obj, var/mob/user as mob) ..() @@ -42,10 +42,10 @@ else S.item_quants[G.name] = 1 else - user << "The seed bag is full." + to_chat(user, "The seed bag is full.") S.updateUsrDialog() return - user << "You pick up all the seeds." + to_chat(user, "You pick up all the seeds.") else if (S.contents.len < S.capacity) S.contents += src; @@ -54,7 +54,7 @@ else S.item_quants[name] = 1 else - user << "The seed bag is full." + to_chat(user, "The seed bag is full.") S.updateUsrDialog() return diff --git a/code/game/objects/items/weapons/id cards/cards.dm b/code/game/objects/items/weapons/id cards/cards.dm index 389601ef1f..6179632f9b 100644 --- a/code/game/objects/items/weapons/id cards/cards.dm +++ b/code/game/objects/items/weapons/id cards/cards.dm @@ -20,6 +20,7 @@ var/associated_account_number = 0 var/list/files = list( ) + drop_sound = 'sound/items/drop/card.ogg' /obj/item/weapon/card/data name = "data disk" @@ -70,10 +71,10 @@ origin_tech = list(TECH_MAGNET = 2, TECH_ILLEGAL = 2) var/uses = 10 -/obj/item/weapon/card/emag/resolve_attackby(atom/A, mob/user) +/obj/item/weapon/card/emag/resolve_attackby(atom/A, mob/user, var/click_parameters) var/used_uses = A.emag_act(uses, user, src) if(used_uses < 0) - return ..(A, user) + return ..(A, user, click_parameters) uses -= used_uses A.add_fingerprint(user) @@ -98,9 +99,9 @@ if(istype(O, /obj/item/stack/telecrystal)) var/obj/item/stack/telecrystal/T = O if(T.amount < 1) - usr << "You are not adding enough telecrystals to fuel \the [src]." + to_chat(usr, "You are not adding enough telecrystals to fuel \the [src].") return uses += T.amount/2 //Gives 5 uses per 10 TC uses = CEILING(uses, 1) //Ensures no decimal uses nonsense, rounds up to be nice - usr << "You add \the [O] to \the [src]. Increasing the uses of \the [src] to [uses]." + to_chat(usr, "You add \the [O] to \the [src]. Increasing the uses of \the [src] to [uses].") qdel(O) \ No newline at end of file diff --git a/code/game/objects/items/weapons/id cards/station_ids.dm b/code/game/objects/items/weapons/id cards/station_ids.dm index 7a2e39014c..c43217919a 100644 --- a/code/game/objects/items/weapons/id cards/station_ids.dm +++ b/code/game/objects/items/weapons/id cards/station_ids.dm @@ -35,9 +35,9 @@ set src in oview(1) if(in_range(usr, src)) show(usr) - usr << desc + to_chat(usr,desc) else - usr << "It is too far away." + to_chat(usr, "It is too far away.") /obj/item/weapon/card/id/proc/prevent_tracking() return 0 @@ -91,8 +91,8 @@ return dat /obj/item/weapon/card/id/attack_self(mob/user as mob) - user.visible_message("\The [user] shows you: \icon[src] [src.name]. The assignment on the card: [src.assignment]",\ - "You flash your ID card: \icon[src] [src.name]. The assignment on the card: [src.assignment]") + user.visible_message("\The [user] shows you: [bicon(src)] [src.name]. The assignment on the card: [src.assignment]",\ + "You flash your ID card: [bicon(src)] [src.name]. The assignment on the card: [src.assignment]") src.add_fingerprint(user) return @@ -108,10 +108,10 @@ set category = "Object" set src in usr - usr << text("\icon[] []: The current assignment on the card is [].", src, src.name, src.assignment) - usr << "The blood type on the card is [blood_type]." - usr << "The DNA hash on the card is [dna_hash]." - usr << "The fingerprint hash on the card is [fingerprint_hash]." + to_chat(usr, "[bicon(src)] [src.name]: The current assignment on the card is [src.assignment].") + to_chat(usr, "The blood type on the card is [blood_type].") + to_chat(usr, "The DNA hash on the card is [dna_hash].") + to_chat(usr, "The fingerprint hash on the card is [fingerprint_hash].") return /obj/item/weapon/card/id/get_worn_icon_state(var/slot_name) diff --git a/code/game/objects/items/weapons/id cards/syndicate_ids.dm b/code/game/objects/items/weapons/id cards/syndicate_ids.dm index bbf05718fc..68cb0298a4 100644 --- a/code/game/objects/items/weapons/id cards/syndicate_ids.dm +++ b/code/game/objects/items/weapons/id cards/syndicate_ids.dm @@ -91,7 +91,7 @@ var/user = usr if(href_list["electronic_warfare"]) electronic_warfare = text2num(href_list["electronic_warfare"]) - user << "Electronic warfare [electronic_warfare ? "enabled" : "disabled"]." + to_chat(user, "Electronic warfare [electronic_warfare ? "enabled" : "disabled"].") else if(href_list["set"]) switch(href_list["set"]) if("Age") @@ -101,20 +101,20 @@ age = initial(age) else age = new_age - user << "Age has been set to '[age]'." + to_chat(user, "Age has been set to '[age]'.") . = 1 if("Appearance") var/datum/card_state/choice = input(user, "Select the appearance for this card.", "Agent Card Appearance") as null|anything in id_card_states() if(choice && CanUseTopic(user, state)) src.icon_state = choice.icon_state src.item_state = choice.item_state - usr << "Appearance changed to [choice]." + to_chat(usr, "Appearance changed to [choice].") . = 1 if("Assignment") var/new_job = sanitize(input(user,"What assignment would you like to put on this card?\nChanging assignment will not grant or remove any access levels.","Agent Card Assignment", assignment) as null|text) if(!isnull(new_job) && CanUseTopic(user, state)) src.assignment = new_job - user << "Occupation changed to '[new_job]'." + to_chat(user, "Occupation changed to '[new_job]'.") update_name() . = 1 if("Blood Type") @@ -126,7 +126,7 @@ var/new_blood_type = sanitize(input(user,"What blood type would you like to be written on this card?","Agent Card Blood Type",default) as null|text) if(!isnull(new_blood_type) && CanUseTopic(user, state)) src.blood_type = new_blood_type - user << "Blood type changed to '[new_blood_type]'." + to_chat(user, "Blood type changed to '[new_blood_type]'.") . = 1 if("DNA Hash") var/default = dna_hash @@ -137,7 +137,7 @@ var/new_dna_hash = sanitize(input(user,"What DNA hash would you like to be written on this card?","Agent Card DNA Hash",default) as null|text) if(!isnull(new_dna_hash) && CanUseTopic(user, state)) src.dna_hash = new_dna_hash - user << "DNA hash changed to '[new_dna_hash]'." + to_chat(user, "DNA hash changed to '[new_dna_hash]'.") . = 1 if("Fingerprint Hash") var/default = fingerprint_hash @@ -148,24 +148,24 @@ var/new_fingerprint_hash = sanitize(input(user,"What fingerprint hash would you like to be written on this card?","Agent Card Fingerprint Hash",default) as null|text) if(!isnull(new_fingerprint_hash) && CanUseTopic(user, state)) src.fingerprint_hash = new_fingerprint_hash - user << "Fingerprint hash changed to '[new_fingerprint_hash]'." + to_chat(user, "Fingerprint hash changed to '[new_fingerprint_hash]'.") . = 1 if("Name") var/new_name = sanitizeName(input(user,"What name would you like to put on this card?","Agent Card Name", registered_name) as null|text) if(!isnull(new_name) && CanUseTopic(user, state)) src.registered_name = new_name update_name() - user << "Name changed to '[new_name]'." + to_chat(user, "Name changed to '[new_name]'.") . = 1 if("Photo") set_id_photo(user) - user << "Photo changed." + to_chat(user, "Photo changed.") . = 1 if("Sex") var/new_sex = sanitize(input(user,"What sex would you like to put on this card?","Agent Card Sex", sex) as null|text) if(!isnull(new_sex) && CanUseTopic(user, state)) src.sex = new_sex - user << "Sex changed to '[new_sex]'." + to_chat(user, "Sex changed to '[new_sex]'.") . = 1 if("Factory Reset") if(alert("This will factory reset the card, including access and owner. Continue?", "Factory Reset", "No", "Yes") == "Yes" && CanUseTopic(user, state)) @@ -181,7 +181,7 @@ registered_name = initial(registered_name) unset_registered_user() sex = initial(sex) - user << "All information has been deleted from \the [src]." + to_chat(user, "All information has been deleted from \the [src].") . = 1 // Always update the UI, or buttons will spin indefinitely diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index ff4146b8a0..0de385500e 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -53,7 +53,7 @@ return 0 /obj/item/weapon/implant/proc/meltdown() //breaks it down, making implant unrecongizible - imp_in << "You feel something melting inside [part ? "your [part.name]" : "you"]!" + to_chat(imp_in, "You feel something melting inside [part ? "your [part.name]" : "you"]!") if (part) part.take_damage(burn = 15, used_weapon = "Electronics meltdown") else @@ -284,7 +284,7 @@ Implant Specifics:
"} var/list/replacechars = list("'" = "","\"" = "",">" = "","<" = "","(" = "",")" = "") phrase = replace_characters(phrase, replacechars) usr.mind.store_memory("Explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.", 0, 0) - usr << "The implanted explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate." + to_chat(usr, "The implanted explosive implant in [source] can be activated by saying something containing the phrase ''[src.phrase]'', say [src.phrase] to attempt to activate.") /obj/item/weapon/implant/explosive/emp_act(severity) if (malfunction) @@ -381,9 +381,9 @@ the implant may become unstable and either pre-maturely inject the subject or si if((!cause) || (!src.imp_in)) return 0 var/mob/living/carbon/R = src.imp_in src.reagents.trans_to_mob(R, cause, CHEM_BLOOD) - R << "You hear a faint *beep*." + to_chat(R, "You hear a faint *beep*.") if(!src.reagents.total_volume) - R << "You hear a faint click from your chest." + to_chat(R, "You hear a faint click from your chest.") playsound(R, 'sound/weapons/empty.ogg', 10, 1) spawn(0) qdel(src) @@ -472,7 +472,7 @@ the implant may become unstable and either pre-maturely inject the subject or si if (src.uses < 1) return 0 if (emote == "pale") src.uses-- - source << "You feel a sudden surge of energy!" + to_chat(source, "You feel a sudden surge of energy!") source.SetStunned(0) source.SetWeakened(0) source.SetParalysis(0) @@ -481,7 +481,7 @@ the implant may become unstable and either pre-maturely inject the subject or si /obj/item/weapon/implant/adrenalin/post_implant(mob/source) source.mind.store_memory("A implant can be activated by using the pale emote, say *pale to attempt to activate.", 0, 0) - source << "The implanted freedom implant can be activated by using the pale emote, say *pale to attempt to activate." + to_chat(source, "The implanted freedom implant can be activated by using the pale emote, say *pale to attempt to activate.") ////////////////////////////// // Death Alarm Implant @@ -595,7 +595,7 @@ the implant may become unstable and either pre-maturely inject the subject or si return 0 if (emote == src.activation_emote) - source << "The air glows as \the [src.scanned.name] uncompresses." + to_chat(source, "The air glows as \the [src.scanned.name] uncompresses.") activate() /obj/item/weapon/implant/compressed/activate() @@ -610,7 +610,7 @@ the implant may become unstable and either pre-maturely inject the subject or si src.activation_emote = input("Choose activation emote:") in list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") if (source.mind) source.mind.store_memory("Compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - source << "The implanted compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate." + to_chat(source, "The implanted compressed matter implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") /obj/item/weapon/implant/compressed/islegal() diff --git a/code/game/objects/items/weapons/implants/implantaugment.dm b/code/game/objects/items/weapons/implants/implantaugment.dm index 10873d1d06..be6c8721ee 100644 --- a/code/game/objects/items/weapons/implants/implantaugment.dm +++ b/code/game/objects/items/weapons/implants/implantaugment.dm @@ -75,12 +75,12 @@ var/obj/item/organ/external/E = setup_augment_slots(H, NewOrgan) to_chat(H, "You feel a tingling sensation in your [part].") - if(E && istype(E) && !(H.internal_organs_by_name[NewOrgan.organ_tag])) + NewOrgan.forceMove(H) + NewOrgan.owner = H + if(E && istype(E) && !(H.internal_organs_by_name[NewOrgan.organ_tag]) && NewOrgan.check_verb_compatability()) spawn(rand(1 SECONDS, 30 SECONDS)) to_chat(H, "You feel a pressure in your [E] as the tingling fades, the lump caused by the implant now gone.") - NewOrgan.forceMove(H) - NewOrgan.owner = H if(E.internal_organs == null) E.internal_organs = list() E.internal_organs |= NewOrgan diff --git a/code/game/objects/items/weapons/implants/implantcase.dm b/code/game/objects/items/weapons/implants/implantcase.dm index 93fef6ca92..3bcb295e2a 100644 --- a/code/game/objects/items/weapons/implants/implantcase.dm +++ b/code/game/objects/items/weapons/implants/implantcase.dm @@ -35,11 +35,11 @@ if(!src.imp) return if(!src.imp.allow_reagents) return if(src.imp.reagents.total_volume >= src.imp.reagents.maximum_volume) - user << "\The [src] is full." + to_chat(user, "\The [src] is full.") else spawn(5) I.reagents.trans_to_obj(src.imp, 5) - user << "You inject 5 units of the solution. The syringe now contains [I.reagents.total_volume] units." + to_chat(user, "You inject 5 units of the solution. The syringe now contains [I.reagents.total_volume] units.") else if (istype(I, /obj/item/weapon/implanter)) var/obj/item/weapon/implanter/M = I if (M.imp) diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index 5b1ba3f6c5..e1464bd124 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -108,10 +108,10 @@ put_mob(mob/living/carbon/M as mob) if(!iscarbon(M)) - usr << "\The [src] cannot hold this!" + to_chat(usr, "\The [src] cannot hold this!") return if(src.occupant) - usr << "\The [src] is already occupied!" + to_chat(usr, "\The [src] is already occupied!") return if(M.client) M.client.perspective = EYE_PERSPECTIVE diff --git a/code/game/objects/items/weapons/implants/implantfreedom.dm b/code/game/objects/items/weapons/implants/implantfreedom.dm index fab692775d..b55274cc92 100644 --- a/code/game/objects/items/weapons/implants/implantfreedom.dm +++ b/code/game/objects/items/weapons/implants/implantfreedom.dm @@ -21,7 +21,7 @@ if (emote == src.activation_emote) src.uses-- - source << "You feel a faint click." + to_chat(source, "You feel a faint click.") if (source.handcuffed) var/obj/item/weapon/W = source.handcuffed source.handcuffed = null @@ -50,7 +50,7 @@ /obj/item/weapon/implant/freedom/post_implant(mob/source) source.mind.store_memory("Freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - source << "The implanted freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate." + to_chat(source, "The implanted freedom implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") /obj/item/weapon/implant/freedom/get_data() var/dat = {" diff --git a/code/game/objects/items/weapons/implants/implantuplink.dm b/code/game/objects/items/weapons/implants/implantuplink.dm index 82809cfe61..873a09149c 100644 --- a/code/game/objects/items/weapons/implants/implantuplink.dm +++ b/code/game/objects/items/weapons/implants/implantuplink.dm @@ -15,7 +15,7 @@ listening_objects |= src activation_emote = input("Choose activation emote:") in list("blink", "blink_r", "eyebrow", "chuckle", "twitch", "frown", "nod", "blush", "giggle", "grin", "groan", "shrug", "smile", "pale", "sniff", "whimper", "wink") source.mind.store_memory("Uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.", 0, 0) - source << "The implanted uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate." + to_chat(source, "The implanted uplink implant can be activated by using the [src.activation_emote] emote, say *[src.activation_emote] to attempt to activate.") /obj/item/weapon/implant/uplink/trigger(emote, mob/source as mob) if(hidden_uplink && usr == source) // Let's not have another people activate our uplink diff --git a/code/game/objects/items/weapons/material/ashtray.dm b/code/game/objects/items/weapons/material/ashtray.dm index 5a26720b7c..5cd017f005 100644 --- a/code/game/objects/items/weapons/material/ashtray.dm +++ b/code/game/objects/items/weapons/material/ashtray.dm @@ -4,6 +4,7 @@ var/global/list/ashtray_cache = list() name = "ashtray" icon = 'icons/obj/objects.dmi' icon_state = "blank" + randpixel = 5 force_divisor = 0.1 thrown_force_divisor = 0.1 var/image/base_image @@ -15,8 +16,7 @@ var/global/list/ashtray_cache = list() qdel(src) return max_butts = round(material.hardness/5) //This is arbitrary but whatever. - src.pixel_y = rand(-5, 5) - src.pixel_x = rand(-6, 6) + randpixel_xy() update_icon() return @@ -48,7 +48,7 @@ var/global/list/ashtray_cache = list() return if (istype(W,/obj/item/weapon/cigbutt) || istype(W,/obj/item/clothing/mask/smokable/cigarette) || istype(W, /obj/item/weapon/flame/match)) if (contents.len >= max_butts) - user << "\The [src] is full." + to_chat(user, "\The [src] is full.") return user.remove_from_mob(W) W.loc = src @@ -65,7 +65,7 @@ var/global/list/ashtray_cache = list() //spawn(1) // TemperatureAct(150) else if (cig.lit == 0) - user << "You place [cig] in [src] without even smoking it. Why would you do that?" + to_chat(user, "You place [cig] in [src] without even smoking it. Why would you do that?") src.visible_message("[user] places [W] in [src].") user.update_inv_l_hand() @@ -74,7 +74,7 @@ var/global/list/ashtray_cache = list() update_icon() else health = max(0,health - W.force) - user << "You hit [src] with [W]." + to_chat(user, "You hit [src] with [W].") if (health < 1) shatter() return diff --git a/code/game/objects/items/weapons/material/kitchen.dm b/code/game/objects/items/weapons/material/kitchen.dm index 913fb6fddf..b2369f995d 100644 --- a/code/game/objects/items/weapons/material/kitchen.dm +++ b/code/game/objects/items/weapons/material/kitchen.dm @@ -50,7 +50,7 @@ overlays.Cut() return else - user << "You don't have anything on \the [src]." //if we have help intent and no food scooped up DON'T STAB OURSELVES WITH THE FORK + to_chat(user, "You don't have anything on \the [src].") //if we have help intent and no food scooped up DON'T STAB OURSELVES WITH THE FORK return /obj/item/weapon/material/kitchen/utensil/fork @@ -82,7 +82,7 @@ /* From the time of Clowns. Commented out for posterity, and sanity. /obj/item/weapon/material/knife/attack(target as mob, mob/living/user as mob) if ((CLUMSY in user.mutations) && prob(50)) - user << "You accidentally cut yourself with \the [src]." + to_chat(user, "You accidentally cut yourself with \the [src].") user.take_organ_damage(20) return return ..() @@ -103,10 +103,11 @@ force_divisor = 0.7 // 10 when wielded with weight 15 (wood) dulled_divisor = 0.75 // Still a club thrown_force_divisor = 1 // as above + drop_sound = 'sound/items/drop/wooden.ogg' /obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob) if ((CLUMSY in user.mutations) && prob(50)) - user << "\The [src] slips out of your hand and hits your head." + to_chat(user, "\The [src] slips out of your hand and hits your head.") user.take_organ_damage(10) user.Paralyse(2) return diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index bce6cb22dd..cf5a706525 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -9,6 +9,7 @@ attack_verb = list("patted", "tapped") force_divisor = 0.25 // 15 when wielded with hardness 60 (steel) thrown_force_divisor = 0.25 // 5 when thrown with weight 20 (steel) + drop_sound = 'sound/items/drop/knife.ogg' /obj/item/weapon/material/butterfly/update_force() if(active) @@ -44,10 +45,10 @@ /obj/item/weapon/material/butterfly/attack_self(mob/user) active = !active if(active) - user << "You flip out \the [src]." + to_chat(user, "You flip out \the [src].") playsound(user, 'sound/weapons/flipblade.ogg', 15, 1) else - user << "\The [src] can now be concealed." + to_chat(user, "\The [src] can now be concealed.") update_force() add_fingerprint(user) @@ -65,12 +66,13 @@ matter = list(DEFAULT_WALL_MATERIAL = 12000) origin_tech = list(TECH_MATERIAL = 1) attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + drop_sound = 'sound/items/drop/knife.ogg' /obj/item/weapon/material/knife/suicide_act(mob/user) var/datum/gender/TU = gender_datums[user.get_visible_gender()] - viewers(user) << pick("\The [user] is slitting [TU.his] wrists with \the [src]! It looks like [TU.hes] trying to commit suicide.", \ + to_chat(viewers(user), pick("\The [user] is slitting [TU.his] wrists with \the [src]! It looks like [TU.hes] trying to commit suicide.", \ "\The [user] is slitting [TU.his] throat with \the [src]! It looks like [TU.hes] trying to commit suicide.", \ - "\The [user] is slitting [TU.his] stomach open with \the [src]! It looks like [TU.hes] trying to commit seppuku.") + "\The [user] is slitting [TU.his] stomach open with \the [src]! It looks like [TU.hes] trying to commit seppuku.")) return (BRUTELOSS) // These no longer inherit from hatchets. diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm index 9d4592c17c..738db57fb9 100644 --- a/code/game/objects/items/weapons/material/material_weapons.dm +++ b/code/game/objects/items/weapons/material/material_weapons.dm @@ -59,7 +59,7 @@ force = round(force*dulled_divisor) throwforce = round(material.get_blunt_damage()*thrown_force_divisor) //spawn(1) - // world << "[src] has force [force] and throwforce [throwforce] when made from default material [material.name]" + // to_world("[src] has force [force] and throwforce [throwforce] when made from default material [material.name]") /obj/item/weapon/material/proc/set_material(var/new_material) material = get_material_by_name(new_material) diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm index 050307eb7e..29193b1c08 100644 --- a/code/game/objects/items/weapons/material/misc.dm +++ b/code/game/objects/items/weapons/material/misc.dm @@ -21,6 +21,7 @@ origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1) attack_verb = list("chopped", "torn", "cut") applies_material_colour = 0 + drop_sound = 'sound/items/drop/axe.ogg' /obj/item/weapon/material/knife/machete/hatchet/unathiknife name = "duelling knife" @@ -72,12 +73,14 @@ /obj/item/weapon/material/snow/snowball/attack_self(mob/user as mob) if(user.a_intent == I_HURT) - visible_message("[user] has smashed the snowball in their hand!", "You smash the snowball in your hand.") + //visible_message("[user] has smashed the snowball in their hand!", "You smash the snowball in your hand.") + to_chat(user, "You smash the snowball in your hand.") var/atom/S = new /obj/item/stack/material/snow(user.loc) del(src) user.put_in_hands(S) else - visible_message("[user] starts compacting the snowball.", "You start compacting the snowball.") + //visible_message("[user] starts compacting the snowball.", "You start compacting the snowball.") + to_chat(user, "You start compacting the snowball.") if(do_after(user, 2 SECONDS)) var/atom/S = new /obj/item/weapon/material/snow/snowball/reinforced(user.loc) del(src) diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm index 8df81e434c..e2394537f3 100644 --- a/code/game/objects/items/weapons/material/shards.dm +++ b/code/game/objects/items/weapons/material/shards.dm @@ -5,6 +5,7 @@ icon = 'icons/obj/shards.dmi' desc = "Made of nothing. How does this even exist?" // set based on material, if this desc is visible it's a bug (shards default to being made of glass) icon_state = "large" + randpixel = 8 sharp = 1 edge = 1 w_class = ITEMSIZE_SMALL @@ -18,8 +19,8 @@ /obj/item/weapon/material/shard/suicide_act(mob/user) var/datum/gender/TU = gender_datums[user.get_visible_gender()] - viewers(user) << pick("\The [user] is slitting [TU.his] wrists with \the [src]! It looks like [TU.hes] trying to commit suicide.", - "\The [user] is slitting [TU.his] throat with \the [src]! It looks like [TU.hes] trying to commit suicide.") + to_chat(viewers(user), pick("\The [user] is slitting [TU.his] wrists with \the [src]! It looks like [TU.hes] trying to commit suicide.", + "\The [user] is slitting [TU.his] throat with \the [src]! It looks like [TU.hes] trying to commit suicide.")) return (BRUTELOSS) /obj/item/weapon/material/shard/set_material(var/new_material) @@ -28,8 +29,7 @@ return icon_state = "[material.shard_icon][pick("large", "medium", "small")]" - pixel_x = rand(-8, 8) - pixel_y = rand(-8, 8) + randpixel_xy() update_icon() if(material.shard_type) @@ -106,14 +106,10 @@ qdel(src) return -/obj/item/weapon/material/shard/Crossed(AM as mob|obj) +/obj/item/weapon/material/shard/Crossed(atom/movable/AM as mob|obj) ..() - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(AM.is_incorporeal()) + return if(isliving(AM)) var/mob/M = AM diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm index 46551e588a..3affd40887 100644 --- a/code/game/objects/items/weapons/material/swords.dm +++ b/code/game/objects/items/weapons/material/swords.dm @@ -9,6 +9,7 @@ edge = 1 attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") hitsound = 'sound/weapons/bladeslice.ogg' + drop_sound = 'sound/items/drop/sword.ogg' /obj/item/weapon/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") if(unique_parry_check(user, attacker, damage_source) && prob(50)) @@ -19,7 +20,7 @@ /obj/item/weapon/material/sword/suicide_act(mob/user) var/datum/gender/TU = gender_datums[user.get_visible_gender()] - viewers(user) << "[user] is falling on the [src.name]! It looks like [TU.he] [TU.is] trying to commit suicide." + to_chat(viewers(user),"[user] is falling on the [src.name]! It looks like [TU.he] [TU.is] trying to commit suicide.") return(BRUTELOSS) /obj/item/weapon/material/sword/katana diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm index c0640d7b49..108751654a 100644 --- a/code/game/objects/items/weapons/material/twohanded.dm +++ b/code/game/objects/items/weapons/material/twohanded.dm @@ -51,7 +51,7 @@ force_unwielded = round(force_wielded*unwielded_force_divisor) force = force_unwielded throwforce = round(force*thrown_force_divisor) - //world << "[src] has unwielded force [force_unwielded], wielded force [force_wielded] and throwforce [throwforce] when made from default material [material.name]" + //to_world("[src] has unwielded force [force_unwielded], wielded force [force_wielded] and throwforce [throwforce] when made from default material [material.name]") /obj/item/weapon/material/twohanded/New() ..() @@ -95,6 +95,7 @@ attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") applies_material_colour = 0 can_cleave = TRUE + drop_sound = 'sound/items/drop/axe.ogg' /obj/item/weapon/material/twohanded/fireaxe/update_held_icon() var/mob/living/M = loc diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 17d7bc4621..9145786d69 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -168,7 +168,7 @@ . = ..() var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") blade_overlay.color = lcolor - color = lcolor + color = lcolor if(rainbow) blade_overlay = mutable_appearance(icon, "[icon_state]_blade_rainbow") blade_overlay.color = "FFFFFF" @@ -211,6 +211,8 @@ desc = "An energised battle axe." icon_state = "eaxe" item_state = "eaxe" + colorable = FALSE + lcolor = null //active_force = 150 //holy... active_force = 60 active_throwforce = 35 diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index f8669aef9d..fc2e6c2534 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -23,7 +23,7 @@ GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/weapon/mop) if(!proximity) return if(istype(A, /turf) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay) || istype(A, /obj/effect/rune)) if(reagents.total_volume < 1) - user << "Your mop is dry!" + to_chat(user, "Your mop is dry!") return user.visible_message("[user] begins to clean \the [get_turf(A)].") @@ -32,7 +32,7 @@ GLOBAL_LIST_BOILERPLATE(all_mops, /obj/item/weapon/mop) var/turf/T = get_turf(A) if(T) T.clean(src, user) - user << "You have finished mopping!" + to_chat(user, "You have finished mopping!") /obj/effect/attackby(obj/item/I, mob/user) diff --git a/code/game/objects/items/weapons/mop_deploy.dm b/code/game/objects/items/weapons/mop_deploy.dm index 93b3657f49..2e2ea6aebc 100644 --- a/code/game/objects/items/weapons/mop_deploy.dm +++ b/code/game/objects/items/weapons/mop_deploy.dm @@ -1,6 +1,7 @@ /obj/item/weapon/mop_deploy name = "mop" desc = "Deployable mop." + icon = 'icons/obj/janitor.dmi' icon_state = "mop" force = 3 anchored = 1 // Never spawned outside of inventory, should be fine. @@ -40,7 +41,7 @@ var/turf/T = get_turf(A) if(T) T.clean_deploy(src) - user << "You have finished mopping!" + to_chat(user, "You have finished mopping!") /obj/effect/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/weapon/mop_deploy) || istype(I, /obj/item/weapon/soap)) diff --git a/code/game/objects/items/weapons/scrolls.dm b/code/game/objects/items/weapons/scrolls.dm index 2756822401..639a1e9bc6 100644 --- a/code/game/objects/items/weapons/scrolls.dm +++ b/code/game/objects/items/weapons/scrolls.dm @@ -12,7 +12,7 @@ /obj/item/weapon/teleportation_scroll/attack_self(mob/user as mob) if((user.mind && !wizards.is_antagonist(user.mind))) - usr << "You stare at the scroll but cannot make sense of the markings!" + to_chat(usr, "You stare at the scroll but cannot make sense of the markings!") return user.set_machine(src) @@ -69,7 +69,7 @@ L+=T if(!L.len) - user <<"The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry." + to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") return if(user && user.buckled) diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 6d3310cdc4..744a92e370 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -155,7 +155,7 @@ /obj/item/weapon/shield/energy/attack_self(mob/living/user as mob) if ((CLUMSY in user.mutations) && prob(50)) - user << "You beat yourself in the head with [src]." + to_chat(user, "You beat yourself in the head with [src].") user.take_organ_damage(5) active = !active if (active) @@ -164,7 +164,7 @@ w_class = ITEMSIZE_LARGE slot_flags = null playsound(user, 'sound/weapons/saberon.ogg', 50, 1) - user << "\The [src] is now active." + to_chat(user, "\The [src] is now active.") else force = 3 @@ -172,7 +172,7 @@ w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) - user << "\The [src] can now be concealed." + to_chat(user, "\The [src] can now be concealed.") if(istype(user,/mob/living/carbon/human)) var/mob/living/carbon/human/H = user @@ -186,15 +186,22 @@ var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") if(lcolor) blade_overlay.color = lcolor + color = lcolor cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other if(active) add_overlay(blade_overlay) item_state = "[icon_state]_blade" set_light(lrange, lpower, lcolor) else + color = "FFFFFF" set_light(0) item_state = "[icon_state]" + if(istype(usr,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = usr + H.update_inv_l_hand() + H.update_inv_r_hand() + /obj/item/weapon/shield/energy/AltClick(mob/living/user) if(!in_range(src, user)) //Basic checks to prevent abuse return @@ -204,7 +211,7 @@ if(alert("Are you sure you want to recolor your shield?", "Confirm Recolor", "Yes", "No") == "Yes") var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null if(energy_color_input) - lcolor = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1) + lcolor = sanitize_hexcolor(energy_color_input) update_icon() /obj/item/weapon/shield/energy/examine(mob/user) @@ -241,14 +248,14 @@ throw_speed = 2 w_class = ITEMSIZE_LARGE slot_flags = SLOT_BACK - user << "You extend \the [src]." + to_chat(user, "You extend \the [src].") else force = 3 throwforce = 3 throw_speed = 3 w_class = ITEMSIZE_NORMAL slot_flags = null - user << "[src] can now be concealed." + to_chat(user, "[src] can now be concealed.") if(istype(user,/mob/living/carbon/human)) var/mob/living/carbon/human/H = user diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index f5ea4ead6f..6b347e0cb6 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -16,11 +16,8 @@ max_storage_space = INVENTORY_STANDARD_SPACE var/flippable = 0 var/side = 0 //0 = right, 1 = left + drop_sound = 'sound/items/drop/backpack.ogg' -/obj/item/weapon/storage/backpack/attackby(obj/item/weapon/W as obj, mob/user as mob) - if (src.use_sound) - playsound(src.loc, src.use_sound, 50, 1, -5) - ..() /obj/item/weapon/storage/backpack/equipped(var/mob/user, var/slot) if (slot == slot_back && src.use_sound) @@ -53,7 +50,7 @@ /obj/item/weapon/storage/backpack/holding/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/storage/backpack/holding)) - user << "The Bluespace interfaces of the two devices conflict and malfunction." + to_chat(user, "The Bluespace interfaces of the two devices conflict and malfunction.") qdel(W) return . = ..() diff --git a/code/game/objects/items/weapons/storage/backpack_vr.dm b/code/game/objects/items/weapons/storage/backpack_vr.dm index 5e121a2b35..b14fbf5ff0 100644 --- a/code/game/objects/items/weapons/storage/backpack_vr.dm +++ b/code/game/objects/items/weapons/storage/backpack_vr.dm @@ -19,7 +19,7 @@ slowdown = initial(slowdown) return 1 else - H << "[no_message]" + to_chat(H, "[no_message]") return 0 /* If anyone wants to make some... this is how you would. @@ -102,7 +102,7 @@ slowdown = initial(slowdown) return 1 else - H << "[no_message]" + to_chat(H, "[no_message]") return 0 /obj/item/weapon/storage/backpack/saddlebag_common/robust //Shared bag for other taurs with sturdy backs @@ -134,3 +134,6 @@ sprite_sheets = list( SPECIES_TESHARI = 'icons/mob/species/seromi/back.dmi', SPECIES_WEREBEAST = 'icons/mob/species/werebeast/back.dmi') + +/obj/item/weapon/storage/backpack/ert + max_storage_space = INVENTORY_DUFFLEBAG_SPACE diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index 29e86dbbd4..005def5410 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -23,6 +23,7 @@ display_contents_with_number = 0 // UNStABLE AS FuCK, turn on when it stops crashing clients use_to_pickup = 1 slot_flags = SLOT_BELT + drop_sound = 'sound/items/drop/backpack.ogg' // ----------------------------- // Trash bag @@ -33,6 +34,7 @@ icon = 'icons/obj/janitor.dmi' icon_state = "trashbag0" item_state_slots = list(slot_r_hand_str = "trashbag", slot_l_hand_str = "trashbag") + drop_sound = 'sound/items/drop/wrapper.ogg' w_class = ITEMSIZE_LARGE max_w_class = ITEMSIZE_SMALL @@ -59,6 +61,7 @@ desc = "It's a very flimsy, very noisy alternative to a bag." icon = 'icons/obj/trash.dmi' icon_state = "plasticbag" + drop_sound = 'sound/items/drop/wrapper.ogg' w_class = ITEMSIZE_LARGE max_w_class = ITEMSIZE_SMALL @@ -217,14 +220,14 @@ /obj/item/weapon/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W as obj, stop_messages = 0) if(!istype(W,/obj/item/stack/material)) if(!stop_messages) - usr << "The snatcher does not accept [W]." + to_chat(usr, "The snatcher does not accept [W].") return 0 var/current = 0 for(var/obj/item/stack/material/S in contents) current += S.amount if(capacity == current)//If it's full, you're done if(!stop_messages) - usr << "The snatcher is full." + to_chat(usr, "The snatcher is full.") return 0 return 1 diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index ddb8c489f6..c900e729c5 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -9,6 +9,7 @@ slot_flags = SLOT_BELT attack_verb = list("whipped", "lashed", "disciplined") sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/seromi/belt.dmi') + drop_sound = 'sound/items/drop/leather.ogg' var/show_above_suit = 0 @@ -17,7 +18,7 @@ set category = "Object" if(show_above_suit == -1) - usr << "\The [src] cannot be worn above your suit!" + to_chat(usr, "\The [src] cannot be worn above your suit!") return show_above_suit = !show_above_suit update_icon() @@ -162,6 +163,7 @@ /obj/item/weapon/melee/baton, /obj/item/weapon/gun/energy/taser, /obj/item/weapon/gun/energy/stunrevolver, + /obj/item/weapon/gun/magnetic/railgun/heater/pistol, /obj/item/weapon/gun/energy/gun, /obj/item/weapon/flame/lighter, /obj/item/device/flashlight, diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm index 783d8cd74a..0d97b4f878 100644 --- a/code/game/objects/items/weapons/storage/bible.dm +++ b/code/game/objects/items/weapons/storage/bible.dm @@ -7,6 +7,8 @@ w_class = ITEMSIZE_NORMAL var/mob/affecting = null var/deity_name = "Christ" + use_sound = 'sound/bureaucracy/bookopen.ogg' + drop_sound = 'sound/bureaucracy/bookclose.ogg' /obj/item/weapon/storage/bible/booze name = "bible" @@ -26,7 +28,7 @@ if(!proximity) return if(user.mind && (user.mind.assigned_role == "Chaplain")) if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder - user << "You bless [A]." + to_chat(user, "You bless [A].") var/water2holy = A.reagents.get_reagent_amount("water") A.reagents.del_reagent("water") A.reagents.add_reagent("holywater",water2holy) diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index 44b378113b..926e6bc5c5 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -24,9 +24,12 @@ desc = "It's just an ordinary box." icon_state = "box" item_state = "syringe_kit" + center_of_mass = list("x" = 13,"y" = 10) var/foldable = /obj/item/stack/material/cardboard // BubbleWrap - if set, can be folded (when empty) into a sheet of cardboard max_w_class = ITEMSIZE_SMALL max_storage_space = INVENTORY_BOX_SPACE + use_sound = 'sound/items/storage/box.ogg' + drop_sound = 'sound/items/drop/box.ogg' // BubbleWrap - A box can be folded up to make card /obj/item/weapon/storage/box/attack_self(mob/user as mob) @@ -48,7 +51,8 @@ if ( !found ) // User is too far away return // Now make the cardboard - user << "You fold [src] flat." + to_chat(user, "You fold [src] flat.") + playsound(src.loc, 'sound/items/storage/boxfold.ogg', 30, 1) new foldable(get_turf(src)) qdel(src) @@ -404,12 +408,13 @@ can_hold = list(/obj/item/weapon/flame/match) starts_with = list(/obj/item/weapon/flame/match = 10) -/obj/item/weapon/storage/box/matches/attackby(obj/item/weapon/flame/match/W as obj, mob/user as mob) +/obj/item/weapon/storage/box/matches/attackby(var/obj/item/weapon/flame/match/W, var/mob/user) if(istype(W) && !W.lit && !W.burnt) - W.lit = 1 - W.damtype = "burn" - W.icon_state = "match_lit" - START_PROCESSING(SSobj, W) + if(prob(25)) + W.light(user) + user.visible_message("[user] manages to light the match on the matchbox.") + else + playsound(src, 'sound/items/cigs_lighters/matchstick_hit.ogg', 25, 0, -1) W.update_icon() return diff --git a/code/game/objects/items/weapons/storage/briefcase.dm b/code/game/objects/items/weapons/storage/briefcase.dm index 4c8a631fa3..3d24c6dd0a 100644 --- a/code/game/objects/items/weapons/storage/briefcase.dm +++ b/code/game/objects/items/weapons/storage/briefcase.dm @@ -8,6 +8,8 @@ w_class = ITEMSIZE_LARGE max_w_class = ITEMSIZE_NORMAL max_storage_space = ITEMSIZE_COST_NORMAL * 4 + use_sound = 'sound/items/storage/briefcase.ogg' + drop_sound = 'sound/items/drop/backpack.ogg' /obj/item/weapon/storage/briefcase/clutch name = "clutch purse" diff --git a/code/game/objects/items/weapons/storage/fancy.dm b/code/game/objects/items/weapons/storage/fancy.dm index e9a809e55c..830f5a2631 100644 --- a/code/game/objects/items/weapons/storage/fancy.dm +++ b/code/game/objects/items/weapons/storage/fancy.dm @@ -30,11 +30,11 @@ return if(contents.len <= 0) - user << "There are no [icon_type]s left in the box." + to_chat(user, "There are no [icon_type]s left in the box.") else if(contents.len == 1) - user << "There is one [icon_type] left in the box." + to_chat(user, "There is one [icon_type] left in the box.") else - user << "There are [contents.len] [icon_type]s in the box." + to_chat(user, "There are [contents.len] [icon_type]s in the box.") return @@ -47,6 +47,7 @@ icon_state = "eggbox" icon_type = "egg" name = "egg box" + center_of_mass = list("x" = 16,"y" = 7) storage_slots = 12 can_hold = list( /obj/item/weapon/reagent_containers/food/snacks/egg, @@ -245,7 +246,7 @@ var/obj/item/clothing/mask/smokable/cigarette/cig = locate() in src if(cig == null) - user << "Looks like the packet is out of cigarettes." + to_chat(user, "Looks like the packet is out of cigarettes.") return // Instead of running equip_to_slot_if_possible() we check here first, @@ -259,7 +260,7 @@ user.equip_to_slot(cig, slot_wear_mask) reagents.maximum_volume = 15 * contents.len - user << "You take a cigarette out of the pack." + to_chat(user, "You take a cigarette out of the pack.") update_icon() else ..() diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 2fd85ec7c2..c47e564e01 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -16,6 +16,7 @@ throw_range = 8 max_storage_space = ITEMSIZE_COST_SMALL * 7 // 14 // var/list/icon_variety // VOREStation edit + drop_sound = 'sound/items/drop/box.ogg' /obj/item/weapon/storage/firstaid/Initialize() . = ..() @@ -173,13 +174,14 @@ desc = "It's an airtight container for storing medication." icon_state = "pill_canister" icon = 'icons/obj/chemical.dmi' + drop_sound = 'sound/items/drop/pillbottle.ogg' item_state_slots = list(slot_r_hand_str = "contsolid", slot_l_hand_str = "contsolid") w_class = ITEMSIZE_SMALL can_hold = list(/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/dice,/obj/item/weapon/paper) allow_quick_gather = 1 allow_quick_empty = 1 use_to_pickup = 1 - use_sound = null + use_sound = 'sound/items/storage/pillbottle.ogg' max_storage_space = ITEMSIZE_COST_TINY * 14 max_w_class = ITEMSIZE_TINY var/wrapper_color diff --git a/code/game/objects/items/weapons/storage/firstaid_vr.dm b/code/game/objects/items/weapons/storage/firstaid_vr.dm index db74be0b93..e73cbf9f85 100644 --- a/code/game/objects/items/weapons/storage/firstaid_vr.dm +++ b/code/game/objects/items/weapons/storage/firstaid_vr.dm @@ -114,4 +114,10 @@ /obj/item/weapon/storage/mrebag/pill/sleevingcure name = "vacuum-sealed pill (Vey-Med Resleeving Booster)" desc = "A small vacuum-sealed package containing a singular pill. For emergencies only." - starts_with = list(/obj/item/weapon/reagent_containers/pill/sleevingcure) \ No newline at end of file + starts_with = list(/obj/item/weapon/reagent_containers/pill/sleevingcure) + +/obj/item/weapon/storage/pill_bottle/paracetamol + name = "pill bottle (Paracetamol)" + desc = "Contains over the counter medicine to treat pain." + starts_with = list(/obj/item/weapon/reagent_containers/pill/paracetamol = 7) + wrapper_color = COLOR_GRAY diff --git a/code/game/objects/items/weapons/storage/laundry_basket.dm b/code/game/objects/items/weapons/storage/laundry_basket.dm index 722d68e634..e8e2cc59e4 100644 --- a/code/game/objects/items/weapons/storage/laundry_basket.dm +++ b/code/game/objects/items/weapons/storage/laundry_basket.dm @@ -28,17 +28,17 @@ if (user.hand) temp = H.get_organ("l_hand") if(!temp) - user << "You need two hands to pick this up!" + to_chat(user, "You need two hands to pick this up!") return if(user.get_inactive_hand()) - user << "You need your other hand to be empty" + to_chat(user, "You need your other hand to be empty") return return ..() /obj/item/weapon/storage/laundry_basket/attack_self(mob/user as mob) var/turf/T = get_turf(user) - user << "You dump the [src]'s contents onto \the [T]." + to_chat(user, "You dump the [src]'s contents onto \the [T].") return ..() /obj/item/weapon/storage/laundry_basket/pickup(mob/user) diff --git a/code/game/objects/items/weapons/storage/lockbox.dm b/code/game/objects/items/weapons/storage/lockbox.dm index e9527b2313..2fa8e54e87 100644 --- a/code/game/objects/items/weapons/storage/lockbox.dm +++ b/code/game/objects/items/weapons/storage/lockbox.dm @@ -20,21 +20,21 @@ attackby(obj/item/weapon/W as obj, mob/user as mob) if (istype(W, /obj/item/weapon/card/id)) if(src.broken) - user << "It appears to be broken." + to_chat(user, "It appears to be broken.") return if(src.allowed(user)) src.locked = !( src.locked ) if(src.locked) src.icon_state = src.icon_locked - user << "You lock \the [src]!" + to_chat(user, "You lock \the [src]!") close_all() return else src.icon_state = src.icon_closed - user << "You unlock \the [src]!" + to_chat(user, "You unlock \the [src]!") return else - user << "Access Denied" + to_chat(user, "Access Denied") else if(istype(W, /obj/item/weapon/melee/energy/blade)) if(emag_act(INFINITY, user, W, "The locker has been sliced open by [user] with an energy blade!", "You hear metal being sliced and sparks flying.")) var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() @@ -45,13 +45,13 @@ if(!locked) ..() else - user << "It's locked!" + to_chat(user, "It's locked!") return show_to(mob/user as mob) if(locked) - user << "It's locked!" + to_chat(user, "It's locked!") else ..() return diff --git a/code/game/objects/items/weapons/storage/misc.dm b/code/game/objects/items/weapons/storage/misc.dm index 3d3c3aad83..9f6d4929b8 100644 --- a/code/game/objects/items/weapons/storage/misc.dm +++ b/code/game/objects/items/weapons/storage/misc.dm @@ -7,6 +7,7 @@ icon_state = "donutbox" name = "donut box" desc = "A box that holds tasty donuts, if you're lucky." + center_of_mass = list("x" = 16,"y" = 9) max_storage_space = ITEMSIZE_COST_SMALL * 6 can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/donut) foldable = /obj/item/stack/material/cardboard diff --git a/code/game/objects/items/weapons/storage/quickdraw.dm b/code/game/objects/items/weapons/storage/quickdraw.dm index 5e6f58fd53..98cbe58d99 100644 --- a/code/game/objects/items/weapons/storage/quickdraw.dm +++ b/code/game/objects/items/weapons/storage/quickdraw.dm @@ -78,4 +78,22 @@ /obj/item/weapon/reagent_containers/syringe, /obj/item/weapon/reagent_containers/syringe, /obj/item/weapon/reagent_containers/syringe - ) \ No newline at end of file + ) + +/obj/item/weapon/storage/quickdraw/syringe_case/clotting + desc = "A small case for safely carrying sharps around. This one is deluxe!" + max_w_class = ITEMSIZE_SMALL + starts_with = list( + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting + ) + +/obj/item/weapon/storage/quickdraw/syringe_case/bonemed + desc = "A small case for safely carrying sharps around. This one is deluxe!" + max_w_class = ITEMSIZE_SMALL + starts_with = list( + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed, + /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed + ) diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index 7859ee97d5..df775b3cc0 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -26,10 +26,11 @@ w_class = ITEMSIZE_NORMAL max_w_class = ITEMSIZE_SMALL max_storage_space = ITEMSIZE_SMALL * 7 + use_sound = 'sound/items/storage/briefcase.ogg' examine(mob/user) if(..(user, 1)) - user << text("The service panel is [src.open ? "open" : "closed"].") + to_chat(user, "The service panel is [src.open ? "open" : "closed"].") attackby(obj/item/weapon/W as obj, mob/user as mob) if(locked) diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index f400f87bb3..1b25d5e19a 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -141,8 +141,8 @@ is_seeing -= user /obj/item/weapon/storage/proc/open(mob/user as mob) - if (src.use_sound && !isobserver(user)) - playsound(src.loc, src.use_sound, 50, 1, -5) + if (use_sound) + playsound(src.loc, src.use_sound, 50, 0, -5) orient2hud(user) if (user.s_active) diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm index 8edc046972..d23a5e2d69 100644 --- a/code/game/objects/items/weapons/storage/toolbox.dm +++ b/code/game/objects/items/weapons/storage/toolbox.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/storage.dmi' icon_state = "red" item_state_slots = list(slot_r_hand_str = "toolbox_red", slot_l_hand_str = "toolbox_red") + center_of_mass = list("x" = 16,"y" = 11) force = 10 throwforce = 10 throw_speed = 1 @@ -13,6 +14,8 @@ max_storage_space = ITEMSIZE_COST_SMALL * 7 //enough to hold all starting contents origin_tech = list(TECH_COMBAT = 1) attack_verb = list("robusted") + use_sound = 'sound/items/storage/toolbox.ogg' + drop_sound = 'sound/items/drop/metalboots.ogg' /obj/item/weapon/storage/toolbox/emergency name = "emergency toolbox" diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index 9624a85ca2..abae12ed8a 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -142,15 +142,7 @@ name = "chameleon kit" desc = "Comes with all the clothes you need to impersonate most people. Acting lessons sold seperately." starts_with = list( - /obj/item/clothing/under/chameleon, - /obj/item/clothing/head/chameleon, - /obj/item/clothing/suit/chameleon, - /obj/item/clothing/shoes/chameleon, - /obj/item/weapon/storage/backpack/chameleon, - /obj/item/clothing/gloves/chameleon, - /obj/item/clothing/mask/chameleon, - /obj/item/clothing/glasses/chameleon, - /obj/item/clothing/accessory/chameleon, + /obj/item/weapon/storage/backpack/chameleon/full, /obj/item/weapon/gun/energy/chameleon ) @@ -274,6 +266,37 @@ /obj/item/weapon/tool/screwdriver ) +/obj/item/weapon/storage/box/syndie_kit/voidsuit + starts_with = list( + /obj/item/clothing/suit/space/void/merc, + /obj/item/clothing/head/helmet/space/void/merc, + /obj/item/clothing/shoes/magboots, + /obj/item/weapon/tank/jetpack/oxygen + ) + +/obj/item/weapon/storage/box/syndie_kit/voidsuit/fire + starts_with = list( + /obj/item/clothing/suit/space/void/merc/fire, + /obj/item/clothing/head/helmet/space/void/merc/fire, + /obj/item/clothing/shoes/magboots, + /obj/item/weapon/tank/jetpack/oxygen + ) + +/obj/item/weapon/storage/box/syndie_kit/concussion_grenade + starts_with = list( + /obj/item/weapon/grenade/concussion = 8 + ) + +/obj/item/weapon/storage/box/syndie_kit/deadliest_game + starts_with = list( + /obj/item/weapon/beartrap/hunting = 4 + ) + +/obj/item/weapon/storage/box/syndie_kit/viral + starts_with = list( + /obj/item/weapon/virusdish/random = 3 + ) + /obj/item/weapon/storage/secure/briefcase/rifle name = "secure briefcase" starts_with = list( @@ -283,6 +306,15 @@ /obj/item/ammo_casing/a145 = 4 ) +/obj/item/weapon/storage/secure/briefcase/flamer + name = "secure briefcase" + starts_with = list( + /obj/item/weapon/gun/magnetic/gasthrower, + /obj/item/weapon/cell/super, + /obj/item/weapon/stock_parts/capacitor/adv, + /obj/item/weapon/tank/phoron/pressurized = 2 + ) + /obj/item/weapon/storage/secure/briefcase/fuelrod name = "heavy briefcase" desc = "A heavy, locked briefcase." diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index c30166afee..3bf10a4b78 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -105,9 +105,9 @@ return if(bcell) - user <<"The baton is [round(bcell.percent())]% charged." + to_chat(user, "The baton is [round(bcell.percent())]% charged.") if(!bcell) - user <<"The baton does not have a power source installed." + to_chat(user, "The baton does not have a power source installed.") /obj/item/weapon/melee/baton/attackby(obj/item/weapon/W, mob/user) if(use_external_power) @@ -118,12 +118,12 @@ user.drop_item() W.loc = src bcell = W - user << "You install a cell in [src]." + to_chat(user, "You install a cell in [src].") update_icon() else - user << "[src] already has a cell." + to_chat(user, "[src] already has a cell.") else - user << "This cell is not fitted for [src]." + to_chat(user, "This cell is not fitted for [src].") /obj/item/weapon/melee/baton/attack_hand(mob/user as mob) if(user.get_inactive_hand() == src) @@ -131,7 +131,7 @@ bcell.update_icon() user.put_in_hands(bcell) bcell = null - user << "You remove the cell from the [src]." + to_chat(user, "You remove the cell from the [src].") status = 0 update_icon() return @@ -147,20 +147,20 @@ bcell = R.cell if(bcell && bcell.charge > hitcost) status = !status - user << "[src] is now [status ? "on" : "off"]." + to_chat(user, "[src] is now [status ? "on" : "off"].") playsound(loc, "sparks", 75, 1, -1) update_icon() else status = 0 if(!bcell) - user << "[src] does not have a power source!" + to_chat(user, "[src] does not have a power source!") else - user << "[src] is out of charge." + to_chat(user, "[src] is out of charge.") add_fingerprint(user) /obj/item/weapon/melee/baton/attack(mob/M, mob/user) if(status && (CLUMSY in user.mutations) && prob(50)) - user << "You accidentally hit yourself with the [src]!" + to_chat(user, "You accidentally hit yourself with the [src]!") user.Weaken(30) deductcharge(hitcost) return @@ -236,12 +236,12 @@ user.drop_item() W.loc = src bcell = W - user << "You install a cell in [src]." + to_chat(user, "You install a cell in [src].") update_icon() else - user << "[src] already has a cell." + to_chat(user, "[src] already has a cell.") else - user << "This cell is not fitted for [src]." + to_chat(user, "This cell is not fitted for [src].") /obj/item/weapon/melee/baton/get_description_interaction() var/list/results = list() diff --git a/code/game/objects/items/weapons/surgery_tools.dm b/code/game/objects/items/weapons/surgery_tools.dm index 292f047f0f..e5580082a1 100644 --- a/code/game/objects/items/weapons/surgery_tools.dm +++ b/code/game/objects/items/weapons/surgery_tools.dm @@ -30,6 +30,7 @@ icon_state = "retractor" matter = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 5000) origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 1) + drop_sound = 'sound/items/drop/scrap.ogg' /* * Hemostat @@ -41,6 +42,7 @@ matter = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 2500) origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 1) attack_verb = list("attacked", "pinched") + drop_sound = 'sound/items/drop/scrap.ogg' /* * Cautery @@ -52,6 +54,7 @@ matter = list(DEFAULT_WALL_MATERIAL = 5000, "glass" = 2500) origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 1) attack_verb = list("burnt") + drop_sound = 'sound/items/drop/scrap.ogg' /* * Surgical Drill @@ -66,11 +69,12 @@ w_class = ITEMSIZE_NORMAL origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 1) attack_verb = list("drilled") + drop_sound = 'sound/items/drop/accessory.ogg' suicide_act(mob/user) var/datum/gender/TU = gender_datums[user.get_visible_gender()] - viewers(user) << pick("\The [user] is pressing \the [src] to [TU.his] temple and activating it! It looks like [TU.hes] trying to commit suicide.", - "\The [user] is pressing \the [src] to [TU.his] chest and activating it! It looks like [TU.hes] trying to commit suicide.") + to_chat(viewers(user),pick("\The [user] is pressing \the [src] to [TU.his] temple and activating it! It looks like [TU.hes] trying to commit suicide.", + "\The [user] is pressing \the [src] to [TU.his] chest and activating it! It looks like [TU.hes] trying to commit suicide.")) return (BRUTELOSS) /* @@ -91,12 +95,13 @@ origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 1) matter = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 5000) attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + drop_sound = 'sound/items/drop/knife.ogg' suicide_act(mob/user) var/datum/gender/TU = gender_datums[user.get_visible_gender()] - viewers(user) << pick("\The [user] is slitting [TU.his] wrists with the [src.name]! It looks like [TU.hes] trying to commit suicide.", \ + to_chat(viewers(user),pick("\The [user] is slitting [TU.his] wrists with the [src.name]! It looks like [TU.hes] trying to commit suicide.", \ "\The [user] is slitting [TU.his] throat with the [src.name]! It looks like [TU.hes] trying to commit suicide.", \ - "\The [user] is slitting [TU.his] stomach open with the [src.name]! It looks like [TU.hes] trying to commit seppuku.") + "\The [user] is slitting [TU.his] stomach open with the [src.name]! It looks like [TU.hes] trying to commit seppuku.")) return (BRUTELOSS) /* @@ -145,6 +150,7 @@ desc = "For heavy duty cutting." icon_state = "saw3" hitsound = 'sound/weapons/circsawhit.ogg' + drop_sound = 'sound/items/drop/accessory.ogg' force = 15.0 w_class = ITEMSIZE_NORMAL throwforce = 9.0 @@ -176,6 +182,7 @@ icon_state = "bone-gel" force = 0 throwforce = 1.0 + drop_sound = 'sound/items/drop/bottle.ogg' /obj/item/weapon/surgical/FixOVein name = "FixOVein" @@ -185,6 +192,7 @@ throwforce = 1.0 origin_tech = list(TECH_MATERIAL = 1, TECH_BIO = 3) var/usage_amount = 10 + drop_sound = 'sound/items/drop/accessory.ogg' /obj/item/weapon/surgical/bonesetter name = "bone setter" @@ -195,6 +203,7 @@ throw_speed = 3 throw_range = 5 attack_verb = list("attacked", "hit", "bludgeoned") + drop_sound = 'sound/items/drop/scrap.ogg' /obj/item/weapon/surgical/bone_clamp name = "bone clamp" diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index e1deceadde..ec9e62c86b 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -8,6 +8,17 @@ /* * Classic Baton */ + +/obj/item/weapon/melee + name = "weapon" + desc = "Murder device." + icon = 'icons/obj/weapons.dmi' + icon_state = "baton" + item_state = "classic_baton" + slot_flags = SLOT_BELT + force = 10 + drop_sound = 'sound/items/drop/metalweapon.ogg' + /obj/item/weapon/melee/classic_baton name = "police baton" desc = "A wooden truncheon for beating criminal scum." @@ -19,7 +30,7 @@ /obj/item/weapon/melee/classic_baton/attack(mob/M as mob, mob/living/user as mob) if ((CLUMSY in user.mutations) && prob(50)) - user << "You club yourself over the head." + to_chat(user, "You club yourself over the head.") user.Weaken(3 * force) if(ishuman(user)) var/mob/living/carbon/human/H = user @@ -35,12 +46,12 @@ desc = "A compact yet rebalanced personal defense weapon. Can be concealed when folded." icon = 'icons/obj/weapons.dmi' icon_state = "telebaton0" + item_state = "telebaton0" slot_flags = SLOT_BELT w_class = ITEMSIZE_SMALL force = 3 var/on = 0 - /obj/item/weapon/melee/telebaton/attack_self(mob/user as mob) on = !on if(on) @@ -48,6 +59,7 @@ "You extend the baton.",\ "You hear an ominous click.") icon_state = "telebaton1" + item_state = icon_state w_class = ITEMSIZE_NORMAL force = 15//quite robust attack_verb = list("smacked", "struck", "slapped") @@ -56,6 +68,7 @@ "You collapse the baton.",\ "You hear a click.") icon_state = "telebaton0" + item_state = icon_state w_class = ITEMSIZE_SMALL force = 3//not so robust now attack_verb = list("hit", "punched") @@ -83,7 +96,7 @@ /obj/item/weapon/melee/telebaton/attack(mob/target as mob, mob/living/user as mob) if(on) if ((CLUMSY in user.mutations) && prob(50)) - user << "You club yourself over the head." + to_chat(user, "You club yourself over the head.") user.Weaken(3 * force) if(ishuman(user)) var/mob/living/carbon/human/H = user diff --git a/code/game/objects/items/weapons/syndie.dm b/code/game/objects/items/weapons/syndie.dm index ee528cb6f8..c4e273deea 100644 --- a/code/game/objects/items/weapons/syndie.dm +++ b/code/game/objects/items/weapons/syndie.dm @@ -50,7 +50,7 @@ icon_state = "c-4[size]_1" playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1) for(var/mob/O in hearers(src, null)) - O.show_message("\icon[src] The [src.name] beeps! ") + O.show_message("[bicon(src)] The [src.name] beeps! ") sleep(50) explosion(get_turf(src), devastate, heavy_impact, light_impact, flash_range) for(var/dirn in cardinal) //This is to guarantee that C4 at least breaks down all immediately adjacent walls and doors. diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index 9200d58820..528b8eb447 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -30,14 +30,14 @@ /obj/item/weapon/tank/jetpack/examine(mob/user) . = ..() if(air_contents.total_moles < 5) - user << "The meter on \the [src] indicates you are almost out of gas!" + to_chat(user, "The meter on \the [src] indicates you are almost out of gas!") playsound(user, 'sound/effects/alert.ogg', 50, 1) /obj/item/weapon/tank/jetpack/verb/toggle_rockets() set name = "Toggle Jetpack Stabilization" set category = "Object" stabilization_on = !( stabilization_on ) - usr << "You toggle the stabilization [stabilization_on? "on":"off"]." + to_chat(usr, "You toggle the stabilization [stabilization_on? "on":"off"].") /obj/item/weapon/tank/jetpack/verb/toggle() set name = "Toggle Jetpack" @@ -56,7 +56,7 @@ M.update_inv_back() M.update_action_buttons() - usr << "You toggle the thrusters [on? "on":"off"]." + to_chat(usr, "You toggle the thrusters [on? "on":"off"].") /obj/item/weapon/tank/jetpack/proc/allow_thrust(num, mob/living/user as mob) if(!on) @@ -116,7 +116,7 @@ var/obj/item/weapon/rig/holder /obj/item/weapon/tank/jetpack/rig/examine() - usr << "It's a jetpack. If you can see this, report it on the bug tracker." + to_chat(usr, "It's a jetpack. If you can see this, report it on the bug tracker.") return 0 /obj/item/weapon/tank/jetpack/rig/allow_thrust(num, mob/living/user as mob) diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 9e984ba05f..6d6839a4a5 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -113,6 +113,19 @@ air_contents.adjust_gas("phoron", (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) return +/obj/item/weapon/tank/phoron/pressurized + name = "fuel can" + icon_state = "phoron_vox" + w_class = ITEMSIZE_NORMAL + +/obj/item/weapon/tank/phoron/pressurized/Initialize() + ..() + + adjust_scale(0.8) + + air_contents.adjust_gas("phoron", (7*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)) + return + /* * Emergency Oxygen */ diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index a2113aab63..bfb484b307 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -10,6 +10,7 @@ var/list/global/tank_gauge_cache = list() sprite_sheets = list( SPECIES_TESHARI = 'icons/mob/species/seromi/back.dmi' ) + drop_sound = 'sound/items/drop/gascan.ogg' var/gauge_icon = "indicator_tank" var/last_gauge_pressure @@ -77,7 +78,6 @@ var/list/global/tank_gauge_cache = list() if(istype(loc, /obj/item/device/transfer_valve)) var/obj/item/device/transfer_valve/TTV = loc TTV.remove_tank(src) - qdel(TTV) . = ..() @@ -328,6 +328,9 @@ var/list/global/tank_gauge_cache = list() /obj/item/weapon/tank/remove_air(amount) return air_contents.remove(amount) +/obj/item/weapon/tank/proc/remove_air_by_flag(flag, amount) + return air_contents.remove_by_flag(flag, amount) + /obj/item/weapon/tank/return_air() return air_contents @@ -464,7 +467,7 @@ var/list/global/tank_gauge_cache = list() return T.assume_air(air_contents) playsound(get_turf(src), 'sound/weapons/Gunshot_shotgun.ogg', 20, 1) - visible_message("\icon[src] \The [src] flies apart!", "You hear a bang!") + visible_message("[bicon(src)] \The [src] flies apart!", "You hear a bang!") T.hotspot_expose(air_contents.temperature, 70, 1) @@ -509,7 +512,7 @@ var/list/global/tank_gauge_cache = list() T.assume_air(leaked_gas) if(!leaking) - visible_message("\icon[src] \The [src] relief valve flips open with a hiss!", "You hear hissing.") + visible_message("[bicon(src)] \The [src] relief valve flips open with a hiss!", "You hear hissing.") playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3) leaking = 1 #ifdef FIREDBG diff --git a/code/game/objects/items/weapons/tape.dm b/code/game/objects/items/weapons/tape.dm index ce2f0dabfc..62153d440b 100644 --- a/code/game/objects/items/weapons/tape.dm +++ b/code/game/objects/items/weapons/tape.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/bureaucracy.dmi' icon_state = "taperoll" w_class = ITEMSIZE_TINY + drop_sound = 'sound/items/drop/box.ogg' toolspeed = 2 //It is now used in surgery as a not awful, but probably dangerous option, due to speed. @@ -20,22 +21,22 @@ can_place = 1 break if(!can_place) - user << "You need to have a firm grip on [H] before you can use \the [src]!" + to_chat(user, "You need to have a firm grip on [H] before you can use \the [src]!") return else if(user.zone_sel.selecting == O_EYES) if(!H.organs_by_name[BP_HEAD]) - user << "\The [H] doesn't have a head." + to_chat(user, "\The [H] doesn't have a head.") return if(!H.has_eyes()) - user << "\The [H] doesn't have any eyes." + to_chat(user, "\The [H] doesn't have any eyes.") return if(H.glasses) - user << "\The [H] is already wearing somethign on their eyes." + to_chat(user, "\The [H] is already wearing somethign on their eyes.") return if(H.head && (H.head.body_parts_covered & FACE)) - user << "Remove their [H.head] first." + to_chat(user, "Remove their [H.head] first.") return user.visible_message("\The [user] begins taping over \the [H]'s eyes!") @@ -64,16 +65,16 @@ else if(user.zone_sel.selecting == O_MOUTH || user.zone_sel.selecting == BP_HEAD) if(!H.organs_by_name[BP_HEAD]) - user << "\The [H] doesn't have a head." + to_chat(user, "\The [H] doesn't have a head.") return if(!H.check_has_mouth()) - user << "\The [H] doesn't have a mouth." + to_chat(user, "\The [H] doesn't have a mouth.") return if(H.wear_mask) - user << "\The [H] is already wearing a mask." + to_chat(user, "\The [H] is already wearing a mask.") return if(H.head && (H.head.body_parts_covered & FACE)) - user << "Remove their [H.head] first." + to_chat(user, "Remove their [H.head] first.") return user.visible_message("\The [user] begins taping up \the [H]'s mouth!") @@ -141,6 +142,7 @@ w_class = ITEMSIZE_TINY plane = MOB_PLANE anchored = FALSE + drop_sound = null var/obj/item/weapon/stuck = null @@ -162,7 +164,7 @@ if(!stuck) return - user << "You remove \the [initial(name)] from [stuck]." + to_chat(user, "You remove \the [initial(name)] from [stuck].") user.drop_from_inventory(src) stuck.forceMove(get_turf(src)) @@ -196,7 +198,7 @@ if(target_turf != source_turf) dir_offset = get_dir(source_turf, target_turf) if(!(dir_offset in cardinal)) - user << "You cannot reach that from here." // can only place stuck papers in cardinal directions, to + to_chat(user, "You cannot reach that from here.") // can only place stuck papers in cardinal directions, to return // reduce papers around corners issue. user.drop_from_inventory(src) diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm index f4d21e7b65..fb33d8f30d 100644 --- a/code/game/objects/items/weapons/teleportation.dm +++ b/code/game/objects/items/weapons/teleportation.dm @@ -48,7 +48,7 @@ Frequency: return var/turf/current_location = get_turf(usr)//What turf is the user on? if(!current_location||current_location.z==3)//If turf was not found or they're on z level 2. - usr << "The [src] is malfunctioning." + to_chat(usr, "The [src] is malfunctioning.") return if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf)))) usr.set_machine(src) @@ -133,7 +133,7 @@ Frequency: /obj/item/weapon/hand_tele/attack_self(mob/user as mob) var/turf/current_location = get_turf(user)//What turf is the user on? if(!current_location || current_location.z in using_map.admin_levels || current_location.block_tele)//If turf was not found or they're on z level 2 or >7 which does not currently exist. - user << "\The [src] is malfunctioning." + to_chat(user, "\The [src] is malfunctioning.") return var/list/L = list( ) for(var/obj/machinery/teleport/hub/R in machines) diff --git a/code/game/objects/items/weapons/tools/combitool.dm b/code/game/objects/items/weapons/tools/combitool.dm index 96b64d18f7..b154f059af 100644 --- a/code/game/objects/items/weapons/tools/combitool.dm +++ b/code/game/objects/items/weapons/tools/combitool.dm @@ -27,7 +27,7 @@ if(loc == usr && tools.len) to_chat(usr, "It has the following fittings:") for(var/obj/item/tool in tools) - to_chat(usr, "\icon[tool] - [tool.name][tools[current_tool]==tool?" (selected)":""]") + to_chat(usr, "[bicon(tool)] - [tool.name][tools[current_tool]==tool?" (selected)":""]") /obj/item/weapon/combitool/New() ..() diff --git a/code/game/objects/items/weapons/tools/crowbar.dm b/code/game/objects/items/weapons/tools/crowbar.dm index fab0a6fb98..56afaf8219 100644 --- a/code/game/objects/items/weapons/tools/crowbar.dm +++ b/code/game/objects/items/weapons/tools/crowbar.dm @@ -17,6 +17,7 @@ matter = list(DEFAULT_WALL_MATERIAL = 50) attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked") usesound = 'sound/items/crowbar.ogg' + drop_sound = 'sound/items/drop/sword.ogg' toolspeed = 1 /obj/item/weapon/tool/crowbar/is_crowbar() diff --git a/code/game/objects/items/weapons/tools/screwdriver.dm b/code/game/objects/items/weapons/tools/screwdriver.dm index 1969987ff9..8045445ca3 100644 --- a/code/game/objects/items/weapons/tools/screwdriver.dm +++ b/code/game/objects/items/weapons/tools/screwdriver.dm @@ -6,6 +6,7 @@ desc = "You can be totally screwwy with this." icon = 'icons/obj/tools.dmi' icon_state = "screwdriver" + center_of_mass = list("x" = 13,"y" = 7) slot_flags = SLOT_BELT | SLOT_EARS force = 6 w_class = ITEMSIZE_TINY @@ -14,6 +15,7 @@ throw_range = 5 hitsound = 'sound/weapons/bladeslice.ogg' usesound = 'sound/items/screwdriver.ogg' + drop_sound = 'sound/items/drop/scrap.ogg' matter = list(DEFAULT_WALL_MATERIAL = 75) attack_verb = list("stabbed") sharp = 1 @@ -22,8 +24,8 @@ /obj/item/weapon/tool/screwdriver/suicide_act(mob/user) var/datum/gender/TU = gender_datums[user.get_visible_gender()] - viewers(user) << pick("\The [user] is stabbing the [src.name] into [TU.his] temple! It looks like [TU.hes] trying to commit suicide.", \ - "\The [user] is stabbing the [src.name] into [TU.his] heart! It looks like [TU.hes] trying to commit suicide.") + to_chat(viewers(user), pick("\The [user] is stabbing the [src.name] into [TU.his] temple! It looks like [TU.hes] trying to commit suicide.", \ + "\The [user] is stabbing the [src.name] into [TU.his] heart! It looks like [TU.hes] trying to commit suicide.")) return(BRUTELOSS) /obj/item/weapon/tool/screwdriver/New() diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm index 491d815776..ece27fb2c9 100644 --- a/code/game/objects/items/weapons/tools/weldingtool.dm +++ b/code/game/objects/items/weapons/tools/weldingtool.dm @@ -37,6 +37,7 @@ var/burned_fuel_for = 0 // Keeps track of how long the welder's been on, used to gradually empty the welder if left one, without RNG. var/always_process = FALSE // If true, keeps the welder on the process list even if it's off. Used for when it needs to regenerate fuel. toolspeed = 1 + drop_sound = 'sound/items/drop/scrap.ogg' /obj/item/weapon/weldingtool/Initialize() . = ..() @@ -57,7 +58,7 @@ /obj/item/weapon/weldingtool/examine(mob/user) if(..(user, 0)) if(max_fuel) - to_chat(user, text("\icon[] The [] contains []/[] units of fuel!", src, src.name, get_fuel(),src.max_fuel )) + to_chat(user, "[bicon(src)] The [src.name] contains [get_fuel()]/[src.max_fuel] units of fuel!") /obj/item/weapon/weldingtool/attack(atom/A, mob/living/user, def_zone) if(ishuman(A) && user.a_intent == I_HELP) @@ -67,6 +68,15 @@ if(!S || S.robotic < ORGAN_ROBOT || S.open == 3) return ..() + if(S.organ_tag == BP_HEAD) + if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) + to_chat(user, "You can't apply [src] through [H.head]!") + return 1 + else + if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) + to_chat(user, "You can't apply [src] through [H.wear_suit]!") + return 1 + if(!welding) to_chat(user, "You'll need to turn [src] on to patch the damage on [H]'s [S.name]!") return 1 @@ -549,11 +559,11 @@ /obj/item/weapon/weldingtool/electric/examine(mob/user) if(get_dist(src, user) > 1) to_chat(user, desc) - else // The << need to stay, for some reason + else if(power_supply) - user << text("\icon[] The [] has [] charge left.", src, src.name, get_fuel()) + to_chat(user, "[bicon(src)] The [src.name] has [get_fuel()] charge left.") else - user << text("\icon[] The [] has no power cell!", src, src.name) + to_chat(user, "[bicon(src)] The [src.name] has no power cell!") /obj/item/weapon/weldingtool/electric/get_fuel() if(use_external_power) @@ -635,6 +645,10 @@ var/obj/item/weapon/rig/suit = H.back if(istype(suit)) return suit.cell + if(istype(src.loc, /obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/mounting = src.loc + if(mounting.chassis && mounting.chassis.cell) + return mounting.chassis.cell return null /obj/item/weapon/weldingtool/electric/mounted @@ -643,4 +657,26 @@ /obj/item/weapon/weldingtool/electric/mounted/cyborg toolspeed = 0.5 -#undef WELDER_FUEL_BURN_INTERVAL \ No newline at end of file +/obj/item/weapon/weldingtool/electric/mounted/exosuit + var/obj/item/mecha_parts/mecha_equipment/equip_mount = null + flame_intensity = 1 + eye_safety_modifier = 2 + always_process = TRUE + +/obj/item/weapon/weldingtool/electric/mounted/exosuit/Initialize() + ..() + + if(istype(loc, /obj/item/mecha_parts/mecha_equipment)) + equip_mount = loc + +/obj/item/weapon/weldingtool/electric/mounted/exosuit/process() + ..() + + if(equip_mount && equip_mount.chassis) + var/obj/mecha/M = equip_mount.chassis + if(M.selected == equip_mount && get_fuel()) + setWelding(TRUE, M.occupant) + else + setWelding(FALSE, M.occupant) + +#undef WELDER_FUEL_BURN_INTERVAL diff --git a/code/game/objects/items/weapons/tools/wirecutters.dm b/code/game/objects/items/weapons/tools/wirecutters.dm index 4d61609db4..28e9e8f6cf 100644 --- a/code/game/objects/items/weapons/tools/wirecutters.dm +++ b/code/game/objects/items/weapons/tools/wirecutters.dm @@ -6,6 +6,7 @@ desc = "This cuts wires." icon = 'icons/obj/tools.dmi' icon_state = "cutters" + center_of_mass = list("x" = 18,"y" = 10) slot_flags = SLOT_BELT force = 6 throw_speed = 2 @@ -16,6 +17,7 @@ attack_verb = list("pinched", "nipped") hitsound = 'sound/items/wirecutter.ogg' usesound = 'sound/items/wirecutter.ogg' + drop_sound = 'sound/items/drop/knife.ogg' sharp = 1 edge = 1 toolspeed = 1 diff --git a/code/game/objects/items/weapons/tools/wrench.dm b/code/game/objects/items/weapons/tools/wrench.dm index 3f02a2f8b3..89a51cf217 100644 --- a/code/game/objects/items/weapons/tools/wrench.dm +++ b/code/game/objects/items/weapons/tools/wrench.dm @@ -15,6 +15,7 @@ attack_verb = list("bashed", "battered", "bludgeoned", "whacked") usesound = 'sound/items/ratchet.ogg' toolspeed = 1 + drop_sound = 'sound/items/drop/sword.ogg' /obj/item/weapon/tool/wrench/is_wrench() return TRUE diff --git a/code/game/objects/items/weapons/towels.dm b/code/game/objects/items/weapons/towels.dm index 0c433a63a0..ba1b662ebb 100644 --- a/code/game/objects/items/weapons/towels.dm +++ b/code/game/objects/items/weapons/towels.dm @@ -8,6 +8,7 @@ attack_verb = list("whipped") hitsound = 'sound/weapons/towelwhip.ogg' desc = "A soft cotton towel." + drop_sound = 'sound/items/drop/clothing.ogg' /obj/item/weapon/towel/attack_self(mob/living/user as mob) user.visible_message(text("[] uses [] to towel themselves off.", user, src)) diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index 1febf6b1ae..8e6667c759 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -6,6 +6,8 @@ icon = 'icons/obj/items.dmi' icon_state = "beartrap0" desc = "A mechanically activated leg trap. Low-tech, but reliable. Looks like it could really hurt if you set it off." + randpixel = 0 + center_of_mass = null throwforce = 0 w_class = ITEMSIZE_NORMAL origin_tech = list(TECH_MATERIAL = 1) @@ -16,7 +18,7 @@ /obj/item/weapon/beartrap/suicide_act(mob/user) var/datum/gender/T = gender_datums[user.get_visible_gender()] - viewers(user) << "[user] is putting the [src.name] on [T.his] head! It looks like [T.hes] trying to commit suicide." + to_chat(viewers(user),"[user] is putting the [src.name] on [T.his] head! It looks like [T.hes] trying to commit suicide.") return (BRUTELOSS) /obj/item/weapon/beartrap/proc/can_use(mob/user) @@ -101,17 +103,14 @@ can_buckle = 1 buckle_mob(L) L.Stun(stun_length) - L << "The steel jaws of \the [src] bite into you, trapping you in place!" + to_chat(L, "The steel jaws of \the [src] bite into you, trapping you in place!") deployed = 0 + anchored = FALSE can_buckle = initial(can_buckle) -/obj/item/weapon/beartrap/Crossed(AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/item/weapon/beartrap/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return if(deployed && isliving(AM)) var/mob/living/L = AM if(L.m_intent == "run") diff --git a/code/game/objects/items/weapons/trays.dm b/code/game/objects/items/weapons/trays.dm index 21c8b635f3..48d65ffb16 100644 --- a/code/game/objects/items/weapons/trays.dm +++ b/code/game/objects/items/weapons/trays.dm @@ -14,6 +14,7 @@ matter = list(DEFAULT_WALL_MATERIAL = 3000) var/list/carrying = list() // List of things on the tray. - Doohl var/max_carry = 10 + drop_sound = 'sound/items/trayhit1.ogg' /obj/item/weapon/tray/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) user.setClickCooldown(user.get_attack_speed(src)) @@ -31,7 +32,7 @@ if((CLUMSY in user.mutations) && prob(50)) //What if he's a clown? - M << "You accidentally slam yourself with the [src]!" + to_chat(M, "You accidentally slam yourself with the [src]!") M.Weaken(1) user.take_organ_damage(2) if(prob(50)) @@ -78,7 +79,7 @@ break if(protected) - M << "You get slammed in the face with the tray, against your mask!" + to_chat(M, "You get slammed in the face with the tray, against your mask!") if(prob(33)) src.add_blood(H) if (H.wear_mask) @@ -108,7 +109,7 @@ return else //No eye or head protection, tough luck! - M << "You get slammed in the face with the tray!" + to_chat(M, "You get slammed in the face with the tray!") if(prob(33)) src.add_blood(M) var/turf/location = H.loc diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 9875ec035a..ff982da257 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -9,10 +9,11 @@ throw_range = 4 throwforce = 10 w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/sword.ogg' suicide_act(mob/user) var/datum/gender/T = gender_datums[user.get_visible_gender()] - viewers(user) << "[user] is impaling [T.himself] with the [src.name]! It looks like [T.he] [T.is] trying to commit suicide." + to_chat(viewers(user),"[user] is impaling [T.himself] with the [src.name]! It looks like [T.he] [T.is] trying to commit suicide.") return (BRUTELOSS|FIRELOSS) /obj/item/weapon/nullrod/attack(mob/M as mob, mob/living/user as mob) //Paste from old-code to decult with a null rod. @@ -23,26 +24,26 @@ user.do_attack_animation(M) if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") - user << "You don't have the dexterity to do this!" + to_chat(user, "You don't have the dexterity to do this!") return if ((CLUMSY in user.mutations) && prob(50)) - user << "The rod slips out of your hand and hits your head." + to_chat(user, "The rod slips out of your hand and hits your head.") user.take_organ_damage(10) user.Paralyse(20) return if (M.stat !=2) if(cult && (M.mind in cult.current_antagonists) && prob(33)) - M << "The power of [src] clears your mind of the cult's influence!" - user << "You wave [src] over [M]'s head and see their eyes become clear, their mind returning to normal." + to_chat(M, "The power of [src] clears your mind of the cult's influence!") + to_chat(user, "You wave [src] over [M]'s head and see their eyes become clear, their mind returning to normal.") cult.remove_antagonist(M.mind) M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") else if(prob(10)) - user << "The rod slips in your hand." + to_chat(user, "The rod slips in your hand.") ..() else - user << "The rod appears to do nothing." + to_chat(user, "The rod appears to do nothing.") M.visible_message("\The [user] waves \the [src] over \the [M]'s head.") return @@ -50,7 +51,7 @@ if(!proximity) return if (istype(A, /turf/simulated/floor)) - user << "You hit the floor with the [src]." + to_chat(user, "You hit the floor with the [src].") call(/obj/effect/rune/proc/revealrunes)(src) /obj/item/weapon/energy_net @@ -111,7 +112,7 @@ /obj/effect/energy_net/Destroy() if(has_buckled_mobs()) for(var/A in buckled_mobs) - to_chat(A,"You are free of the net!") + to_chat(A, "You are free of the net!") unbuckle_mob(A) STOP_PROCESSING(SSobj, src) diff --git a/code/game/objects/items/weapons/weldbackpack.dm b/code/game/objects/items/weapons/weldbackpack.dm index 4225ed810a..2f946a5942 100644 --- a/code/game/objects/items/weapons/weldbackpack.dm +++ b/code/game/objects/items/weapons/weldbackpack.dm @@ -9,6 +9,7 @@ var/obj/item/weapon/nozzle = null //Attached welder, or other spray device. var/nozzle_type = /obj/item/weapon/weldingtool/tubefed var/nozzle_attached = 0 + drop_sound = 'sound/items/drop/backpack.ogg' /obj/item/weapon/weldpack/Initialize() . = ..() @@ -57,14 +58,14 @@ if(T.welding & prob(50)) message_admins("[key_name_admin(user)] triggered a fueltank explosion.") log_game("[key_name(user)] triggered a fueltank explosion.") - to_chat(user,"That was stupid of you.") + to_chat(user, "That was stupid of you.") explosion(get_turf(src),-1,0,2) if(src) qdel(src) return else if(T.status) if(T.welding) - to_chat(user,"That was close!") + to_chat(user, "That was close!") src.reagents.trans_to_obj(W, T.max_fuel) to_chat(user, "Welder refilled!") playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6) @@ -72,16 +73,16 @@ else if(nozzle) if(nozzle == W) if(!user.unEquip(W)) - to_chat(user,"\The [W] seems to be stuck to your hand.") + to_chat(user, "\The [W] seems to be stuck to your hand.") return if(!nozzle_attached) return_nozzle() - to_chat(user,"You attach \the [W] to the [src].") + to_chat(user, "You attach \the [W] to the [src].") return else - to_chat(user,"The [src] already has a nozzle!") + to_chat(user, "The [src] already has a nozzle!") else - to_chat(user,"The tank scoffs at your insolence. It only provides services to welders.") + to_chat(user, "The tank scoffs at your insolence. It only provides services to welders.") return /obj/item/weapon/weldpack/attack_hand(mob/user as mob) @@ -92,7 +93,7 @@ if(!wearer.incapacitated()) get_nozzle(user) else - to_chat(user,"\The [src] does not have a nozzle attached!") + to_chat(user, "\The [src] does not have a nozzle attached!") else ..() else @@ -103,11 +104,11 @@ return if (istype(O, /obj/structure/reagent_dispensers/fueltank) && src.reagents.total_volume < max_fuel) O.reagents.trans_to_obj(src, max_fuel) - to_chat(user,"You crack the cap off the top of the pack and fill it back up again from the tank.") + to_chat(user, "You crack the cap off the top of the pack and fill it back up again from the tank.") playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6) return else if (istype(O, /obj/structure/reagent_dispensers/fueltank) && src.reagents.total_volume == max_fuel) - to_chat(user,"The pack is already full!") + to_chat(user, "The pack is already full!") return /obj/item/weapon/weldpack/MouseDrop(obj/over_object as obj) //This is terrifying. @@ -144,7 +145,7 @@ /obj/item/weapon/weldpack/examine(mob/user) ..(user) - user << text("\icon[] [] units of fuel left!", src, src.reagents.total_volume) + to_chat(user, "[bicon(src)] [src.reagents.total_volume] units of fuel left!") return /obj/item/weapon/weldpack/survival diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index c7b8c07f8d..faafdb62f3 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -58,7 +58,7 @@ /obj/CanUseTopic(var/mob/user, var/datum/topic_state/state = default_state) if(user.CanUseObjTopic(src)) return ..() - to_chat(user, "\icon[src]Access Denied!") + to_chat(user, "[bicon(src)]Access Denied!") return STATUS_CLOSE /mob/living/silicon/CanUseObjTopic(var/obj/O) diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm index f9180c99f1..4e29ac66ec 100644 --- a/code/game/objects/random/misc.dm +++ b/code/game/objects/random/misc.dm @@ -229,7 +229,7 @@ icon_state = "purplecomb" spawn_nothing_percentage = 50 /obj/random/contraband/item_to_spawn() - return pick(prob(6);/obj/item/weapon/storage/pill_bottle/tramadol, + return pick(prob(6);/obj/item/weapon/storage/pill_bottle/paracetamol, //VOREStation Edit, prob(8);/obj/item/weapon/haircomb, prob(4);/obj/item/weapon/storage/pill_bottle/happy, prob(4);/obj/item/weapon/storage/pill_bottle/zoom, @@ -310,6 +310,19 @@ prob(1);/obj/item/weapon/reagent_containers/food/snacks/meat/corgi, prob(1);/obj/item/weapon/reagent_containers/food/snacks/xenomeat) +/obj/random/pizzabox + name = "random pizza box" + desc = "This is a random pizza box." + icon = 'icons/obj/food.dmi' + icon_state = "pizzabox1" + +/obj/random/pizzabox/item_to_spawn() + return pick(/obj/item/pizzabox/margherita, + /obj/item/pizzabox/mushroom, + /obj/item/pizzabox/meat, + /obj/item/pizzabox/vegetable, + /obj/item/pizzabox/pineapple) + /obj/random/material //Random materials for building stuff name = "random material" desc = "This is a random material." diff --git a/code/game/objects/random/mob_vr.dm b/code/game/objects/random/mob_vr.dm index c1ce7aaf15..3db11f0626 100644 --- a/code/game/objects/random/mob_vr.dm +++ b/code/game/objects/random/mob_vr.dm @@ -141,11 +141,12 @@ icon = 'icons/obj/items.dmi' icon_state = "purplecomb" spawn_nothing_percentage = 0 + /obj/random/cargopod/item_to_spawn() return pick(prob(10);/obj/item/weapon/contraband/poster,\ prob(8);/obj/item/weapon/haircomb,\ prob(6);/obj/item/weapon/material/wirerod,\ - prob(6);/obj/item/weapon/storage/pill_bottle/tramadol,\ + prob(6);/obj/item/weapon/storage/pill_bottle/paracetamol,\ prob(6);/obj/item/weapon/material/butterflyblade,\ prob(6);/obj/item/weapon/material/butterflyhandle,\ prob(4);/obj/item/weapon/storage/pill_bottle/happy,\ @@ -231,4 +232,21 @@ prob(500);/obj/random/maintenance/clean) /obj/random/action_figure/supplypack - drop_get_turf = FALSE \ No newline at end of file + drop_get_turf = FALSE + +/obj/random/roguemineloot + name = "Random Rogue Mines Item" + desc = "Hot Stuff. Hopefully" + icon = 'icons/obj/items.dmi' + icon_state = "spickaxe" + spawn_nothing_percentage = 0 + +/obj/random/roguemineloot/item_to_spawn() + return pick(prob(5);/obj/random/mre, + prob(5);/obj/random/maintenance, + prob(4);/obj/random/firstaid, + prob(3);/obj/random/toolbox, + prob(2);/obj/random/multiple/minevault, + prob(1);/obj/random/coin, + prob(1);/obj/random/drinkbottle, + prob(1);/obj/random/tool/alien) \ No newline at end of file diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 4d0eb2b9ee..3e571009d2 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -73,12 +73,12 @@ return 0 if (!user.Adjacent(src)) - user << "You can't climb there, the way is blocked." + to_chat(user, "You can't climb there, the way is blocked.") return 0 var/obj/occupied = turf_is_crowded() if(occupied) - user << "There's \a [occupied] in the way." + to_chat(user, "There's \a [occupied] in the way.") return 0 return 1 @@ -118,21 +118,21 @@ /obj/structure/proc/structure_shaken() for(var/mob/living/M in climbers) M.Weaken(1) - M << "You topple as you are shaken off \the [src]!" + to_chat(M, "You topple as you are shaken off \the [src]!") climbers.Cut(1,2) for(var/mob/living/M in get_turf(src)) if(M.lying) return //No spamming this on people. M.Weaken(3) - M << "You topple as \the [src] moves under you!" + to_chat(M, "You topple as \the [src] moves under you!") if(prob(25)) var/damage = rand(15,30) var/mob/living/carbon/human/H = M if(!istype(H)) - H << "You land heavily!" + to_chat(H, "You land heavily!") M.adjustBruteLoss(damage) return @@ -151,12 +151,12 @@ affecting = H.get_organ(BP_HEAD) if(affecting) - M << "You land heavily on your [affecting.name]!" + to_chat(M, "You land heavily on your [affecting.name]!") affecting.take_damage(damage, 0) if(affecting.parent) affecting.parent.add_autopsy_data("Misadventure", damage) else - H << "You land heavily!" + to_chat(H, "You land heavily!") H.adjustBruteLoss(damage) H.UpdateDamageIcon() @@ -169,12 +169,12 @@ if(!Adjacent(user)) return 0 if (user.restrained() || user.buckled) - user << "You need your hands and legs free for this." + to_chat(user, "You need your hands and legs free for this.") return 0 if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) return 0 if (isAI(user)) - user << "You need hands for this." + to_chat(user, "You need hands for this.") return 0 return 1 diff --git a/code/game/objects/structures/barsign.dm b/code/game/objects/structures/barsign.dm index 7da51f6a66..13babf8a3d 100644 --- a/code/game/objects/structures/barsign.dm +++ b/code/game/objects/structures/barsign.dm @@ -17,13 +17,13 @@ ..() switch(icon_state) if("Off") - user << "It appears to be switched off." + to_chat(user, "It appears to be switched off.") if("narsiebistro") - user << "It shows a picture of a large black and red being. Spooky!" + to_chat(user, "It shows a picture of a large black and red being. Spooky!") if("on", "empty") - user << "The lights are on, but there's no picture." + to_chat(user, "The lights are on, but there's no picture.") else - user << "It says '[icon_state]'" + to_chat(user, "It says '[icon_state]'") /obj/structure/sign/double/barsign/New() ..() @@ -40,9 +40,9 @@ if(!sign_type) return icon_state = sign_type - user << "You change the barsign." + to_chat(user, "You change the barsign.") else - user << "Access denied." + to_chat(user, "Access denied.") return return ..() diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index eddffe55ea..9f1af1cf1b 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -16,6 +16,7 @@ LINEN BINS throw_speed = 1 throw_range = 2 w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/clothing.ogg' /obj/item/weapon/bedsheet/attack_self(mob/user as mob) user.drop_item() @@ -30,7 +31,7 @@ LINEN BINS if(is_sharp(I)) user.visible_message("\The [user] begins cutting up [src] with [I].", "You begin cutting up [src] with [I].") if(do_after(user, 50)) - user << "You cut [src] into pieces!" + to_chat(user, "You cut [src] into pieces!") for(var/i in 1 to rand(2,5)) new /obj/item/weapon/reagent_containers/glass/rag(drop_location()) qdel(src) @@ -172,12 +173,12 @@ LINEN BINS ..(user) if(amount < 1) - user << "There are no bed sheets in the bin." + to_chat(user, "There are no bed sheets in the bin.") return if(amount == 1) - user << "There is one bed sheet in the bin." + to_chat(user, "There is one bed sheet in the bin.") return - user << "There are [amount] bed sheets in the bin." + to_chat(user, "There are [amount] bed sheets in the bin.") /obj/structure/bedsheetbin/update_icon() @@ -193,12 +194,12 @@ LINEN BINS I.loc = src sheets.Add(I) amount++ - user << "You put [I] in [src]." + to_chat(user, "You put [I] in [src].") else if(amount && !hidden && I.w_class < ITEMSIZE_LARGE) //make sure there's sheets to hide it among, make sure nothing else is hidden in there. user.drop_item() I.loc = src hidden = I - user << "You hide [I] among the sheets." + to_chat(user, "You hide [I] among the sheets.") /obj/structure/bedsheetbin/attack_hand(mob/user as mob) if(amount >= 1) @@ -214,11 +215,11 @@ LINEN BINS B.loc = user.loc user.put_in_hands(B) - user << "You take [B] out of [src]." + to_chat(user, "You take [B] out of [src].") if(hidden) hidden.loc = user.loc - user << "[hidden] falls out of [B]!" + to_chat(user, "[hidden] falls out of [B]!") hidden = null @@ -237,7 +238,7 @@ LINEN BINS B = new /obj/item/weapon/bedsheet(loc) B.loc = loc - user << "You telekinetically remove [B] from [src]." + to_chat(user, "You telekinetically remove [B] from [src].") update_icon() if(hidden) diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index 5c606422d7..26e040c7e9 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -87,14 +87,8 @@ return ..() /obj/structure/catwalk/Crossed() - //VOREStation Edit begin: SHADEKIN - var/mob/SK = usr - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN . = ..() - if(isliving(usr)) + if(isliving(usr) && !usr.is_incorporeal()) playsound(src, pick('sound/effects/footstep/catwalk1.ogg', 'sound/effects/footstep/catwalk2.ogg', 'sound/effects/footstep/catwalk3.ogg', 'sound/effects/footstep/catwalk4.ogg', 'sound/effects/footstep/catwalk5.ogg'), 25, 1) /obj/structure/catwalk/CheckExit(atom/movable/O, turf/target) diff --git a/code/game/objects/structures/coathanger.dm b/code/game/objects/structures/coathanger.dm index c457c35f67..848168dd08 100644 --- a/code/game/objects/structures/coathanger.dm +++ b/code/game/objects/structures/coathanger.dm @@ -24,7 +24,7 @@ user.drop_from_inventory(coat, src) update_icon() else - user << "You cannot hang [W] on [src]" + to_chat(user, "You cannot hang [W] on [src]") return ..() /obj/structure/coatrack/CanPass(atom/movable/mover, turf/target) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 95d38c4473..b75e3de6ed 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -58,7 +58,7 @@ update_icon() /obj/structure/closet/examine(mob/user) - if(..(user, 1) && !opened) + if(!src.opened && (..(user, 1) || isobserver(user))) var/content_size = 0 for(var/obj/item/I in src.contents) if(!I.anchored) @@ -74,6 +74,9 @@ else to_chat(user, "It is full.") + if(!src.opened && isobserver(user)) + to_chat(user, "It contains: [counting_english_list(contents)]") + /obj/structure/closet/CanPass(atom/movable/mover, turf/target) if(wall_mounted) return TRUE @@ -360,12 +363,6 @@ if(!src.toggle()) to_chat(usr, "It won't budge!") -/obj/structure/closet/attack_ghost(mob/ghost) - if(ghost.client && ghost.client.inquisitive_ghost) - ghost.examinate(src) - if (!src.opened) - to_chat(ghost, "It contains: [english_list(contents)].") - /obj/structure/closet/verb/verb_toggleopen() set src in oview(1) set category = "Object" @@ -474,4 +471,4 @@ return dump_contents() spawn(1) qdel(src) - return 1 \ No newline at end of file + return 1 diff --git a/code/game/objects/structures/crates_lockers/closets/secure/closets_yw.dm b/code/game/objects/structures/crates_lockers/closets/secure/closets_yw.dm index 6aafe31f4a..e1057a1f9c 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/closets_yw.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/closets_yw.dm @@ -1,6 +1,6 @@ /obj/structure/closet/secure_closet/blueshield name = "blueshield's locker" - req_access = list(access_teleporter) + req_access = list(access_blueshield) icon = 'icons/obj/closet_yw.dmi' icon_state = "bssecure1" icon_closed = "bssecure" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm index d5f5da1c40..7c90e2a50a 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm @@ -63,7 +63,7 @@ var/obj/item/weapon/card/id/I = W.GetID() if(src.broken) - user << "It appears to be broken." + to_chat(user, "It appears to be broken.") return if(!I || !I.registered_name) return if(src.allowed(user) || !src.registered_name || (istype(I) && (src.registered_name == I.registered_name))) @@ -76,7 +76,7 @@ src.registered_name = I.registered_name src.desc = "Owned by [I.registered_name]." else - user << "Access Denied" + to_chat(user, "Access Denied") else if(istype(W, /obj/item/weapon/melee/energy/blade)) if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() @@ -85,7 +85,7 @@ playsound(src.loc, 'sound/weapons/blade1.ogg', 50, 1) playsound(src.loc, "sparks", 50, 1) else - user << "Access Denied" + to_chat(user, "Access Denied") return /obj/structure/closet/secure_closet/personal/emag_act(var/remaining_charges, var/mob/user, var/visual_feedback, var/audible_feedback) @@ -107,9 +107,9 @@ if(ishuman(usr)) src.add_fingerprint(usr) if (src.locked || !src.registered_name) - usr << "You need to unlock it first." + to_chat(usr, "You need to unlock it first.") else if (src.broken) - usr << "It appears to be broken." + to_chat(usr, "It appears to be broken.") else if (src.opened) if(!src.close()) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 4b985c2128..3ee917e753 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -39,7 +39,7 @@ /obj/item/device/radio/headset/heads/hop, /obj/item/device/radio/headset/heads/hop/alt, /obj/item/weapon/storage/box/ids = 2, - /obj/item/weapon/gun/energy/gun, + ///obj/item/weapon/gun/energy/gun, //YW Edit, /obj/item/weapon/gun/energy/gun/martin, //VOREStation Add, /obj/item/weapon/storage/box/commandkeys, //VOREStation Add, /obj/item/weapon/storage/box/servicekeys, //VOREStation Add, @@ -114,6 +114,7 @@ /obj/item/device/flash, /obj/item/weapon/melee/baton/loaded, /obj/item/weapon/gun/magnetic/railgun/heater/pistol/hos, + /obj/item/weapon/rcd_ammo/large, /obj/item/weapon/cell/device/weapon, /obj/item/clothing/accessory/holster/waist, /obj/item/weapon/melee/telebaton, diff --git a/code/game/objects/structures/crates_lockers/closets/walllocker.dm b/code/game/objects/structures/crates_lockers/closets/walllocker.dm index 526f12ec26..3e351f1fb3 100644 --- a/code/game/objects/structures/crates_lockers/closets/walllocker.dm +++ b/code/game/objects/structures/crates_lockers/closets/walllocker.dm @@ -19,6 +19,8 @@ var/list/spawnitems = list(/obj/item/weapon/tank/emergency/oxygen,/obj/item/clothing/mask/breath,/obj/item/weapon/tool/crowbar/red,/obj/item/device/flashlight/flare,) var/amount = 2 // spawns each items X times. icon_state = "emerg" + icon_closed = "emerg" + icon_opened = "emerg_open" /obj/structure/closet/walllocker/emerglocker/toggle(mob/user as mob) src.attack_hand(user) @@ -31,10 +33,10 @@ if (istype(user, /mob/living/silicon/ai)) //Added by Strumpetplaya - AI shouldn't be able to return //activate emergency lockers. This fixes that. (Does this make sense, the AI can't call attack_hand, can it? --Mloc) if(!amount) - usr << "It's empty.." + to_chat(usr, "It's empty..") return if(amount) - usr << "You take out some items from \the [src]." + to_chat(usr, "You take out some items from \the [src].") for(var/path in spawnitems) new path(src.loc) amount-- diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index cc59e67f28..431b60f2ee 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -82,21 +82,21 @@ else if(istype(W, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = W if(rigged) - user << "[src] is already rigged!" + to_chat(user, "[src] is already rigged!") return if (C.use(1)) - user << "You rig [src]." + to_chat(user , "You rig [src].") rigged = 1 return else if(istype(W, /obj/item/device/radio/electropack)) if(rigged) - user << "You attach [W] to [src]." + to_chat(user , "You attach [W] to [src].") user.drop_item() W.forceMove(src) return else if(W.is_wirecutter()) if(rigged) - user << "You cut away the wiring." + to_chat(user , "You cut away the wiring.") playsound(src.loc, W.usesound, 100, 1) rigged = 0 return @@ -149,15 +149,15 @@ /obj/structure/closet/crate/secure/proc/togglelock(mob/user as mob) if(src.opened) - user << "Close the crate first." + to_chat(user, "Close the crate first.") return if(src.broken) - user << "The crate appears to be broken." + to_chat(user, "The crate appears to be broken.") return if(src.allowed(user)) set_locked(!locked, user) else - user << "Access Denied" + to_chat(user, "Access Denied") /obj/structure/closet/crate/secure/proc/set_locked(var/newlocked, mob/user = null) if(locked == newlocked) return @@ -181,7 +181,7 @@ src.add_fingerprint(usr) src.togglelock(usr) else - usr << "This mob type can't use this verb." + to_chat(usr, "This mob type can't use this verb.") /obj/structure/closet/crate/secure/attack_hand(mob/user as mob) src.add_fingerprint(user) @@ -209,7 +209,7 @@ playsound(src.loc, "sparks", 60, 1) src.locked = 0 src.broken = 1 - user << "You unlock \the [src]." + to_chat(user, "You unlock \the [src].") return 1 /obj/structure/closet/crate/secure/emp_act(severity) diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index 31fe06f686..202ae10144 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -64,10 +64,9 @@ icon_state = "vehiclecrate" /obj/structure/largecrate/vehicle/Initialize() - ..() - spawn(1) - for(var/obj/O in contents) - O.update_icon() + . = ..() + for(var/obj/O in contents) + O.update_icon() /obj/structure/largecrate/vehicle/bike name = "spacebike crate" diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index 2fbe187af3..cbf9fad58f 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -40,9 +40,9 @@ /obj/structure/curtain/attackby(obj/item/P, mob/user) if(P.is_wirecutter()) playsound(src, P.usesound, 50, 1) - user << "You start to cut the shower curtains." + to_chat(user, "You start to cut the shower curtains.") if(do_after(user, 10)) - user << "You cut the shower curtains." + to_chat(user, "You cut the shower curtains.") var/obj/item/stack/material/plastic/A = new /obj/item/stack/material/plastic( src.loc ) A.amount = 3 qdel(src) diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index d1516a9e66..088dbd33c6 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -66,16 +66,16 @@ /obj/structure/displaycase/attack_hand(mob/user as mob) if (src.destroyed && src.occupied) new /obj/item/weapon/gun/energy/captain( src.loc ) - user << "You deactivate the hover field built into the case." + to_chat(user, "You deactivate the hover field built into the case.") src.occupied = 0 src.add_fingerprint(user) update_icon() return else - usr << text("You kick the display case.") + to_chat(usr, "You kick the display case.") for(var/mob/O in oviewers()) if ((O.client && !( O.blinded ))) - O << "[usr] kicks the display case." + to_chat(O, "[usr] kicks the display case.") src.health -= 2 healthcheck() return diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index 2be63df8e7..0879e196f2 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -32,15 +32,15 @@ user.remove_from_mob(O) contents += O has_extinguisher = O - user << "You place [O] in [src]." + to_chat(user, "You place [O] in [src].") else opened = !opened if(O.is_wrench()) if(!has_extinguisher) - user << "You start to unwrench the extinguisher cabinet." + to_chat(user, "You start to unwrench the extinguisher cabinet.") playsound(src.loc, O.usesound, 50, 1) if(do_after(user, 15 * O.toolspeed)) - user << "You unwrench the extinguisher cabinet." + to_chat(user, "You unwrench the extinguisher cabinet.") new /obj/item/frame/extinguisher_cabinet( src.loc ) qdel(src) return @@ -58,11 +58,11 @@ if (user.hand) temp = H.organs_by_name["l_hand"] if(temp && !temp.is_usable()) - user << "You try to move your [temp.name], but cannot!" + to_chat(user, "You try to move your [temp.name], but cannot!") return if(has_extinguisher) user.put_in_hands(has_extinguisher) - user << "You take [has_extinguisher] from [src]." + to_chat(user, "You take [has_extinguisher] from [src].") has_extinguisher = null opened = 1 else @@ -72,7 +72,7 @@ /obj/structure/extinguisher_cabinet/attack_tk(mob/user) if(has_extinguisher) has_extinguisher.loc = loc - user << "You telekinetically remove [has_extinguisher] from [src]." + to_chat(user, "You telekinetically remove [has_extinguisher] from [src].") has_extinguisher = null opened = 1 else diff --git a/code/game/objects/structures/fitness_vr.dm b/code/game/objects/structures/fitness_vr.dm new file mode 100644 index 0000000000..341246051e --- /dev/null +++ b/code/game/objects/structures/fitness_vr.dm @@ -0,0 +1,79 @@ +/obj/structure/fitness/boxing_ropes + name = "Ropes" + desc = "Firm yet springy, perhaps this could be useful!" + icon = 'icons/obj/fitness_vr.dmi' + icon_state = "ropes" + density = 1 + throwpass = 1 + climbable = 1 + layer = WINDOW_LAYER + anchored = 1 + flags = ON_BORDER +/obj/structure/fitness/boxing_ropes/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + if(get_dir(mover, target) == turn(dir, 180)) + return !density + return TRUE +/obj/structure/fitness/boxing_ropes/CheckExit(atom/movable/O as mob|obj, target as turf) + if(istype(O) && O.checkpass(PASSTABLE)) + return 1 + if(get_dir(O.loc, target) == dir) + return 0 + return 1 + +/obj/structure/fitness/boxing_ropes_bottom + name = "Ropes" + desc = "Firm yet springy, perhaps this could be useful!" + icon = 'icons/obj/fitness_vr.dmi' + icon_state = "ropes" + density = 1 + throwpass = 1 + climbable = 1 + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + anchored = 1 + flags = ON_BORDER +/obj/structure/fitness/boxing_ropes_bottom/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + if(get_dir(mover, target) == turn(dir, 180)) + return !density + return TRUE +/obj/structure/fitness/boxing_ropes_bottom/CheckExit(atom/movable/O as mob|obj, target as turf) + if(istype(O) && O.checkpass(PASSTABLE)) + return 1 + if(get_dir(O.loc, target) == dir) + return 0 + return 1 + + + +/obj/structure/fitness/boxing_turnbuckle + name = "Turnbuckle" + desc = "A sturdy post that looks like it could support even the most heaviest of heavy weights!" + icon = 'icons/obj/fitness_vr.dmi' + icon_state = "turnbuckle" + density = 1 + throwpass = 1 + climbable = 1 + layer = WINDOW_LAYER + anchored = 1 + flags = ON_BORDER +/obj/structure/fitness/boxing_turnbuckle/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSTABLE)) + return TRUE + if(get_dir(mover, target) == turn(dir, 180)) + return !density + return TRUE +/obj/structure/fitness/boxing_turnbuckle/CheckExit(atom/movable/O as mob|obj, target as turf) + if(istype(O) && O.checkpass(PASSTABLE)) + return 1 + if(get_dir(O.loc, target) == dir) + return 0 + return 1 + +/turf/simulated/fitness + name = "Mat" + icon = 'icons/turf/floors_vr.dmi' + icon_state = "fit_mat" \ No newline at end of file diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 15bfe8ac89..a5a8704dc1 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -222,6 +222,44 @@ icon_state = "plant-01" plane = OBJ_PLANE + var/obj/item/stored_item + +/obj/structure/flora/pottedplant/examine(mob/user) + ..() + if(in_range(user, src) && stored_item) + to_chat(user, "You can see something in there...") + +/obj/structure/flora/pottedplant/attackby(obj/item/I, mob/user) + if(stored_item) + to_chat(user, "[I] won't fit in. There already appears to be something in here...") + return + + if(I.w_class > ITEMSIZE_TINY) + to_chat(user, "[I] is too big to fit inside [src].") + return + + if(do_after(user, 10)) + user.drop_from_inventory(I, src) + I.forceMove(src) + stored_item = I + src.visible_message("\icon[src] \icon[I] [user] places [I] into [src].") + return + else + to_chat(user, "You refrain from putting things into the plant pot.") + return + + ..() + +/obj/structure/flora/pottedplant/attack_hand(mob/user) + if(!stored_item) + to_chat(user, "You see nothing of interest in [src]...") + else + if(do_after(user, 10)) + to_chat(user, "You find \icon[stored_item] [stored_item] in [src]!") + stored_item.forceMove(get_turf(src)) + stored_item = null + ..() + /obj/structure/flora/pottedplant/large name = "large potted plant" diff --git a/code/game/objects/structures/ghost_pods/ghost_pods.dm b/code/game/objects/structures/ghost_pods/ghost_pods.dm index 7413b4b9d4..12cfe24dc2 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods.dm @@ -65,18 +65,13 @@ /obj/structure/ghost_pod/automatic/Initialize() . = ..() - spawn(delay_to_self_open) - if(src) - trigger() + addtimer(CALLBACK(src, .proc/trigger), delay_to_self_open) /obj/structure/ghost_pod/automatic/trigger() . = ..() if(. == FALSE) // If we failed to get a volunteer, try again later if allowed to. if(delay_to_try_again) - spawn(delay_to_try_again) - if(src) - trigger() - + addtimer(CALLBACK(src, .proc/trigger), delay_to_try_again) // This type is triggered by a ghost clicking on it, as opposed to a living player. A ghost query type isn't needed. /obj/structure/ghost_pod/ghost_activated diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 1057a5094c..16bdbdf120 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -129,23 +129,23 @@ else dir_to_set = 4 else - user << "You can't reach." + to_chat(user, "You can't reach.") return //Only works for cardinal direcitons, diagonals aren't supposed to work like this. for(var/obj/structure/window/WINDOW in loc) if(WINDOW.dir == dir_to_set) - user << "There is already a window facing this way there." + to_chat(user, "There is already a window facing this way there.") return - user << "You start placing the window." + to_chat(user, "You start placing the window.") if(do_after(user,20)) for(var/obj/structure/window/WINDOW in loc) if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. - user << "There is already a window facing this way there." + to_chat(user, "There is already a window facing this way there.") return var/wtype = ST.material.created_window if (ST.use(1)) var/obj/structure/window/WD = new wtype(loc, dir_to_set, 1) - user << "You place the [WD] on [src]." + to_chat(user, "You place the [WD] on [src].") WD.update_icon() return //window placing end diff --git a/code/game/objects/structures/holoplant.dm b/code/game/objects/structures/holoplant.dm index bbd92b81ee..9a46b8ca15 100644 --- a/code/game/objects/structures/holoplant.dm +++ b/code/game/objects/structures/holoplant.dm @@ -48,13 +48,13 @@ plant = prepare_icon(emagged ? "emagged" : null) overlays += plant set_light(2) - use_power = 2 + use_power = USE_POWER_ACTIVE /obj/machinery/holoplant/proc/deactivate() overlays -= plant QDEL_NULL(plant) set_light(0) - use_power = 0 + use_power = USE_POWER_OFF /obj/machinery/holoplant/power_change() ..() diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index f5bebdf529..995bf4eece 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -3,6 +3,7 @@ desc = "A folded membrane which rapidly expands into a large cubical shape on activation." icon = 'icons/obj/inflatable.dmi' icon_state = "folded_wall" + drop_sound = 'sound/items/drop/rubber.ogg' w_class = ITEMSIZE_NORMAL var/deploy_path = /obj/structure/inflatable @@ -14,7 +15,7 @@ if(!user) return if(!user.Adjacent(A)) - to_chat(user,"You can't reach!") + to_chat(user, "You can't reach!") return if(istype(A, /turf)) inflate(user,A) @@ -94,7 +95,7 @@ /obj/item/inflatable/proc/inflate(var/mob/user,var/location) playsound(location, 'sound/items/zip.ogg', 75, 1) - to_chat(user,"You inflate [src].") + to_chat(user, "You inflate [src].") var/obj/structure/inflatable/R = new deploy_path(location) src.transfer_fingerprints_to(R) R.add_fingerprint(user) @@ -102,7 +103,7 @@ /obj/structure/inflatable/proc/deflate() playsound(loc, 'sound/machines/hiss.ogg', 75, 1) - //user << "You slowly deflate the inflatable wall." + //to_chat(user, "You slowly deflate the inflatable wall.") visible_message("[src] slowly deflates.") spawn(50) var/obj/item/inflatable/R = new /obj/item/inflatable(loc) @@ -247,7 +248,7 @@ icon_state = "folded_wall_torn" attack_self(mob/user) - user << "The inflatable wall is too torn to be inflated!" + to_chat(user, "The inflatable wall is too torn to be inflated!") add_fingerprint(user) /obj/item/inflatable/door/torn @@ -257,7 +258,7 @@ icon_state = "folded_door_torn" attack_self(mob/user) - user << "The inflatable door is too torn to be inflated!" + to_chat(user, "The inflatable door is too torn to be inflated!") add_fingerprint(user) /obj/item/weapon/storage/briefcase/inflatable diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 8ff7ee844f..93c48fe8a9 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -25,7 +25,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) /obj/structure/janitorialcart/examine(mob/user) if(..(user, 1)) - user << "[src] \icon[src] contains [reagents.total_volume] unit\s of liquid!" + to_chat(user, "[src] [bicon(src)] contains [reagents.total_volume] unit\s of liquid!") //everything else is visible, so doesn't need to be mentioned @@ -36,15 +36,15 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) I.loc = src update_icon() updateUsrDialog() - user << "You put [I] into [src]." + to_chat(user, "You put [I] into [src].") else if(istype(I, /obj/item/weapon/mop)) if(I.reagents.total_volume < I.reagents.maximum_volume) //if it's not completely soaked we assume they want to wet it, otherwise store it if(reagents.total_volume < 1) - user << "[src] is out of water!" + to_chat(user, "[src] is out of water!") else reagents.trans_to_obj(I, 5) // - user << "You wet [I] in [src]." + to_chat(user, "You wet [I] in [src].") playsound(loc, 'sound/effects/slosh.ogg', 25, 1) return if(!mymop) @@ -53,7 +53,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) I.loc = src update_icon() updateUsrDialog() - user << "You put [I] into [src]." + to_chat(user, "You put [I] into [src].") else if(istype(I, /obj/item/weapon/reagent_containers/spray) && !myspray) user.drop_item() @@ -61,7 +61,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) I.loc = src update_icon() updateUsrDialog() - user << "You put [I] into [src]." + to_chat(user, "You put [I] into [src].") else if(istype(I, /obj/item/device/lightreplacer) && !myreplacer) user.drop_item() @@ -69,7 +69,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) I.loc = src update_icon() updateUsrDialog() - user << "You put [I] into [src]." + to_chat(user, "You put [I] into [src].") else if(istype(I, /obj/item/weapon/caution)) if(signs < 4) @@ -78,9 +78,9 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) signs++ update_icon() updateUsrDialog() - user << "You put [I] into [src]." + to_chat(user, "You put [I] into [src].") else - user << "[src] can't hold any more signs." + to_chat(user, "[src] can't hold any more signs.") else if(istype(I, /obj/item/weapon/reagent_containers/glass)) return // So we do not put them in the trash bag as we mean to fill the mop bucket @@ -120,29 +120,29 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) if("garbage") if(mybag) user.put_in_hands(mybag) - user << "You take [mybag] from [src]." + to_chat(user, "You take [mybag] from [src].") mybag = null if("mop") if(mymop) user.put_in_hands(mymop) - user << "You take [mymop] from [src]." + to_chat(user, "You take [mymop] from [src].") mymop = null if("spray") if(myspray) user.put_in_hands(myspray) - user << "You take [myspray] from [src]." + to_chat(user, "You take [myspray] from [src].") myspray = null if("replacer") if(myreplacer) user.put_in_hands(myreplacer) - user << "You take [myreplacer] from [src]." + to_chat(user, "You take [myreplacer] from [src].") myreplacer = null if("sign") if(signs) var/obj/item/weapon/caution/Sign = locate() in src if(Sign) user.put_in_hands(Sign) - user << "You take \a [Sign] from [src]." + to_chat(user, "You take \a [Sign] from [src].") signs-- else warning("[src] signs ([signs]) didn't match contents") @@ -189,23 +189,23 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) if(!..(user, 1)) return - user << "\icon[src] This [callme] contains [reagents.total_volume] unit\s of water!" + to_chat(user, "[bicon(src)] This [callme] contains [reagents.total_volume] unit\s of water!") if(mybag) - user << "\A [mybag] is hanging on the [callme]." + to_chat(user, "\A [mybag] is hanging on the [callme].") /obj/structure/bed/chair/janicart/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/weapon/mop)) if(reagents.total_volume > 1) reagents.trans_to_obj(I, 2) - user << "You wet [I] in the [callme]." + to_chat(user, "You wet [I] in the [callme].") playsound(loc, 'sound/effects/slosh.ogg', 25, 1) else - user << "This [callme] is out of water!" + to_chat(user, "This [callme] is out of water!") else if(istype(I, /obj/item/key)) - user << "Hold [I] in one of your hands while you drive this [callme]." + to_chat(user, "Hold [I] in one of your hands while you drive this [callme].") else if(istype(I, /obj/item/weapon/storage/bag/trash)) - user << "You hook the trashbag onto the [callme]." + to_chat(user, "You hook the trashbag onto the [callme].") user.drop_item() I.loc = src mybag = I @@ -227,7 +227,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) step(src, direction) update_mob() else - user << "You'll need the keys in one of your hands to drive this [callme]." + to_chat(user, "You'll need the keys in one of your hands to drive this [callme].") /obj/structure/bed/chair/janicart/Move() diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index 01d282b40c..295aa9239c 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -63,14 +63,14 @@ var/obj/item/weapon/weldingtool/WT = C if(WT.welding == 1) if(WT.remove_fuel(0, user)) - user << "Slicing lattice joints ..." + to_chat(user, "Slicing lattice joints ...") new /obj/item/stack/rods(src.loc) qdel(src) return if (istype(C, /obj/item/stack/rods)) var/obj/item/stack/rods/R = C if(R.use(2)) - user << "You start connecting \the [R.name] to \the [src.name] ..." + to_chat(user, "You start connecting \the [R.name] to \the [src.name] ...") if(do_after(user, 5 SECONDS)) src.alpha = 0 // Note: I don't know why this is set, Eris did it, just trusting for now. ~Leshana new /obj/structure/catwalk(src.loc) diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm index e3607fdeea..e14c9ba9e3 100644 --- a/code/game/objects/structures/loot_piles.dm +++ b/code/game/objects/structures/loot_piles.dm @@ -258,7 +258,7 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/item/weapon/reagent_containers/syringe/steroid, /obj/item/weapon/storage/pill_bottle/zoom, /obj/item/weapon/storage/pill_bottle/happy, - /obj/item/weapon/storage/pill_bottle/tramadol + /obj/item/weapon/storage/pill_bottle/paracetamol //VOREStation Edit ) // Contains loads of different types of boxes, which may have items inside! diff --git a/code/game/objects/structures/medical_stand_vr.dm b/code/game/objects/structures/medical_stand_vr.dm new file mode 100644 index 0000000000..60bddde660 --- /dev/null +++ b/code/game/objects/structures/medical_stand_vr.dm @@ -0,0 +1,444 @@ +/obj/structure/medical_stand + name = "medical stand" + icon = 'icons/obj/medical_stand_vr.dmi' + desc = "Medical stand used to hang reagents for transfusion and to hold anesthetic tank." + icon_state = "medical_stand_empty" + + //gas stuff + var/obj/item/weapon/tank/tank + var/mob/living/carbon/human/breather + var/obj/item/clothing/mask/breath/contained + + var/spawn_type = null + var/mask_type = /obj/item/clothing/mask/breath/medical + + var/is_loosen = TRUE + var/valve_opened = FALSE + //blood stuff + var/mob/living/carbon/attached + var/mode = 1 // 1 is injecting, 0 is taking blood. + var/obj/item/weapon/reagent_containers/beaker + var/list/transfer_amounts = list(REM, 1, 2) + var/transfer_amount = 1 + +/obj/structure/medical_stand/New() + ..() + if (spawn_type) + tank = new spawn_type (src) + contained = new mask_type (src) + update_icon() + +/obj/structure/medical_stand/update_icon() + overlays.Cut() + + if (tank) + if (breather) + overlays += "tube_active" + else + overlays += "tube" + if(istype(tank,/obj/item/weapon/tank/anesthetic)) + overlays += "tank_anest" + else if(istype(tank,/obj/item/weapon/tank/nitrogen)) + overlays += "tank_nitro" + else if(istype(tank,/obj/item/weapon/tank/oxygen)) + overlays += "tank_oxyg" + else if(istype(tank,/obj/item/weapon/tank/phoron)) + overlays += "tank_plasma" + //else if(istype(tank,/obj/item/weapon/tank/hydrogen)) + // overlays += "tank_hydro" + else + overlays += "tank_other" + + if(beaker) + overlays += "beaker" + if(attached) + overlays += "line_active" + else + overlays += "line" + var/datum/reagents/reagents = beaker.reagents + var/percent = round((reagents.total_volume / beaker.volume) * 100) + if(reagents.total_volume) + var/image/filling = image('icons/obj/medical_stand_vr.dmi', src, "reagent") + + switch(percent) + if(10 to 24) filling.icon_state = "reagent10" + if(25 to 49) filling.icon_state = "reagent25" + if(50 to 74) filling.icon_state = "reagent50" + if(75 to 79) filling.icon_state = "reagent75" + if(80 to 90) filling.icon_state = "reagent80" + if(91 to INFINITY) filling.icon_state = "reagent100" + if (filling.icon) + filling.icon += reagents.get_color() + overlays += filling + +/obj/structure/medical_stand/Destroy() + STOP_PROCESSING(SSobj,src) + if(breather) + breather.internal = null + breather.internals?.icon_state = "internal0" + if(tank) + qdel(tank) + if(breather) + breather.remove_from_mob(contained) + src.visible_message("The mask rapidly retracts just before /the [src] is destroyed!") + qdel(contained) + contained = null + breather = null + + attached = null + qdel(beaker) + beaker = null + return ..() + +/obj/structure/medical_stand/attack_robot(var/mob/user) + if(Adjacent(user)) + attack_hand(user) + +/obj/structure/medical_stand/MouseDrop(var/mob/living/carbon/human/target, src_location, over_location) + ..() + if(istype(target)) + if(usr.stat == DEAD || !CanMouseDrop(target)) + return + var/list/available_options = list() + if (tank) + available_options += "Gas mask" + if (beaker) + available_options += "Drip needle" + + var/action_type + if(available_options.len > 1) + action_type = input(usr, "What do you want to attach/detach?") as null|anything in available_options + else if(available_options.len) + action_type = available_options[1] + if(usr.stat == DEAD || !CanMouseDrop(target)) + return + switch (action_type) + if("Gas mask") + if(!can_apply_to_target(target, usr)) // There is no point in attempting to apply a mask if it's impossible. + return + if (breather) + src.add_fingerprint(usr) + if(!do_mob(usr, target, 30) || !can_apply_to_target(target, usr)) + return + if(tank) + tank.forceMove(src) + if (breather.wear_mask == contained) + breather.remove_from_mob(contained) + contained.forceMove(src) + else + qdel(contained) + contained = new mask_type(src) + breather = null + src.visible_message("\The [contained] slips to \the [src]!") + update_icon() + return + usr.visible_message("\The [usr] begins carefully placing the mask onto [target].", + "You begin carefully placing the mask onto [target].") + if(!do_mob(usr, target, 100) || !can_apply_to_target(target, usr)) + return + // place mask and add fingerprints + usr.visible_message("\The [usr] has placed \the mask on [target]'s mouth.", + "You have placed \the mask on [target]'s mouth.") + if(attach_mask(target)) + src.add_fingerprint(usr) + update_icon() + START_PROCESSING(SSobj,src) + return + if("Drip needle") + if(attached) + if(!do_mob(usr, target, 20)) + return + visible_message("\The [attached] is taken off \the [src]") + attached = null + else if(ishuman(target)) + usr.visible_message("\The [usr] begins inserting needle into [target]'s vein.", + "You begin inserting needle into [target]'s vein.") + if(!do_mob(usr, target, 50)) + usr.visible_message("\The [usr]'s hand slips and pricks \the [target].", + "Your hand slips and pricks \the [target].") + target.apply_damage(3, BRUTE, pick(BP_R_ARM, BP_L_ARM)) + return + usr.visible_message("\The [usr] hooks \the [target] up to \the [src].", + "You hook \the [target] up to \the [src].") + attached = target + START_PROCESSING(SSobj,src) + update_icon() + + +/obj/structure/medical_stand/attack_hand(mob/user as mob) + var/list/available_options = list() + if (tank) + available_options += "Toggle valve" + available_options += "Remove tank" + if (beaker) + available_options += "Remove vessel" + + var/action_type + if(available_options.len > 1) + action_type = input(user, "What do you want to do?") as null|anything in available_options + else if(available_options.len) + action_type = available_options[1] + switch (action_type) + if ("Remove tank") + if (!tank) + to_chat(user, "There is no tank in \the [src]!") + return + else if (tank && is_loosen) + user.visible_message("\The [user] removes \the [tank] from \the [src].", "You remove \the [tank] from \the [src].>") + user.put_in_hands(tank) + tank = null + update_icon() + return + else if (!is_loosen) + user.visible_message("\The [user] tries to removes \the [tank] from \the [src] but it won't budge.", "You try to removes \the [tank] from \the [src] but it won't budge.>") + return + if ("Toggle valve") + if (!tank) + to_chat(user, "There is no tank in \the [src]!") + return + else + if (valve_opened) + src.visible_message("\The [user] closes valve on \the [src]!", + "You close valve on \the [src].") + if(breather) + breather.internals?.icon_state = "internal0" + breather.internal = null + valve_opened = FALSE + update_icon() + else + src.visible_message("\The [user] opens valve on \the [src]!", + "You open valve on \the [src].") + if(breather) + breather.internal = tank + breather.internals?.icon_state = "internal1" + valve_opened = TRUE + //playsound(get_turf(src), 'sound/effects/internals.ogg', 100, 1) + update_icon() + START_PROCESSING(SSobj,src) + if ("Remove vessel") + if(beaker) + beaker.forceMove(loc) + beaker = null + update_icon() + +/obj/structure/medical_stand/verb/toggle_mode() + set category = "Object" + set name = "Toggle IV Mode" + set src in view(1) + + if(!istype(usr, /mob/living)) + to_chat(usr, "You can't do that.") + return + + if(usr.incapacitated()) + return + + mode = !mode + to_chat(usr, "The IV drip is now [mode ? "injecting" : "taking blood"].") + +/obj/structure/medical_stand/verb/set_APTFT() + set name = "Set IV transfer amount" + set category = "Object" + set src in range(1) + var/N = input("Amount per transfer from this:","[src]") as null|anything in transfer_amounts + if(N) + transfer_amount = N + +/obj/structure/medical_stand/proc/attach_mask(var/mob/living/carbon/C) + if(C && istype(C)) + if(C.equip_to_slot_if_possible(contained, slot_wear_mask)) + if(tank) + tank.forceMove(C) + breather = C + return TRUE + +/obj/structure/medical_stand/proc/can_apply_to_target(var/mob/living/carbon/human/target, var/mob/user) + if(!user) + user = target + // Check target validity + if(!istype(target)) + to_chat(user, "\The [target] not compatible with machine.") + return + if(!target.organs_by_name[BP_HEAD]) + to_chat(user, "\The [target] doesn't have a head.") + return + if(!target.check_has_mouth()) + to_chat(user, "\The [target] doesn't have a mouth.") + return + if(target.wear_mask && target != breather) + to_chat(user, "\The [target] is already wearing a mask.") + return + if(target.head && (target.head.body_parts_covered & FACE)) + to_chat(user, "Remove their [target.head] first.") + return + if(!tank) + to_chat(user, "There is no tank in \the [src].") + return + if(is_loosen) + to_chat(user, "Tighten \the nut with a wrench first.") + return + if(!Adjacent(target)) + return + //when there is a breather: + if(breather && target != breather) + to_chat(user, "\The [src] is already in use.") + return + //Checking if breather is still valid + if(target == breather && target.wear_mask != contained) + to_chat(user, "\The [target] is not using the supplied mask.") + return + return 1 + +/obj/structure/medical_stand/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype (W, /obj/item/weapon/tool)) + if (valve_opened) + to_chat(user, "Close the valve first.") + return + if (tank) + if(!W.is_wrench()) + return + if (!is_loosen) + is_loosen = TRUE + else + is_loosen = FALSE + if (valve_opened) + START_PROCESSING(SSobj,src) + user.visible_message( + "The [user] [is_loosen == TRUE ? "loosen" : "tighten"] the nut holding [tank] in place.", + "You [is_loosen == TRUE ? "loosen" : "tighten"] the nut holding [tank] in place.") + + else + to_chat(user, "There is no tank in \the [src].") + + else if(istype(W, /obj/item/weapon/tank)) + if(tank) + to_chat(user, "\The [src] already has a tank installed!") + else if(!is_loosen) + to_chat(user, "Loosen the nut with a wrench first.") + else + user.drop_item() + W.forceMove(src) + tank = W + user.visible_message("\The [user] attaches \the [tank] to \the [src].", "You attach \the [tank] to \the [src].") + src.add_fingerprint(user) + update_icon() + + else if (istype(W, /obj/item/weapon/reagent_containers)) + if(!isnull(src.beaker)) + to_chat(user, "There is already a reagent container loaded!") + return + user.drop_item() + W.forceMove(src) + beaker = W + to_chat(user, "You attach \the [W] to \the [src].") + update_icon() + else + return ..() + +/obj/structure/medical_stand/examine(var/mob/user) + . = ..() + + if (get_dist(src, user) > 2) + return + + if(beaker) + to_chat(user, "The IV drip is [mode ? "injecting" : "taking blood"].") + to_chat(user, "It is set to transfer [transfer_amount]u of chemicals per cycle.") + if(beaker.reagents && beaker.reagents.total_volume) + to_chat(user, "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid.") + else + to_chat(user, "Attached is an empty [beaker].") + to_chat(user, "[attached ? attached : "No one"] is hooked up to it.") + else + to_chat(user, "There is no vessel.") + + if(tank) + if (!is_loosen) + to_chat(user, "\The [tank] connected.") + to_chat(user, "The meter shows [round(tank.air_contents.return_pressure())]. The valve is [valve_opened == TRUE ? "open" : "closed"].") + if (tank.distribute_pressure == 0) + to_chat(user, "Use wrench to replace tank.") + else + to_chat(user, "There is no tank.") + +/obj/structure/medical_stand/process() + //Gas Stuff + if(breather) + if(!can_apply_to_target(breather)) + if(tank) + tank.forceMove(src) + if (breather.wear_mask == contained) + breather.remove_from_mob(contained) + contained.forceMove(src) + else + qdel(contained) + contained = new mask_type (src) + src.visible_message("\The [contained] slips to \the [src]!") + breather = null + update_icon() + return + if(valve_opened) + if (tank) + breather.internal = tank + breather.internals?.icon_state = "internal1" + else + breather.internals?.icon_state = "internal0" + breather.internal = null + else if (valve_opened) + var/datum/gas_mixture/removed = tank.remove_air(0.01) + var/datum/gas_mixture/environment = loc.return_air() + environment.merge(removed) + + //Reagent Stuff + if(attached) + if(!Adjacent(attached)) + visible_message("The needle is ripped out of [src.attached], doesn't that hurt?") + attached.apply_damage(3, BRUTE, pick(BP_R_ARM, BP_L_ARM)) + attached = null + update_icon() + + if(beaker) + if(mode) // Give blood + if(beaker.volume > 0) + beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_BLOOD) + update_icon() + else // Take blood + var/amount = beaker.reagents.maximum_volume - beaker.reagents.total_volume + amount = min(amount, 4) + + if(amount == 0) // If the beaker is full, ping + if(prob(5)) visible_message("\The [src] pings.") + return + + var/mob/living/carbon/human/H = attached + if(!istype(H)) + return + if(!H.dna) + return + if(NOCLONE in H.mutations) + return + if(H.species.flags & NO_BLOOD) + return + if(!H.should_have_organ(O_HEART)) + return + + // If the human is losing too much blood, beep. + if(((H.vessel.get_reagent_amount("blood")/H.species.blood_volume)*100) < BLOOD_VOLUME_SAFE) + visible_message("\The [src] beeps loudly.") + + var/datum/reagent/B = H.take_blood(beaker,amount) + if (B) + beaker.reagents.reagent_list |= B + beaker.reagents.update_total() + beaker.on_reagent_change() + beaker.reagents.handle_reactions() + update_icon() + + if ((!valve_opened || tank.distribute_pressure == 0) && !breather && !attached) + return PROCESS_KILL + +/obj/structure/medical_stand/anesthetic + spawn_type = /obj/item/weapon/tank/anesthetic + mask_type = /obj/item/clothing/mask/breath/medical + is_loosen = FALSE + diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm index c1180aa6db..17e594c4c3 100644 --- a/code/game/objects/structures/mop_bucket.dm +++ b/code/game/objects/structures/mop_bucket.dm @@ -18,7 +18,7 @@ GLOBAL_LIST_BOILERPLATE(all_mopbuckets, /obj/structure/mopbucket) /obj/structure/mopbucket/examine(mob/user) if(..(user, 1)) - to_chat(user, "[src] \icon[src] contains [reagents.total_volume] unit\s of water!") + to_chat(user, "[src] [bicon(src)] contains [reagents.total_volume] unit\s of water!") /obj/structure/mopbucket/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/weapon/mop) || istype(I, /obj/item/weapon/soap) || istype(I, /obj/item/weapon/reagent_containers/glass/rag)) //VOREStation Edit - "Allows soap and rags to be used on mopbuckets" diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index a74f789d37..2043ca2a11 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -219,7 +219,7 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) /obj/structure/morgue/crematorium/attack_hand(mob/user as mob) if (cremating) - usr << "It's locked." + to_chat(usr, "It's locked.") return if ((src.connected) && (src.locked == 0)) for(var/atom/movable/A as mob|obj in src.connected.loc) @@ -285,16 +285,16 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) if(contents.len <= 0) for (var/mob/M in viewers(src)) - to_chat(M,"You hear a hollow crackle.") + to_chat(M, "You hear a hollow crackle.") return else if(!isemptylist(src.search_contents_for(/obj/item/weapon/disk/nuclear))) - to_chat(user,"You get the feeling that you shouldn't cremate one of the items in the cremator.") + to_chat(user, "You get the feeling that you shouldn't cremate one of the items in the cremator.") return for (var/mob/M in viewers(src)) - to_chat(M,"You hear a roar as the crematorium activates.") + to_chat(M, "You hear a roar as the crematorium activates.") cremating = 1 locked = 1 @@ -349,4 +349,4 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) if (!C.cremating) C.cremate(user) else - to_chat(user,"Access denied.") + to_chat(user, "Access denied.") diff --git a/code/game/objects/structures/morgue_vr.dm b/code/game/objects/structures/morgue_vr.dm index eaf27b5ad0..2abe7098d1 100644 --- a/code/game/objects/structures/morgue_vr.dm +++ b/code/game/objects/structures/morgue_vr.dm @@ -15,7 +15,7 @@ return else if(!isemptylist(src.search_contents_for(/obj/item/weapon/disk/nuclear))) - usr << "You get the feeling that you shouldn't cremate one of the items in the cremator." + to_chat(usr, "You get the feeling that you shouldn't cremate one of the items in the cremator.") return for(var/I in contents) diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm index 2aa11fffa5..f3bfc92d8e 100644 --- a/code/game/objects/structures/noticeboard.dm +++ b/code/game/objects/structures/noticeboard.dm @@ -38,14 +38,14 @@ O.loc = src notices++ icon_state = "nboard0[notices]" //update sprite - user << "You pin the paper to the noticeboard." + to_chat(user, "You pin the paper to the noticeboard.") else - user << "You reach to pin your paper to the board but hesitate. You are certain your paper will not be seen among the many others already attached." + to_chat(user, "You reach to pin your paper to the board but hesitate. You are certain your paper will not be seen among the many others already attached.") if(O.is_wrench()) - user << "You start to unwrench the noticeboard." + to_chat(user, "You start to unwrench the noticeboard.") playsound(src.loc, O.usesound, 50, 1) if(do_after(user, 15 * O.toolspeed)) - user << "You unwrench the noticeboard." + to_chat(user, "You unwrench the noticeboard.") new /obj/item/frame/noticeboard( src.loc ) qdel(src) return @@ -92,7 +92,7 @@ add_fingerprint(M) P.attackby(E, usr) else - M << "You'll need something to write with!" + to_chat(M, "You'll need something to write with!") if(href_list["read"]) var/obj/item/weapon/paper/P = locate(href_list["read"]) if((P && P.loc == src)) diff --git a/code/game/objects/structures/props/alien_props_vr.dm b/code/game/objects/structures/props/alien_props_vr.dm new file mode 100644 index 0000000000..b306d610ba --- /dev/null +++ b/code/game/objects/structures/props/alien_props_vr.dm @@ -0,0 +1,13 @@ +/obj/structure/prop/alien/computer/hybrid + name = "hybrid console" + desc = "The console flashes what appear to be symbols you've never seen before, intermixed with human writing moving too fast to comprehend." + icon = 'icons/obj/abductor_vr.dmi' + icon_state = "console-c" + interaction_message = "The screen flashes an 'access denied' message at you. Perhaps for the best." + +/obj/structure/prop/alien/pod/hybrid + name = "hybrid pod" + desc = "This seems to be a container for something." + icon = 'icons/obj/abductor_vr.dmi' + icon_state = "experiment" + interaction_message = "You don't see any mechanism to open this thing. Probably for the best." \ No newline at end of file diff --git a/code/game/objects/structures/props/beam_prism.dm b/code/game/objects/structures/props/beam_prism.dm index f5c4c3d3c0..b0122ace47 100644 --- a/code/game/objects/structures/props/beam_prism.dm +++ b/code/game/objects/structures/props/beam_prism.dm @@ -197,16 +197,18 @@ P.rotate_auto(new_bearing) /obj/structure/prop/prismcontrol/Initialize() - ..() + . = ..() if(my_turrets.len) //Preset controls. for(var/obj/structure/prop/prism/P in my_turrets) P.remote_dial = src - return - spawn() - for(var/obj/structure/prop/prism/P in orange(src, world.view)) //Don't search a huge area. - if(P.dialID == dialID && !P.remote_dial && P.external_control_lock) - my_turrets |= P - P.remote_dial = src + else + . = INITIALIZE_HINT_LATELOAD + +/obj/structure/prop/prismcontrol/LateInitialize() + for(var/obj/structure/prop/prism/P in orange(src, world.view)) //Don't search a huge area. + if(P.dialID == dialID && !P.remote_dial && P.external_control_lock) + my_turrets |= P + P.remote_dial = src /obj/structure/prop/prismcontrol/Destroy() for(var/obj/structure/prop/prism/P in my_turrets) diff --git a/code/game/objects/structures/salvageable.dm b/code/game/objects/structures/salvageable.dm new file mode 100644 index 0000000000..2703a7eed6 --- /dev/null +++ b/code/game/objects/structures/salvageable.dm @@ -0,0 +1,366 @@ +/obj/structure/salvageable + name = "broken macninery" + desc = "Broken beyond repair, but looks like you can still salvage something from this if you had a prying implement." + icon = 'icons/obj/salvageable.dmi' + density = 1 + anchored = 1 + var/salvageable_parts = list() + +/obj/structure/salvageable/proc/dismantle() + new /obj/structure/frame (src.loc) + for(var/path in salvageable_parts) + if(prob(salvageable_parts[path])) + new path (loc) + return + +/obj/structure/salvageable/attackby(obj/item/I, mob/user) + if(I.is_crowbar()) + playsound(loc, I.usesound, 50, 1) + var/actual_time = I.toolspeed * 170 + user.visible_message( \ + "\The [user] begins salvaging from \the [src].", \ + "You start salvaging from \the [src].") + if(do_after(user, actual_time, target = src)) + user.visible_message( \ + "\The [user] has salvaged \the [src].", \ + "You salvage \the [src].") + dismantle() + qdel(src) + return TRUE + return ..() + +//Types themself, use them, but not the parent object + +/obj/structure/salvageable/machine + name = "broken machine" + icon_state = "machine1" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 80, + /obj/item/trash/material/circuit = 60, + /obj/item/trash/material/metal = 60, + /obj/item/weapon/stock_parts/capacitor = 40, + /obj/item/weapon/stock_parts/capacitor = 40, + /obj/item/weapon/stock_parts/scanning_module = 40, + /obj/item/weapon/stock_parts/scanning_module = 40, + /obj/item/weapon/stock_parts/manipulator = 40, + /obj/item/weapon/stock_parts/manipulator = 40, + /obj/item/weapon/stock_parts/micro_laser = 40, + /obj/item/weapon/stock_parts/micro_laser = 40, + /obj/item/weapon/stock_parts/matter_bin = 40, + /obj/item/weapon/stock_parts/matter_bin = 40, + /obj/item/weapon/stock_parts/capacitor/adv = 20, + /obj/item/weapon/stock_parts/scanning_module/adv = 20, + /obj/item/weapon/stock_parts/manipulator/nano = 20, + /obj/item/weapon/stock_parts/micro_laser/high = 20, + /obj/item/weapon/stock_parts/matter_bin/adv = 20 + ) + +/obj/structure/salvageable/machine/Initialize() + . = ..() + icon_state = "machine[rand(0,6)]" + +/obj/structure/salvageable/computer + name = "broken computer" + icon_state = "computer0" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/stack/material/glass{amount = 5} = 90, + /obj/item/trash/material/circuit = 60, + /obj/item/trash/material/metal = 60, + /obj/item/weapon/stock_parts/capacitor = 60, + /obj/item/weapon/stock_parts/capacitor = 60, + /obj/item/weapon/computer_hardware/network_card = 40, + /obj/item/weapon/computer_hardware/network_card = 40, + /obj/item/weapon/computer_hardware/processor_unit = 40, + /obj/item/weapon/computer_hardware/processor_unit = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/stock_parts/capacitor/adv = 30, + /obj/item/weapon/computer_hardware/network_card/advanced = 20 + ) +obj/structure/salvageable/computer/Initialize() + . = ..() + icon_state = "computer[rand(0,7)]" + +/obj/structure/salvageable/autolathe + name = "broken autolathe" + icon_state = "autolathe" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 80, + /obj/item/trash/material/circuit = 60, + /obj/item/trash/material/metal = 60, + /obj/item/weapon/stock_parts/capacitor = 40, + /obj/item/weapon/stock_parts/scanning_module = 40, + /obj/item/weapon/stock_parts/manipulator = 40, + /obj/item/weapon/stock_parts/micro_laser = 40, + /obj/item/weapon/stock_parts/micro_laser = 40, + /obj/item/weapon/stock_parts/micro_laser = 40, + /obj/item/weapon/stock_parts/matter_bin = 40, + /obj/item/weapon/stock_parts/matter_bin = 40, + /obj/item/weapon/stock_parts/matter_bin = 40, + /obj/item/weapon/stock_parts/matter_bin = 40, + /obj/item/weapon/stock_parts/capacitor/adv = 20, + /obj/item/weapon/stock_parts/micro_laser/high = 20, + /obj/item/weapon/stock_parts/micro_laser/high = 20, + /obj/item/weapon/stock_parts/matter_bin/adv = 20, + /obj/item/weapon/stock_parts/matter_bin/adv = 20, + /obj/item/stack/material/steel{amount = 20} = 40, + /obj/item/stack/material/glass{amount = 20} = 40, + /obj/item/stack/material/plastic{amount = 20} = 40, + /obj/item/stack/material/plasteel{amount = 10} = 40, + /obj/item/stack/material/silver{amount = 10} = 20, + /obj/item/stack/material/gold{amount = 10} = 20, + /obj/item/stack/material/phoron{amount = 10} = 20 + ) + +/obj/structure/salvageable/implant_container + name = "old container" + icon_state = "implant_container0" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 80, + /obj/item/trash/material/circuit = 60, + /obj/item/trash/material/metal = 60, + /obj/item/weapon/implant/death_alarm = 15, + /obj/item/weapon/implant/explosive = 10, + /obj/item/weapon/implant/freedom = 5, + /obj/item/weapon/implant/tracking = 10, + /obj/item/weapon/implant/chem = 10, + /obj/item/weapon/implantcase = 30, + /obj/item/weapon/implanter = 30, + /obj/item/stack/material/steel{amount = 10} = 30, + /obj/item/stack/material/glass{amount = 10} = 30, + /obj/item/stack/material/silver{amount = 10} = 30 + ) + +obj/structure/salvageable/implant_container/Initialize() + . = ..() + icon_state = "implant_container[rand(0,1)]" + +/obj/structure/salvageable/data + name = "broken data storage" + icon_state = "data0" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/stack/material/glass{amount = 5} = 90, + /obj/item/trash/material/circuit = 60, + /obj/item/trash/material/metal = 60, + /obj/item/weapon/computer_hardware/network_card = 40, + /obj/item/weapon/computer_hardware/network_card = 40, + /obj/item/weapon/computer_hardware/processor_unit = 40, + /obj/item/weapon/computer_hardware/processor_unit = 40, + /obj/item/weapon/computer_hardware/hard_drive = 50, + /obj/item/weapon/computer_hardware/hard_drive = 50, + /obj/item/weapon/computer_hardware/hard_drive = 50, + /obj/item/weapon/computer_hardware/hard_drive = 50, + /obj/item/weapon/computer_hardware/hard_drive = 50, + /obj/item/weapon/computer_hardware/hard_drive = 50, + /obj/item/weapon/computer_hardware/hard_drive/advanced = 30, + /obj/item/weapon/computer_hardware/hard_drive/advanced = 30, + /obj/item/weapon/computer_hardware/network_card/advanced = 20 + ) + +obj/structure/salvageable/data/Initialize() + . = ..() + icon_state = "data[rand(0,1)]" + +/obj/structure/salvageable/server + name = "broken server" + icon_state = "server0" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/stack/material/glass{amount = 5} = 90, + /obj/item/trash/material/circuit = 60, + /obj/item/trash/material/metal = 60, + /obj/item/weapon/computer_hardware/network_card = 40, + /obj/item/weapon/computer_hardware/network_card = 40, + /obj/item/weapon/computer_hardware/processor_unit = 40, + /obj/item/weapon/computer_hardware/processor_unit = 40, + /obj/item/weapon/stock_parts/subspace/amplifier = 40, + /obj/item/weapon/stock_parts/subspace/amplifier = 40, + /obj/item/weapon/stock_parts/subspace/analyzer = 40, + /obj/item/weapon/stock_parts/subspace/analyzer = 40, + /obj/item/weapon/stock_parts/subspace/ansible = 40, + /obj/item/weapon/stock_parts/subspace/ansible = 40, + /obj/item/weapon/stock_parts/subspace/transmitter = 40, + /obj/item/weapon/stock_parts/subspace/transmitter = 40, + /obj/item/weapon/stock_parts/subspace/crystal = 30, + /obj/item/weapon/stock_parts/subspace/crystal = 30, + /obj/item/weapon/computer_hardware/network_card/advanced = 20 + ) + +obj/structure/salvageable/server/Initialize() + . = ..() + icon_state = "server[rand(0,1)]" + +/obj/structure/salvageable/personal + name = "personal terminal" + icon_state = "personal0" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 90, + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/stack/material/glass{amount = 5} = 70, + /obj/item/trash/material/circuit = 60, + /obj/item/trash/material/metal = 60, + /obj/item/weapon/computer_hardware/network_card = 60, + /obj/item/weapon/computer_hardware/network_card/advanced = 40, + /obj/item/weapon/computer_hardware/network_card/wired = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/computer_hardware/processor_unit = 60, + /obj/item/weapon/computer_hardware/processor_unit/small = 50, + /obj/item/weapon/computer_hardware/processor_unit/photonic = 40, + /obj/item/weapon/computer_hardware/processor_unit/photonic/small = 30, + /obj/item/weapon/computer_hardware/hard_drive = 60, + /obj/item/weapon/computer_hardware/hard_drive/advanced = 40 + ) + +obj/structure/salvageable/personal/Initialize() + . = ..() + icon_state = "personal[rand(0,12)]" + new /obj/structure/table/reinforced (loc) + +/obj/structure/salvageable/bliss + name = "strange terminal" + icon_state = "bliss0" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 90, + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/weapon/computer_hardware/processor_unit/photonic = 60, + /obj/item/weapon/computer_hardware/hard_drive/cluster = 50 + ) + +obj/structure/salvageable/bliss/Initialize() + . = ..() + icon_state = "bliss[rand(0,1)]" + +/obj/structure/salvageable/bliss/attackby(obj/item/I, mob/user) + if((. = ..())) + playsound(user, 'sound/machines/shutdown.ogg', 60, 1) + +////////////////// +//// ONE STAR //// +////////////////// + +/obj/structure/salvageable/machine_os + name = "broken machine" + icon_state = "os-machine" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 80, + /obj/item/weapon/stock_parts/capacitor = 40, + /obj/item/weapon/stock_parts/capacitor = 40, + /obj/item/weapon/stock_parts/scanning_module = 40, + /obj/item/weapon/stock_parts/scanning_module = 40, + /obj/item/weapon/stock_parts/manipulator = 40, + /obj/item/weapon/stock_parts/manipulator = 40, + /obj/item/weapon/stock_parts/micro_laser = 40, + /obj/item/weapon/stock_parts/micro_laser = 40, + /obj/item/weapon/stock_parts/matter_bin = 40, + /obj/item/weapon/stock_parts/matter_bin = 40 + ) + +/obj/structure/salvageable/computer_os + name = "broken computer" + icon_state = "os-computer" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/stack/material/glass{amount = 5} = 90, + /obj/item/weapon/stock_parts/capacitor = 60, + /obj/item/weapon/stock_parts/capacitor = 60, + /obj/item/weapon/computer_hardware/processor_unit/photonic = 40, + /obj/item/weapon/computer_hardware/processor_unit/photonic = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/computer_hardware/network_card/advanced = 40 + ) + +/obj/structure/salvageable/implant_container_os + name = "old container" + icon_state = "os-container" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 80, + /obj/item/weapon/implant/death_alarm = 30, + /obj/item/weapon/implant/explosive = 20, + /obj/item/weapon/implant/freedom = 20, + /obj/item/weapon/implant/tracking = 30, + /obj/item/weapon/implant/chem = 30, + /obj/item/weapon/implantcase = 30, + /obj/item/weapon/implanter = 30 + ) + +/obj/structure/salvageable/data_os + name = "broken data storage" + icon_state = "os-data" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 90, + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/stack/material/glass{amount = 5} = 90, + /obj/item/weapon/computer_hardware/processor_unit/small = 60, + /obj/item/weapon/computer_hardware/processor_unit/photonic = 50, + /obj/item/weapon/computer_hardware/hard_drive/super = 50, + /obj/item/weapon/computer_hardware/hard_drive/super = 50, + /obj/item/weapon/computer_hardware/hard_drive/cluster = 50, + /obj/item/weapon/computer_hardware/network_card/wired = 40 + ) + +/obj/structure/salvageable/server_os + name = "broken server" + icon_state = "os-server" + salvageable_parts = list( + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/stack/material/glass{amount = 5} = 90, + /obj/item/weapon/computer_hardware/network_card/wired = 40, + /obj/item/weapon/computer_hardware/network_card/wired = 40, + /obj/item/weapon/computer_hardware/processor_unit = 40, + /obj/item/weapon/computer_hardware/processor_unit/photonic = 40, + /obj/item/weapon/stock_parts/subspace/amplifier = 40, + /obj/item/weapon/stock_parts/subspace/amplifier = 40, + /obj/item/weapon/stock_parts/subspace/analyzer = 40, + /obj/item/weapon/stock_parts/subspace/analyzer = 40, + /obj/item/weapon/stock_parts/subspace/ansible = 40, + /obj/item/weapon/stock_parts/subspace/ansible = 40, + /obj/item/weapon/stock_parts/subspace/transmitter = 40, + /obj/item/weapon/stock_parts/subspace/transmitter = 40, + /obj/item/weapon/stock_parts/subspace/crystal = 30, + /obj/item/weapon/stock_parts/subspace/crystal = 30, + /obj/item/weapon/computer_hardware/network_card/wired = 20 + ) + +/obj/structure/salvageable/console_os + name = "pristine console" + desc = "Despite being in pristine condition this console doesn't respond to anything, but looks like you can still salvage something from this." + icon_state = "os_console" + salvageable_parts = list( + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/weapon/stock_parts/capacitor = 60, + /obj/item/weapon/stock_parts/capacitor = 60, + /obj/item/weapon/computer_hardware/processor_unit/small = 40, + /obj/item/weapon/computer_hardware/processor_unit/photonic = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/computer_hardware/network_card/advanced = 40 + ) + +/obj/structure/salvageable/console_broken_os + name = "broken console" + icon_state = "os_console_broken" + salvageable_parts = list( + /obj/item/stack/cable_coil{amount = 5} = 90, + /obj/item/weapon/stock_parts/console_screen = 80, + /obj/item/weapon/stock_parts/capacitor = 60, + /obj/item/weapon/stock_parts/capacitor = 60, + /obj/item/weapon/computer_hardware/processor_unit = 40, + /obj/item/weapon/computer_hardware/processor_unit/photonic = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/computer_hardware/card_slot = 40, + /obj/item/weapon/computer_hardware/network_card/advanced = 40 + ) diff --git a/code/game/objects/structures/simple_doors_vr.dm b/code/game/objects/structures/simple_doors_vr.dm new file mode 100644 index 0000000000..7738674569 --- /dev/null +++ b/code/game/objects/structures/simple_doors_vr.dm @@ -0,0 +1,23 @@ +/material/flockium + name = "flockium" + //stack_type = /obj/item/stack/material/sandstone + icon_base = "flock" + icon_reinf = "flock" + icon_colour = "#FFFFFF" + //shard_type = SHARD_STONE_PIECE + weight = 30 + hardness = 200 + protectiveness = 5 // 20% + conductive = 0 + conductivity = 0 + door_icon_base = "flockdoor" + sheet_singular_name = "quanta" + sheet_plural_name = "quanta" + +/obj/structure/simple_door/flock + name = "aperture" + icon = 'icons/goonstation/featherzone.dmi' + icon_state = "flockdoor" + +/obj/structure/simple_door/flock/New(var/newloc, var/newmat) + ..(newloc, "flockium") \ No newline at end of file diff --git a/code/game/objects/structures/snowman.dm b/code/game/objects/structures/snowman.dm index b3947621b9..f3041e0287 100644 --- a/code/game/objects/structures/snowman.dm +++ b/code/game/objects/structures/snowman.dm @@ -7,7 +7,7 @@ /obj/structure/snowman/attack_hand(mob/user as mob) if(user.a_intent == I_HURT) - user << "In one hit, [src] easily crumples into a pile of snow. You monster." + to_chat(user, "In one hit, [src] easily crumples into a pile of snow. You monster.") var/turf/simulated/floor/F = get_turf(src) if (istype(F)) new /obj/item/stack/material/snow(F) diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm index 72edab89e3..1a9d4e7315 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm @@ -42,16 +42,15 @@ /obj/structure/bed/update_icon() // Prep icon. icon_state = "" - overlays.Cut() + cut_overlays() // Base icon. var/cache_key = "[base_icon]-[material.name]" if(isnull(stool_cache[cache_key])) - var/image/I = image(icon, base_icon) //VOREStation Edit - //var/image/I = image('icons/obj/furniture.dmi', base_icon) //From Polaris Sync. Not sure if this is a better way of doing it or not. Uncomment if so. + var/image/I = image(icon, base_icon) if(applies_material_colour) //VOREStation Add - Goes with added var I.color = material.icon_colour stool_cache[cache_key] = I - overlays |= stool_cache[cache_key] + add_overlay(stool_cache[cache_key]) // Padding overlay. if(padding_material) var/padding_cache_key = "[base_icon]-padding-[padding_material.name]" @@ -59,7 +58,7 @@ var/image/I = image(icon, "[base_icon]_padding") I.color = padding_material.icon_colour stool_cache[padding_cache_key] = I - overlays |= stool_cache[padding_cache_key] + add_overlay(stool_cache[padding_cache_key]) // Strings. desc = initial(desc) if(padding_material) @@ -232,6 +231,7 @@ desc = "A collapsed roller bed that can be carried around." icon = 'icons/obj/rollerbed.dmi' icon_state = "folded_rollerbed" + center_of_mass = list("x" = 17,"y" = 7) slot_flags = SLOT_BACK w_class = ITEMSIZE_LARGE var/rollertype = /obj/item/roller @@ -288,6 +288,7 @@ /obj/structure/bed/roller/Move() ..() + playsound(src, 'sound/effects/roll.ogg', 100, 1) if(has_buckled_mobs()) for(var/A in buckled_mobs) var/mob/living/L = A @@ -347,4 +348,4 @@ return // Doesn't care about material or anything else. /obj/structure/bed/alien/attackby(obj/item/weapon/W, mob/user) - return // No deconning. \ No newline at end of file + return // No deconning. diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index 8950533b8e..5be1d6d198 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -9,11 +9,9 @@ buckle_lying = 0 //force people to sit up in chairs when buckled var/propelled = 0 // Check for fire-extinguisher-driven chairs -/obj/structure/bed/chair/New() - ..() //Todo make metal/stone chairs display as thrones - spawn(3) //sorry. i don't think there's a better way to do this. - update_layer() - return +/obj/structure/bed/chair/Initialize() + . = ..() + update_layer() /obj/structure/bed/chair/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() @@ -43,15 +41,16 @@ /obj/structure/bed/chair/update_icon() ..() - if(has_buckled_mobs() && padding_material) - var/cache_key = "[base_icon]-armrest-[padding_material.name]" + if(has_buckled_mobs()) + var/cache_key = "[base_icon]-armrest-[padding_material ? padding_material.name : "no_material"]" if(isnull(stool_cache[cache_key])) var/image/I = image(icon, "[base_icon]_armrest") - I.layer = MOB_LAYER + 0.1 I.plane = MOB_PLANE - I.color = padding_material.icon_colour + I.layer = ABOVE_MOB_LAYER + if(padding_material) + I.color = padding_material.icon_colour stool_cache[cache_key] = I - overlays |= stool_cache[cache_key] + add_overlay(stool_cache[cache_key]) /obj/structure/bed/chair/proc/update_layer() if(src.dir == NORTH) @@ -85,15 +84,16 @@ /obj/structure/bed/chair/shuttle name = "chair" desc = "You sit in this. Either by will or force." - icon_state = "shuttle_chair" + icon_state = "shuttlechair" color = null - base_icon = "shuttle_chair" + base_icon = "shuttlechair" applies_material_colour = 0 // Leaving this in for the sake of compilation. /obj/structure/bed/chair/comfy desc = "It's a chair. It looks comfy." - icon_state = "comfychair_preview" + icon_state = "comfychair" + base_icon = "comfychair" /obj/structure/bed/chair/comfy/brown/New(var/newloc,var/newmaterial) ..(newloc,"steel","leather") @@ -122,6 +122,12 @@ /obj/structure/bed/chair/comfy/lime/New(var/newloc,var/newmaterial) ..(newloc,"steel","lime") +/obj/structure/bed/chair/comfy/yellow/New(var/newloc,var/newmaterial) + ..(newloc,"steel","yellow") + +/obj/structure/bed/chair/comfy/orange/New(var/newloc,var/newmaterial) + ..(newloc,"steel","orange") + /obj/structure/bed/chair/office anchored = 0 buckle_movable = 1 @@ -136,6 +142,7 @@ /obj/structure/bed/chair/office/Move() ..() + playsound(src, 'sound/effects/roll.ogg', 100, 1) if(has_buckled_mobs()) for(var/A in buckled_mobs) var/mob/living/occupant = A @@ -203,3 +210,172 @@ /obj/structure/bed/chair/wood/wings icon_state = "wooden_chair_wings" + +//sofa + +/obj/structure/bed/chair/sofa + name = "sofa" + desc = "It's a sofa. You sit on it. Possibly with someone else." + icon = 'icons/obj/sofas.dmi' + base_icon = "sofamiddle" + icon_state = "sofamiddle" + applies_material_colour = 1 + var/sofa_material = "carpet" + +/obj/structure/bed/chair/sofa/update_icon() + if(applies_material_colour && sofa_material) + var/material/color_material = get_material_by_name(sofa_material) + color = color_material.icon_colour + + if(sofa_material == "carpet") + name = "red [initial(name)]" + else + name = "[sofa_material] [initial(name)]" + +/obj/structure/bed/chair/sofa/left + icon_state = "sofaend_left" + base_icon = "sofaend_left" + +/obj/structure/bed/chair/sofa/right + icon_state = "sofaend_right" + base_icon = "sofaend_right" + +/obj/structure/bed/chair/sofa/corner + icon_state = "sofacorner" + base_icon = "sofacorner" + +//color variations + +/obj/structure/bed/chair/sofa + sofa_material = "carpet" + +/obj/structure/bed/chair/sofa/brown + sofa_material = "leather" + +/obj/structure/bed/chair/sofa/teal + sofa_material = "teal" + +/obj/structure/bed/chair/sofa/black + sofa_material = "black" + +/obj/structure/bed/chair/sofa/green + sofa_material = "green" + +/obj/structure/bed/chair/sofa/purp + sofa_material = "purple" + +/obj/structure/bed/chair/sofa/blue + sofa_material = "blue" + +/obj/structure/bed/chair/sofa/beige + sofa_material = "beige" + +/obj/structure/bed/chair/sofa/lime + sofa_material = "lime" + +/obj/structure/bed/chair/sofa/yellow + sofa_material = "yellow" + +/obj/structure/bed/chair/sofa/orange + sofa_material = "orange" + +//sofa directions + +/obj/structure/bed/chair/sofa/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/brown/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/brown/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/brown/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/teal/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/teal/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/teal/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/black/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/black/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/black/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/green/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/green/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/green/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/purp/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/purp/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/purp/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/blue/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/blue/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/blue/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/beige/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/beige/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/beige/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/lime/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/lime/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/lime/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/yellow/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/yellow/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/yellow/corner + icon_state = "sofacorner" + +/obj/structure/bed/chair/sofa/orange/left + icon_state = "sofaend_left" + +/obj/structure/bed/chair/sofa/orange/right + icon_state = "sofaend_right" + +/obj/structure/bed/chair/sofa/orange/corner + icon_state = "sofacorner" diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs_vr.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs_vr.dm index e82ef467bb..79b970e1cf 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs_vr.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs_vr.dm @@ -1,20 +1,245 @@ -/obj/structure/bed/chair/sofa - name = "sofa" - desc = "A padded, comfy sofa. Great for lazing on." - base_icon = "sofamiddle" +/obj/structure/bed/chair/modern_chair + name = "modern chair" + desc = "It's like sitting in an egg." + icon_state = "modern_chair" + color = null + base_icon = "modern_chair" + applies_material_colour = 0 -/obj/structure/bed/chair/sofa/left - base_icon = "sofaend_left" +/obj/structure/bed/chair/modern_chair/Initialize() + . = ..() + var/image/I = image(icon, "[base_icon]_over") + I.layer = ABOVE_MOB_LAYER + I.plane = MOB_PLANE + add_overlay(I) -/obj/structure/bed/chair/sofa/right - base_icon = "sofaend_right" +/obj/structure/bed/chair/bar_stool + name = "bar stool" + desc = "How vibrant!" + icon_state = "modern_stool" + color = null + base_icon = "modern_stool" + applies_material_colour = 0 -/obj/structure/bed/chair/sofa/corner - base_icon = "sofacorner" +/obj/structure/bed/chair/backed_grey + name = "grey chair" + desc = "Also available in red." + icon_state = "onestar_chair_grey" + color = null + base_icon = "onestar_chair_grey" + applies_material_colour = 0 -/obj/structure/bed/chair/sofa/corner/update_layer() - if(src.dir == NORTH || src.dir == WEST) - plane = MOB_PLANE - layer = MOB_LAYER + 0.1 +/obj/structure/bed/chair/backed_red + name = "red chair" + desc = "Also available in grey." + icon_state = "onestar_chair_red" + color = null + base_icon = "onestar_chair_red" + applies_material_colour = 0 + +// Baystation12 chairs with their larger update_icons proc +/obj/structure/bed/chair/bay/update_icon() + // Strings. + desc = initial(desc) + if(padding_material) + name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now. + desc += " It's made of [material.use_name] and covered with [padding_material.use_name]." else - reset_plane_and_layer() \ No newline at end of file + name = "[material.display_name] [initial(name)]" + desc += " It's made of [material.use_name]." + + // Prep icon. + icon_state = "" + cut_overlays() + + // Base icon (base material color) + var/cache_key = "[base_icon]-[material.name]" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, base_icon) + if(applies_material_colour) + I.color = material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + + // Padding ('_padding') (padding material color) + if(padding_material) + var/padding_cache_key = "[base_icon]-padding-[padding_material.name]" + if(isnull(stool_cache[padding_cache_key])) + var/image/I = image(icon, "[base_icon]_padding") + I.color = padding_material.icon_colour + stool_cache[padding_cache_key] = I + add_overlay(stool_cache[padding_cache_key]) + + // Over ('_over') (base material color) + cache_key = "[base_icon]-[material.name]-over" + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, "[base_icon]_over") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + if(applies_material_colour) + I.color = material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + + // Padding Over ('_padding_over') (padding material color) + if(padding_material) + var/padding_cache_key = "[base_icon]-padding-[padding_material.name]-over" + if(isnull(stool_cache[padding_cache_key])) + var/image/I = image(icon, "[base_icon]_padding_over") + I.color = padding_material.icon_colour + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + stool_cache[padding_cache_key] = I + add_overlay(stool_cache[padding_cache_key]) + + if(has_buckled_mobs()) + if(padding_material) + cache_key = "[base_icon]-armrest-[padding_material.name]" + // Armrest ('_armrest') (base material color) + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, "[base_icon]_armrest") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + if(applies_material_colour) + I.color = material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + if(padding_material) + cache_key = "[base_icon]-padding-armrest-[padding_material.name]" + // Padding Armrest ('_padding_armrest') (padding material color) + if(isnull(stool_cache[cache_key])) + var/image/I = image(icon, "[base_icon]_padding_armrest") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + I.color = padding_material.icon_colour + stool_cache[cache_key] = I + add_overlay(stool_cache[cache_key]) + +/obj/structure/bed/chair/bay/chair + name = "mounted chair" + desc = "Like a normal chair, but more stationary." + icon_state = "bay_chair_preview" + base_icon = "bay_chair" + +/obj/structure/bed/chair/bay/chair/padded/red/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "carpet") + +/obj/structure/bed/chair/bay/chair/padded/brown/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "leather") + +/obj/structure/bed/chair/bay/chair/padded/teal/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "teal") + +/obj/structure/bed/chair/bay/chair/padded/black/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "black") + +/obj/structure/bed/chair/bay/chair/padded/green/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "green") + +/obj/structure/bed/chair/bay/chair/padded/purple/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "purple") + +/obj/structure/bed/chair/bay/chair/padded/blue/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "blue") + +/obj/structure/bed/chair/bay/chair/padded/beige/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "beige") + +/obj/structure/bed/chair/bay/chair/padded/lime/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "lime") + +/obj/structure/bed/chair/bay/chair/padded/yellow/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "yellow") + +/obj/structure/bed/chair/bay/comfy + name = "comfy mounted chair" + desc = "Like a normal chair, but more stationary, and with more padding." + icon_state = "bay_comfychair_preview" + base_icon = "bay_comfychair" + +/obj/structure/bed/chair/bay/comfy/red/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "carpet") + +/obj/structure/bed/chair/bay/comfy/brown/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "leather") + +/obj/structure/bed/chair/bay/comfy/teal/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "teal") + +/obj/structure/bed/chair/bay/comfy/black/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "black") + +/obj/structure/bed/chair/bay/comfy/green/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "green") + +/obj/structure/bed/chair/bay/comfy/purple/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "purple") + +/obj/structure/bed/chair/bay/comfy/blue/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "blue") + +/obj/structure/bed/chair/bay/comfy/beige/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "beige") + +/obj/structure/bed/chair/bay/comfy/lime/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "lime") + +/obj/structure/bed/chair/bay/comfy/yellow/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, new_material, "yellow") + +/obj/structure/bed/chair/bay/comfy/captain + name = "captain chair" + desc = "It's a chair. Only for the highest ranked asses." + icon_state = "capchair_preview" + base_icon = "capchair" + buckle_movable = 1 + +/obj/structure/bed/chair/bay/comfy/captain/update_icon() + ..() + var/image/I = image(icon, "[base_icon]_special") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + add_overlay(I) + +/obj/structure/bed/chair/bay/comfy/captain/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, DEFAULT_WALL_MATERIAL, "blue") + +/obj/structure/bed/chair/bay/shuttle + name = "shuttle seat" + desc = "A comfortable, secure seat. It has a sturdy-looking buckling system for smoother flights." + base_icon = "shuttle_chair" + icon_state = "shuttle_chair_preview" + var/buckling_sound = 'sound/effects/metal_close.ogg' + var/padding = "blue" + +/obj/structure/bed/chair/bay/shuttle/New(var/newloc, var/new_material, var/new_padding_material) + ..(newloc, DEFAULT_WALL_MATERIAL, padding) + +/obj/structure/bed/chair/bay/shuttle/post_buckle_mob() + playsound(loc,buckling_sound,75,1) + if(has_buckled_mobs()) + base_icon = "shuttle_chair-b" + else + base_icon = "shuttle_chair" + ..() + +/obj/structure/bed/chair/bay/shuttle/update_icon() + ..() + if(!has_buckled_mobs()) + var/image/I = image(icon, "[base_icon]_special") + I.plane = MOB_PLANE + I.layer = ABOVE_MOB_LAYER + if(applies_material_colour) + I.color = material.icon_colour + add_overlay(I) + +/obj/structure/bed/chair/bay/chair/padded/red/smallnest + name = "teshari nest" + desc = "Smells like cleaning products." + icon_state = "nest_chair" + base_icon = "nest_chair" + +/obj/structure/bed/chair/bay/chair/padded/red/bignest + name = "large teshari nest" + icon_state = "nest_chair_large" + base_icon = "nest_chair_large" diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools.dm b/code/game/objects/structures/stool_bed_chair_nest/stools.dm index d86c9d4606..525b9883d5 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/stools.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/stools.dm @@ -6,6 +6,8 @@ var/global/list/stool_cache = list() //haha stool desc = "Apply butt." icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - new Icons icon_state = "stool_preview" //set for the map + randpixel = 0 + center_of_mass = null force = 10 throwforce = 10 w_class = ITEMSIZE_HUGE @@ -35,22 +37,22 @@ var/global/list/stool_cache = list() //haha stool /obj/item/weapon/stool/update_icon() // Prep icon. icon_state = "" - overlays.Cut() + cut_overlays() // Base icon. var/cache_key = "stool-[material.name]" if(isnull(stool_cache[cache_key])) var/image/I = image(icon, base_icon) I.color = material.icon_colour stool_cache[cache_key] = I - overlays |= stool_cache[cache_key] + add_overlay(stool_cache[cache_key]) // Padding overlay. if(padding_material) var/padding_cache_key = "stool-padding-[padding_material.name]" if(isnull(stool_cache[padding_cache_key])) - var/image/I = image(icon, "stool_padding") + var/image/I = image(icon, "[base_icon]_padding") //VOREStation Edit I.color = padding_material.icon_colour stool_cache[padding_cache_key] = I - overlays |= stool_cache[padding_cache_key] + add_overlay(stool_cache[padding_cache_key]) // Strings. if(padding_material) name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now. @@ -114,7 +116,7 @@ var/global/list/stool_cache = list() //haha stool qdel(src) else if(istype(W,/obj/item/stack)) if(padding_material) - user << "\The [src] is already padded." + to_chat(user, "\The [src] is already padded.") return var/obj/item/stack/C = W if(C.get_amount() < 1) // How?? @@ -129,20 +131,20 @@ var/global/list/stool_cache = list() //haha stool if(M.material && (M.material.flags & MATERIAL_PADDING)) padding_type = "[M.material.name]" if(!padding_type) - user << "You cannot pad \the [src] with that." + to_chat(user, "You cannot pad \the [src] with that.") return C.use(1) if(!istype(src.loc, /turf)) user.drop_from_inventory(src) src.loc = get_turf(src) - user << "You add padding to \the [src]." + to_chat(user, "You add padding to \the [src].") add_padding(padding_type) return else if (W.is_wirecutter()) if(!padding_material) - user << "\The [src] has no padding to remove." + to_chat(user, "\The [src] has no padding to remove.") return - user << "You remove the padding from \the [src]." + to_chat(user, "You remove the padding from \the [src].") playsound(src.loc, W.usesound, 50, 1) remove_padding() else diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools_vr.dm b/code/game/objects/structures/stool_bed_chair_nest/stools_vr.dm new file mode 100644 index 0000000000..f1f0b35a19 --- /dev/null +++ b/code/game/objects/structures/stool_bed_chair_nest/stools_vr.dm @@ -0,0 +1,15 @@ +/obj/item/weapon/stool/baystool + name = "bar stool" + desc = "Apply butt." + icon = 'icons/obj/furniture_vr.dmi' //VOREStation Edit - new Icons + icon_state = "bar_stool_preview" //set for the map + randpixel = 0 + center_of_mass = null + force = 10 + throwforce = 10 + w_class = ITEMSIZE_HUGE + base_icon = "bar_stool_base" + anchored = 1 + +/obj/item/weapon/stool/baystool/padded/New(var/newloc, var/new_material) + ..(newloc, "steel", "carpet") diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index e5eba3f20a..8a3902fa1d 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -14,9 +14,9 @@ /obj/structure/bed/chair/wheelchair/set_dir() ..() - overlays = null + cut_overlays() var/image/O = image(icon = 'icons/obj/furniture.dmi', icon_state = "w_overlay", layer = FLY_LAYER, dir = src.dir) - overlays += O + add_overlay(O) if(has_buckled_mobs()) for(var/A in buckled_mobs) var/mob/living/L = A @@ -33,7 +33,7 @@ if(user==pulling) pulling = null user.pulledby = null - user << "You lost your grip!" + to_chat(user, "You lost your grip!") return if(has_buckled_mobs() && pulling && user in buckled_mobs) if(pulling.stat || pulling.stunned || pulling.weakened || pulling.paralysis || pulling.lying || pulling.restrained()) @@ -51,10 +51,10 @@ if(user==pulling) return if(pulling && (get_dir(src.loc, pulling.loc) == direction)) - user << "You cannot go there." + to_chat(user, "You cannot go there.") return if(pulling && has_buckled_mobs() && (user in buckled_mobs)) - user << "You cannot drive while being pushed." + to_chat(user, "You cannot drive while being pushed.") return // Let's roll @@ -93,6 +93,8 @@ /obj/structure/bed/chair/wheelchair/Move() ..() + cut_overlays() + playsound(src, 'sound/effects/roll.ogg', 75, 1) if(has_buckled_mobs()) for(var/A in buckled_mobs) var/mob/living/occupant = A @@ -109,7 +111,7 @@ unbuckle_mob() if (pulling && (get_dist(src, pulling) > 1)) pulling.pulledby = null - pulling << "You lost your grip!" + to_chat(pulling, "You lost your grip!") pulling = null else if (occupant && (src.loc != occupant.loc)) @@ -128,7 +130,7 @@ if(in_range(src, user)) if(!ishuman(user)) return if(has_buckled_mobs() && user in buckled_mobs) - user << "You realize you are unable to push the wheelchair you sit in." + to_chat(user, "You realize you are unable to push the wheelchair you sit in.") return if(!pulling) pulling = user @@ -136,9 +138,9 @@ if(user.pulling) user.stop_pulling() user.set_dir(get_dir(user, src)) - user << "You grip \the [name]'s handles." + to_chat(user, "You grip \the [name]'s handles.") else - usr << "You let go of \the [name]'s handles." + to_chat(usr, "You let go of \the [name]'s handles.") pulling.pulledby = null pulling = null return diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index d9d31dfb55..e994e1b760 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -54,11 +54,11 @@ I.loc = src oxytanks.Add(I) oxygentanks++ - user << "You put [I] in [src]." + to_chat(user, "You put [I] in [src].") if(oxygentanks < 5) update_icon() else - user << "[src] is full." + to_chat(user, "[src] is full.") updateUsrDialog() return if(istype(I, /obj/item/weapon/tank/phoron)) @@ -67,19 +67,19 @@ I.loc = src platanks.Add(I) phorontanks++ - user << "You put [I] in [src]." + to_chat(user, "You put [I] in [src].") if(oxygentanks < 6) update_icon() else - user << "[src] is full." + to_chat(user, "[src] is full.") updateUsrDialog() return if(I.is_wrench()) if(anchored) - user << "You lean down and unwrench [src]." + to_chat(user, "You lean down and unwrench [src].") anchored = 0 else - user << "You wrench [src] into place." + to_chat(user, "You wrench [src] into place.") anchored = 1 return @@ -97,7 +97,7 @@ else O = new /obj/item/weapon/tank/oxygen(loc) O.loc = loc - usr << "You take [O] out of [src]." + to_chat(usr, "You take [O] out of [src].") oxygentanks-- update_icon() if(href_list["phoron"]) @@ -109,7 +109,7 @@ else P = new /obj/item/weapon/tank/phoron(loc) P.loc = loc - usr << "You take [P] out of [src]." + to_chat(usr, "You take [P] out of [src].") phorontanks-- update_icon() add_fingerprint(usr) diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index fae45b8f51..55d8b37d0f 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -30,7 +30,7 @@ W.loc = loc W.layer = ABOVE_JUNK_LAYER pinned_target = W - user << "You slide the target into the stake." + to_chat(user, "You slide the target into the stake.") return attack_hand(mob/user as mob) @@ -44,9 +44,9 @@ if(ishuman(user)) if(!user.get_active_hand()) user.put_in_hands(pinned_target) - user << "You take the target out of the stake." + to_chat(user, "You take the target out of the stake.") else pinned_target.loc = get_turf(user) - user << "You take the target out of the stake." + to_chat(user, "You take the target out of the stake.") pinned_target = null diff --git a/code/game/objects/structures/transit_tubes.dm b/code/game/objects/structures/transit_tubes.dm index c260103a56..32fa0724a4 100644 --- a/code/game/objects/structures/transit_tubes.dm +++ b/code/game/objects/structures/transit_tubes.dm @@ -101,11 +101,11 @@ obj/structure/ex_act(severity) /obj/structure/transit_tube/Bumped(mob/AM as mob|obj) var/obj/structure/transit_tube/T = locate() in AM.loc if(T) - AM << "The tube's support pylons block your way." + to_chat(AM, "The tube's support pylons block your way.") return ..() else AM.loc = src.loc - AM << "You slip under the tube." + to_chat(AM, "You slip under the tube.") /obj/structure/transit_tube/station/New(loc) @@ -117,7 +117,7 @@ obj/structure/ex_act(severity) if(!pod_moving && icon_state == "open" && istype(AM, /mob)) for(var/obj/structure/transit_tube_pod/pod in loc) if(pod.contents.len) - AM << "The pod is already occupied." + to_chat(AM, "The pod is already occupied.") return else if(!pod.moving && pod.dir in directions()) AM.loc = pod diff --git a/code/game/objects/structures/trash_pile.dm b/code/game/objects/structures/trash_pile_vr.dm similarity index 99% rename from code/game/objects/structures/trash_pile.dm rename to code/game/objects/structures/trash_pile_vr.dm index 36a1d9a285..bf43f36e5e 100644 --- a/code/game/objects/structures/trash_pile.dm +++ b/code/game/objects/structures/trash_pile_vr.dm @@ -220,7 +220,7 @@ return I /obj/structure/trash_pile/proc/produce_beta_item() - var/path = pick(prob(6);/obj/item/weapon/storage/pill_bottle/tramadol, + var/path = pick(prob(6);/obj/item/weapon/storage/pill_bottle/paracetamol, prob(4);/obj/item/weapon/storage/pill_bottle/happy, prob(4);/obj/item/weapon/storage/pill_bottle/zoom, prob(4);/obj/item/seeds/ambrosiavulgarisseed, diff --git a/code/game/objects/structures/under_wardrobe.dm b/code/game/objects/structures/under_wardrobe.dm index d26b421ab5..cb5db5411d 100644 --- a/code/game/objects/structures/under_wardrobe.dm +++ b/code/game/objects/structures/under_wardrobe.dm @@ -7,7 +7,7 @@ /obj/structure/undies_wardrobe/attack_hand(var/mob/user) if(!human_who_can_use_underwear(user)) - user << "Sadly there's nothing in here for you to wear." + to_chat(user, "Sadly there's nothing in here for you to wear.") return interact(user) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 464cf08f7d..467579a225 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -124,7 +124,7 @@ icon_state = "shower" density = 0 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF var/on = 0 var/obj/effect/mist/mymist = null var/ismist = 0 //needs a var so we can make it linger~ @@ -384,6 +384,7 @@ return to_chat(usr, "You start washing your hands.") + playsound(loc, 'sound/effects/sink_long.ogg', 75, 1) busy = 1 sleep(40) @@ -406,6 +407,7 @@ if (istype(RG) && RG.is_open_container()) RG.reagents.add_reagent("water", min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this)) user.visible_message("[user] fills \the [RG] using \the [src].","You fill \the [RG] using \the [src].") + playsound(loc, 'sound/effects/sink.ogg', 75, 1) return 1 else if (istype(O, /obj/item/weapon/melee/baton)) diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index c4facdaa20..353df5ecb8 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -192,7 +192,7 @@ obj/structure/windoor_assembly/Destroy() if(src.electronics && istype(src.electronics, /obj/item/weapon/circuitboard/broken)) to_chat(usr,"The assembly has broken airlock electronics.") return - to_chat(usr,browse(null, "window=windoor_access")) //Not sure what this actually does... -Ner + usr << browse(null, "window=windoor_access") //Not sure what this actually does... -Ner playsound(src, W.usesound, 100, 1) user.visible_message("[user] pries the windoor into the frame.", "You start prying the windoor into the frame.") diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index b2867d5ead..d8542d0c96 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -442,6 +442,13 @@ //this way it will only update full-tile ones overlays.Cut() if(!is_fulltile()) + // Rotate the sprite somewhat so non-fulltiled windows can be seen as needing repair. + var/full_tilt_degrees = 15 + var/tilt_to_apply = abs((health / maxhealth) - 1) + if(tilt_to_apply && prob(50)) + tilt_to_apply = -tilt_to_apply + adjust_rotation(LERP(0, full_tilt_degrees, tilt_to_apply)) + icon_state = "[basestate]" return var/list/dirs = list() diff --git a/code/game/response_team.dm b/code/game/response_team.dm index 952a3b64dd..2b3ae97ae2 100644 --- a/code/game/response_team.dm +++ b/code/game/response_team.dm @@ -13,16 +13,16 @@ var/silent_ert = 0 set desc = "Send an emergency response team to the station" if(!holder) - usr << "Only administrators may use this command." + to_chat(usr, "Only administrators may use this command.") return if(!ticker) - usr << "The game hasn't started yet!" + to_chat(usr, "The game hasn't started yet!") return if(ticker.current_state == 1) - usr << "The round hasn't started yet!" + to_chat(usr, "The round hasn't started yet!") return if(send_emergency_team) - usr << "[using_map.boss_name] has already dispatched an emergency response team!" + to_chat(usr, "[using_map.boss_name] has already dispatched an emergency response team!") return if(alert("Do you want to dispatch an Emergency Response Team?",,"Yes","No") != "Yes") return @@ -33,7 +33,7 @@ var/silent_ert = 0 if("No") return if(send_emergency_team) - usr << "Looks like somebody beat you to it!" + to_chat(usr, "Looks like somebody beat you to it!") return message_admins("[key_name_admin(usr)] is dispatching an Emergency Response Team.", 1) @@ -46,22 +46,22 @@ client/verb/JoinResponseTeam() set category = "IC" if(!MayRespawn(1)) - usr << "You cannot join the response team at this time." + to_chat(usr, "You cannot join the response team at this time.") return if(istype(usr,/mob/observer/dead) || istype(usr,/mob/new_player)) if(!send_emergency_team) - usr << "No emergency response team is currently being sent." + to_chat(usr, "No emergency response team is currently being sent.") return if(jobban_isbanned(usr, "Syndicate") || jobban_isbanned(usr, "Emergency Response Team") || jobban_isbanned(usr, "Security Officer")) - usr << "You are jobbanned from the emergency reponse team!" + to_chat(usr, "You are jobbanned from the emergency reponse team!") return if(ert.current_antagonists.len >= ert.hard_cap) - usr << "The emergency response team is already full!" + to_chat(usr, "The emergency response team is already full!") return ert.create_default(usr) else - usr << "You need to be an observer or new player to use this." + to_chat(usr, "You need to be an observer or new player to use this.") // returns a number of dead players in % proc/percentage_dead() @@ -131,6 +131,7 @@ proc/trigger_armed_response_team(var/force = 0) can_call_ert = 0 // Only one call per round, gentleman. send_emergency_team = 1 + consider_ert_load() //VOREStation Add sleep(600 * 5) send_emergency_team = 0 // Can no longer join the ERT. diff --git a/code/game/response_team_vr.dm b/code/game/response_team_vr.dm new file mode 100644 index 0000000000..5b9c93ed6c --- /dev/null +++ b/code/game/response_team_vr.dm @@ -0,0 +1,11 @@ +GLOBAL_VAR(ert_loaded) + +/proc/consider_ert_load() + if(!GLOB.ert_loaded) + GLOB.ert_loaded = TRUE + var/datum/map_template/MT = SSmapping.map_templates["Special Area - ERT"] + if(!istype(MT)) + error("ERT Area is not a valid map template!") + else + MT.load_new_z(centered = TRUE) + log_and_message_admins("Loaded the ERT shuttle just now.") \ No newline at end of file diff --git a/code/game/sound.dm b/code/game/sound.dm index cd3b140bd9..98e24a1f34 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -137,6 +137,82 @@ if ("button") soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') if ("switch") soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') if ("casing_sound") soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') + if ("pickaxe") soundin = pick('sound/weapons/mine/pickaxe1.ogg', 'sound/weapons/mine/pickaxe2.ogg','sound/weapons/mine/pickaxe3.ogg','sound/weapons/mine/pickaxe4.ogg') + if("shatter") + soundin = pick('sound/effects/Glassbr1.ogg','sound/effects/Glassbr2.ogg','sound/effects/Glassbr3.ogg') + if("explosion") + soundin = pick( + 'sound/effects/Explosion1.ogg', + 'sound/effects/Explosion2.ogg', + 'sound/effects/Explosion3.ogg', + 'sound/effects/Explosion4.ogg', + 'sound/effects/Explosion5.ogg', + 'sound/effects/Explosion6.ogg') + if("sparks") + soundin = pick( + 'sound/effects/sparks1.ogg', + 'sound/effects/sparks2.ogg', + 'sound/effects/sparks3.ogg', + 'sound/effects/sparks5.ogg', + 'sound/effects/sparks6.ogg', + 'sound/effects/sparks7.ogg') + if("rustle") + soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') + if("punch") + soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') + if("clownstep") + soundin = pick('sound/effects/clownstep1.ogg','sound/effects/clownstep2.ogg') + if("swing_hit") + soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') + if("hiss") + soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') + if("pageturn") + soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') + if("fracture") + soundin = pick('sound/effects/bonebreak1.ogg','sound/effects/bonebreak2.ogg','sound/effects/bonebreak3.ogg','sound/effects/bonebreak4.ogg') + if("canopen") + soundin = pick('sound/effects/can_open1.ogg','sound/effects/can_open2.ogg','sound/effects/can_open3.ogg','sound/effects/can_open4.ogg') + if("mechstep") + soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg') + if("thunder") + soundin = pick( + 'sound/effects/thunder/thunder1.ogg', + 'sound/effects/thunder/thunder2.ogg', + 'sound/effects/thunder/thunder3.ogg', + 'sound/effects/thunder/thunder4.ogg', + 'sound/effects/thunder/thunder5.ogg', + 'sound/effects/thunder/thunder6.ogg', + 'sound/effects/thunder/thunder7.ogg', + 'sound/effects/thunder/thunder8.ogg', + 'sound/effects/thunder/thunder9.ogg', + 'sound/effects/thunder/thunder10.ogg') + if("keyboard") + soundin = pick( + 'sound/effects/keyboard/keyboard1.ogg', + 'sound/effects/keyboard/keyboard2.ogg', + 'sound/effects/keyboard/keyboard3.ogg', + 'sound/effects/keyboard/keyboard4.ogg') + if("button") + soundin = pick('sound/machines/button1.ogg','sound/machines/button2.ogg','sound/machines/button3.ogg','sound/machines/button4.ogg') + if("switch") + soundin = pick('sound/machines/switch1.ogg','sound/machines/switch2.ogg','sound/machines/switch3.ogg','sound/machines/switch4.ogg') + if("casing_sound") + soundin = pick('sound/weapons/casingfall1.ogg','sound/weapons/casingfall2.ogg','sound/weapons/casingfall3.ogg') + if("ricochet") + soundin = pick( + 'sound/weapons/effects/ric1.ogg', + 'sound/weapons/effects/ric2.ogg', + 'sound/weapons/effects/ric3.ogg', + 'sound/weapons/effects/ric4.ogg', + 'sound/weapons/effects/ric5.ogg') + if("bullet_miss") + soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') + if ("pickaxe") + soundin = pick( + 'sound/weapons/mine/pickaxe1.ogg', + 'sound/weapons/mine/pickaxe2.ogg', + 'sound/weapons/mine/pickaxe3.ogg', + 'sound/weapons/mine/pickaxe4.ogg') //VORESTATION EDIT - vore sounds for better performance if ("hunger_sounds") soundin = pick('sound/vore/growl1.ogg','sound/vore/growl2.ogg','sound/vore/growl3.ogg','sound/vore/growl4.ogg','sound/vore/growl5.ogg') diff --git a/code/game/trader_visit.dm b/code/game/trader_visit.dm index b589e0b8cc..31889dfff7 100644 --- a/code/game/trader_visit.dm +++ b/code/game/trader_visit.dm @@ -9,16 +9,16 @@ var/can_call_traders = 1 set desc = "Invite players to join the Beruang." if(!holder) - usr << "Only administrators may use this command." + to_chat(usr, "Only administrators may use this command.") return if(!ticker) - usr << "The game hasn't started yet!" + to_chat(usr, "The game hasn't started yet!") return if(ticker.current_state == 1) - usr << "The round hasn't started yet!" + to_chat(usr, "The round hasn't started yet!") return if(send_beruang) - usr << "The Beruang has already been sent this round!" + to_chat(usr, "The Beruang has already been sent this round!") return if(alert("Do you want to dispatch the Beruang trade ship?",,"Yes","No") != "Yes") return @@ -27,7 +27,7 @@ var/can_call_traders = 1 if("No") return if(send_beruang) - usr << "Looks like somebody beat you to it!" + to_chat(usr, "Looks like somebody beat you to it!") return message_admins("[key_name_admin(usr)] is dispatching the Beruang.", 1) @@ -40,19 +40,19 @@ client/verb/JoinTraders() set category = "IC" if(!MayRespawn(1)) - usr << "You cannot join the traders." + to_chat(usr, "You cannot join the traders.") return if(istype(usr,/mob/observer/dead) || istype(usr,/mob/new_player)) if(!send_beruang) - usr << "The Beruang is not currently heading to the station." + to_chat(usr, "The Beruang is not currently heading to the station.") return if(traders.current_antagonists.len >= traders.hard_cap) - usr << "The number of trader slots is already full!" + to_chat(usr, "The number of trader slots is already full!") return traders.create_default(usr) else - usr << "You need to be an observer or new player to use this." + to_chat(usr, "You need to be an observer or new player to use this.") proc/trigger_trader_visit() if(!can_call_traders) @@ -64,6 +64,7 @@ proc/trigger_trader_visit() can_call_traders = 0 // Only one call per round. send_beruang = 1 + consider_trader_load() //VOREStation Add sleep(600 * 5) send_beruang = 0 // Can no longer join the traders. diff --git a/code/game/trader_visit_vr.dm b/code/game/trader_visit_vr.dm new file mode 100644 index 0000000000..81b9b3adcd --- /dev/null +++ b/code/game/trader_visit_vr.dm @@ -0,0 +1,11 @@ +GLOBAL_VAR(trader_loaded) + +/proc/consider_trader_load() + if(!GLOB.trader_loaded) + GLOB.trader_loaded = TRUE + var/datum/map_template/MT = SSmapping.map_templates["Special Area - Trader"] + if(!istype(MT)) + error("Trader is not a valid map template!") + else + MT.load_new_z(centered = TRUE) + log_and_message_admins("Loaded the trade shuttle just now.") \ No newline at end of file diff --git a/code/game/turfs/flooring/flooring.dm b/code/game/turfs/flooring/flooring.dm index 2f45eb7b51..60157c1ee8 100644 --- a/code/game/turfs/flooring/flooring.dm +++ b/code/game/turfs/flooring/flooring.dm @@ -173,7 +173,7 @@ var/list/flooring_types /decl/flooring/tiling/tech desc = "Scuffed from the passage of countless greyshirts." - icon = 'icons/turf/flooring/techfloor_vr.dmi' + icon = 'icons/turf/flooring/techfloor.dmi' icon_base = "techfloor_gray" build_type = /obj/item/stack/tile/floor/techgrey can_paint = null diff --git a/code/game/turfs/flooring/flooring_decals_vr.dm b/code/game/turfs/flooring/flooring_decals_vr.dm index 5326eeff86..438f8a1223 100644 --- a/code/game/turfs/flooring/flooring_decals_vr.dm +++ b/code/game/turfs/flooring/flooring_decals_vr.dm @@ -98,3 +98,35 @@ /obj/effect/floor_decal/corner/yellow/border/shifted icon_state = "bordercolor_shifted" + +/obj/effect/floor_decal/emblem/blu + icon_state = "blu" +/obj/effect/floor_decal/emblem/blu_big + icon_state = "blu_big" + +/obj/effect/floor_decal/emblem/red + icon_state = "red" +/obj/effect/floor_decal/emblem/red_big + icon_state = "red_big" + +/obj/effect/floor_decal/emblem/black + icon_state = "black" +/obj/effect/floor_decal/emblem/black_big + icon_state = "black_big" + +/obj/effect/floor_decal/emblem/sword + icon_state = "sword" +/obj/effect/floor_decal/emblem/sword_big + icon_state = "sword_big" + +/obj/effect/floor_decal/emblem/ark + icon_state = "ark" +/obj/effect/floor_decal/emblem/ark_big + icon_state = "ark_big" + +/obj/effect/floor_decal/emblem/talon + icon_state = "talon" +/obj/effect/floor_decal/emblem/talon_big + icon_state = "talon_big" +/obj/effect/floor_decal/emblem/talon_big/center + icon_state = "talon_center" \ No newline at end of file diff --git a/code/game/turfs/flooring/flooring_premade.dm b/code/game/turfs/flooring/flooring_premade.dm index 624ba0a766..5453c2da40 100644 --- a/code/game/turfs/flooring/flooring_premade.dm +++ b/code/game/turfs/flooring/flooring_premade.dm @@ -114,7 +114,7 @@ /turf/simulated/floor/tiled/techfloor name = "floor" - icon = 'icons/turf/flooring/techfloor_vr.dmi' + icon = 'icons/turf/flooring/techfloor.dmi' icon_state = "techfloor_gray" initial_flooring = /decl/flooring/tiling/tech diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 6bf7cf38b4..9e48b3777c 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -55,8 +55,8 @@ B.clean_blood() ..() -/turf/simulated/New() - ..() +/turf/simulated/Initialize(mapload) + . = ..() if(istype(loc, /area/chapel)) holy = 1 levelupdate() @@ -78,7 +78,7 @@ /turf/simulated/Entered(atom/A, atom/OL) if(movement_disabled && usr.ckey != movement_disabled_exception) - usr << "Movement is admin-disabled." //This is to identify lag problems + to_chat(usr, "Movement is admin-disabled.") //This is to identify lag problems return if (istype(A,/mob/living)) diff --git a/code/game/turfs/simulated/dungeon/wall.dm b/code/game/turfs/simulated/dungeon/wall.dm index 8a70473842..2bfc23145e 100644 --- a/code/game/turfs/simulated/dungeon/wall.dm +++ b/code/game/turfs/simulated/dungeon/wall.dm @@ -3,8 +3,8 @@ /turf/simulated/wall/dungeon block_tele = TRUE // Anti-cheese. -/turf/simulated/wall/dungeon/New(var/newloc) - ..(newloc,"dungeonium") +/turf/simulated/wall/dungeon/Initialize(mapload) + . = ..(mapload, "dungeonium") /turf/simulated/wall/dungeon/attackby() return @@ -16,27 +16,55 @@ return /turf/simulated/wall/solidrock //for more stylish anti-cheese. - name = "solid rock" - desc = "This rock seems dense, impossible to drill." description_info = "Probably not going to be able to drill or bomb your way through this, best to try and find a way around." - icon_state = "bedrock" - var/base_state = "bedrock" + var/rock_side = "rock_side" block_tele = TRUE - -/turf/simulated/wall/solidrock/update_icon() - for(var/direction in cardinal) - var/turf/T = get_step(src,direction) - if(istype(T) && !T.density) - var/place_dir = turn(direction, 180) - if(!mining_overlay_cache["rock_side_[place_dir]"]) - mining_overlay_cache["rock_side_[place_dir]"] = image('icons/turf/walls.dmi', "rock_side", dir = place_dir) - T.add_overlay(mining_overlay_cache["rock_side_[place_dir]"]) +/turf/simulated/wall/solidrock/Initialize(mapload) + . = ..(mapload, "bedrock") /turf/simulated/wall/solidrock/Initialize() - icon_state = base_state + . = ..() update_icon(1) +/turf/simulated/wall/solidrock/update_material() + name = "solid rock" + desc = "This rock seems dense, impossible to drill." + +/turf/simulated/wall/solidrock/proc/get_cached_border(var/cache_id, var/direction, var/icon_file, var/icon_state, var/offset = 32) + if(!mining_overlay_cache["[cache_id]_[direction]"]) + var/image/new_cached_image = image(icon_state, dir = direction, layer = ABOVE_TURF_LAYER) + switch(direction) + if(NORTH) + new_cached_image.pixel_y = offset + if(SOUTH) + new_cached_image.pixel_y = -offset + if(EAST) + new_cached_image.pixel_x = offset + if(WEST) + new_cached_image.pixel_x = -offset + mining_overlay_cache["[cache_id]_[direction]"] = new_cached_image + return new_cached_image + + return mining_overlay_cache["[cache_id]_[direction]"] + +/turf/simulated/wall/solidrock/update_icon(var/update_neighbors) + if(density) + var/image/I + for(var/i = 1 to 4) + I = image('icons/turf/wall_masks.dmi', "rock[wall_connections[i]]", dir = 1<<(i-1)) + add_overlay(I) + for(var/direction in cardinal) + var/turf/T = get_step(src,direction) + if(istype(T) && !T.density) + add_overlay(get_cached_border(rock_side,direction,icon,rock_side)) + + else if(update_neighbors) + for(var/direction in alldirs) + if(istype(get_step(src, direction), /turf/simulated/wall/solidrock)) + var/turf/simulated/wall/solidrock/M = get_step(src, direction) + M.update_icon() + /turf/simulated/wall/solidrock/attackby() return @@ -44,4 +72,31 @@ return /turf/simulated/wall/solidrock/take_damage() //These things are suppose to be unbreakable - return \ No newline at end of file + return + + +//Mossy rocks for POI. Unbreakable, no teleport. + +/turf/simulated/wall/solidrock/mossyrockpoi // Version for POI labyrinths. No teleporting, no breaking. + desc = "An old, yet impressively durably rock wall." + var/mossyrock_side = "mossyrock_side" + +/turf/simulated/wall/solidrock/Initialize(mapload) + . = ..(mapload, "mossyrock") + +/turf/simulated/wall/solidrock/mossyrockpoi/update_icon(var/update_neighbors) + if(density) + var/image/I + for(var/i = 1 to 4) + I = image('icons/turf/wall_masks.dmi', "mossyrock[wall_connections[i]]", dir = 1<<(i-1)) + add_overlay(I) + for(var/direction in cardinal) + var/turf/T = get_step(src,direction) + if(istype(T) && !T.density) + add_overlay(get_cached_border(mossyrock_side,direction,icon,mossyrock_side)) + + else if(update_neighbors) + for(var/direction in alldirs) + if(istype(get_step(src, direction), /turf/simulated/wall/solidrock/mossyrockpoi)) + var/turf/simulated/wall/solidrock/mossyrockpoi/M = get_step(src, direction) + M.update_icon() \ No newline at end of file diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index 31bfd62684..af6d681122 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -20,7 +20,7 @@ 'sound/effects/footstep/plating4.ogg', 'sound/effects/footstep/plating5.ogg')) - var/list/old_decals = null // VOREStation Edit - Remember what decals we had between being pried up and replaced. + var/list/old_decals = null // Flooring data. var/flooring_override @@ -35,12 +35,12 @@ /turf/simulated/floor/is_plating() return !flooring -/turf/simulated/floor/New(var/newloc, var/floortype) - ..(newloc) +/turf/simulated/floor/Initialize(mapload, floortype) + . = ..() if(!floortype && initial_flooring) floortype = initial_flooring if(floortype) - set_flooring(get_flooring_data(floortype)) + set_flooring(get_flooring_data(floortype), TRUE) else footstep_sounds = base_footstep_sounds if(can_dirty && can_start_dirty) @@ -48,29 +48,24 @@ dirt += rand(50,100) update_dirt() //5% chance to start with dirt on a floor tile- give the janitor something to do -/turf/simulated/floor/proc/set_flooring(var/decl/flooring/newflooring) +/turf/simulated/floor/proc/swap_decals() + var/current_decals = decals + decals = old_decals + old_decals = current_decals + +/turf/simulated/floor/proc/set_flooring(var/decl/flooring/newflooring, var/initializing) make_plating(defer_icon_update = 1) + if(!flooring && !initializing) // Plating -> Flooring + swap_decals() flooring = newflooring footstep_sounds = newflooring.footstep_sounds - // VOREStation Edit - We are plating switching to flooring, swap out old_decals for decals - var/tmp/list/overfloor_decals = old_decals - old_decals = decals - decals = overfloor_decals - // VOREStation Edit End update_icon(1) levelupdate() //This proc will set floor_type to null and the update_icon() proc will then change the icon_state of the turf //This proc auto corrects the grass tiles' siding. /turf/simulated/floor/proc/make_plating(var/place_product, var/defer_icon_update) - cut_overlays() - // VOREStation Edit - We are flooring switching to plating, swap out old_decals for decals. - if(flooring) - var/tmp/list/underfloor_decals = old_decals - old_decals = decals - decals = underfloor_decals - // VOREStation Edit End name = base_name desc = base_desc @@ -78,7 +73,8 @@ icon_state = base_icon_state footstep_sounds = base_footstep_sounds - if(flooring) + if(flooring) // Flooring -> Plating + swap_decals() if(flooring.build_type && place_product) new flooring.build_type(src) flooring = null diff --git a/code/game/turfs/simulated/floor_attackby.dm b/code/game/turfs/simulated/floor_attackby.dm index 83d2444f96..70bc2646a2 100644 --- a/code/game/turfs/simulated/floor_attackby.dm +++ b/code/game/turfs/simulated/floor_attackby.dm @@ -152,6 +152,8 @@ if(T.type == flooring.build_type) return var/obj/item/weapon/W = user.is_holding_item_of_type(/obj/item/weapon) + if(!istype(W)) + return if(!try_deconstruct_tile(W, user)) return if(flooring) diff --git a/code/game/turfs/simulated/floor_types.dm b/code/game/turfs/simulated/floor_types.dm index d670fc3a40..151efd819c 100644 --- a/code/game/turfs/simulated/floor_types.dm +++ b/code/game/turfs/simulated/floor_types.dm @@ -55,7 +55,7 @@ return new_dest -/obj/landed_holder/proc/leave_turf() +/obj/landed_holder/proc/leave_turf(var/turf/base_turf = null) var/turf/new_source //Change our source to whatever it was before if(turf_type) @@ -67,7 +67,7 @@ new_source.underlays = underlays new_source.decals = decals else - new_source = my_turf.ChangeTurf(get_base_turf_by_area(my_turf),,1) + new_source = my_turf.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(my_turf),,1) return new_source @@ -83,11 +83,31 @@ var/turf/under_turf //Underlay override turf path. var/join_flags = 0 //Bitstring to represent adjacency of joining walls var/join_group = "shuttle" //A tag for what other walls to join with. Null if you don't want them to. + var/static/list/antilight_cache + +/turf/simulated/shuttle/Initialize(mapload) + ..() + if(!antilight_cache) + antilight_cache = list() + for(var/diag in cornerdirs) + var/image/I = image(LIGHTING_ICON, null, icon_state = "diagonals", layer = 10, dir = diag) + I.plane = PLANE_LIGHTING + antilight_cache["[diag]"] = I /turf/simulated/shuttle/Destroy() landed_holder = null ..() +// For joined corners touching static lighting turfs, add an overlay to cancel out that part of our lighting overlay. +/turf/simulated/shuttle/proc/update_breaklights() + if(join_flags in cornerdirs) //We're joined at an angle + //Dynamic lighting dissolver + var/turf/T = get_step(src, turn(join_flags,180)) + if(!T || !T.dynamic_lighting || !get_area(T).dynamic_lighting) + add_overlay(antilight_cache["[join_flags]"], TRUE) + return + cut_overlay(antilight_cache["[join_flags]"], TRUE) + /turf/simulated/shuttle/proc/underlay_update() if(!takes_underlays) //Basically, if it's not forced, and we don't care, don't do it. @@ -103,11 +123,19 @@ //Well if this isn't our first rodeo, we know EXACTLY what we landed on, and it looks like this. if(landed_holder && !interior_corner) - var/mutable_appearance/landed_on = new(landed_holder) - landed_on.layer = FLOAT_LAYER //Not turf - landed_on.plane = FLOAT_PLANE //Not turf - us.underlays = list(landed_on) - appearance = us + //Space gets special treatment + if(ispath(landed_holder.turf_type, /turf/space)) + var/image/spaceimage = image(landed_holder.icon, landed_holder.icon_state) + spaceimage.plane = SPACE_PLANE + underlays = list(spaceimage) + else + var/mutable_appearance/landed_on = new(landed_holder) + landed_on.layer = FLOAT_LAYER //Not turf + landed_on.plane = FLOAT_PLANE //Not turf + us.underlays = list(landed_on) + appearance = us + + spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected return if(!under) @@ -143,12 +171,15 @@ under_ma = new(under) if(under_ma) - if(ispath(under,/turf/space)) //Scramble space turfs - under_ma.icon_state = "[rand(1,25)]" + if(ispath(under,/turf/space) || istype(under,/turf/space)) //Space gets weird treatment + under_ma.icon_state = "white" + under_ma.plane = SPACE_PLANE us.underlays = list(under_ma) appearance = us + spawn update_breaklights() //So that we update the breaklight overlays only after turfs are connected + return under /turf/simulated/shuttle/floor diff --git a/code/game/turfs/simulated/floor_types_vr.dm b/code/game/turfs/simulated/floor_types_vr.dm index e89c3d0b29..c4315b29b2 100644 --- a/code/game/turfs/simulated/floor_types_vr.dm +++ b/code/game/turfs/simulated/floor_types_vr.dm @@ -1,3 +1,17 @@ +/turf/simulated/shuttle/floor/alienplating/blue + icon = 'icons/turf/shuttle_alien_blue.dmi' + icon_state = "alienplating" + +/turf/simulated/shuttle/floor/alienplating/blue/half + icon_state = "alienplatinghalf" + +/turf/simulated/shuttle/floor/alien/blue + icon = 'icons/turf/shuttle_alien_blue.dmi' + icon_state = "alienpod1" + light_range = 4 + light_power = 0.8 + light_color = "#66ffff" // Bright cyan. + /turf/simulated/floor/flesh name = "flesh" desc = "This slick flesh ripples and squishes under your touch" @@ -14,4 +28,17 @@ return /turf/simulated/floor/flesh/ex_act(severity) - return \ No newline at end of file + return + +/turf/simulated/floor/flock + icon = 'icons/goonstation/featherzone.dmi' + icon_state = "floor" + +/turf/simulated/floor/flock/Crossed(var/atom/movable/AM) + . = ..() + if(isliving(AM)) + icon_state = "floor-on" + set_light(3,3,"#26c5a9") + spawn(5 SECONDS) + icon_state = "floor" + set_light(0,0,"#ffffff") \ No newline at end of file diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm index acf0c17317..01ad912b70 100644 --- a/code/game/turfs/simulated/lava.dm +++ b/code/game/turfs/simulated/lava.dm @@ -39,15 +39,15 @@ /turf/simulated/floor/lava/Entered(atom/movable/AM) if(burn_stuff(AM)) - START_PROCESSING(SSobj, src) + START_PROCESSING(SSturfs, src) /turf/simulated/floor/lava/hitby(atom/movable/AM) if(burn_stuff(AM)) - START_PROCESSING(SSobj, src) + START_PROCESSING(SSturfs, src) /turf/simulated/floor/lava/process() if(!burn_stuff()) - STOP_PROCESSING(SSobj, src) + return PROCESS_KILL /turf/simulated/floor/lava/proc/is_safe() //if anything matching this typecache is found in the lava, we don't burn things diff --git a/code/game/turfs/simulated/outdoors/outdoors.dm b/code/game/turfs/simulated/outdoors/outdoors.dm index a378cc48da..7bef49cd49 100644 --- a/code/game/turfs/simulated/outdoors/outdoors.dm +++ b/code/game/turfs/simulated/outdoors/outdoors.dm @@ -24,10 +24,10 @@ var/list/turf_edge_cache = list() update_icon() . = ..() -/turf/simulated/floor/New() +/turf/simulated/floor/Initialize(mapload) if(outdoors) SSplanets.addTurf(src) - ..() + . = ..() /turf/simulated/floor/Destroy() if(outdoors) diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index f9ae1e7bc3..1ef11c40de 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -232,7 +232,7 @@ if(WT.remove_fuel(0,user)) to_chat(user, "You start repairing the damage to [src].") - playsound(src.loc, WT.usesound, 100, 1) + playsound(src, WT.usesound, 100, 1) if(do_after(user, max(5, damage / 5) * WT.toolspeed) && WT && WT.isOn()) to_chat(user, "You finish repairing the damage to [src].") take_damage(-damage) diff --git a/code/game/turfs/simulated/wall_icon.dm b/code/game/turfs/simulated/wall_icon.dm index f277ea79f5..5bf41c4a5f 100644 --- a/code/game/turfs/simulated/wall_icon.dm +++ b/code/game/turfs/simulated/wall_icon.dm @@ -116,6 +116,15 @@ wall_connections = dirs_to_corner_states(dirs) /turf/simulated/wall/proc/can_join_with(var/turf/simulated/wall/W) - if(material && W.material && material.icon_base == W.material.icon_base) + //VOREStation Edit Start + //No blending if no material + if(!material || !W.material) + return 0 + //We can blend if either is the same, or a subtype, of the other one + if(istype(W.material, material.type) || istype(material, W.material.type)) return 1 + //Also blend if they have the same iconbase + if(material.icon_base == W.material.icon_base) + return 1 + //VOREStation Edit End return 0 diff --git a/code/game/turfs/simulated/wall_types.dm b/code/game/turfs/simulated/wall_types.dm index 1b530d32b0..5a68478bb2 100644 --- a/code/game/turfs/simulated/wall_types.dm +++ b/code/game/turfs/simulated/wall_types.dm @@ -1,91 +1,108 @@ /turf/simulated/wall/r_wall icon_state = "rgeneric" -/turf/simulated/wall/r_wall/New(var/newloc) - ..(newloc, "plasteel","plasteel") //3strong +/turf/simulated/wall/r_wall/Initialize(mapload) + . = ..(mapload, "plasteel","plasteel") //3strong -/turf/simulated/wall/shull/New(var/newloc) //Spaaaace ship. - ..(newloc, MAT_STEELHULL, null, MAT_STEELHULL) -/turf/simulated/wall/rshull/New(var/newloc) - ..(newloc, MAT_STEELHULL, MAT_STEELHULL, MAT_STEELHULL) -/turf/simulated/wall/pshull/New(var/newloc) //Spaaaace-er ship. - ..(newloc, MAT_PLASTEELHULL, null, MAT_PLASTEELHULL) -/turf/simulated/wall/rpshull/New(var/newloc) - ..(newloc, MAT_PLASTEELHULL, MAT_PLASTEELHULL, MAT_PLASTEELHULL) -/turf/simulated/wall/dshull/New(var/newloc) //Spaaaace-est ship. - ..(newloc, MAT_DURASTEELHULL, null, MAT_DURASTEELHULL) -/turf/simulated/wall/rdshull/New(var/newloc) - ..(newloc, MAT_DURASTEELHULL, MAT_DURASTEELHULL, MAT_DURASTEELHULL) -/turf/simulated/wall/thull/New(var/newloc) - ..(newloc, MAT_TITANIUMHULL, null, MAT_TITANIUMHULL) -/turf/simulated/wall/rthull/New(var/newloc) - ..(newloc, MAT_TITANIUMHULL, MAT_TITANIUMHULL, MAT_TITANIUMHULL) +/turf/simulated/wall/shull + icon_state = "hull-steel" +/turf/simulated/wall/shull/Initialize(mapload) //Spaaaace ship. + . = ..(mapload, MAT_STEELHULL, null, MAT_STEELHULL) +/turf/simulated/wall/rshull + icon_state = "hull-r_steel" +/turf/simulated/wall/rshull/Initialize(mapload) + . = ..(mapload, MAT_STEELHULL, MAT_STEELHULL, MAT_STEELHULL) +/turf/simulated/wall/pshull + icon_state = "hull-plasteel" +/turf/simulated/wall/pshull/Initialize(mapload) //Spaaaace-er ship. + . = ..(mapload, MAT_PLASTEELHULL, null, MAT_PLASTEELHULL) +/turf/simulated/wall/rpshull + icon_state = "hull-r_plasteel" +/turf/simulated/wall/rpshull/Initialize(mapload) + . = ..(mapload, MAT_PLASTEELHULL, MAT_PLASTEELHULL, MAT_PLASTEELHULL) +/turf/simulated/wall/dshull + icon_state = "hull-durasteel" +/turf/simulated/wall/dshull/Initialize(mapload) //Spaaaace-est ship. + . = ..(mapload, MAT_DURASTEELHULL, null, MAT_DURASTEELHULL) +/turf/simulated/wall/rdshull + icon_state = "hull-r_durasteel" +/turf/simulated/wall/rdshull/Initialize(mapload) + . = ..(mapload, MAT_DURASTEELHULL, MAT_DURASTEELHULL, MAT_DURASTEELHULL) +/turf/simulated/wall/thull + icon_state = "hull-titanium" +/turf/simulated/wall/thull/Initialize(mapload) + . = ..(mapload, MAT_TITANIUMHULL, null, MAT_TITANIUMHULL) +/turf/simulated/wall/rthull + icon_state = "hull-r_titanium" +/turf/simulated/wall/rthull/Initialize(mapload) + . = ..(mapload, MAT_TITANIUMHULL, MAT_TITANIUMHULL, MAT_TITANIUMHULL) /turf/simulated/wall/cult icon_state = "cult" -/turf/simulated/wall/cult/New(var/newloc) - ..(newloc,"cult","cult2","cult") +/turf/simulated/wall/cult/Initialize(mapload) + . = ..(mapload, "cult","cult2","cult") /turf/unsimulated/wall/cult name = "cult wall" desc = "Hideous images dance beneath the surface." icon = 'icons/turf/wall_masks.dmi' icon_state = "cult" -/turf/simulated/wall/r_wall -/turf/simulated/wall/iron/New(var/newloc) - ..(newloc,"iron") -/turf/simulated/wall/uranium/New(var/newloc) - ..(newloc,"uranium") -/turf/simulated/wall/diamond/New(var/newloc) - ..(newloc,"diamond") -/turf/simulated/wall/gold/New(var/newloc) - ..(newloc,"gold") -/turf/simulated/wall/silver/New(var/newloc) - ..(newloc,"silver") -/turf/simulated/wall/lead/New(var/newloc) - ..(newloc,"lead") -/turf/simulated/wall/r_lead/New(var/newloc) - ..(newloc,"lead", "lead") -/turf/simulated/wall/phoron/New(var/newloc) - ..(newloc,"phoron") -/turf/simulated/wall/sandstone/New(var/newloc) - ..(newloc,"sandstone") -/turf/simulated/wall/ironphoron/New(var/newloc) - ..(newloc,"iron","phoron") -/turf/simulated/wall/golddiamond/New(var/newloc) - ..(newloc,"gold","diamond") -/turf/simulated/wall/silvergold/New(var/newloc) - ..(newloc,"silver","gold") -/turf/simulated/wall/sandstonediamond/New(var/newloc) - ..(newloc,"sandstone","diamond") -/turf/simulated/wall/snowbrick/New(var/newloc) - ..(newloc,"packed snow") +/turf/simulated/wall/iron/Initialize(mapload) + . = ..(mapload, "iron") +/turf/simulated/wall/uranium/Initialize(mapload) + . = ..(mapload, "uranium") +/turf/simulated/wall/diamond/Initialize(mapload) + . = ..(mapload, "diamond") +/turf/simulated/wall/gold/Initialize(mapload) + . = ..(mapload, "gold") +/turf/simulated/wall/silver/Initialize(mapload) + . = ..(mapload, "silver") +/turf/simulated/wall/lead/Initialize(mapload) + . = ..(mapload, "lead") +/turf/simulated/wall/r_lead/Initialize(mapload) + . = ..(mapload, "lead", "lead") +/turf/simulated/wall/phoron/Initialize(mapload) + . = ..(mapload, "phoron") +/turf/simulated/wall/sandstone/Initialize(mapload) + . = ..(mapload, "sandstone") +/turf/simulated/wall/ironphoron/Initialize(mapload) + . = ..(mapload, "iron","phoron") +/turf/simulated/wall/golddiamond/Initialize(mapload) + . = ..(mapload, "gold","diamond") +/turf/simulated/wall/silvergold/Initialize(mapload) + . = ..(mapload, "silver","gold") +/turf/simulated/wall/sandstonediamond/Initialize(mapload) + . = ..(mapload, "sandstone","diamond") +/turf/simulated/wall/snowbrick/Initialize(mapload) + . = ..(mapload, "packed snow") -/turf/simulated/wall/resin/New(var/newloc) - ..(newloc,"resin",null,"resin") +/turf/simulated/wall/resin/Initialize(mapload) + . = ..(mapload, "resin",null,"resin") // Kind of wondering if this is going to bite me in the butt. -/turf/simulated/wall/skipjack/New(var/newloc) - ..(newloc,"alienalloy") +/turf/simulated/wall/skipjack/Initialize(mapload) + . = ..(mapload, "alienalloy") /turf/simulated/wall/skipjack/attackby() return -/turf/simulated/wall/titanium/New(var/newloc) - ..(newloc,"titanium") +/turf/simulated/wall/titanium/Initialize(mapload) + . = ..(mapload, "titanium") +/turf/simulated/wall/durasteel/Initialize(mapload) + . = ..(mapload, "durasteel", "durasteel") /turf/simulated/wall/durasteel/blueserg/New(var/newloc) ..(newloc,"durasteel", "durasteel") /turf/simulated/wall/durasteel/blueserg/attackby(obj/item/I, mob/user) return -/turf/simulated/wall/wood/New(var/newloc) - ..(newloc, MAT_WOOD) +/turf/simulated/wall/wood/Initialize(mapload) + . = ..(mapload, MAT_WOOD) -/turf/simulated/wall/sifwood/New(var/newloc) - ..(newloc, MAT_SIFWOOD) +/turf/simulated/wall/sifwood/Initialize(mapload) + . = ..(mapload, MAT_SIFWOOD) -/turf/simulated/wall/log/New(var/newloc) - ..(newloc, MAT_LOG) +/turf/simulated/wall/log/Initialize(mapload) + . = ..(mapload, MAT_LOG) -/turf/simulated/wall/log_sif/New(var/newloc) - ..(newloc, MAT_SIFLOG) +/turf/simulated/wall/log_sif/Initialize(mapload) + . = ..(mapload, MAT_SIFLOG) /turf/unsimulated/wall/ice name = "Ice wall" @@ -167,16 +184,14 @@ icon_state = "alien-nj" join_group = null -/turf/simulated/shuttle/wall/New() - ..() - //To allow mappers to rename shuttle walls to like "redfloor interior" or whatever for ease of use. - name = true_name - /turf/simulated/shuttle/wall/Initialize() . = ..() + //To allow mappers to rename shuttle walls to like "redfloor interior" or whatever for ease of use. + name = true_name + if(join_group) - src.auto_join() + auto_join() else icon_state = base_state diff --git a/code/game/turfs/simulated/wall_types_vr.dm b/code/game/turfs/simulated/wall_types_vr.dm index 4d939c2004..6ebf9df004 100644 --- a/code/game/turfs/simulated/wall_types_vr.dm +++ b/code/game/turfs/simulated/wall_types_vr.dm @@ -1,3 +1,19 @@ +/turf/simulated/shuttle/wall/alien/blue + name = "hybrid wall" + desc = "Seems slightly more friendly than if the wall were ominous purple." + icon = 'icons/turf/shuttle_alien_blue.dmi' + light_color = "#1fdbf4" // Cyan-ish + +/turf/simulated/shuttle/wall/alien/blue/hard_corner + name = "hybrid wall" + icon_state = "alien-hc" + hard_corner = 1 + +/turf/simulated/shuttle/wall/alien/blue/no_join + name = "hybrid wall" + icon_state = "alien-nj" + join_group = null + /turf/simulated/flesh name = "flesh wall" desc = "The fleshy surface of this wall squishes nicely under your touch but looks and feels extremly strong" @@ -19,8 +35,8 @@ /turf/simulated/flesh/attackby() return -/turf/simulated/flesh/New() - ..() +/turf/simulated/flesh/Initialize(mapload) + . = ..() update_icon(1) var/list/flesh_overlay_cache = list() @@ -43,4 +59,15 @@ var/list/flesh_overlay_cache = list() for(var/direction in alldirs) if(istype(get_step(src, direction), /turf/simulated/flesh)) var/turf/simulated/flesh/F = get_step(src, direction) - F.update_icon() \ No newline at end of file + F.update_icon() + +/turf/simulated/shuttle/wall/flock + icon = 'icons/goonstation/featherzone.dmi' + icon_state = "flockwall0" + base_state = "flockwall" + hard_corner = 1 //They're all HC + true_name = "wall" + +/turf/simulated/shuttle/wall/flock/Initialize() + . = ..() + set_light(3,3,"#26c5a9") diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index bf5f711b20..cbb9c6dbb7 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -27,8 +27,8 @@ for(var/obj/O in src) O.hide(1) -/turf/simulated/wall/New(var/newloc, var/materialtype, var/rmaterialtype, var/girdertype) - ..(newloc) +/turf/simulated/wall/Initialize(mapload, materialtype, rmaterialtype, girdertype) + . = ..() icon_state = "blank" if(!materialtype) materialtype = DEFAULT_WALL_MATERIAL @@ -231,10 +231,25 @@ // Wall-rot effect, a nasty fungus that destroys walls. /turf/simulated/wall/proc/rot() if(locate(/obj/effect/overlay/wallrot) in src) - return + return FALSE + + // Wall-rot can't go onto walls that are surrounded in all four cardinal directions. + // Because of spores, or something. It's actually to avoid the pain that is removing wallrot surrounded by + // four r-walls. + var/at_least_one_open_turf = FALSE + for(var/direction in GLOB.cardinal) + var/turf/T = get_step(src, direction) + if(!T.check_density()) + at_least_one_open_turf = TRUE + break + + if(!at_least_one_open_turf) + return FALSE + var/number_rots = rand(2,3) for(var/i=0, i[user] fills \the [RG] using \the [src].","You fill \the [RG] using \the [src].") + return 1 + + else if(istype(O, /obj/item/weapon/mop)) + O.reagents.add_reagent(reagent_type, 5) + to_chat(user, "You wet \the [O] in \the [src].") + playsound(loc, 'sound/effects/slosh.ogg', 25, 1) + return 1 + + else return ..() + /turf/simulated/floor/water/return_air_for_internal_lifeform(var/mob/living/L) if(L && L.lying) if(L.can_breathe_water()) // For squid. @@ -129,6 +146,18 @@ var/list/shoreline_icon_cache = list() +/turf/simulated/floor/water/beach + name = "beach shoreline" + desc = "The waves look calm and inviting." + icon_state = "beach" + depth = 0 + +/turf/simulated/floor/water/beach/update_icon() + return + +/turf/simulated/floor/water/beach/corner + icon_state = "beachcorner" + /turf/simulated/floor/water/shoreline name = "shoreline" desc = "The waves look calm and inviting." @@ -161,3 +190,17 @@ var/list/shoreline_icon_cache = list() if(L.get_water_protection() < 1) return FALSE return ..() + +/turf/simulated/floor/water/contaminated + desc = "This water smells pretty acrid." + var/poisonlevel = 10 + +turf/simulated/floor/water/contaminated/Entered(atom/movable/AM, atom/oldloc) + ..() + if(istype(AM, /mob/living)) + var/mob/living/L = AM + if(L.isSynthetic()) + return + poisonlevel *= 1 - L.get_water_protection() + if(poisonlevel > 0) + L.adjustToxLoss(poisonlevel) \ No newline at end of file diff --git a/code/game/turfs/space/cracked_asteroid.dm b/code/game/turfs/space/cracked_asteroid.dm index 75cf25fd3f..ca77a8cd2c 100644 --- a/code/game/turfs/space/cracked_asteroid.dm +++ b/code/game/turfs/space/cracked_asteroid.dm @@ -7,6 +7,7 @@ icon_state = "asteroid_cracked" dynamic_lighting = TRUE keep_sprite = TRUE + plane = TURF_PLANE //It's not really space /turf/space/cracked_asteroid/is_space() // So people don't start floating when standing on it. return FALSE diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 55e0dd3b67..793942ecbe 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -1,20 +1,66 @@ /turf/space icon = 'icons/turf/space.dmi' name = "\proper space" - icon_state = "0" + icon_state = "default" dynamic_lighting = 0 + plane = SPACE_PLANE temperature = T20C thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT can_build_into_floor = TRUE var/keep_sprite = FALSE -// heat_capacity = 700000 No. + var/edge = 0 //If we're an edge + var/forced_dirs = 0 //Force this one to pretend it's an overedge turf /turf/space/Initialize() - . = ..() - if(!keep_sprite) - icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" - update_starlight() + if(config.starlight) + update_starlight() + + //Sprite stuff only beyond here + if(keep_sprite) + return ..() + + //We might be an edge + if(y == world.maxy || forced_dirs & NORTH) + edge |= NORTH + else if(y == 1 || forced_dirs & SOUTH) + edge |= SOUTH + + if(x == 1 || forced_dirs & WEST) + edge |= WEST + else if(x == world.maxx || forced_dirs & EAST) + edge |= EAST + + if(edge) //Magic edges + appearance = SSskybox.mapedge_cache["[edge]"] + else //Dust + appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] + + return ..() + +/turf/space/proc/toggle_transit(var/direction) + if(edge) //Not a great way to do this yet. Maybe we'll come up with one. We could pre-make sprites... or tile the overlay over it? + return + + if(!direction) //Stopping our transit + appearance = SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"] + else if(direction & (NORTH|SOUTH)) //Starting transit vertically + var/x_shift = SSskybox.phase_shift_by_x[src.x % (SSskybox.phase_shift_by_x.len - 1) + 1] + var/transit_state = ((direction & SOUTH ? world.maxy - src.y : src.y) + x_shift)%15 + appearance = SSskybox.speedspace_cache["NS_[transit_state]"] + else if(direction & (EAST|WEST)) //Starting transit horizontally + var/y_shift = SSskybox.phase_shift_by_y[src.y % (SSskybox.phase_shift_by_y.len - 1) + 1] + var/transit_state = ((direction & WEST ? world.maxx - src.x : src.x) + y_shift)%15 + appearance = SSskybox.speedspace_cache["EW_[transit_state]"] + + for(var/atom/movable/AM in src) + if (!AM.simulated) + continue + + if(!AM.anchored) + AM.throw_at(get_step(src,reverse_direction(direction)), 5, 1) + else if (istype(AM, /obj/effect/decal)) + qdel(AM) //No more space blood coming with the shuttle /turf/space/is_space() return 1 @@ -24,9 +70,10 @@ for(var/obj/O in src) O.hide(0) +/turf/space/is_solid_structure() + return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice + /turf/space/proc/update_starlight() - if(!config.starlight) - return if(locate(/turf/simulated) in orange(src,1)) set_light(config.starlight) else @@ -86,24 +133,16 @@ // If that's changed, then you'll want to swipe the rest of the roofing code from code/game/turfs/simulated/floor_attackby.dm return - -// Ported from unstable r355 - -/turf/space/Entered(atom/movable/A as mob|obj) - if(movement_disabled) - to_chat(usr, "Movement is admin-disabled.") //This is to identify lag problems - return +/turf/space/Entered(var/atom/movable/A) ..() - if ((!(A) || src != A.loc)) return + + if (!A || src != A.loc) + return inertial_drift(A) - if(ticker && ticker.mode) - - // Okay, so let's make it so that people can travel z levels but not nuke disks! - // if(ticker.mode.name == "mercenary") return - if (A.x <= TRANSITIONEDGE || A.x >= (world.maxx - TRANSITIONEDGE + 1) || A.y <= TRANSITIONEDGE || A.y >= (world.maxy - TRANSITIONEDGE + 1)) - A.touch_map_edge() + if(edge && ticker?.mode) + A.touch_map_edge() /turf/space/proc/Sandbox_Spacemove(atom/movable/A as mob|obj) var/cur_x @@ -127,9 +166,9 @@ target_z = y_arr[cur_y] /* //debug - world << "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]" - world << "Target Z = [target_z]" - world << "Next X = [next_x]" + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Target Z = [target_z]") + to_world("Next X = [next_x]") //debug */ if(target_z) @@ -152,9 +191,9 @@ target_z = y_arr[cur_y] /* //debug - world << "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]" - world << "Target Z = [target_z]" - world << "Next X = [next_x]" + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Target Z = [target_z]") + to_world("Next X = [next_x]") //debug */ if(target_z) @@ -176,9 +215,9 @@ target_z = y_arr[next_y] /* //debug - world << "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]" - world << "Next Y = [next_y]" - world << "Target Z = [target_z]" + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Next Y = [next_y]") + to_world("Target Z = [target_z]") //debug */ if(target_z) @@ -201,9 +240,9 @@ target_z = y_arr[next_y] /* //debug - world << "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]" - world << "Next Y = [next_y]" - world << "Target Z = [target_z]" + to_world("Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_world("Next Y = [next_y]") + to_world("Target Z = [target_z]") //debug */ if(target_z) diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 0bfcb4e0f6..02c589e966 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -1,5 +1,4 @@ /turf/space/transit - keep_sprite = TRUE can_build_into_floor = FALSE var/pushdirection // push things that get caught in the transit tile this direction @@ -7,87 +6,26 @@ /turf/space/transit/attackby(obj/O as obj, mob/user as mob) return -//generates a list used to randomize transit animations so they aren't in lockstep -/turf/space/transit/proc/get_cross_shift_list(var/size) - var/list/result = list() - - result += rand(0, 14) - for(var/i in 2 to size) - var/shifts = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) - shifts -= result[i - 1] //consecutive shifts should not be equal - if(i == size) - shifts -= result[1] //because shift list is a ring buffer - result += pick(shifts) - - return result +/turf/space/transit/Initialize() + . = ..() + toggle_transit(reverse_dir[pushdirection]) //------------------------ /turf/space/transit/north // moving to the north icon_state = "arrow-north" pushdirection = SOUTH // south because the space tile is scrolling south - var/static/list/phase_shift_by_x - -/turf/space/transit/north/New() - ..() - if(!phase_shift_by_x) - phase_shift_by_x = get_cross_shift_list(15) - - var/x_shift = phase_shift_by_x[src.x % (phase_shift_by_x.len - 1) + 1] - var/transit_state = (world.maxy - src.y + x_shift)%15 + 1 - - icon_state = "speedspace_ns_[transit_state]" -//------------------------ /turf/space/transit/south // moving to the south icon_state = "arrow-south" pushdirection = SOUTH // south because the space tile is scrolling south - var/static/list/phase_shift_by_x - -/turf/space/transit/south/New() - ..() - if(!phase_shift_by_x) - phase_shift_by_x = get_cross_shift_list(15) - - var/x_shift = phase_shift_by_x[src.x % (phase_shift_by_x.len - 1) + 1] - var/transit_state = (world.maxy - src.y + x_shift)%15 + 1 - - var/icon/I = new(icon, "speedspace_ns_[transit_state]") - I.Flip(SOUTH) - icon = I -//------------------------ /turf/space/transit/east // moving to the east icon_state = "arrow-east" pushdirection = WEST - var/static/list/phase_shift_by_y - -/turf/space/transit/east/New() - ..() - if(!phase_shift_by_y) - phase_shift_by_y = get_cross_shift_list(15) - - var/y_shift = phase_shift_by_y[src.y % (phase_shift_by_y.len - 1) + 1] - var/transit_state = (world.maxx - src.x + y_shift)%15 + 1 - - icon_state = "speedspace_ew_[transit_state]" -//------------------------ /turf/space/transit/west // moving to the west icon_state = "arrow-west" pushdirection = WEST - var/static/list/phase_shift_by_y - -/turf/space/transit/west/New() - ..() - if(!phase_shift_by_y) - phase_shift_by_y = get_cross_shift_list(15) - - var/y_shift = phase_shift_by_y[src.y % (phase_shift_by_y.len - 1) + 1] - var/transit_state = (world.maxx - src.x + y_shift)%15 + 1 - - var/icon/I = new(icon, "speedspace_ew_[transit_state]") - I.Flip(WEST) - icon = I //------------------------ \ No newline at end of file diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index eb85d4aeb5..96edd3c9d4 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -34,26 +34,22 @@ var/can_build_into_floor = FALSE // Used for things like RCDs (and maybe lattices/floor tiles in the future), to see if a floor should replace it. var/list/dangerous_objects // List of 'dangerous' objs that the turf holds that can cause something bad to happen when stepped on, used for AI mobs. -/turf/New() - ..() - for(var/atom/movable/AM as mob|obj in src) - spawn( 0 ) - src.Entered(AM) - return - turfs |= src - - if(dynamic_lighting) - luminosity = 0 - else - luminosity = 1 +/turf/Initialize(mapload) + . = ..() + for(var/atom/movable/AM in src) + Entered(AM) + //Lighting related + luminosity = !(dynamic_lighting) + has_opaque_atom |= (opacity) + + //Pathfinding related if(movement_cost && pathweight == 1) // This updates pathweight automatically. pathweight = movement_cost /turf/Destroy() - turfs -= src + . = QDEL_HINT_IWILLGC ..() - return QDEL_HINT_IWILLGC /turf/ex_act(severity) return 0 @@ -64,6 +60,10 @@ /turf/proc/is_intact() return 0 +// Used by shuttle code to check if this turf is empty enough to not crush want it lands on. +/turf/proc/is_solid_structure() + return 1 + /turf/attack_hand(mob/user) if(!(user.canmove) || user.restrained() || !(user.pulling)) return 0 @@ -146,7 +146,7 @@ var/const/enterloopsanity = 100 /turf/Entered(atom/atom as mob|obj) if(movement_disabled) - usr << "Movement is admin-disabled." //This is to identify lag problems + to_chat(usr, "Movement is admin-disabled.") //This is to identify lag problems return ..() @@ -193,7 +193,7 @@ var/const/enterloopsanity = 100 //There's a lot of QDELETED() calls here if someone can figure out how to optimize this but not runtime when something gets deleted by a Bump/CanPass/Cross call, lemme know or go ahead and fix this mess - kevinz000 /turf/Enter(atom/movable/mover, atom/oldloc) if(movement_disabled && usr.ckey != movement_disabled_exception) - usr << "Movement is admin-disabled." //This is to identify lag problems + to_chat(usr, "Movement is admin-disabled.") //This is to identify lag problems return // Do not call ..() // Byond's default turf/Enter() doesn't have the behaviour we want with Bump() @@ -250,7 +250,7 @@ var/const/enterloopsanity = 100 /turf/proc/inertial_drift(atom/movable/A as mob|obj) if(!(A.last_move)) return - if((istype(A, /mob/) && src.x > 2 && src.x < (world.maxx - 1) && src.y > 2 && src.y < (world.maxy-1))) + if((istype(A, /mob/) && src.x > 1 && src.x < (world.maxx) && src.y > 1 && src.y < (world.maxy))) var/mob/M = A if(M.Process_Spacemove(1)) M.inertia_dir = 0 @@ -268,20 +268,23 @@ var/const/enterloopsanity = 100 for(var/obj/O in src) O.hide(O.hides_under_flooring() && !is_plating()) -/turf/proc/AdjacentTurfs() - var/L[] = new() - for(var/turf/simulated/t in oview(src,1)) - if(!t.density) - if(!LinkBlocked(src, t) && !TurfBlockedNonWindow(t)) - L.Add(t) - return L +/turf/proc/AdjacentTurfs(var/check_blockage = TRUE) + . = list() + for(var/t in (trange(1,src) - src)) + var/turf/T = t + if(check_blockage) + if(!T.density) + if(!LinkBlocked(src, T) && !TurfBlockedNonWindow(T)) + . += t + else + . += t -/turf/proc/CardinalTurfs() - var/L[] = new() - for(var/turf/simulated/T in AdjacentTurfs()) +/turf/proc/CardinalTurfs(var/check_blockage = TRUE) + . = list() + for(var/ad in AdjacentTurfs(check_blockage)) + var/turf/T = ad if(T.x == src.x || T.y == src.y) - L.Add(T) - return L + . += T /turf/proc/Distance(turf/t) if(get_dist(src,t) == 1) @@ -318,7 +321,7 @@ var/const/enterloopsanity = 100 if(istype(O,/obj/effect/rune) || istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay)) qdel(O) else - user << "\The [source] is too dry to wash that." + to_chat(user, "\The [source] is too dry to wash that.") source.reagents.trans_to_turf(src, 1, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly. /turf/proc/update_blood_overlays() diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index cb3496a96b..b3988f3f77 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -40,7 +40,7 @@ var/old_outdoors = outdoors var/old_dangerous_objects = dangerous_objects - //world << "Replacing [src.type] with [N]" + //to_world("Replacing [src.type] with [N]") if(connections) connections.erase_all() diff --git a/code/game/turfs/unsimulated.dm b/code/game/turfs/unsimulated.dm index 894fac57b2..584137ff45 100644 --- a/code/game/turfs/unsimulated.dm +++ b/code/game/turfs/unsimulated.dm @@ -10,9 +10,10 @@ icon = 'icons/turf/space.dmi' icon_state = "0" dynamic_lighting = FALSE + initialized = FALSE -/turf/unsimulated/fake_space/New() - ..() +/turf/unsimulated/fake_space/Initialize(mapload) + . = ..() icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" //VOREStation Add End diff --git a/code/game/turfs/unsimulated/beach.dm b/code/game/turfs/unsimulated/beach.dm index 4638b15cab..056facb60c 100644 --- a/code/game/turfs/unsimulated/beach.dm +++ b/code/game/turfs/unsimulated/beach.dm @@ -14,9 +14,10 @@ /turf/unsimulated/beach/water name = "Water" icon_state = "water" + initialized = FALSE -/turf/unsimulated/beach/water/New() - ..() +/turf/unsimulated/beach/water/Initialize() + . = ..() add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water2","layer"=MOB_LAYER+0.1)) /turf/simulated/floor/beach @@ -54,6 +55,6 @@ /turf/simulated/floor/beach/water/ocean icon_state = "seadeep" -/turf/simulated/floor/beach/water/New() - ..() +/turf/simulated/floor/beach/water/Initialize() + . = ..() add_overlay(image("icon"='icons/misc/beach.dmi',"icon_state"="water5","layer"=MOB_LAYER+0.1)) diff --git a/code/game/turfs/unsimulated/planetary.dm b/code/game/turfs/unsimulated/planetary.dm index 35cd7aa4a8..4fb26f16b6 100644 --- a/code/game/turfs/unsimulated/planetary.dm +++ b/code/game/turfs/unsimulated/planetary.dm @@ -9,6 +9,7 @@ density = 1 alpha = 0 blocks_air = 0 + initialized = FALSE // Set these to get your desired planetary atmosphere. oxygen = 0 @@ -17,8 +18,8 @@ phoron = 0 temperature = T20C -/turf/unsimulated/wall/planetary/New() - ..() +/turf/unsimulated/wall/planetary/Initialize() + . = ..() SSplanets.addTurf(src) /turf/unsimulated/wall/planetary/Destroy() diff --git a/code/game/verbs/advanced_who.dm b/code/game/verbs/advanced_who.dm index bbde65fd07..9f067054f2 100644 --- a/code/game/verbs/advanced_who.dm +++ b/code/game/verbs/advanced_who.dm @@ -71,4 +71,5 @@ msg += "[line]\n" msg += "Total Players: [length(Lines)]" + msg = "" + msg + "" to_chat(src, msg) \ No newline at end of file diff --git a/code/game/verbs/ignore.dm b/code/game/verbs/ignore.dm index 686a9ed5af..702d462a0e 100644 --- a/code/game/verbs/ignore.dm +++ b/code/game/verbs/ignore.dm @@ -8,15 +8,15 @@ key_to_ignore = ckey(sanitize(key_to_ignore)) if(prefs && prefs.ignored_players) if(key_to_ignore in prefs.ignored_players) - usr << "[key_to_ignore] is already being ignored." + to_chat(usr, "[key_to_ignore] is already being ignored.") return if(key_to_ignore == usr.ckey) - usr <<"You can't ignore yourself." + to_chat(usr, "You can't ignore yourself.") return prefs.ignored_players |= key_to_ignore SScharacter_setup.queue_preferences_save(prefs) - usr << "Now ignoring [key_to_ignore]." + to_chat(usr, "Now ignoring [key_to_ignore].") /client/verb/unignore(key_to_unignore as text) set name = "Unignore" @@ -28,11 +28,11 @@ key_to_unignore = ckey(sanitize(key_to_unignore)) if(prefs && prefs.ignored_players) if(!(key_to_unignore in prefs.ignored_players)) - usr << "[key_to_unignore] isn't being ignored." + to_chat(usr, "[key_to_unignore] isn't being ignored.") return prefs.ignored_players -= key_to_unignore SScharacter_setup.queue_preferences_save(prefs) - usr << "Reverted ignore on [key_to_unignore]." + to_chat(usr, "Reverted ignore on [key_to_unignore].") /mob/proc/is_key_ignored(var/key_to_check) if(client) diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index 0c7596ed84..a6e2625257 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -4,7 +4,7 @@ set category = "OOC" if(say_disabled) //This is here to try to identify lag problems - usr << "Speech is currently admin-disabled." + to_chat(usr, "Speech is currently admin-disabled.") return if(!mob) return @@ -24,7 +24,7 @@ to_chat(src, "OOC is globally muted.") return if(!config.dooc_allowed && (mob.stat == DEAD)) - usr << "OOC for dead mobs has been turned off." + to_chat(usr, "OOC for dead mobs has been turned off.") return if(prefs.muted & MUTE_OOC) to_chat(src, "You cannot use OOC (muted).") @@ -36,7 +36,7 @@ return //VOREStation Add - No talking during voting if(SSvote && SSvote.mode) - to_chat(src,"OOC is not allowed during voting.") + to_chat(src, "OOC is not allowed during voting.") return //VOREStation Add End @@ -70,10 +70,10 @@ display_name = "[holder.fakekey]/([src.key])" else display_name = holder.fakekey - if(holder && !holder.fakekey && (holder.rights & R_ADMIN) && config.allow_admin_ooccolor && (src.prefs.ooccolor != initial(src.prefs.ooccolor))) // keeping this for the badmins - target << "" + create_text_tag("ooc", "OOC:", target) + " [display_name]: [msg]" + if(holder && !holder.fakekey && (holder.rights & R_ADMIN|R_FUN|R_EVENT) && config.allow_admin_ooccolor && (src.prefs.ooccolor != initial(src.prefs.ooccolor))) // keeping this for the badmins + to_chat(target, "" + create_text_tag("ooc", "OOC:", target) + " [display_name]: [msg]") else - target << "" + create_text_tag("ooc", "OOC:", target) + " [display_name]: [msg]" + to_chat(target, "" + create_text_tag("ooc", "OOC:", target) + " [display_name]: [msg]") /client/verb/looc(msg as text) set name = "LOOC" @@ -81,7 +81,7 @@ set category = "OOC" if(say_disabled) //This is here to try to identify lag problems - usr << "Speech is currently admin-disabled." + to_chat(usr, "Speech is currently admin-disabled.") return if(!mob) @@ -104,7 +104,7 @@ to_chat(src, "LOOC is globally muted.") return if(!config.dooc_allowed && (mob.stat == DEAD)) - usr << "OOC for dead mobs has been turned off." + to_chat(usr, "OOC for dead mobs has been turned off.") return if(prefs.muted & MUTE_OOC) to_chat(src, "You cannot use OOC (muted).") @@ -162,12 +162,12 @@ if(target in admins) admin_stuff += "/([key])" - target << "" + create_text_tag("looc", "LOOC:", target) + " [display_name][admin_stuff]: [msg]" + to_chat(target, "" + create_text_tag("looc", "LOOC:", target) + " [display_name][admin_stuff]: [msg]") for(var/client/target in r_receivers) var/admin_stuff = "/([key])([admin_jump_link(mob, target.holder)])" - target << "" + create_text_tag("looc", "LOOC:", target) + " (R)[display_name][admin_stuff]: [msg]" + to_chat(target, "" + create_text_tag("looc", "LOOC:", target) + " (R)[display_name][admin_stuff]: [msg]") /mob/proc/get_looc_source() return src diff --git a/code/game/verbs/suicide.dm b/code/game/verbs/suicide.dm index 5a6bfefdb4..ce0b82df9b 100644 --- a/code/game/verbs/suicide.dm +++ b/code/game/verbs/suicide.dm @@ -107,7 +107,7 @@ if(confirm == "Yes") suiciding = 1 - viewers(loc) << "[src]'s brain is growing dull and lifeless. It looks like it's lost the will to live." + to_chat(viewers(loc),"[src]'s brain is growing dull and lifeless. It looks like it's lost the will to live.") spawn(50) death(0) suiciding = 0 @@ -127,7 +127,7 @@ if(confirm == "Yes") suiciding = 1 - viewers(src) << "[src] is powering down. It looks like they're trying to commit suicide." + to_chat(viewers(src),"[src] is powering down. It looks like they're trying to commit suicide.") //put em at -175 adjustOxyLoss(max(getMaxHealth() * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) updatehealth() @@ -147,7 +147,7 @@ if(confirm == "Yes") suiciding = 1 - viewers(src) << "[src] is powering down. It looks like they're trying to commit suicide." + to_chat(viewers(src),"[src] is powering down. It looks like they're trying to commit suicide.") //put em at -175 adjustOxyLoss(max(getMaxHealth() * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) updatehealth() diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm index b30d73e421..4b03bcb4ce 100644 --- a/code/game/verbs/who.dm +++ b/code/game/verbs/who.dm @@ -61,7 +61,7 @@ msg += "[line]\n" msg += "Total Players: [length(Lines)]" - src << msg + to_chat(src,msg) /client/verb/staffwho() set category = "Admin" @@ -207,4 +207,4 @@ if(config.show_event_managers) msg += "\n Current Miscellaneous ([num_event_managers_online]):\n" + eventMmsg //VOREStation Edit - src << msg + to_chat(src,msg) diff --git a/code/game/world.dm b/code/game/world.dm index da0e2ae371..a71a3e6cdb 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -1,6 +1,6 @@ #define RECOMMENDED_VERSION 501 /world/New() - world.log << "Map Loading Complete" + to_world_log("Map Loading Complete") //logs //VOREStation Edit Start log_path += time2text(world.realtime, "YYYY/MM-Month/DD-Day/round-hh-mm-ss") @@ -13,7 +13,7 @@ changelog_hash = md5('html/changelog.html') //used for telling if the changelog has changed recently if(byond_version < RECOMMENDED_VERSION) - world.log << "Your server's byond version does not meet the recommended requirements for this server. Please update BYOND" + to_world_log("Your server's byond version does not meet the recommended requirements for this server. Please update BYOND") config.post_load() @@ -28,6 +28,7 @@ GLOB.timezoneOffset = text2num(time2text(0,"hh")) * 36000 callHook("startup") + init_vchat() //Emergency Fix load_mods() //end-emergency fix @@ -59,11 +60,8 @@ //Must be done now, otherwise ZAS zones and lighting overlays need to be recreated. //createRandomZlevel() //VOREStation Removal: Deprecated - processScheduler = new master_controller = new /datum/controller/game_controller() - processScheduler.deferSetupFor(/datum/controller/process/ticker) - processScheduler.setup() Master.Initialize(10, FALSE) spawn(1) @@ -153,15 +151,15 @@ var/world_topic_spam_protect_time = world.timeofday else if(T == "manifest") var/list/positions = list() var/list/set_names = list( - "heads" = command_positions, - "sec" = security_positions, - "eng" = engineering_positions, - "med" = medical_positions, - "sci" = science_positions, - "car" = cargo_positions, - "pla" = planet_positions, //VOREStation Edit, - "civ" = civilian_positions, - "bot" = nonhuman_positions + "heads" = SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND), + "sec" = SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY), + "eng" = SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING), + "med" = SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL), + "sci" = SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH), + "car" = SSjob.get_job_titles_in_department(DEPARTMENT_CARGO), + "pla" = SSjob.get_job_titles_in_department(DEPARTMENT_PLANET), //VOREStation Add, + "civ" = SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN), + "bot" = SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC) ) for(var/datum/data/record/t in data_core.general) @@ -180,6 +178,17 @@ var/world_topic_spam_protect_time = world.timeofday if(!positions["misc"]) positions["misc"] = list() positions["misc"][name] = rank + + for(var/datum/data/record/t in data_core.hidden_general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/real_rank = make_list_rank(t.fields["real_rank"]) + + var/datum/job/J = SSjob.get_job(real_rank) + if(J?.offmap_spawn) + if(!positions["off"]) + positions["off"] = list() + positions["off"][name] = rank // Synthetics don't have actual records, so we will pull them from here. for(var/mob/living/silicon/ai/ai in mob_list) @@ -329,12 +338,12 @@ var/world_topic_spam_protect_time = world.timeofday C.irc_admin = input["sender"] C << 'sound/effects/adminhelp.ogg' - C << message + to_chat(C,message) for(var/client/A in admins) if(A != C) - A << amessage + to_chat(A,amessage) return "Message Successful" @@ -390,12 +399,11 @@ var/world_topic_spam_protect_time = world.timeofday if (usr) log_admin("[key_name(usr)] Has requested an immediate world restart via client side debugging tools") message_admins("[key_name_admin(usr)] Has requested an immediate world restart via client side debugging tools") - world << "[key_name_admin(usr)] has requested an immediate world restart via client side debugging tools" + to_world("[key_name_admin(usr)] has requested an immediate world restart via client side debugging tools") else - world << "Rebooting world immediately due to host request" + to_world("Rebooting world immediately due to host request") else - processScheduler.stop() Master.Shutdown() //run SS shutdowns for(var/client/C in GLOB.clients) if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite @@ -546,11 +554,11 @@ var/failed_old_db_connections = 0 /hook/startup/proc/connectDB() if(!config.sql_enabled) - world.log << "SQL connection disabled in config." + to_world_log("SQL connection disabled in config.") else if(!setup_database_connection()) - world.log << "Your server failed to establish a connection with the feedback database." + to_world_log("Your server failed to establish a connection with the feedback database.") else - world.log << "Feedback database connection established." + to_world_log("Feedback database connection established.") return 1 proc/setup_database_connection() @@ -573,7 +581,7 @@ proc/setup_database_connection() failed_db_connections = 0 //If this connection succeeded, reset the failed connections counter. else failed_db_connections++ //If it failed, increase the failed connections counter. - world.log << dbcon.ErrorMsg() + to_world_log(dbcon.ErrorMsg()) return . @@ -590,11 +598,11 @@ proc/establish_db_connection() /hook/startup/proc/connectOldDB() if(!config.sql_enabled) - world.log << "SQL connection disabled in config." + to_world_log("SQL connection disabled in config.") else if(!setup_old_database_connection()) - world.log << "Your server failed to establish a connection with the SQL database." + to_world_log("Your server failed to establish a connection with the SQL database.") else - world.log << "SQL database connection established." + to_world_log("SQL database connection established.") return 1 //These two procs are for the old database, while it's being phased out. See the tgstation.sql file in the SQL folder for more information. @@ -618,7 +626,7 @@ proc/setup_old_database_connection() failed_old_db_connections = 0 //If this connection succeeded, reset the failed connections counter. else failed_old_db_connections++ //If it failed, increase the failed connections counter. - world.log << dbcon.ErrorMsg() + to_world_log(dbcon.ErrorMsg()) return . @@ -645,6 +653,20 @@ proc/establish_old_db_connection() maxz++ max_z_changed() +// Call this to change world.fps, don't modify it directly. +/world/proc/change_fps(new_value = 20) + if(new_value <= 0) + CRASH("change_fps() called with [new_value] new_value.") + if(fps == new_value) + return //No change required. + + fps = new_value + on_tickrate_change() + +// Called whenver world.tick_lag or world.fps are changed. +/world/proc/on_tickrate_change() + SStimer?.reset_buckets() + #undef FAILED_DB_CONNECTION_CUTOFF /world/New() SERVER_TOOLS_ON_NEW diff --git a/code/global.dm b/code/global.dm index 5950ccfd14..31535369df 100644 --- a/code/global.dm +++ b/code/global.dm @@ -5,7 +5,6 @@ // Items that ask to be called every cycle. var/global/datum/datacore/data_core = null -var/global/list/all_areas = list() var/global/list/machines = list() // ALL Machines, wether processing or not. var/global/list/processing_machines = list() // TODO - Move into SSmachines var/global/list/processing_power_items = list() // TODO - Move into SSmachines @@ -107,7 +106,6 @@ var/gravity_is_on = 1 var/join_motd = null -var/datum/game_master/game_master = new() // Game Master, an AI for choosing events. var/datum/metric/metric = new() // Metric datum, used to keep track of the round. var/list/awaydestinations = list() // Away missions. A list of landmarks that the warpgate can take you to. @@ -178,6 +176,7 @@ var/list/station_departments = list("Command", "Medical", "Engineering", "Scienc //Icons for in-game HUD glasses. Why don't we just share these a little bit? var/static/icon/ingame_hud = icon('icons/mob/hud.dmi') var/static/icon/ingame_hud_med = icon('icons/mob/hud_med.dmi') +var/static/icon/buildmode_hud = icon('icons/misc/buildmode.dmi') //Keyed list for caching icons so you don't need to make them for records, IDs, etc all separately. //Could be useful for AI impersonation or something at some point? diff --git a/code/global_init.dm b/code/global_init.dm index c562209183..7a96219a0c 100644 --- a/code/global_init.dm +++ b/code/global_init.dm @@ -14,9 +14,17 @@ var/global/datum/global_init/init = new () Pre-map initialization stuff should go here. */ /datum/global_init/New() - - makeDatumRefLists() +/* VOREStation Removal - Ours is even earlier, in world/New() + //logs + log_path += time2text(world.realtime, "YYYY/MM-Month/DD-Day/round-hh-mm-ss") + diary = file("[log_path].log") + href_logfile = file("[log_path]-hrefs.htm") + error_log = file("[log_path]-error.log") + debug_log = file("[log_path]-debug.log") + debug_log << "[log_end]\n[log_end]\nStarting up. [time_stamp()][log_end]\n---------------------[log_end]" +*/ //VOREStation Removal End load_configuration() + makeDatumRefLists() initialize_integrated_circuits_list() diff --git a/code/global_vr.dm b/code/global_vr.dm index 2bdcc387ef..d8d1887bde 100644 --- a/code/global_vr.dm +++ b/code/global_vr.dm @@ -12,7 +12,7 @@ var/list/shell_module_types = list( "Standard", "Engineering", "Surgeon", "Crisis", "Miner", "Janitor", "Service", "Clerical", "Medihound", "Janihound", - "Servicehound" //YW changes + "Servicehound", "BoozeHound" //YW changes ) var/list/eventdestinations = list() // List of scatter landmarks for VOREStation event portals diff --git a/code/modules/Phorochemistry/phororeagent.dm b/code/modules/Phorochemistry/phororeagent.dm index 7b5fa8747c..0877309137 100644 --- a/code/modules/Phorochemistry/phororeagent.dm +++ b/code/modules/Phorochemistry/phororeagent.dm @@ -130,22 +130,22 @@ var/induromol_code = rand(1, 50) love_name = H.name if(love_name) - M << "You see [love_name]..." + to_chat(M, "You see [love_name]...") spawn(0) sleep(10) - M << "They are beautiful" + to_chat(M, "They are beautiful") if(M.mind) //give protect objective var/datum/objective/protection = new/datum/objective() protection.explanation_text = "Protect [love_name] at all costs" M.mind.objectives.Add(protection) var/obj_count = 1 - M << "Your current objectives:" + to_chat(M, "Your current objectives:") for(var/datum/objective/objective in M.mind.objectives) - M << "Objective #[obj_count]: [objective.explanation_text]" + to_chat(M, "Objective #[obj_count]: [objective.explanation_text]") obj_count++ - M << "
" + to_chat(M, "
)") else if(prob(5)) if(prob(98)) @@ -155,10 +155,10 @@ var/induromol_code = rand(1, 50) "[love_name] seems to be the essence of perfection", "[love_name] can never be allowed to leave your side") - M << "[pick(love_messages)]" + to_chat(M, "[pick(love_messages)]") else - M << "You begin to build a trouser tent" + to_chat(M, "You begin to build a trouser tent") return ..() /datum/reagent/phororeagent/love_potion/on_remove(var/atom/A) @@ -170,7 +170,7 @@ var/induromol_code = rand(1, 50) var/end_message = "" var/list/message2list = list() var/i = 1 - var/length = lentext(message) + var/length = length(message) while(i <= length) message2list += copytext(message, i, i + 1) i++ @@ -186,18 +186,18 @@ var/induromol_code = rand(1, 50) end_message += "[char]" col_inc += col_perc - M << end_message + to_chat(M, end_message) for(var/datum/objective/O in M.mind.objectives) if(findtext(O.explanation_text, "Protect [love_name] at all costs")) M.mind.objectives.Remove(O) var/obj_count = 1 - M << "Your current objectives:" + to_chat(M, "Your current objectives:") for(var/datum/objective/objective in M.mind.objectives) - M << "Objective #[obj_count]: [objective.explanation_text]" + to_chat(M, "Objective #[obj_count]: [objective.explanation_text]") obj_count++ - M << "
" + to_chat(M, "
") /datum/reagent/phororeagent/love_potion/on_mob_death(var/mob/M) //update objectives @@ -206,12 +206,12 @@ var/induromol_code = rand(1, 50) if(findtext(O.explanation_text, "Protect [love_name] at all costs")) M.mind.objectives.Remove(O) var/obj_count = 1 - M << "Your current objectives:" + to_chat(M, "Your current objectives:") for(var/datum/objective/objective in M.mind.objectives) - M << "Objective #[obj_count]: [objective.explanation_text]" + to_chat(M, "Objective #[obj_count]: [objective.explanation_text]") obj_count++ - M << "
" + to_chat(M, "
") break /obj/item/weapon/reagent_containers/glass/beaker/lovepotion @@ -317,7 +317,7 @@ var/induromol_code = rand(1, 50) if(istype(A, /mob/living)) var/mob/living/M = A M.universal_understand = 0 - M << "You no longer feel attuned to the spoken word." + to_chat(M, "You no longer feel attuned to the spoken word.") /datum/reagent/phororeagent/babelizine/on_mob_death(var/mob/M) holder.remove_reagent(src.id, src.volume) @@ -455,7 +455,7 @@ var/induromol_code = rand(1, 50) if(data[1]) if(istype(A, /mob)) var/mob/M = A - M << "You feel the last traces of chemicals leave your body as you return to death once more..." + to_chat(M, "You feel the last traces of chemicals leave your body as you return to death once more...") M.death(0) //Reagent giveth, and reagent taketh away @@ -500,14 +500,14 @@ var/induromol_code = rand(1, 50) H.f_style = "Shaved" if(!M.digitalcamo) - M << "Your skin starts to feel strange" + to_chat(M, "Your skin starts to feel strange") M.digitalcamo = 1 return ..() /datum/reagent/phororeagent/tegoxane/on_remove(var/atom/A) if(istype(A, /mob)) var/mob/M = A - M << "Your skin feels normal again" + to_chat(M, "Your skin feels normal again") M.digitalcamo = 0 M.icon = saved_icon if(istype(M, /mob/living/carbon/human)) @@ -539,7 +539,7 @@ var/induromol_code = rand(1, 50) /datum/reagent/phororeagent/expulsicol/on_mob_life(var/mob/living/M as mob, var/alien) if(!message_given) - M << "You don't feel very good..." + to_chat(M, "You don't feel very good...") message_given = 1 spawn(20) @@ -580,7 +580,7 @@ var/induromol_code = rand(1, 50) if(eyes.status & ORGAN_ROBOT) return ..() - M << "You blink and your eyes quickly adapt to enhanced function." + to_chat(M, "You blink and your eyes quickly adapt to enhanced function.") M.client.view = 10 return ..() @@ -596,7 +596,7 @@ var/induromol_code = rand(1, 50) if(M.client) M.client.view = 7 - M << "After a few blinks, you realize the Oculusosone has worn off." + to_chat(M, "After a few blinks, you realize the Oculusosone has worn off.") return ..() ////////////////////////////////////////////////////////////////////////////////// @@ -699,7 +699,7 @@ var/induromol_code = rand(1, 50) /datum/reagent/phororeagent/caloran/on_mob_life(var/mob/living/M as mob, var/alien) if(volume >= 2) if(burn == -1) - M << "You feel your skin painfully harden." + to_chat(M, "You feel your skin painfully harden.") M.take_overall_damage(20, 0) burn = M.getFireLoss() else @@ -712,7 +712,7 @@ var/induromol_code = rand(1, 50) /datum/reagent/phororeagent/caloran/on_remove(var/atom/A) if(istype(A, /mob)) var/mob/M = A - M << "Your skin returns to normal, no longer desensitized to extreme heat." + to_chat(M, "Your skin returns to normal, no longer desensitized to extreme heat.") return ..() /datum/reagent/phororeagent/the_stuff @@ -725,7 +725,7 @@ var/induromol_code = rand(1, 50) /datum/reagent/phororeagent/the_stuff/on_mob_life(var/mob/living/M as mob, var/alien) if(!init) - M << "You start tripping balls." + to_chat(M, "You start tripping balls.") init = 1 var/drugs = list("space_drugs", "serotrotium", "psilocybin", "nuka_cola", "atomicbomb", "hippiesdelight") for(var/drug in drugs) @@ -741,7 +741,7 @@ var/induromol_code = rand(1, 50) /datum/reagent/phororeagent/frioline/on_mob_life(var/mob/living/M as mob, var/alien) if(M.bodytemperature > 310) - M << "You suddenly feel very cold." + to_chat(M, "You suddenly feel very cold.") M.bodytemperature = max(165, M.bodytemperature - 30) return ..() @@ -895,10 +895,10 @@ var/induromol_code = rand(1, 50) if(world.time - initial_time >= 30) //three second startup lag if(!metabolism) metabolism = 1 - M << "You begin to feel transcendental." + to_chat(M, "You begin to feel transcendental.") if(M.z > 5 || M.z == 2 || M.z < 1) //no centcomm teleport, also not dealing with other unknown sectors - M << "You feel the bluespace leave your body on this sector, nothing happens." + to_chat(M, "You feel the bluespace leave your body on this sector, nothing happens.") src = null return @@ -1094,11 +1094,11 @@ not yet finished to a satisfactory degree, or I just don't like it enough to kee if(volume <= 10) return for(var/mob/M in viewers(7, T)) - M << "You hear sizzling as the solution begins to eat away the [T.name]." + to_chat(M, "You hear sizzling as the solution begins to eat away the [T.name].") sleep(30) if(volume >= 50) for(var/mob/M in viewers(7, T)) - M << "The acid melts through the [T.name]!" + to_chat(M, "The acid melts through the [T.name]!") if(istype(T, /turf/simulated/wall)) for(var/obj/O in T.contents) //Shamelessly stolen from walls.dm @@ -1113,7 +1113,7 @@ not yet finished to a satisfactory degree, or I just don't like it enough to kee //del(src) else for(var/mob/M in viewers(7, T)) - M << "The sizzling stops leaving the floor intact." + to_chat(M, "The sizzling stops leaving the floor intact.") return reaction_obj(var/obj/O, var/volume) @@ -1122,15 +1122,15 @@ not yet finished to a satisfactory degree, or I just don't like it enough to kee if(volume <= 10) return for(var/mob/M in viewers(7, O)) - M << "You hear sizzling as the solution begins to eat away the [O.name]." + to_chat(M, "You hear sizzling as the solution begins to eat away the [O.name].") sleep(30) if(volume >= 50) for(var/mob/M in viewers(7, O)) - M << "The acid melts through the [O.name]!" + to_chat(M, "The acid melts through the [O.name]!") del(O) else for(var/mob/M in viewers(7, O)) - M << "The sizzling stops leaving the floor intact." + to_chat(M, "The sizzling stops leaving the floor intact.") else return ..() diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index dd52884833..fc1a8e05ca 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -50,9 +50,10 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = if(query.NextRow()) validckey = 1 if(!validckey) - if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) - message_admins("[key_name_admin(usr)] attempted to ban [ckey], but [ckey] has not been seen yet. Please only ban actual players.",1) - return + if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) //VOREStation Edit Start. + var/confirm = alert(usr, "This ckey hasn't been seen, are you sure?", "Confirm Badmin" , "Yes", "No") + if(confirm == "No") + return //VOREStation Edit End var/a_ckey var/a_computerid @@ -82,7 +83,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = var/sql = "INSERT INTO erro_ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)" var/DBQuery/query_insert = dbcon.NewQuery(sql) query_insert.Execute() - usr << "Ban saved to database." + to_chat(usr, "Ban saved to database.") message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) @@ -136,17 +137,17 @@ datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") ban_number++; if(ban_number == 0) - usr << "Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin." + to_chat(usr, "Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.") return if(ban_number > 1) - usr << "Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin." + to_chat(usr, "Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin.") return if(istext(ban_id)) ban_id = text2num(ban_id) if(!isnum(ban_id)) - usr << "Database update failed due to a ban ID mismatch. Contact the database admin." + to_chat(usr, "Database update failed due to a ban ID mismatch. Contact the database admin.") return DB_ban_unban_by_id(ban_id) @@ -156,7 +157,7 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) if(!check_rights(R_BAN)) return if(!isnum(banid) || !istext(param)) - usr << "Cancelled" + to_chat(usr, "Cancelled") return var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason FROM erro_ban WHERE id = [banid]") @@ -172,7 +173,7 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) duration = query.item[2] reason = query.item[3] else - usr << "Invalid ban id. Contact the database admin" + to_chat(usr, "Invalid ban id. Contact the database admin") return reason = sql_sanitize_text(reason) @@ -184,7 +185,7 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) value = sanitize(input("Insert the new reason for [pckey]'s ban", "New Reason", "[reason]", null) as null|text) value = sql_sanitize_text(value) if(!value) - usr << "Cancelled" + to_chat(usr, "Cancelled") return var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from \\\"[reason]\\\" to \\\"[value]\\\"
') WHERE id = [banid]") @@ -194,7 +195,7 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) if(!value) value = input("Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) as null|num if(!isnum(value) || !value) - usr << "Cancelled" + to_chat(usr, "Cancelled") return var/DBQuery/update_query = dbcon.NewQuery("UPDATE erro_ban SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]
'), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]") @@ -205,10 +206,10 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) DB_ban_unban_by_id(banid) return else - usr << "Cancelled" + to_chat(usr, "Cancelled") return else - usr << "Cancelled" + to_chat(usr, "Cancelled") return datum/admins/proc/DB_ban_unban_by_id(var/id) @@ -231,11 +232,11 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) ban_number++; if(ban_number == 0) - usr << "Database update failed due to a ban id not being present in the database." + to_chat(usr, "Database update failed due to a ban id not being present in the database.") return if(ban_number > 1) - usr << "Database update failed due to multiple bans having the same ID. Contact the database admin." + to_chat(usr, "Database update failed due to multiple bans having the same ID. Contact the database admin.") return if(!src.owner || !istype(src.owner, /client)) @@ -271,7 +272,7 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) establish_db_connection() if(!dbcon.IsConnected()) - usr << "Failed to establish database connection" + to_chat(usr, "Failed to establish database connection") return var/output = "
" @@ -300,7 +301,7 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) output += "" for(var/j in get_all_jobs()) output += "" - for(var/j in nonhuman_positions) + for(var/j in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) output += "" var/list/bantypes = list("traitor","changeling","operative","revolutionary","cultist","wizard") //For legacy bans. for(var/antag_type in all_antag_types) // Grab other bans. diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 18efe723f0..944cdc552a 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -106,7 +106,7 @@ var/savefile/Banlist Banlist.cd = "/base" if ( Banlist.dir.Find("[ckey][computerid]") ) - usr << text("Ban already exists.") + to_chat(usr, "Ban already exists.") return 0 else Banlist.dir.Add("[ckey][computerid]") @@ -208,17 +208,17 @@ var/savefile/Banlist Banlist.cd = "/base" Banlist.dir.Add("trash[i]trashid[i]") Banlist.cd = "/base/trash[i]trashid[i]" - Banlist["key"] << "trash[i]" + to_chat(Banlist["key"], "trash[i]") else Banlist.cd = "/base" Banlist.dir.Add("[last]trashid[i]") Banlist.cd = "/base/[last]trashid[i]" Banlist["key"] << last - Banlist["id"] << "trashid[i]" - Banlist["reason"] << "Trashban[i]." + to_chat(Banlist["id"], "trashid[i]") + to_chat(Banlist["reason"], "Trashban[i].") Banlist["temp"] << a Banlist["minutes"] << CMinutes + rand(1,2000) - Banlist["bannedby"] << "trashmin" + to_chat(Banlist["bannedby"], "trashmin") last = "trash[i]" Banlist.cd = "/base" diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm index f4ddd3e3f2..1e2d5ef49c 100644 --- a/code/modules/admin/ToRban.dm +++ b/code/modules/admin/ToRban.dm @@ -37,7 +37,8 @@ F[cleaned] << 1 F["last_update"] << world.realtime log_misc("ToR data updated!") - if(usr) usr << "ToRban updated." + if(usr) + to_chat(usr, "ToRban updated.") return 1 log_misc("ToR data update aborted: no data.") return 0 diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 500bc42633..6fc26c7d48 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -10,7 +10,7 @@ var/global/floorIsLava = 0 for(var/client/C in admins) if((R_ADMIN|R_MOD) & C.holder.rights) - C << msg + to_chat(C,msg) /proc/msg_admin_attack(var/text) //Toggleable Attack Messages var/rendered = "ATTACK: [text]" @@ -18,12 +18,12 @@ var/global/floorIsLava = 0 if((R_ADMIN|R_MOD) & C.holder.rights) if(C.is_preference_enabled(/datum/client_preference/mod/show_attack_logs)) var/msg = rendered - C << msg + to_chat(C,msg) proc/admin_notice(var/message, var/rights) for(var/mob/M in mob_list) if(check_rights(rights, 0, M)) - M << message + to_chat(M,message) ///////////////////////////////////////////////////////////////////////////////////////////////Panels @@ -33,12 +33,12 @@ proc/admin_notice(var/message, var/rights) set desc="Edit player (respawn, ban, heal, etc)" if(!M) - usr << "You seem to be selecting a mob that doesn't exist anymore." + to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") return if (!istype(src,/datum/admins)) src = usr.client.holder if (!istype(src,/datum/admins)) - usr << "Error: you are not an admin!" + to_chat(usr, "Error: you are not an admin!") return var/body = "Options for [M.key]" @@ -75,6 +75,7 @@ proc/admin_notice(var/message, var/rights) if(M.client) body += "| Prison | " + body += "\ Send back to Lobby | " var/muted = M.client.prefs.muted body += {"
Mute: \[IC | @@ -90,7 +91,7 @@ proc/admin_notice(var/message, var/rights) Get | Send To

- [check_rights(R_ADMIN|R_MOD,0) ? "Traitor panel | " : "" ] + [check_rights(R_ADMIN|R_MOD|R_EVENT,0) ? "Traitor panel | " : "" ] Narrate to | Subtle message "} @@ -128,6 +129,8 @@ proc/admin_notice(var/message, var/rights) else body += "Animalize | " + body += "Respawn | " + // DNA2 - Admin Hax if(M.dna && iscarbon(M)) body += "

" @@ -222,7 +225,7 @@ proc/admin_notice(var/message, var/rights) if (!istype(src,/datum/admins)) src = usr.client.holder if (!istype(src,/datum/admins)) - usr << "Error: you are not an admin!" + to_chat(usr, "Error: you are not an admin!") return PlayerNotesPage(1) @@ -280,7 +283,7 @@ proc/admin_notice(var/message, var/rights) if (!istype(src,/datum/admins)) src = usr.client.holder if (!istype(src,/datum/admins)) - usr << "Error: you are not an admin!" + to_chat(usr, "Error: you are not an admin!") return var/dat = "Info on [key]" dat += "" @@ -330,7 +333,7 @@ proc/admin_notice(var/message, var/rights) if (!istype(src,/datum/admins)) src = usr.client.holder if (!istype(src,/datum/admins)) - usr << "Error: you are not an admin!" + to_chat(usr, "Error: you are not an admin!") return var/dat dat = text("Admin Newscaster

Admin Newscaster Unit

") @@ -561,8 +564,8 @@ proc/admin_notice(var/message, var/rights) else dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" - //world << "Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]" - //world << "Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]" + //to_world("Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]") + //to_world("Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]") usr << browse(dat, "window=admincaster_main;size=400x600") onclose(usr, "admincaster_main") @@ -645,7 +648,7 @@ proc/admin_notice(var/message, var/rights) if(confirm == "Cancel") return if(confirm == "Yes") - world << "Restarting world!Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]!" + to_world("Restarting world!Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]!") log_admin("[key_name(usr)] initiated a reboot.") feedback_set_details("end_error","admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]") @@ -669,7 +672,7 @@ proc/admin_notice(var/message, var/rights) if(!check_rights(R_SERVER,0)) message = sanitize(message, 500, extra = 0) message = replacetext(message, "\n", "
") // required since we're putting it in a

tag - world << "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:

[message]

" + to_world("[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:

[message]

") log_admin("Announce: [key_name(usr)] : [message]") feedback_add_details("admin_verb","A") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -710,7 +713,7 @@ var/datum/announcement/minor/admin_min_announcer = new if(!channel) //They picked a channel return - to_chat(usr,"Intercom Convo Directions
Start the conversation with the sender, a pipe (|), and then the message on one line. Then hit enter to \ + to_chat(usr, "Intercom Convo Directions
Start the conversation with the sender, a pipe (|), and then the message on one line. Then hit enter to \ add another line, and type a (whole) number of seconds to pause between that message, and the next message, then repeat the message syntax up to 20 times. For example:
\ --- --- ---
\ Some Guy|Hello guys, what's up?
\ @@ -734,12 +737,12 @@ var/datum/announcement/minor/admin_min_announcer = new //Time to find how they screwed up. //Wasn't the right length if((decomposed.len) % 3) //+1 to accomidate the lack of a wait time for the last message - to_chat(usr,"You passed [decomposed.len] segments (senders+messages+pauses). You must pass a multiple of 3, minus 1 (no pause after the last message). That means a sender and message on every other line (starting on the first), separated by a pipe character (|), and a number every other line that is a pause in seconds.") + to_chat(usr, "You passed [decomposed.len] segments (senders+messages+pauses). You must pass a multiple of 3, minus 1 (no pause after the last message). That means a sender and message on every other line (starting on the first), separated by a pipe character (|), and a number every other line that is a pause in seconds.") return //Too long a conversation if((decomposed.len / 3) > 20) - to_chat(usr,"This conversation is too long! 20 messages maximum, please.") + to_chat(usr, "This conversation is too long! 20 messages maximum, please.") return //Missed some sleeps, or sanitized to nothing. @@ -748,24 +751,24 @@ var/datum/announcement/minor/admin_min_announcer = new //Sanitize sender var/clean_sender = sanitize(decomposed[i]) if(!clean_sender) - to_chat(usr,"One part of your conversation was not able to be sanitized. It was the sender of the [(i+2)/3]\th message.") + to_chat(usr, "One part of your conversation was not able to be sanitized. It was the sender of the [(i+2)/3]\th message.") return decomposed[i] = clean_sender //Sanitize message var/clean_message = sanitize(decomposed[++i]) if(!clean_message) - to_chat(usr,"One part of your conversation was not able to be sanitized. It was the body of the [(i+2)/3]\th message.") + to_chat(usr, "One part of your conversation was not able to be sanitized. It was the body of the [(i+2)/3]\th message.") return decomposed[i] = clean_message //Sanitize wait time var/clean_time = text2num(decomposed[++i]) if(!isnum(clean_time)) - to_chat(usr,"One part of your conversation was not able to be sanitized. It was the wait time after the [(i+2)/3]\th message.") + to_chat(usr, "One part of your conversation was not able to be sanitized. It was the wait time after the [(i+2)/3]\th message.") return if(clean_time > 60) - to_chat(usr,"Max 60 second wait time between messages for sanity's sake please.") + to_chat(usr, "Max 60 second wait time between messages for sanity's sake please.") return decomposed[i] = clean_time @@ -791,9 +794,9 @@ var/datum/announcement/minor/admin_min_announcer = new config.ooc_allowed = !(config.ooc_allowed) if (config.ooc_allowed) - world << "The OOC channel has been globally enabled!" + to_world("The OOC channel has been globally enabled!") else - world << "The OOC channel has been globally disabled!" + to_world("The OOC channel has been globally disabled!") log_and_message_admins("toggled OOC.") feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -807,9 +810,9 @@ var/datum/announcement/minor/admin_min_announcer = new config.looc_allowed = !(config.looc_allowed) if (config.looc_allowed) - world << "The LOOC channel has been globally enabled!" + to_world("The LOOC channel has been globally enabled!") else - world << "The LOOC channel has been globally disabled!" + to_world("The LOOC channel has been globally disabled!") log_and_message_admins("toggled LOOC.") feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -824,9 +827,9 @@ var/datum/announcement/minor/admin_min_announcer = new config.dsay_allowed = !(config.dsay_allowed) if (config.dsay_allowed) - world << "Deadchat has been globally enabled!" + to_world("Deadchat has been globally enabled!") else - world << "Deadchat has been globally disabled!" + to_world("Deadchat has been globally disabled!") log_admin("[key_name(usr)] toggled deadchat.") message_admins("[key_name_admin(usr)] toggled deadchat.", 1) feedback_add_details("admin_verb","TDSAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc @@ -868,21 +871,26 @@ var/datum/announcement/minor/admin_min_announcer = new /datum/admins/proc/startnow() set category = "Server" - set desc="Start the round RIGHT NOW" + set desc="Start the round ASAP" set name="Start Now" - if(!ticker) - alert("Unable to start the game as it is not set up.") + + if(!check_rights(R_SERVER|R_EVENT)) return - if(ticker.current_state == GAME_STATE_PREGAME) - ticker.current_state = GAME_STATE_SETTING_UP - Master.SetRunLevel(RUNLEVEL_SETUP) - log_admin("[usr.key] has started the game.") - message_admins("[usr.key] has started the game.") + if(SSticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "Error: Start Now: Game has already started.") + return + if(!SSticker.start_immediately) + SSticker.start_immediately = TRUE + var/msg = "" + if(SSticker.current_state == GAME_STATE_INIT) + msg = " (The server is still setting up, but the round will be started as soon as possible.)" + log_admin("[key_name(usr)] has started the game.[msg]") + message_admins("[key_name_admin(usr)] has started the game.[msg]") feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return 1 else - usr << "Error: Start Now: Game has already started." - return 0 + SSticker.start_immediately = FALSE + to_chat(world, "Immediate game start canceled. Normal startup resumed.") + log_and_message_admins("cancelled immediate game start.") /datum/admins/proc/toggleenter() set category = "Server" @@ -890,9 +898,9 @@ var/datum/announcement/minor/admin_min_announcer = new set name="Toggle Entering" config.enter_allowed = !(config.enter_allowed) if (!(config.enter_allowed)) - world << "New players may no longer enter the game." + to_world("New players may no longer enter the game.") else - world << "New players may now enter the game." + to_world("New players may now enter the game.") log_admin("[key_name(usr)] toggled new player game entering.") message_admins("[key_name_admin(usr)] toggled new player game entering.", 1) world.update_status() @@ -904,9 +912,9 @@ var/datum/announcement/minor/admin_min_announcer = new set name="Toggle AI" config.allow_ai = !( config.allow_ai ) if (!( config.allow_ai )) - world << "The AI job is no longer chooseable." + to_world("The AI job is no longer chooseable.") else - world << "The AI job is chooseable now." + to_world("The AI job is chooseable now.") log_admin("[key_name(usr)] toggled AI allowed.") world.update_status() feedback_add_details("admin_verb","TAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -917,9 +925,9 @@ var/datum/announcement/minor/admin_min_announcer = new set name="Toggle Respawn" config.abandon_allowed = !(config.abandon_allowed) if(config.abandon_allowed) - world << "You may now respawn." + to_world("You may now respawn.") else - world << "You may no longer respawn :(" + to_world("You may no longer respawn :(") message_admins("[key_name_admin(usr)] toggled respawn to [config.abandon_allowed ? "On" : "Off"].", 1) log_admin("[key_name(usr)] toggled respawn to [config.abandon_allowed ? "On" : "Off"].") world.update_status() @@ -949,17 +957,17 @@ var/datum/announcement/minor/admin_min_announcer = new set name="Delay" if(!check_rights(R_SERVER|R_EVENT)) return - if (!ticker || ticker.current_state != GAME_STATE_PREGAME) - ticker.delay_end = !ticker.delay_end - log_admin("[key_name(usr)] [ticker.delay_end ? "delayed the round end" : "has made the round end normally"].") - message_admins("[key_name(usr)] [ticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) + if (SSticker.current_state >= GAME_STATE_PLAYING) + SSticker.delay_end = !SSticker.delay_end + log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") + message_admins("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) return //alert("Round end delayed", null, null, null, null, null) round_progressing = !round_progressing if (!round_progressing) - world << "The game start has been delayed." + to_world("The game start has been delayed.") log_admin("[key_name(usr)] delayed the game.") else - world << "The game will start soon." + to_world("The game will start soon.") log_admin("[key_name(usr)] removed the delay.") feedback_add_details("admin_verb","DELAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -994,7 +1002,7 @@ var/datum/announcement/minor/admin_min_announcer = new if(!usr.client.holder) return if( alert("Reboot server?",,"Yes","No") == "No") return - world << "Rebooting world! Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]!" + to_world("Rebooting world! Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]!") log_admin("[key_name(usr)] initiated an immediate reboot.") feedback_set_details("end_error","immediate admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]") @@ -1088,18 +1096,18 @@ var/datum/announcement/minor/admin_min_announcer = new if(!check_rights(R_SPAWN)) return if(!custom_items) - usr << "Custom item list is null." + to_chat(usr, "Custom item list is null.") return if(!custom_items.len) - usr << "Custom item list not populated." + to_chat(usr, "Custom item list not populated.") return for(var/assoc_key in custom_items) - usr << "[assoc_key] has:" + to_chat(usr, "[assoc_key] has:") var/list/current_items = custom_items[assoc_key] for(var/datum/custom_item/item in current_items) - usr << "- name: [item.name] icon: [item.item_icon] path: [item.item_path] desc: [item.item_desc]" + to_chat(usr, "- name: [item.name] icon: [item.item_icon] path: [item.item_path] desc: [item.item_desc]") /datum/admins/proc/spawn_plant(seedtype in plant_controller.seeds) set category = "Debug" @@ -1154,10 +1162,10 @@ var/datum/announcement/minor/admin_min_announcer = new set name = "Show Traitor Panel" if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(!M.mind) - usr << "This mob has no mind!" + to_chat(usr, "This mob has no mind!") return M.mind.edit_memory() @@ -1243,9 +1251,9 @@ var/datum/announcement/minor/admin_min_announcer = new set name="Toggle tinted welding helmets." config.welder_vision = !( config.welder_vision ) if (config.welder_vision) - world << "Reduced welder vision has been enabled!" + to_world("Reduced welder vision has been enabled!") else - world << "Reduced welder vision has been disabled!" + to_world("Reduced welder vision has been disabled!") log_admin("[key_name(usr)] toggled welder vision.") message_admins("[key_name_admin(usr)] toggled welder vision.", 1) feedback_add_details("admin_verb","TTWH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -1256,9 +1264,9 @@ var/datum/announcement/minor/admin_min_announcer = new set name="Toggle guests" config.guests_allowed = !(config.guests_allowed) if (!(config.guests_allowed)) - world << "Guests may no longer enter the game." + to_world("Guests may no longer enter the game.") else - world << "Guests may now enter the game." + to_world("Guests may now enter the game.") log_admin("[key_name(usr)] toggled guests game entering [config.guests_allowed?"":"dis"]allowed.") message_admins("[key_name_admin(usr)] toggled guests game entering [config.guests_allowed?"":"dis"]allowed.", 1) feedback_add_details("admin_verb","TGU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -1268,21 +1276,21 @@ var/datum/announcement/minor/admin_min_announcer = new for(var/mob/living/silicon/S in mob_list) ai_number++ if(isAI(S)) - usr << "AI [key_name(S, usr)]'s laws:" + to_chat(usr, "AI [key_name(S, usr)]'s laws:") else if(isrobot(S)) var/mob/living/silicon/robot/R = S - usr << "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independent)"]: laws:" + to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independent)"]: laws:") else if (ispAI(S)) - usr << "pAI [key_name(S, usr)]'s laws:" + to_chat(usr, "pAI [key_name(S, usr)]'s laws:") else - usr << "SOMETHING SILICON [key_name(S, usr)]'s laws:" + to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:") if (S.laws == null) - usr << "[key_name(S, usr)]'s laws are null?? Contact a coder." + to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.") else S.laws.show_laws(usr) if(!ai_number) - usr << "No AIs located" //Just so you know the thing is actually working and not just ignoring you. + to_chat(usr, "No AIs located") //Just so you know the thing is actually working and not just ignoring you. /datum/admins/proc/show_skills() set category = "Admin" @@ -1291,7 +1299,7 @@ var/datum/announcement/minor/admin_min_announcer = new if (!istype(src,/datum/admins)) src = usr.client.holder if (!istype(src,/datum/admins)) - usr << "Error: you are not an admin!" + to_chat(usr, "Error: you are not an admin!") return var/mob/living/carbon/human/M = input("Select mob.", "Select mob.") as null|anything in human_mob_list @@ -1373,7 +1381,7 @@ var/datum/announcement/minor/admin_min_announcer = new return //Extra sanity check to make sure only observers are shoved into things //Same as assume-direct-control perm requirements. - if (!check_rights(R_VAREDIT,0) || !check_rights(R_ADMIN|R_DEBUG,0)) + if (!check_rights(R_VAREDIT,0) || !check_rights(R_ADMIN|R_DEBUG|R_EVENT,0)) return 0 if (!frommob.ckey) return 0 @@ -1403,16 +1411,16 @@ var/datum/announcement/minor/admin_min_announcer = new if (!istype(src,/datum/admins)) src = usr.client.holder if (!istype(src,/datum/admins)) - usr << "Error: you are not an admin!" + to_chat(usr, "Error: you are not an admin!") return if(!ticker || !ticker.mode) - usr << "Mode has not started." + to_chat(usr, "Mode has not started.") return var/antag_type = input("Choose a template.","Force Latespawn") as null|anything in all_antag_types if(!antag_type || !all_antag_types[antag_type]) - usr << "Aborting." + to_chat(usr, "Aborting.") return var/datum/antagonist/antag = all_antag_types[antag_type] @@ -1426,12 +1434,12 @@ var/datum/announcement/minor/admin_min_announcer = new if (!istype(src,/datum/admins)) src = usr.client.holder - if (!istype(src,/datum/admins) || !check_rights(R_ADMIN)) - usr << "Error: you are not an admin!" + if (!istype(src,/datum/admins) || !check_rights(R_ADMIN|R_EVENT|R_FUN)) + to_chat(usr, "Error: you are not an admin!") return if(!ticker || !ticker.mode) - usr << "Mode has not started." + to_chat(usr, "Mode has not started.") return log_and_message_admins("attempting to force mode autospawn.") @@ -1444,7 +1452,7 @@ var/datum/announcement/minor/admin_min_announcer = new var/msg - if(check_rights(R_ADMIN|R_MOD)) + if(check_rights(R_ADMIN|R_MOD|R_EVENT)) if (H.paralysis == 0) H.paralysis = 8000 msg = "has paralyzed [key_name(H)]." @@ -1462,14 +1470,14 @@ var/datum/announcement/minor/admin_min_announcer = new set popup_menu = FALSE //VOREStation Edit - Declutter. var/crystals - if(check_rights(R_ADMIN)) + if(check_rights(R_ADMIN|R_EVENT)) crystals = input("Amount of telecrystals for [H.ckey], currently [H.mind.tcrystals].", crystals) as null|num if (!isnull(crystals)) H.mind.tcrystals = crystals var/msg = "[key_name(usr)] has modified [H.ckey]'s telecrystals to [crystals]." message_admins(msg) else - usr << "You do not have access to this command." + to_chat(usr, "You do not have access to this command.") /datum/admins/proc/add_tcrystals(mob/living/carbon/human/H as mob) set category = "Debug" @@ -1478,14 +1486,14 @@ var/datum/announcement/minor/admin_min_announcer = new set popup_menu = FALSE //VOREStation Edit - Declutter. var/crystals - if(check_rights(R_ADMIN)) + if(check_rights(R_ADMIN|R_EVENT)) crystals = input("Amount of telecrystals to give to [H.ckey], currently [H.mind.tcrystals].", crystals) as null|num if (!isnull(crystals)) H.mind.tcrystals += crystals var/msg = "[key_name(usr)] has added [crystals] to [H.ckey]'s telecrystals." message_admins(msg) else - usr << "You do not have access to this command." + to_chat(usr, "You do not have access to this command.") /datum/admins/proc/sendFax() @@ -1499,7 +1507,7 @@ var/datum/announcement/minor/admin_min_announcer = new if (!istype(src,/datum/admins)) src = usr.client.holder if (!istype(src,/datum/admins)) - usr << "Error: you are not an admin!" + to_chat(usr, "Error: you are not an admin!") return var/replyorigin = input(src.owner, "Please specify who the fax is coming from", "Origin") as text|null @@ -1557,20 +1565,20 @@ datum/admins/var/obj/item/weapon/paper/admin/faxreply // var to hold fax replies if(destination.receivefax(P)) - src.owner << "Message reply to transmitted successfully." + to_chat(src.owner, "Message reply to transmitted successfully.") if(P.sender) // sent as a reply log_admin("[key_name(src.owner)] replied to a fax message from [key_name(P.sender)]") for(var/client/C in admins) - if((R_ADMIN | R_MOD) & C.holder.rights) - C << "FAX LOG:[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(P.sender)] (VIEW)" + if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights) + to_chat(C, "FAX LOG:[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(P.sender)] (VIEW)") else log_admin("[key_name(src.owner)] has sent a fax message to [destination.department]") for(var/client/C in admins) - if((R_ADMIN | R_MOD) & C.holder.rights) - C << "FAX LOG:[key_name_admin(src.owner)] has sent a fax message to [destination.department] (VIEW)" + if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights) + to_chat(C, "FAX LOG:[key_name_admin(src.owner)] has sent a fax message to [destination.department] (VIEW)") else - src.owner << "Message reply failed." + to_chat(src.owner, "Message reply failed.") spawn(100) qdel(P) diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 12a84346b3..8dc1e9de74 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -23,7 +23,7 @@ if(!message) return var/F = investigate_subject2file(subject) if(!F) return - F << "[time2text(world.timeofday,"hh:mm")] \ref[src] ([x],[y],[z]) || [src] [message]
" + to_chat(F, "[time2text(world.timeofday,"hh:mm")] \ref[src] ([x],[y],[z]) || [src] [message]
") //ADMINVERBS /client/proc/investigate_show( subject in list("hrefs","notes","singulo","telesci") ) diff --git a/code/modules/admin/admin_memo.dm b/code/modules/admin/admin_memo.dm index 24ecba7c01..007ca49ba5 100644 --- a/code/modules/admin/admin_memo.dm +++ b/code/modules/admin/admin_memo.dm @@ -26,7 +26,7 @@ return if( findtext(memo,"[memo]" + to_chat(F[ckey], "[key] on [time2text(world.realtime,"(DDD) DD MMM hh:mm")]
[memo]") message_admins("[key] set an admin memo:
[memo]") //show all memos diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 2e2de809c4..257b47b431 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1,6 +1,7 @@ //admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless var/list/admin_verbs_default = list( /datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags, + /client/proc/player_panel_new, //shows an interface for all players, with links to various panels, /client/proc/player_panel, /client/proc/deadmin_self, //destroys our own admin datum so we can play as a regular player, /client/proc/hide_verbs, //hides all our adminverbs, @@ -15,7 +16,6 @@ var/list/admin_verbs_default = list( ) var/list/admin_verbs_admin = list( - /client/proc/player_panel_new, //shows an interface for all players, with links to various panels, /datum/admins/proc/set_tcrystals, /datum/admins/proc/add_tcrystals, /client/proc/invisimin, //allows our mob to go invisible/visible, @@ -107,7 +107,8 @@ var/list/admin_verbs_admin = list( /client/proc/fixatmos, /datum/admins/proc/quick_nif, //VOREStation Add, /datum/admins/proc/sendFax, - /client/proc/despawn_player + /client/proc/despawn_player, + /datum/admins/proc/view_feedback ) var/list/admin_verbs_ban = list( @@ -140,7 +141,7 @@ var/list/admin_verbs_fun = list( /datum/admins/proc/call_drop_pod, /client/proc/smite_vr, //VOREStation Add, /client/proc/smite, - /client/proc/admin_lightning_strike + /client/proc/admin_lightning_strike, ) var/list/admin_verbs_spawn = list( @@ -220,7 +221,6 @@ var/list/admin_verbs_debug = list( /client/proc/enable_debug_verbs, /client/proc/callproc, /client/proc/callproc_datum, - /client/proc/debug_process, //VOREStation Add, /client/proc/SDQL2_query, /client/proc/Jump, /client/proc/debug_rogueminer, @@ -230,17 +230,17 @@ var/list/admin_verbs_debug = list( /client/proc/toggle_debug_logs, /client/proc/admin_ghost, //allows us to ghost/reenter body at will, /datum/admins/proc/view_runtimes, - /client/proc/show_gm_status, + // /client/proc/show_gm_status, // VOREStation Edit - We don't use SSgame_master yet. /datum/admins/proc/change_weather, /datum/admins/proc/change_time, /client/proc/admin_give_modifier, - /client/proc/simple_DPS + /client/proc/simple_DPS, + /datum/admins/proc/view_feedback ) var/list/admin_verbs_paranoid_debug = list( /client/proc/callproc, /client/proc/callproc_datum, - /client/proc/debug_process, //VOREStation Add, /client/proc/debug_controller ) @@ -311,7 +311,6 @@ var/list/admin_verbs_hideable = list( /client/proc/cmd_admin_list_open_jobs, /client/proc/callproc, /client/proc/callproc_datum, - /client/proc/debug_process, //VOREStation Add, /client/proc/Debug2, /client/proc/reload_admins, /client/proc/kill_air, @@ -341,6 +340,8 @@ var/list/admin_verbs_mod = list( /client/proc/cmd_mod_say, /client/proc/cmd_event_say, /datum/admins/proc/show_player_info, + /datum/admins/proc/show_traitor_panel, + /client/proc/colorooc, /client/proc/player_panel_new, /client/proc/dsay, /datum/admins/proc/show_skills, @@ -359,13 +360,137 @@ var/list/admin_verbs_mod = list( /datum/admins/proc/view_atk_log //shows the server combat-log, doesn't do anything presently, ) -//VOREStation Edit Start - Highly Modified List var/list/admin_verbs_event_manager = list( - /client/proc/cmd_admin_say, //admin-only ooc chat, + /client/proc/cmd_admin_say, //VOREStation Edit - Event managers can use asay, + /client/proc/cmd_admin_pm_context, + /client/proc/cmd_admin_pm_panel, + /client/proc/admin_ghost, + /datum/admins/proc/show_player_info, + /client/proc/dsay, + /client/proc/cmd_admin_subtle_message, + /client/proc/debug_variables, + /client/proc/check_antagonists, + /client/proc/aooc, + /datum/admins/proc/paralyze_mob, + /client/proc/cmd_admin_direct_narrate, + /client/proc/allow_character_respawn, + /datum/admins/proc/sendFax, + /client/proc/respawn_character, + /proc/possess, + /proc/release, + /datum/admins/proc/change_weather, + /datum/admins/proc/change_time, + /client/proc/admin_give_modifier, + /client/proc/Jump, + /client/proc/jumptomob, + /client/proc/jumptocoord, + /client/proc/cmd_admin_delete, + /datum/admins/proc/delay, + /client/proc/Set_Holiday, + /client/proc/make_sound, + /client/proc/toggle_random_events, + /datum/admins/proc/cmd_admin_dress, + /client/proc/cmd_admin_gib_self, + /client/proc/drop_bomb, + /client/proc/cmd_admin_add_freeform_ai_law, + /client/proc/cmd_admin_add_random_ai_law, + /client/proc/make_sound, + /client/proc/toggle_random_events, + /client/proc/editappear, + /client/proc/roll_dices, + /datum/admins/proc/call_supply_drop, + /datum/admins/proc/call_drop_pod, + /datum/admins/proc/PlayerNotes, + /client/proc/callproc, + /client/proc/callproc_datum, + /client/proc/debug_controller, + // /client/proc/show_gm_status, // VOREStation Edit - We don't use SSgame_master yet. + /datum/admins/proc/change_weather, + /datum/admins/proc/change_time, + /client/proc/admin_give_modifier, + /datum/admins/proc/cmd_admin_dress, + /client/proc/cmd_admin_gib_self, + /datum/admins/proc/set_tcrystals, + /datum/admins/proc/add_tcrystals, + /client/proc/invisimin, //allows our mob to go invisible/visible, + /datum/admins/proc/show_traitor_panel, //interface which shows a mob's mind., + /datum/admins/proc/show_game_mode, //Configuration window for the current game mode., + /datum/admins/proc/force_mode_latespawn, //Force the mode to try a latespawn proc, + /datum/admins/proc/force_antag_latespawn, //Force a specific template to try a latespawn proc, + /datum/admins/proc/announce, //priority announce something to all clients., + /datum/admins/proc/intercom, //send a fake intercom message, like an arrivals announcement, + /datum/admins/proc/intercom_convo, //send a fake intercom conversation, like an ATC exchange, + /client/proc/colorooc, //allows us to set a custom colour for everythign we say in ooc, + /client/proc/admin_ghost, //allows us to ghost/reenter body at will, + /client/proc/toggle_view_range, //changes how far we can see, + /client/proc/cmd_admin_pm_context, //right-click adminPM interface, + /client/proc/cmd_admin_pm_panel, //admin-pm list, + /client/proc/cmd_admin_subtle_message, //send an message to somebody as a 'voice in their head', + /client/proc/cmd_admin_delete, //delete an instance/object/mob/etc, + /client/proc/cmd_admin_check_contents, //displays the contents of an instance, + /client/proc/cmd_admin_check_player_logs, //checks a player's attack logs, + /client/proc/cmd_admin_check_dialogue_logs, //checks a player's dialogue logs, + /datum/admins/proc/access_news_network, //allows access of newscasters, + /client/proc/jumptocoord, //we ghost and jump to a coordinate, + /client/proc/Getmob, //teleports a mob to our location, + /client/proc/Getkey, //teleports a mob with a certain ckey to our location, + /client/proc/Jump, + /client/proc/jumptokey, //allows us to jump to the location of a mob with a certain ckey, + /client/proc/jumptomob, //allows us to jump to a specific mob, + /client/proc/jumptoturf, //allows us to jump to a specific turf, + /client/proc/admin_call_shuttle, //allows us to call the emergency shuttle, + /client/proc/admin_cancel_shuttle, //allows us to cancel the emergency shuttle, sending it back to CentCom, + /client/proc/cmd_admin_direct_narrate, //send text directly to a player with no padding. Useful for narratives and fluff-text, + /client/proc/cmd_admin_world_narrate, //sends text to all players with no padding, + /client/proc/cmd_admin_create_centcom_report, + /client/proc/check_words, //displays cult-words, + /client/proc/check_ai_laws, //shows AI and borg laws, + /client/proc/rename_silicon, //properly renames silicons, + /client/proc/manage_silicon_laws, // Allows viewing and editing silicon laws. , + /client/proc/check_antagonists, + /client/proc/admin_memo, //admin memo system. show/delete/write. +SERVER needed to delete admin memos of others, + /client/proc/dsay, //talk in deadchat using our ckey/fakekey, + /client/proc/secrets, + /client/proc/game_panel, //game panel, allows to change game-mode etc, /client/proc/cmd_mod_say, - /client/proc/cmd_event_say + /client/proc/cmd_event_say, + /datum/admins/proc/show_player_info, + /client/proc/free_slot, //frees slot for chosen job, + /client/proc/cmd_admin_change_custom_event, + /client/proc/cmd_admin_rejuvenate, + /client/proc/toggleghostwriters, + /datum/admins/proc/show_skills, + /client/proc/man_up, + /client/proc/global_man_up, + /client/proc/response_team, // Response Teams admin verb, + /client/proc/trader_ship, // Trader ship admin verb, + /client/proc/allow_character_respawn, // Allows a ghost to respawn , + /client/proc/event_manager_panel, + /client/proc/aooc, + /client/proc/change_human_appearance_admin, // Allows an admin to change the basic appearance of human-based mobs , + /client/proc/change_human_appearance_self, // Allows the human-based mob itself change its basic appearance , + /client/proc/change_security_level, + /client/proc/makePAI, + /client/proc/toggle_debug_logs, + /client/proc/toggle_attack_logs, + /datum/admins/proc/paralyze_mob, + /client/proc/fixatmos, + /datum/admins/proc/sendFax, + /client/proc/despawn_player, + /datum/admins/proc/view_feedback, + /datum/admins/proc/capture_map, + /client/proc/Set_Holiday, + /datum/admins/proc/startnow, + /datum/admins/proc/restart, + /datum/admins/proc/delay, + /client/proc/cmd_mod_say, + /datum/admins/proc/immreboot, + /client/proc/everyone_random, + /client/proc/cmd_admin_delete, //delete an instance/object/mob/etc, + /client/proc/cmd_debug_del_all, + /client/proc/toggle_random_events, + /client/proc/modify_server_news ) -//VOREStation Edit End /client/proc/add_admin_verbs() if(holder) @@ -475,11 +600,11 @@ var/list/admin_verbs_event_manager = list( if(holder && mob) if(mob.invisibility == INVISIBILITY_OBSERVER) mob.invisibility = initial(mob.invisibility) - mob << "Invisimin off. Invisibility reset." + to_chat(mob, "Invisimin off. Invisibility reset.") mob.alpha = max(mob.alpha + 100, 255) else mob.invisibility = INVISIBILITY_OBSERVER - mob << "Invisimin on. You are now as invisible as a ghost." + to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") mob.alpha = max(mob.alpha - 100, 0) @@ -609,7 +734,7 @@ var/list/admin_verbs_event_manager = list( if(!warned_ckey || !istext(warned_ckey)) return if(warned_ckey in admin_datums) - usr << "Error: warn(): You can't warn admins." + to_chat(usr, "Error: warn(): You can't warn admins.") return var/datum/preferences/D @@ -625,7 +750,7 @@ var/list/admin_verbs_event_manager = list( ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.") if(C) message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban.") - C << "You have been autobanned due to a warning by [ckey].
This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.
" + to_chat(C, "You have been autobanned due to a warning by [ckey].
This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.
") del(C) else message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban.") @@ -633,7 +758,7 @@ var/list/admin_verbs_event_manager = list( feedback_inc("ban_warn",1) else if(C) - C << "You have been formally warned by an administrator.
Further warnings will result in an autoban.
" + to_chat(C, "You have been formally warned by an administrator.
Further warnings will result in an autoban.
") message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") else message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") @@ -765,10 +890,10 @@ var/list/admin_verbs_event_manager = list( set desc = "Toggle Air Processing" if(!SSair.can_fire) SSair.can_fire = TRUE - usr << "Enabled air processing." + to_chat(usr, "Enabled air processing.") else SSair.can_fire = FALSE - usr << "Disabled air processing." + to_chat(usr, "Disabled air processing.") feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] used 'kill air'.") message_admins("[key_name_admin(usr)] used 'kill air'.", 1) @@ -819,7 +944,7 @@ var/list/admin_verbs_event_manager = list( set name = "Rename Silicon" set category = "Admin" - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return var/mob/living/silicon/S = input("Select silicon.", "Rename Silicon.") as null|anything in silicon_mob_list if(!S) return @@ -834,7 +959,7 @@ var/list/admin_verbs_event_manager = list( set name = "Manage Silicon Laws" set category = "Admin" - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT)) return var/mob/living/silicon/S = input("Select silicon.", "Manage Silicon Laws") as null|anything in silicon_mob_list if(!S) return @@ -869,7 +994,7 @@ var/list/admin_verbs_event_manager = list( if(!H) return if(!H.client) - usr << "Only mobs with clients can alter their own appearance." + to_chat(usr, "Only mobs with clients can alter their own appearance.") return var/datum/gender/T = gender_datums[H.get_visible_gender()] switch(alert("Do you wish for [H] to be allowed to select non-whitelisted races?","Alter Mob Appearance","Yes","No","Cancel")) @@ -886,7 +1011,7 @@ var/list/admin_verbs_event_manager = list( set desc = "Sets the station security level" set category = "Admin" - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT)) return var sec_level = input(usr, "It's currently code [get_security_level()].", "Select Security Level") as null|anything in (list("green","yellow","violet","orange","blue","red","delta")-get_security_level()) if(alert("Switch from code [get_security_level()] to code [sec_level]?","Change security level?","Yes","No") == "Yes") set_security_level(sec_level) @@ -912,7 +1037,7 @@ var/list/admin_verbs_event_manager = list( var/mob/living/carbon/human/M = input("Select mob.", "Edit Appearance") as null|anything in human_mob_list if(!istype(M, /mob/living/carbon/human)) - usr << "You can only do this to humans!" + to_chat(usr, "You can only do this to humans!") return switch(alert("Are you sure you wish to edit this mob's appearance? Skrell, Unathi, Tajaran can result in unintended consequences.",,"Yes","No")) if("No") @@ -987,7 +1112,7 @@ var/list/admin_verbs_event_manager = list( if (J.current_positions >= J.total_positions && J.total_positions != -1) jobs += J.title if (!jobs.len) - usr << "There are no fully staffed jobs." + to_chat(usr, "There are no fully staffed jobs.") return var/job = input("Please select job slot to free", "Free job slot") as null|anything in jobs if (job) @@ -1031,8 +1156,8 @@ var/list/admin_verbs_event_manager = list( if(alert("Are you sure you want to tell them to man up?","Confirmation","Deal with it","No")=="No") return - T << "Man up and deal with it." - T << "Move along." + to_chat(T, "Man up and deal with it.") + to_chat(T, "Move along.") log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.") message_admins("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it.", 1) @@ -1045,7 +1170,7 @@ var/list/admin_verbs_event_manager = list( if(alert("Are you sure you want to tell the whole server up?","Confirmation","Deal with it","No")=="No") return for (var/mob/T as mob in mob_list) - T << "
Man up.
Deal with it.

Move along.

" + to_chat(T, "
Man up.
Deal with it.

Move along.

") T << 'sound/voice/ManUp1.ogg' log_admin("[key_name(usr)] told everyone to man up and deal with it.") diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 7819c9a582..1a3781b0c5 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -42,7 +42,7 @@ DEBUG set name = "list all jobbans" for(var/s in jobban_keylist) - world << s + to_world(s) /mob/verb/reload_jobbans() set name = "reload jobbans" diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 114808d3d3..92fd1c300b 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -62,7 +62,7 @@ generally it would be used like so: proc/admin_proc() if(!check_rights(R_ADMIN)) return - world << "you have enough rights!" + to_world("you have enough rights!") NOTE: It checks usr by default. Supply the "user" argument if you wish to check for a specific mob. */ @@ -76,7 +76,7 @@ NOTE: It checks usr by default. Supply the "user" argument if you wish to check return FALSE if(!C.holder) if(show_msg) - C << "Error: You are not an admin." + to_chat(C, "Error: You are not an admin.") return FALSE if(rights_required) @@ -84,7 +84,7 @@ NOTE: It checks usr by default. Supply the "user" argument if you wish to check return TRUE else if(show_msg) - C << "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")]." + to_chat(C, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") return FALSE else return TRUE @@ -98,7 +98,7 @@ NOTE: It checks usr by default. Supply the "user" argument if you wish to check if(usr.client.holder.rights != other.holder.rights) if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights ) return 1 //we have all the rights they have and more - usr << "Error: Cannot proceed. They have more or equal rights to us." + to_chat(usr, "Error: Cannot proceed. They have more or equal rights to us.") return 0 /client/proc/mark_datum(datum/D) diff --git a/code/modules/admin/map_capture.dm b/code/modules/admin/map_capture.dm index ced6d257bf..a8cf7717fc 100644 --- a/code/modules/admin/map_capture.dm +++ b/code/modules/admin/map_capture.dm @@ -7,13 +7,13 @@ return if(isnull(tx) || isnull(ty) || isnull(tz) || isnull(range)) - usr << "Capture Map Part, captures part of a map using camara like rendering." - usr << "Usage: Capture-Map-Part target_x_cord target_y_cord target_z_cord range" - usr << "Target coordinates specify bottom left corner of the capture, range defines render distance to opposite corner." + to_chat(usr, "Capture Map Part, captures part of a map using camara like rendering.") + to_chat(usr, "Usage: Capture-Map-Part target_x_cord target_y_cord target_z_cord range") + to_chat(usr, "Target coordinates specify bottom left corner of the capture, range defines render distance to opposite corner.") return if(range > 32 || range <= 0) - usr << "Capturing range is incorrect, it must be within 1-32." + to_chat(usr, "Capturing range is incorrect, it must be within 1-32.") return if(locate(tx,ty,tz)) @@ -53,7 +53,7 @@ cap.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) var/file_name = "map_capture_x[tx]_y[ty]_z[tz]_r[range].png" - usr << "Saved capture in cache as [file_name]." + to_chat(usr, "Saved capture in cache as [file_name].") usr << browse_rsc(cap, file_name) else - usr << "Target coordinates are incorrect." + to_chat(usr, "Target coordinates are incorrect.") diff --git a/code/modules/admin/newbanjob.dm b/code/modules/admin/newbanjob.dm index 9b1d2eeaa3..b97e354301 100644 --- a/code/modules/admin/newbanjob.dm +++ b/code/modules/admin/newbanjob.dm @@ -141,7 +141,7 @@ var/savefile/Banlistjob Banlistjob.cd = "/base" if ( Banlistjob.dir.Find("[ckey][computerid][rank]") ) - usr << text("Banjob already exists.") + to_chat(usr,"Banjob already exists.") return 0 else Banlistjob.dir.Add("[ckey][computerid][rank]") @@ -219,22 +219,22 @@ var/savefile/Banlistjob /*/datum/admins/proc/permjobban(ckey, computerid, reason, bannedby, temp, minutes, rank) if(AddBanjob(ckey, computerid, reason, usr.ckey, 0, 0, job)) - M << "You have been banned from [job] by [usr.client.ckey].\nReason: [reason]." - M << "This is a permanent ban." + to_chat(M, "You have been banned from [job] by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a permanent ban.") if(config.banappeals) - M << "To try to resolve this matter head to [config.banappeals]" + to_chat(M, "To try to resolve this matter head to [config.banappeals]") else - M << "No ban appeals URL has been set." + to_chat(M, "No ban appeals URL has been set.") log_admin("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis is a permanent ban.") /datum/admins/proc/timejobban(ckey, computerid, reason, bannedby, temp, minutes, rank) if(AddBanjob(ckey, computerid, reason, usr.ckey, 1, mins, job)) - M << "You have been jobbanned from [job] by [usr.client.ckey].\nReason: [reason]." - M << "This is a temporary ban, it will be removed in [mins] minutes." + to_chat(M, "You have been jobbanned from [job] by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") if(config.banappeals) - M << "To try to resolve this matter head to [config.banappeals]" + to_chat(M, "To try to resolve this matter head to [config.banappeals]") else - M << "No ban appeals URL has been set." + to_chat(M, "No ban appeals URL has been set.") log_admin("[usr.client.ckey] has jobbanned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") message_admins("[usr.client.ckey] has banned from [job] [ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.")*/ //////////////////////////////////// DEBUG //////////////////////////////////// @@ -253,17 +253,17 @@ var/savefile/Banlistjob Banlistjob.cd = "/base" Banlistjob.dir.Add("trash[i]trashid[i]") Banlistjob.cd = "/base/trash[i]trashid[i]" - Banlistjob["key"] << "trash[i]" + to_chat(Banlistjob["key"], "trash[i]") else Banlistjob.cd = "/base" Banlistjob.dir.Add("[last]trashid[i]") Banlistjob.cd = "/base/[last]trashid[i]" Banlistjob["key"] << last - Banlistjob["id"] << "trashid[i]" - Banlistjob["reason"] << "Trashban[i]." + to_chat(Banlistjob["id"], "trashid[i]") + to_chat(Banlistjob["reason"], "Trashban[i].") Banlistjob["temp"] << a Banlistjob["minutes"] << CMinutes + rand(1,2000) - Banlistjob["bannedby"] << "trashmin" + to_chat(Banlistjob["bannedby"], "trashmin") last = "trash[i]" Banlistjob.cd = "/base" diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm index d0cc5518c7..e75d008576 100644 --- a/code/modules/admin/permissionverbs/permissionedit.dm +++ b/code/modules/admin/permissionverbs/permissionedit.dm @@ -51,13 +51,13 @@ return if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) - usr << "You do not have permission to do this!" + to_chat(usr, "You do not have permission to do this!") return establish_db_connection() if(!dbcon.IsConnected()) - usr << "Failed to establish database connection" + to_chat(usr, "Failed to establish database connection") return if(!adm_ckey || !new_rank) @@ -85,14 +85,14 @@ insert_query.Execute() var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") log_query.Execute() - usr << "New admin added." + to_chat(usr, "New admin added.") else if(!isnull(admin_id) && isnum(admin_id)) var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]") insert_query.Execute() var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") log_query.Execute() - usr << "Admin rank changed." + to_chat(usr, "Admin rank changed.") /datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) if(config.admin_legacy_system) return @@ -101,12 +101,12 @@ return if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS)) - usr << "You do not have permission to do this!" + to_chat(usr, "You do not have permission to do this!") return establish_db_connection() if(!dbcon.IsConnected()) - usr << "Failed to establish database connection" + to_chat(usr, "Failed to establish database connection") return if(!adm_ckey || !new_permission) @@ -140,10 +140,10 @@ insert_query.Execute() var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") log_query.Execute() - usr << "Permission removed." + to_chat(usr, "Permission removed.") else //This admin doesn't have this permission, so we are adding it. var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") insert_query.Execute() var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") log_query.Execute() - usr << "Permission added." \ No newline at end of file + to_chat(usr, "Permission added.") \ No newline at end of file diff --git a/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm b/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm index 4097632aa3..3767d21a8b 100644 --- a/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/jump_shuttle.dm @@ -2,17 +2,17 @@ name = "Jump a Shuttle" /datum/admin_secret_item/admin_secret/jump_shuttle/can_execute(var/mob/user) - if(!shuttle_controller) return 0 + if(!SSshuttles) return 0 return ..() /datum/admin_secret_item/admin_secret/jump_shuttle/execute(var/mob/user) . = ..() if(!.) return - var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles + var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in SSshuttles.shuttles if (!shuttle_tag) return - var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] var/origin_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world if (!origin_area) return diff --git a/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm b/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm index 9d6ce44a2d..2f253a1a80 100644 --- a/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/launch_shuttle.dm @@ -2,7 +2,7 @@ name = "Launch a Shuttle" /datum/admin_secret_item/admin_secret/launch_shuttle/can_execute(var/mob/user) - if(!shuttle_controller) return 0 + if(!SSshuttles) return 0 return ..() /datum/admin_secret_item/admin_secret/launch_shuttle/execute(var/mob/user) @@ -10,15 +10,15 @@ if(!.) return var/list/valid_shuttles = list() - for (var/shuttle_tag in shuttle_controller.shuttles) - if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry)) + for (var/shuttle_tag in SSshuttles.shuttles) + if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) valid_shuttles += shuttle_tag var/shuttle_tag = input(user, "Which shuttle do you want to launch?") as null|anything in valid_shuttles if (!shuttle_tag) return - var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] if (S.can_launch()) S.launch(user) log_and_message_admins("launched the [shuttle_tag] shuttle", user) diff --git a/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm b/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm index d1a86fec98..592a8da57c 100644 --- a/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm +++ b/code/modules/admin/secrets/admin_secrets/launch_shuttle_forced.dm @@ -2,7 +2,7 @@ name = "Launch a Shuttle (Forced)" /datum/admin_secret_item/admin_secret/launch_shuttle_forced/can_execute(var/mob/user) - if(!shuttle_controller) return 0 + if(!SSshuttles) return 0 return ..() /datum/admin_secret_item/admin_secret/launch_shuttle_forced/execute(var/mob/user) @@ -10,15 +10,15 @@ if(!.) return var/list/valid_shuttles = list() - for (var/shuttle_tag in shuttle_controller.shuttles) - if (istype(shuttle_controller.shuttles[shuttle_tag], /datum/shuttle/ferry)) + for (var/shuttle_tag in SSshuttles.shuttles) + if (istype(SSshuttles.shuttles[shuttle_tag], /datum/shuttle/autodock)) valid_shuttles += shuttle_tag var/shuttle_tag = input(user, "Which shuttle's launch do you want to force?") as null|anything in valid_shuttles if (!shuttle_tag) return - var/datum/shuttle/ferry/S = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/S = SSshuttles.shuttles[shuttle_tag] if (S.can_force()) S.force_launch(user) log_and_message_admins("forced the [shuttle_tag] shuttle", user) diff --git a/code/modules/admin/secrets/admin_secrets/move_shuttle.dm b/code/modules/admin/secrets/admin_secrets/move_shuttle.dm index 5772bbed54..a79a2d7143 100644 --- a/code/modules/admin/secrets/admin_secrets/move_shuttle.dm +++ b/code/modules/admin/secrets/admin_secrets/move_shuttle.dm @@ -2,7 +2,7 @@ name = "Move a Shuttle" /datum/admin_secret_item/admin_secret/move_shuttle/can_execute(var/mob/user) - if(!shuttle_controller) return 0 + if(!SSshuttles) return 0 return ..() /datum/admin_secret_item/admin_secret/move_shuttle/execute(var/mob/user) @@ -13,16 +13,15 @@ if (confirm == "Cancel") return - var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in shuttle_controller.shuttles + var/shuttle_tag = input(user, "Which shuttle do you want to jump?") as null|anything in SSshuttles.shuttles if (!shuttle_tag) return - var/datum/shuttle/S = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/S = SSshuttles.shuttles[shuttle_tag] - var/origin_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world - if (!origin_area) return + var/destination_tag = input(user, "Which landmark do you want to jump to? (IF YOU GET THIS WRONG THINGS WILL BREAK)") as null|anything in SSshuttles.registered_shuttle_landmarks + if (!destination_tag) return + var/destination_location = SSshuttles.get_landmark(destination_tag) + if (!destination_location) return - var/destination_area = input(user, "Which area is the shuttle at now? (MAKE SURE THIS IS CORRECT OR THINGS WILL BREAK)") as null|area in world - if (!destination_area) return - - S.move(origin_area, destination_area) + S.attempt_move(destination_location) log_and_message_admins("moved the [shuttle_tag] shuttle", user) diff --git a/code/modules/admin/secrets/fun_secrets/make_all_areas_powered.dm b/code/modules/admin/secrets/fun_secrets/make_all_areas_powered.dm deleted file mode 100644 index 3309190028..0000000000 --- a/code/modules/admin/secrets/fun_secrets/make_all_areas_powered.dm +++ /dev/null @@ -1,7 +0,0 @@ -/datum/admin_secret_item/fun_secret/make_all_areas_powered - name = "Make All Areas Powered" - -/datum/admin_secret_item/fun_secret/make_all_areas_powered/execute(var/mob/user) - . = ..() - if(.) - power_restore() diff --git a/code/modules/admin/secrets/fun_secrets/make_all_areas_unpowered.dm b/code/modules/admin/secrets/fun_secrets/make_all_areas_unpowered.dm deleted file mode 100644 index a840006a23..0000000000 --- a/code/modules/admin/secrets/fun_secrets/make_all_areas_unpowered.dm +++ /dev/null @@ -1,7 +0,0 @@ -/datum/admin_secret_item/fun_secret/make_all_areas_unpowered - name = "Make All Areas Unpowered" - -/datum/admin_secret_item/fun_secret/make_all_areas_unpowered/execute(var/mob/user) - . = ..() - if(.) - power_failure() diff --git a/code/modules/admin/secrets/fun_secrets/power_failure_begin.dm b/code/modules/admin/secrets/fun_secrets/power_failure_begin.dm new file mode 100644 index 0000000000..a5d424bcbe --- /dev/null +++ b/code/modules/admin/secrets/fun_secrets/power_failure_begin.dm @@ -0,0 +1,7 @@ +/datum/admin_secret_item/fun_secret/power_failure_begin + name = "Power Failure Begin" + +/datum/admin_secret_item/fun_secret/power_failure_begin/execute(var/mob/user) + . = ..() + if(.) + power_failure() diff --git a/code/modules/admin/secrets/fun_secrets/power_failure_end.dm b/code/modules/admin/secrets/fun_secrets/power_failure_end.dm new file mode 100644 index 0000000000..1830928e42 --- /dev/null +++ b/code/modules/admin/secrets/fun_secrets/power_failure_end.dm @@ -0,0 +1,7 @@ +/datum/admin_secret_item/fun_secret/power_failure_end + name = "Power Failure End" + +/datum/admin_secret_item/fun_secret/power_failure_end/execute(var/mob/user) + . = ..() + if(.) + power_restore() diff --git a/code/modules/admin/secrets/random_events/gravity.dm b/code/modules/admin/secrets/random_events/gravity.dm index 2b1dc572e1..b01bb49637 100644 --- a/code/modules/admin/secrets/random_events/gravity.dm +++ b/code/modules/admin/secrets/random_events/gravity.dm @@ -16,8 +16,8 @@ return gravity_is_on = !gravity_is_on - for(var/area/A in all_areas) - A.gravitychange(gravity_is_on,A) + for(var/area/A in world) + A.gravitychange(gravity_is_on) feedback_inc("admin_secrets_fun_used",1) feedback_add_details("admin_secrets_fun_used","Grav") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 07168a4e32..fe6048d0c2 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -11,7 +11,7 @@ return if(href_list["ahelp"]) - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return var/ahelp_ref = href_list["ahelp"] @@ -63,23 +63,23 @@ switch(bantype) if(BANTYPE_PERMA) if(!banckey || !banreason) - usr << "Not enough parameters (Requires ckey and reason)" + to_chat(usr, "Not enough parameters (Requires ckey and reason)") return banduration = null banjob = null if(BANTYPE_TEMP) if(!banckey || !banreason || !banduration) - usr << "Not enough parameters (Requires ckey, reason and duration)" + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") return banjob = null if(BANTYPE_JOB_PERMA) if(!banckey || !banreason || !banjob) - usr << "Not enough parameters (Requires ckey, reason and job)" + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") return banduration = null if(BANTYPE_JOB_TEMP) if(!banckey || !banreason || !banjob || !banduration) - usr << "Not enough parameters (Requires ckey, reason and job)" + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") return var/mob/playermob @@ -116,14 +116,14 @@ var/new_ckey = ckey(input(usr,"New admin's ckey","Admin ckey", null) as text|null) if(!new_ckey) return if(new_ckey in admin_datums) - usr << "Error: Topic 'editrights': [new_ckey] is already an admin" + to_chat(usr, "Error: Topic 'editrights': [new_ckey] is already an admin") return adm_ckey = new_ckey task = "rank" else if(task != "show") adm_ckey = ckey(href_list["ckey"]) if(!adm_ckey) - usr << "Error: Topic 'editrights': No valid ckey" + to_chat(usr, "Error: Topic 'editrights': No valid ckey") return var/datum/admins/D = admin_datums[adm_ckey] @@ -155,7 +155,7 @@ if(config.admin_legacy_system) new_rank = ckeyEx(new_rank) if(!new_rank) - usr << "Error: Topic 'editrights': Invalid rank" + to_chat(usr, "Error: Topic 'editrights': Invalid rank") return if(config.admin_legacy_system) if(admin_ranks.len) @@ -198,7 +198,7 @@ edit_admin_permissions() else if(href_list["call_shuttle"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT)) return if( ticker.mode.name == "blob" ) alert("You can't call the shuttle during blob!") @@ -264,7 +264,7 @@ var/mob/M = locate(href_list["mob"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return var/delmob = 0 @@ -370,14 +370,14 @@ var/mob/M = locate(href_list["jobban2"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(!M.ckey) //sanity - usr << "This mob has no ckey" + to_chat(usr, "This mob has no ckey") return if(!job_master) - usr << "Job Master has not been setup!" + to_chat(usr, "Job Master has not been setup!") return var/dat = "" @@ -394,8 +394,8 @@ //Regular jobs //Command (Blue) jobs += "
" - jobs += "" - for(var/jobPos in command_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -415,8 +415,8 @@ //Security (Red) counter = 0 jobs += "
Command Positions
Command Positions
" - jobs += "" - for(var/jobPos in security_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -436,8 +436,8 @@ //Engineering (Yellow) counter = 0 jobs += "
Security Positions
Security Positions
" - jobs += "" - for(var/jobPos in engineering_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -457,8 +457,8 @@ //Cargo (Yellow) counter = 0 jobs += "
Engineering Positions
Engineering Positions
" - jobs += "" - for(var/jobPos in cargo_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CARGO)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -478,8 +478,8 @@ //Medical (White) counter = 0 jobs += "
Cargo Positions
Cargo Positions
" - jobs += "" - for(var/jobPos in medical_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -499,8 +499,8 @@ //Science (Purple) counter = 0 jobs += "
Medical Positions
Medical Positions
" - jobs += "" - for(var/jobPos in science_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -520,8 +520,8 @@ //Exploration (Purple) counter = 0 jobs += "
Science Positions
Science Positions
" - jobs += "" - for(var/jobPos in planet_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_PLANET)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -541,8 +541,8 @@ //Civilian (Grey) counter = 0 jobs += "
Exploration Positions
Science Positions
" - jobs += "" - for(var/jobPos in civilian_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -568,8 +568,8 @@ //Non-Human (Green) counter = 0 jobs += "
Civilian Positions
Civilian Positions
" - jobs += "" - for(var/jobPos in nonhuman_positions) + jobs += "" + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) if(!jobPos) continue var/datum/job/job = job_master.GetJob(jobPos) if(!job) continue @@ -631,16 +631,16 @@ //JOBBAN'S INNARDS else if(href_list["jobban3"]) if(!check_rights(R_MOD,0) && !check_rights(R_ADMIN,0)) - usr << "You do not have the appropriate permissions to add job bans!" + to_chat(usr, "You do not have the appropriate permissions to add job bans!") return if(check_rights(R_MOD,0) && !check_rights(R_ADMIN,0) && !config.mods_can_job_tempban) // If mod and tempban disabled - usr << "Mod jobbanning is disabled!" + to_chat(usr, "Mod jobbanning is disabled!") return var/mob/M = locate(href_list["jobban4"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(M != usr) //we can jobban ourselves @@ -649,65 +649,65 @@ return if(!job_master) - usr << "Job Master has not been setup!" + to_chat(usr, "Job Master has not been setup!") return //get jobs for department if specified, otherwise just returnt he one job in a list. var/list/joblist = list() switch(href_list["jobban3"]) if("commanddept") - for(var/jobPos in command_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title if("securitydept") - for(var/jobPos in security_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title if("engineeringdept") - for(var/jobPos in engineering_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title if("cargodept") - for(var/jobPos in cargo_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CARGO)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title if("medicaldept") - for(var/jobPos in medical_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title if("sciencedept") - for(var/jobPos in science_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title //VOREStation Edit Start if("explorationdept") - for(var/jobPos in planet_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_PLANET)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title //VOREStation Edit End if("civiliandept") - for(var/jobPos in civilian_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue joblist += temp.title if("nonhumandept") joblist += "pAI" - for(var/jobPos in nonhuman_positions) + for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC)) if(!jobPos) continue var/datum/job/temp = job_master.GetJob(jobPos) if(!temp) continue @@ -726,16 +726,16 @@ switch(alert("Temporary Ban?",,"Yes","No", "Cancel")) if("Yes") if(!check_rights(R_MOD,0) && !check_rights(R_BAN, 0)) - usr << " You Cannot issue temporary job-bans!" + to_chat(usr, " You Cannot issue temporary job-bans!") return if(config.ban_legacy_system) - usr << "Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban." + to_chat(usr, "Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban.") return var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null if(!mins) return if(check_rights(R_MOD, 0) && !check_rights(R_BAN, 0) && mins > config.mod_job_tempban_max) - usr << " Moderators can only job tempban up to [config.mod_job_tempban_max] minutes!" + to_chat(usr, " Moderators can only job tempban up to [config.mod_job_tempban_max] minutes!") return var/reason = sanitize(input(usr,"Reason?","Please State Reason","") as text|null) if(!reason) @@ -755,9 +755,9 @@ msg += ", [job]" notes_add(M.ckey, "Banned from [msg] - [reason]", usr) message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) - M << "You have been jobbanned by [usr.client.ckey] from: [msg]." - M << "The reason is: [reason]" - M << "This jobban will be lifted in [mins] minutes." + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "This jobban will be lifted in [mins] minutes.") href_list["jobban2"] = 1 // lets it fall through and refresh return 1 if("No") @@ -776,9 +776,9 @@ else msg += ", [job]" notes_add(M.ckey, "Banned from [msg] - [reason]", usr) message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) - M << "You have been jobbanned by [usr.client.ckey] from: [msg]." - M << "The reason is: [reason]" - M << "Jobban can be lifted only upon request." + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "Jobban can be lifted only upon request.") href_list["jobban2"] = 1 // lets it fall through and refresh return 1 if("Cancel") @@ -788,7 +788,7 @@ //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. if(!config.ban_legacy_system) - usr << "Unfortunately, database based unbanning cannot be done through this panel" + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") DB_ban_panel(M.ckey) return var/msg @@ -809,7 +809,7 @@ continue if(msg) message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1) - M << "You have been un-jobbanned by [usr.client.ckey] from [msg]." + to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") href_list["jobban2"] = 1 // lets it fall through and refresh return 1 return 0 //we didn't do anything! @@ -847,11 +847,11 @@ else if(href_list["newban"]) if(!check_rights(R_MOD,0) && !check_rights(R_BAN, 0)) - usr << "You do not have the appropriate permissions to add bans!" + to_chat(usr, "You do not have the appropriate permissions to add bans!") return if(check_rights(R_MOD,0) && !check_rights(R_ADMIN, 0) && !config.mods_can_job_tempban) // If mod and tempban disabled - usr << "Mod jobbanning is disabled!" + to_chat(usr, "Mod jobbanning is disabled!") return var/mob/M = locate(href_list["newban"]) @@ -865,7 +865,7 @@ if(!mins) return if(check_rights(R_MOD, 0) && !check_rights(R_BAN, 0) && mins > config.mod_tempban_max) - usr << "Moderators can only job tempban up to [config.mod_tempban_max] minutes!" + to_chat(usr, "Moderators can only job tempban up to [config.mod_tempban_max] minutes!") return if(mins >= 525600) mins = 525599 var/reason = sanitize(input(usr,"Reason?","reason","Griefer") as text|null) @@ -874,15 +874,15 @@ AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") notes_add(M.ckey,"[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.",usr) - M << "You have been banned by [usr.client.ckey].\nReason: [reason]." - M << "This is a temporary ban, it will be removed in [mins] minutes." + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") feedback_inc("ban_tmp",1) DB_ban_record(BANTYPE_TEMP, M, mins, reason) feedback_inc("ban_tmp_mins",mins) if(config.banappeals) - M << "To try to resolve this matter head to [config.banappeals]" + to_chat(M, "To try to resolve this matter head to [config.banappeals]") else - M << "No ban appeals URL has been set." + to_chat(M, "No ban appeals URL has been set.") log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") var/datum/admin_help/AH = M.client ? M.client.current_ticket : null @@ -901,12 +901,12 @@ AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) if("No") AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) - M << "You have been banned by [usr.client.ckey].\nReason: [reason]." - M << "This is a permanent ban." + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a permanent ban.") if(config.banappeals) - M << "To try to resolve this matter head to [config.banappeals]" + to_chat(M, "To try to resolve this matter head to [config.banappeals]") else - M << "No ban appeals URL has been set." + to_chat(M, "No ban appeals URL has been set.") ban_unban_log_save("[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This is a permanent ban.") notes_add(M.ckey,"[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This is a permanent ban.",usr) log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") @@ -935,7 +935,7 @@ cmd_admin_mute(M, mute_type) else if(href_list["c_mode"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT)) return if(ticker && ticker.mode) return alert(usr, "The game has already started.", null, null, null, null) @@ -948,7 +948,7 @@ usr << browse(dat, "window=c_mode") else if(href_list["f_secret"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT)) return if(ticker && ticker.mode) return alert(usr, "The game has already started.", null, null, null, null) @@ -962,20 +962,20 @@ usr << browse(dat, "window=f_secret") else if(href_list["c_mode2"]) - if(!check_rights(R_ADMIN|R_SERVER)) return + if(!check_rights(R_ADMIN|R_SERVER|R_EVENT)) return if (ticker && ticker.mode) return alert(usr, "The game has already started.", null, null, null, null) master_mode = href_list["c_mode2"] log_admin("[key_name(usr)] set the mode as [config.mode_names[master_mode]].") message_admins("[key_name_admin(usr)] set the mode as [config.mode_names[master_mode]].", 1) - world << "The mode is now: [config.mode_names[master_mode]]" + to_world("The mode is now: [config.mode_names[master_mode]]") Game() // updates the main game menu world.save_mode(master_mode) .(href, list("c_mode"=1)) else if(href_list["f_secret2"]) - if(!check_rights(R_ADMIN|R_SERVER)) return + if(!check_rights(R_ADMIN|R_SERVER|R_EVENT)) return if(ticker && ticker.mode) return alert(usr, "The game has already started.", null, null, null, null) @@ -992,7 +992,7 @@ var/mob/living/carbon/human/H = locate(href_list["monkeyone"]) if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)]") @@ -1004,7 +1004,7 @@ var/mob/living/carbon/human/H = locate(href_list["corgione"]) if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return log_admin("[key_name(usr)] attempting to corgize [key_name(H)]") @@ -1016,7 +1016,7 @@ var/mob/M = locate(href_list["forcespeech"]) if(!ismob(M)) - usr << "this can only be used on instances of type /mob" + to_chat(usr, "this can only be used on instances of type /mob") var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. if(!speech) return @@ -1033,10 +1033,10 @@ var/mob/M = locate(href_list["sendtoprison"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - usr << "This cannot be used on instances of type /mob/living/silicon/ai" + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return var/turf/prison_cell = pick(prisonwarp) @@ -1061,10 +1061,33 @@ prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) - M << "You have been sent to the prison station!" + to_chat(M, "You have been sent to the prison station!") log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) + else if(href_list["sendbacktolobby"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["sendbacktolobby"]) + if(!isobserver(M)) + to_chat(usr, "You can only send ghost players back to the Lobby.") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") + message_admins("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") + + var/mob/new_player/NP = new() + NP.ckey = M.ckey + qdel(M) + else if(href_list["tdome1"]) if(!check_rights(R_FUN)) return @@ -1073,10 +1096,10 @@ var/mob/M = locate(href_list["tdome1"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - usr << "This cannot be used on instances of type /mob/living/silicon/ai" + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1086,7 +1109,7 @@ sleep(5) M.loc = pick(tdome1) spawn(50) - M << "You have been sent to the Thunderdome." + to_chat(M, "You have been sent to the Thunderdome.") log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 1)") message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 1)", 1) @@ -1098,10 +1121,10 @@ var/mob/M = locate(href_list["tdome2"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - usr << "This cannot be used on instances of type /mob/living/silicon/ai" + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1111,7 +1134,7 @@ sleep(5) M.loc = pick(tdome2) spawn(50) - M << "You have been sent to the Thunderdome." + to_chat(M, "You have been sent to the Thunderdome.") log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 2)") message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 2)", 1) @@ -1123,17 +1146,17 @@ var/mob/M = locate(href_list["tdomeadmin"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - usr << "This cannot be used on instances of type /mob/living/silicon/ai" + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return M.Paralyse(5) sleep(5) M.loc = pick(tdomeadmin) spawn(50) - M << "You have been sent to the Thunderdome." + to_chat(M, "You have been sent to the Thunderdome.") log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Admin.)") message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Admin.)", 1) @@ -1145,10 +1168,10 @@ var/mob/M = locate(href_list["tdomeobserve"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - usr << "This cannot be used on instances of type /mob/living/silicon/ai" + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1162,7 +1185,7 @@ sleep(5) M.loc = pick(tdomeobserve) spawn(50) - M << "You have been sent to the Thunderdome." + to_chat(M, "You have been sent to the Thunderdome.") log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Observer.)") message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1) @@ -1171,7 +1194,7 @@ var/mob/living/L = locate(href_list["revive"]) if(!istype(L)) - usr << "This can only be used on instances of type /mob/living" + to_chat(usr, "This can only be used on instances of type /mob/living") return if(config.allow_admin_rev) @@ -1179,14 +1202,14 @@ message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!", 1) log_admin("[key_name(usr)] healed / Rrvived [key_name(L)]") else - usr << "Admin Rejuvinates have been disabled" + to_chat(usr, "Admin Rejuvinates have been disabled") else if(href_list["makeai"]) if(!check_rights(R_SPAWN)) return var/mob/living/carbon/human/H = locate(href_list["makeai"]) if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!", 1) @@ -1198,7 +1221,7 @@ var/mob/living/carbon/human/H = locate(href_list["makealien"]) if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return usr.client.cmd_admin_alienize(H) @@ -1208,7 +1231,7 @@ var/mob/living/carbon/human/H = locate(href_list["makerobot"]) if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return usr.client.cmd_admin_robotize(H) @@ -1218,17 +1241,26 @@ var/mob/M = locate(href_list["makeanimal"]) if(istype(M, /mob/new_player)) - usr << "This cannot be used on instances of type /mob/new_player" + to_chat(usr, "This cannot be used on instances of type /mob/new_player") return usr.client.cmd_admin_animalize(M) + else if(href_list["respawn"]) + if(!check_rights(R_SPAWN)) + return + + var/client/C = locate(href_list["respawn"]) + if(!istype(C)) + return + usr.client.respawn_character_proper(C) + else if(href_list["togmutate"]) if(!check_rights(R_SPAWN)) return var/mob/living/carbon/human/H = locate(href_list["togmutate"]) if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return var/block=text2num(href_list["block"]) usr.client.cmd_admin_toggle_block(H,block) @@ -1302,7 +1334,7 @@ else if(href_list["adminmoreinfo"]) var/mob/M = locate(href_list["adminmoreinfo"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return var/location_description = "" @@ -1342,19 +1374,19 @@ if(MALE,FEMALE) gender_description = "[M.gender]" else gender_description = "[M.gender]" - src.owner << "Info about [M.name]: " - src.owner << "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]" - src.owner << "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];" - src.owner << "Location = [location_description];" - src.owner << "[special_role_description]" - src.owner << "(PM) (PP) (VV) (SM) ([admin_jump_link(M, src)]) (CA)" + to_chat(src.owner, "Info about [M.name]: ") + to_chat(src.owner, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]") + to_chat(src.owner, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") + to_chat(src.owner, "Location = [location_description];") + to_chat(src.owner, "[special_role_description]") + to_chat(src.owner, "(PM) (PP) (VV) (SM) ([admin_jump_link(M, src)]) (CA)") else if(href_list["adminspawncookie"]) - if(!check_rights(R_ADMIN|R_FUN)) return + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return var/mob/living/carbon/human/H = locate(href_list["adminspawncookie"]) if(!ishuman(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return H.equip_to_slot_or_del( new /obj/item/weapon/reagent_containers/food/snacks/cookie(H), slot_l_hand ) @@ -1371,24 +1403,24 @@ log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") message_admins("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") feedback_inc("admin_cookies_spawned",1) - H << "Your prayers have been answered!! You received the best cookie!" + to_chat(H, "Your prayers have been answered!! You received the best cookie!") else if(href_list["adminsmite"]) - if(!check_rights(R_ADMIN|R_FUN)) return + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return var/mob/living/carbon/human/H = locate(href_list["adminsmite"]) if(!ishuman(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return owner.smite(H) else if(href_list["BlueSpaceArtillery"]) - if(!check_rights(R_ADMIN|R_FUN)) return + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return var/mob/living/M = locate(href_list["BlueSpaceArtillery"]) if(!isliving(M)) - usr << "This can only be used on instances of type /mob/living" + to_chat(usr, "This can only be used on instances of type /mob/living") return if(alert(src.owner, "Are you sure you wish to hit [key_name(M)] with Blue Space Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes") @@ -1399,41 +1431,41 @@ else if(href_list["CentComReply"]) var/mob/living/L = locate(href_list["CentComReply"]) if(!istype(L)) - usr << "This can only be used on instances of type /mob/living/" + to_chat(usr, "This can only be used on instances of type /mob/living/") return if(L.can_centcom_reply()) var/input = sanitize(input(src.owner, "Please enter a message to reply to [key_name(L)] via their headset.","Outgoing message from CentCom", "")) if(!input) return - src.owner << "You sent [input] to [L] via a secure channel." + to_chat(src.owner, "You sent [input] to [L] via a secure channel.") log_admin("[src.owner] replied to [key_name(L)]'s CentCom message with the message [input].") message_admins("[src.owner] replied to [key_name(L)]'s CentCom message with: \"[input]\"") if(!isAI(L)) - L << "You hear something crackle in your headset for a moment before a voice speaks." - L << "Please stand by for a message from Central Command." - L << "Message as follows." - L << "[input]" - L << "Message ends." + to_chat(L, "You hear something crackle in your headset for a moment before a voice speaks.") + to_chat(L, "Please stand by for a message from Central Command.") + to_chat(L, "Message as follows.") + to_chat(L, "[input]") + to_chat(L, "Message ends.") else - src.owner << "The person you are trying to contact does not have functional radio equipment." + to_chat(src.owner, "The person you are trying to contact does not have functional radio equipment.") else if(href_list["SyndicateReply"]) var/mob/living/carbon/human/H = locate(href_list["SyndicateReply"]) if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(!istype(H.l_ear, /obj/item/device/radio/headset) && !istype(H.r_ear, /obj/item/device/radio/headset)) - usr << "The person you are trying to contact is not wearing a headset" + to_chat(usr, "The person you are trying to contact is not wearing a headset") return var/input = sanitize(input(src.owner, "Please enter a message to reply to [key_name(H)] via their headset.","Outgoing message from a shadowy figure...", "")) if(!input) return - src.owner << "You sent [input] to [H] via a secure channel." + to_chat(src.owner, "You sent [input] to [H] via a secure channel.") log_admin("[src.owner] replied to [key_name(H)]'s illegal message with the message [input].") - H << "You hear something crackle in your headset for a moment before a voice speaks. \"Please stand by for a message from your benefactor. Message as follows, agent. \"[input]\" Message ends.\"" + to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \"Please stand by for a message from your benefactor. Message as follows, agent. \"[input]\" Message ends.\"") else if(href_list["AdminFaxView"]) var/obj/item/fax = locate(href_list["AdminFaxView"]) @@ -1455,7 +1487,7 @@ usr << browse(data, "window=[B.name]") else - usr << "The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]" + to_chat(usr, "The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]") else if (href_list["AdminFaxViewPage"]) var/page = text2num(href_list["AdminFaxViewPage"]) @@ -1488,38 +1520,38 @@ P.adminbrowse() else if(href_list["jumpto"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT|R_MOD)) return var/mob/M = locate(href_list["jumpto"]) usr.client.jumptomob(M) else if(href_list["getmob"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT|R_FUN)) return if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") return var/mob/M = locate(href_list["getmob"]) usr.client.Getmob(M) else if(href_list["sendmob"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT|R_FUN)) return var/mob/M = locate(href_list["sendmob"]) usr.client.sendmob(M) else if(href_list["narrateto"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT|R_FUN)) return var/mob/M = locate(href_list["narrateto"]) usr.client.cmd_admin_direct_narrate(M) else if(href_list["subtlemessage"]) - if(!check_rights(R_MOD,0) && !check_rights(R_ADMIN)) return + if(!check_rights(R_MOD|R_ADMIN|R_EVENT|R_FUN,0)) return var/mob/M = locate(href_list["subtlemessage"]) usr.client.cmd_admin_subtle_message(M) else if(href_list["traitor"]) - if(!check_rights(R_ADMIN|R_MOD)) return + if(!check_rights(R_ADMIN|R_MOD|R_EVENT)) return if(!ticker || !ticker.mode) alert("The game hasn't started yet!") @@ -1527,7 +1559,7 @@ var/mob/M = locate(href_list["traitor"]) if(!ismob(M)) - usr << "This can only be used on instances of type /mob." + to_chat(usr, "This can only be used on instances of type /mob.") return show_traitor_panel(M) @@ -1551,7 +1583,7 @@ if(!check_rights(R_SPAWN)) return if(!config.allow_admin_spawning) - usr << "Spawning of items is not allowed." + to_chat(usr, "Spawning of items is not allowed.") return var/atom/loc = usr.loc @@ -1611,24 +1643,24 @@ where = "onfloor" if( where == "inhand" ) - usr << "Support for inhand not available yet. Will spawn on floor." + to_chat(usr, "Support for inhand not available yet. Will spawn on floor.") where = "onfloor" if ( where == "inhand" ) //Can only give when human or monkey if ( !( ishuman(usr) || issmall(usr) ) ) - usr << "Can only spawn in hand when you're a human or a monkey." + to_chat(usr, "Can only spawn in hand when you're a human or a monkey.") where = "onfloor" else if ( usr.get_active_hand() ) - usr << "Your active hand is full. Spawning on floor." + to_chat(usr, "Your active hand is full. Spawning on floor.") where = "onfloor" if ( where == "inmarked" ) if ( !marked_datum ) - usr << "You don't have any object marked. Abandoning spawn." + to_chat(usr, "You don't have any object marked. Abandoning spawn.") return else if ( !istype(marked_datum,/atom) ) - usr << "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn." + to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") return var/atom/target //Where the object will be spawned @@ -1868,7 +1900,7 @@ populate_inactive_customitems_list(src.owner) else if(href_list["vsc"]) - if(check_rights(R_ADMIN|R_SERVER)) + if(check_rights(R_ADMIN|R_SERVER|R_EVENT)) if(href_list["vsc"] == "airflow") vsc.ChangeSettingsDialog(usr,vsc.settings) if(href_list["vsc"] == "phoron") @@ -1880,26 +1912,26 @@ if(check_rights(R_SPAWN)) //VOREStation Edit var/mob/M = locate(href_list["toglang"]) if(!istype(M)) - usr << "[M] is illegal type, must be /mob!" + to_chat(usr, "[M] is illegal type, must be /mob!") return var/lang2toggle = href_list["lang"] var/datum/language/L = GLOB.all_languages[lang2toggle] if(L in M.languages) if(!M.remove_language(lang2toggle)) - usr << "Failed to remove language '[lang2toggle]' from \the [M]!" + to_chat(usr, "Failed to remove language '[lang2toggle]' from \the [M]!") else if(!M.add_language(lang2toggle)) - usr << "Failed to add language '[lang2toggle]' from \the [M]!" + to_chat(usr, "Failed to add language '[lang2toggle]' from \the [M]!") show_player_panel(M) else if(href_list["cryoplayer"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_ADMIN|R_EVENT)) return var/mob/living/carbon/M = locate(href_list["cryoplayer"]) //VOREStation edit from just an all mob check to mob/living/carbon if(!istype(M)) - to_chat(usr,"Mob doesn't exist!") + to_chat(usr, "Mob doesn't exist!") return var/client/C = usr.client diff --git a/code/modules/admin/verbs/BrokenInhands.dm b/code/modules/admin/verbs/BrokenInhands.dm index e5d6dc2661..654badec7f 100644 --- a/code/modules/admin/verbs/BrokenInhands.dm +++ b/code/modules/admin/verbs/BrokenInhands.dm @@ -31,6 +31,6 @@ var/F = file("broken_icons.txt") fdel(F) F << text - world << "Completeled successfully and written to [F]" + to_world("Completeled successfully and written to [F]") diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index c32fd68a11..589aefed79 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -511,7 +511,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) set name = "Show Ticket List" set category = "Admin" - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG, TRUE)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT, TRUE)) return var/browse_to diff --git a/code/modules/admin/verbs/adminhelp_vr.dm b/code/modules/admin/verbs/adminhelp_vr.dm index f2df8e4eb2..ba0d970f9b 100644 --- a/code/modules/admin/verbs/adminhelp_vr.dm +++ b/code/modules/admin/verbs/adminhelp_vr.dm @@ -25,7 +25,7 @@ to_chat(usr, "Error: You cannot request spice (muted from adminhelps).") return - if(alert(usr, "Are you sure you want to request the admins spice things up for you? You accept the consequences if you do.",,"No","Yes") != "No") + if(alert(usr, "Are you sure you want to request the admins spice things up for you? You accept the consequences if you do.",,"Yes","No") != "No") message_admins("[ADMIN_FULLMONTY(usr)] has requested the round be spiced up a little.") to_chat(usr, "You have requested some more spice in your round.") else diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index 642aa456cb..e414a774cd 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -8,12 +8,12 @@ set name = "Jump to Area" set desc = "Area to jump to" set category = "Admin" - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return if(config.allow_admin_jump) usr.on_mob_jump() - usr.loc = pick(get_area_turfs(A)) + usr.forceMove(pick(get_area_turfs(A))) log_admin("[key_name(usr)] jumped to [A]") message_admins("[key_name_admin(usr)] jumped to [A]", 1) @@ -21,16 +21,16 @@ else alert("Admin jumping disabled") -/client/proc/jumptoturf(var/turf/T in turfs) +/client/proc/jumptoturf(var/turf/T in world) set name = "Jump to Turf" set category = "Admin" - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return if(config.allow_admin_jump) log_admin("[key_name(usr)] jumped to [T.x],[T.y],[T.z] in [T.loc]") message_admins("[key_name_admin(usr)] jumped to [T.x],[T.y],[T.z] in [T.loc]", 1) usr.on_mob_jump() - usr.loc = T + usr.forceMove(T) feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! else alert("Admin jumping disabled") @@ -40,7 +40,8 @@ set category = "Admin" set name = "Jump to Mob" set popup_menu = FALSE //VOREStation Edit - Declutter. - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return if(config.allow_admin_jump) @@ -52,9 +53,9 @@ if(T && isturf(T)) feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! A.on_mob_jump() - A.loc = T + A.forceMove(T) else - A << "This mob is not located in the game world." + to_chat(A, "This mob is not located in the game world.") else alert("Admin jumping disabled") @@ -62,16 +63,18 @@ set category = "Admin" set name = "Jump to Coordinate" - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return if (config.allow_admin_jump) if(src.mob) var/mob/A = src.mob A.on_mob_jump() - A.x = tx - A.y = ty - A.z = tz + var/turf/T = locate(tx, ty, tz) + if(!T) + to_chat(usr, "Those coordinates are outside the boundaries of the map.") + return + A.forceMove(T) feedback_add_details("admin_verb","JC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]") @@ -82,7 +85,7 @@ set category = "Admin" set name = "Jump to Key" - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return if(config.allow_admin_jump) @@ -97,7 +100,7 @@ log_admin("[key_name(usr)] jumped to [key_name(M)]") message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) usr.on_mob_jump() - usr.loc = M.loc + usr.forceMove(get_turf(M)) feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! else alert("Admin jumping disabled") @@ -106,7 +109,7 @@ set category = "Admin" set name = "Get Mob" set desc = "Mob to teleport" - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return if(config.allow_admin_jump) log_admin("[key_name(usr)] jumped to [key_name(M)]") @@ -114,7 +117,7 @@ message_admins(msg) admin_ticket_log(M, msg) M.on_mob_jump() - M.loc = get_turf(usr) + M.forceMove(get_turf(usr)) feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! else alert("Admin jumping disabled") @@ -124,7 +127,7 @@ set name = "Get Key" set desc = "Key to teleport" - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return if(config.allow_admin_jump) @@ -144,7 +147,7 @@ admin_ticket_log(M, msg) if(M) M.on_mob_jump() - M.loc = get_turf(usr) + M.forceMove(get_turf(usr)) feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! else alert("Admin jumping disabled") @@ -152,13 +155,13 @@ /client/proc/sendmob(var/mob/M in sortmobs()) set category = "Admin" set name = "Send Mob" - if(!check_rights(R_ADMIN|R_MOD|R_DEBUG)) + if(!check_rights(R_ADMIN|R_MOD|R_DEBUG|R_EVENT)) return var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() if(A) if(config.allow_admin_jump) M.on_mob_jump() - M.loc = pick(get_area_turfs(A)) + M.forceMove(pick(get_area_turfs(A))) feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] teleported [key_name(M)]") diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 2bf3a1f5cc..2bd7326b3b 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -6,7 +6,7 @@ set category = null set name = "Admin PM Mob" if(!holder) - to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.") + to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.") return if( !ismob(M) || !M.client ) return @@ -18,7 +18,7 @@ set category = "Admin" set name = "Admin PM" if(!holder) - to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.") + to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.") return var/list/client/targets[0] for(var/client/T) @@ -37,7 +37,7 @@ /client/proc/cmd_ahelp_reply(whom) if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") return var/client/C if(istext(whom)) @@ -48,16 +48,16 @@ C = whom if(!C) if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") + to_chat(src, "Error: Admin-PM: Client not found.") return var/datum/admin_help/AH = C.current_ticket if(AH) - message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.") + message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.") var/msg = input(src,"Message:", "Private message to [key_name(C, 0, 0)]") as text|null if (!msg) - message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.") + message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.") return cmd_admin_pm(whom, msg, AH) @@ -65,12 +65,12 @@ //Fetching a message if needed. src is the sender and C is the target client /client/proc/cmd_admin_pm(whom, msg, datum/admin_help/AH) if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") return if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo - to_chat(src, "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.") - to_chat(src, "Message: [msg]") + to_chat(src, "You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.") + to_chat(src, "Message: [msg]") return var/client/recipient @@ -95,14 +95,14 @@ if(!msg) return if(holder) - to_chat(src, "Error: Use the admin IRC channel, nerd.") + to_chat(src, "Error: Use the admin IRC channel, nerd.") return else if(!recipient) if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") + to_chat(src, "Error: Admin-PM: Client not found.") to_chat(src, msg) else current_ticket.MessageNoRecipient(msg) @@ -116,12 +116,12 @@ return if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") + to_chat(src, "Error: Admin-PM: You are unable to use admin PM-s (muted).") return if(!recipient) if(holder) - to_chat(src, "Error: Admin-PM: Client not found.") + to_chat(src, "Error: Admin-PM: Client not found.") else current_ticket.MessageNoRecipient(msg) return @@ -140,27 +140,27 @@ var/keywordparsedmsg = keywords_lookup(msg) if(irc) - to_chat(src, "PM to-Admins: [rawmsg]") - admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to IRC: [keywordparsedmsg]") + to_chat(src, "PM to-Admins: [rawmsg]") + admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to IRC: [keywordparsedmsg]") ircreplyamount-- send2irc("Reply: [ckey]",rawmsg) else if(recipient.holder) if(holder) //both are admins - to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]") - to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]") + to_chat(recipient, "Admin PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]") + to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]") //omg this is dumb, just fill in both their tickets - var/interaction_message = "PM from-[key_name(src, recipient, 1)] to-[key_name(recipient, src, 1)]: [keywordparsedmsg]" + var/interaction_message = "PM from-[key_name(src, recipient, 1)] to-[key_name(recipient, src, 1)]: [keywordparsedmsg]" admin_ticket_log(src, interaction_message) if(recipient != src) //reeee admin_ticket_log(recipient, interaction_message) else //recipient is an admin but sender is not - var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]" + var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]" admin_ticket_log(src, replymsg) to_chat(recipient, replymsg) - to_chat(src, "PM to-Admins: [msg]") + to_chat(src, "PM to-Admins: [msg]") //play the recieving admin the adminhelp sound (if they have them enabled) if(recipient.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping)) @@ -171,12 +171,12 @@ if(!recipient.current_ticket) new /datum/admin_help(msg, recipient, TRUE) - to_chat(recipient, "-- Administrator private message --") - to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]") - to_chat(recipient, "Click on the administrator's name to reply.") - to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]") + to_chat(recipient, "-- Administrator private message --") + to_chat(recipient, "Admin PM from-[key_name(src, recipient, 0)]: [msg]") + to_chat(recipient, "Click on the administrator's name to reply.") + to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]") - admin_ticket_log(recipient, "PM From [key_name_admin(src)]: [keywordparsedmsg]") + admin_ticket_log(recipient, "PM From [key_name_admin(src)]: [keywordparsedmsg]") //always play non-admin recipients the adminhelp sound recipient << 'sound/effects/adminhelp.ogg' @@ -195,19 +195,19 @@ return else //neither are admins - to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.") + to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.") return if(irc) log_admin("PM: [key_name(src)]->IRC: [rawmsg]") for(var/client/X in admins) - to_chat(X, "PM: [key_name(src, X, 0)]->IRC: [keywordparsedmsg]") + to_chat(X, "PM: [key_name(src, X, 0)]->IRC: [keywordparsedmsg]") else log_admin("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]") //we don't use message_admins here because the sender/receiver might get it too for(var/client/X in admins) if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient - to_chat(X, "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" ) + to_chat(X, "PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]" ) /proc/IrcPm(target,msg,sender) var/client/C = GLOB.directory[target] @@ -256,11 +256,11 @@ message_admins("IRC message from [sender] to [key_name_admin(C)] : [msg]") log_admin("IRC PM: [sender] -> [key_name(C)] : [msg]") - to_chat(C, "-- Administrator private message --") - to_chat(C, "Admin PM from-[adminname]: [msg]") - to_chat(C, "Click on the administrator's name to reply.") + to_chat(C, "-- Administrator private message --") + to_chat(C, "Admin PM from-[adminname]: [msg]") + to_chat(C, "Click on the administrator's name to reply.") - admin_ticket_log(C, "PM From [irc_tagged]: [msg]") + admin_ticket_log(C, "PM From [irc_tagged]: [msg]") window_flash(C, ignorepref = TRUE) //always play non-admin recipients the adminhelp sound diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 200a742b8c..cdd825f0b3 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -14,7 +14,7 @@ //VOREStation Edit Start - Adds R_EVENT for(var/client/C in admins) if(check_rights(R_ADMIN|R_EVENT)) - C << "" + create_text_tag("admin", "ADMIN:", C) + " [key_name(usr, 1)]([admin_jump_link(mob, src)]): [msg]" + to_chat(C, "" + create_text_tag("admin", "ADMIN:", C) + " [key_name(usr, 1)]([admin_jump_link(mob, src)]): [msg]") //VOREStation Edit End feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -38,7 +38,7 @@ sender_name = "[sender_name]" for(var/client/C in admins) if(check_rights(R_ADMIN|R_MOD|R_SERVER|R_STEALTH)) //VOREStation Edit - C << "" + create_text_tag("mod", "MOD:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]" + to_chat(C, "" + create_text_tag("mod", "MOD:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -60,6 +60,6 @@ if(check_rights(R_ADMIN, 0)) sender_name = "[sender_name]" for(var/client/C in admins) - C << "" + create_text_tag("event", "EVENT:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]" + to_chat(C, "" + create_text_tag("event", "EVENT:", C) + " [sender_name]([admin_jump_link(mob, C.holder)]): [msg]") feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! \ No newline at end of file diff --git a/code/modules/admin/verbs/antag-ooc.dm b/code/modules/admin/verbs/antag-ooc.dm index 13c6e6a767..79edaf15d9 100644 --- a/code/modules/admin/verbs/antag-ooc.dm +++ b/code/modules/admin/verbs/antag-ooc.dm @@ -3,7 +3,7 @@ set name = "AOOC" set desc = "Antagonist OOC" - var/is_admin = check_rights(R_ADMIN|R_MOD, show_msg = 0) + var/is_admin = check_rights(R_ADMIN|R_MOD|R_EVENT, show_msg = 0) var/is_antag = usr.mind && usr.mind.special_role if(!is_antag && !is_admin) // Non-antagonists and non-admins have no business using this. @@ -30,7 +30,7 @@ var/player_display = (is_admin && !is_antag) ? "[display_name]([usr.client.holder.rank])" : display_name for(var/mob/M in mob_list) - if(check_rights(R_ADMIN|R_MOD, 0, M)) // Staff can see AOOC unconditionally, and with more details. + if(check_rights(R_ADMIN|R_MOD|R_EVENT, 0, M)) // Staff can see AOOC unconditionally, and with more details. to_chat(M, "[create_text_tag("aooc", "Antag-OOC:", M.client)] [get_options_bar(src, 0, 1, 1)]([admin_jump_link(usr, M.client.holder)]): [msg]") else if(M.client) // Players can only see AOOC if observing, or if they are an antag type allowed to use AOOC. var/datum/antagonist/A = null diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index cc77c43cd0..2f7d65db05 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -10,25 +10,25 @@ if(alert("WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", "No", "Yes") == "No") return - usr << "Checking for disconnected pipes..." + to_chat(usr, "Checking for disconnected pipes...") //all plumbing - yes, some things might get stated twice, doesn't matter. for (var/obj/machinery/atmospherics/plumbing in machines) if (plumbing.nodealert) - usr << "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])" + to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") //Manifolds for (var/obj/machinery/atmospherics/pipe/manifold/pipe in machines) if (!pipe.node1 || !pipe.node2 || !pipe.node3) - usr << "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])" + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") //Pipes for (var/obj/machinery/atmospherics/pipe/simple/pipe in machines) if (!pipe.node1 || !pipe.node2) - usr << "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])" + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - usr << "Checking for overlapping pipes..." + to_chat(usr, "Checking for overlapping pipes...") next_turf: - for(var/turf/T in turfs) + for(var/turf/T in world) for(var/dir in cardinal) var/list/connect_types = list(1 = 0, 2 = 0, 3 = 0) for(var/obj/machinery/atmospherics/pipe in T) @@ -36,9 +36,9 @@ for(var/connect_type in pipe.connect_types) connect_types[connect_type] += 1 if(connect_types[1] > 1 || connect_types[2] > 1 || connect_types[3] > 1) - usr << "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])" + to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") continue next_turf - usr << "Done" + to_chat(usr, "Done") /client/proc/powerdebug() set category = "Mapping" @@ -52,9 +52,9 @@ if (!PN.nodes || !PN.nodes.len) if(PN.cables && (PN.cables.len > 1)) var/obj/structure/cable/C = PN.cables[1] - usr << "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]" + to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") if (!PN.cables || (PN.cables.len < 10)) if(PN.cables && (PN.cables.len > 1)) var/obj/structure/cable/C = PN.cables[1] - usr << "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]" \ No newline at end of file + to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") \ No newline at end of file diff --git a/code/modules/admin/verbs/buildmode.dm b/code/modules/admin/verbs/buildmode.dm index 4cfa952b67..a5e9c15029 100644 --- a/code/modules/admin/verbs/buildmode.dm +++ b/code/modules/admin/verbs/buildmode.dm @@ -1,3 +1,15 @@ +#define BUILDMODE_BASIC 1 +#define BUILDMODE_ADVANCED 2 +#define BUILDMODE_EDIT 3 +#define BUILDMODE_THROW 4 +#define BUILDMODE_ROOM 5 +#define BUILDMODE_LADDER 6 +#define BUILDMODE_CONTENTS 7 +#define BUILDMODE_LIGHTS 8 +#define BUILDMODE_AI 9 + +#define LAST_BUILDMODE 9 + /proc/togglebuildmode(mob/M as mob in player_list) set name = "Toggle Build Mode" set category = "Special Verbs" @@ -6,6 +18,7 @@ log_admin("[key_name(usr)] has left build mode.") M.client.buildmode = 0 M.client.show_popup_menus = 1 + M.plane_holder.set_vis(VIS_BUILDMODE, FALSE) for(var/obj/effect/bmode/buildholder/H) if(H.cl == M.client) qdel(H) @@ -13,6 +26,7 @@ log_admin("[key_name(usr)] has entered build mode.") M.client.buildmode = 1 M.client.show_popup_menus = 0 + M.plane_holder.set_vis(VIS_BUILDMODE, TRUE) var/obj/effect/bmode/buildholder/H = new/obj/effect/bmode/buildholder() var/obj/effect/bmode/builddir/A = new/obj/effect/bmode/builddir(H) @@ -72,73 +86,87 @@ screen_loc = "NORTH,WEST+1" Click() switch(master.cl.buildmode) - if(1) // Basic Build - usr << "***********************************************************" - usr << "Left Mouse Button = Construct / Upgrade" - usr << "Right Mouse Button = Deconstruct / Delete / Downgrade" - usr << "Left Mouse Button + ctrl = R-Window" - usr << "Left Mouse Button + alt = Airlock" - usr << "" - usr << "Use the button in the upper left corner to" - usr << "change the direction of built objects." - usr << "***********************************************************" - if(2) // Adv. Build - usr << "***********************************************************" - usr << "Right Mouse Button on buildmode button = Set object type" - usr << "Middle Mouse Button on buildmode button= On/Off object type saying" - usr << "Middle Mouse Button on turf/obj = Capture object type" - usr << "Left Mouse Button on turf/obj = Place objects" - usr << "Right Mouse Button = Delete objects" - usr << "Mouse Button + ctrl = Copy object type" - usr << "" - usr << "Use the button in the upper left corner to" - usr << "change the direction of built objects." - usr << "***********************************************************" - if(3) // Edit - usr << "***********************************************************" - usr << "Right Mouse Button on buildmode button = Select var(type) & value" - usr << "Left Mouse Button on turf/obj/mob = Set var(type) & value" - usr << "Right Mouse Button on turf/obj/mob = Reset var's value" - usr << "***********************************************************" - if(4) // Throw - usr << "***********************************************************" - usr << "Left Mouse Button on turf/obj/mob = Select" - usr << "Right Mouse Button on turf/obj/mob = Throw" - usr << "***********************************************************" - if(5) // Room Build - usr << "***********************************************************" - usr << "Left Mouse Button on turf = Select as point A" - usr << "Right Mouse Button on turf = Select as point B" - usr << "Right Mouse Button on buildmode button = Change floor/wall type" - usr << "***********************************************************" - if(6) // Make Ladders - usr << "***********************************************************" - usr << "Left Mouse Button on turf = Set as upper ladder loc" - usr << "Right Mouse Button on turf = Set as lower ladder loc" - usr << "***********************************************************" - if(7) // Move Into Contents - usr << "***********************************************************" - usr << "Left Mouse Button on turf/obj/mob = Select" - usr << "Right Mouse Button on turf/obj/mob = Move into selection" - usr << "***********************************************************" - if(8) // Make Lights - usr << "***********************************************************" - usr << "Left Mouse Button on turf/obj/mob = Make it glow" - usr << "Right Mouse Button on turf/obj/mob = Reset glowing" - usr << "Right Mouse Button on buildmode button = Change glow properties" - usr << "***********************************************************" - if(9) // Control mobs with ai_holders. - usr << "***********************************************************" - usr << "Left Mouse Button on AI mob = Select/Deselect mob" - usr << "Left Mouse Button + alt on AI mob = Toggle hostility on mob" - usr << "Left Mouse Button + ctrl on AI mob = Reset target/following/movement" - usr << "Right Mouse Button on enemy mob = Command selected mobs to attack mob" - usr << "Right Mouse Button on allied mob = Command selected mobs to follow mob" - usr << "Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction" - usr << "Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)" - usr << "Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be inturrupted by enemies)" - usr << "Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf" - usr << "***********************************************************" + + if(BUILDMODE_BASIC) + to_chat(usr, "***********************************************************") + to_chat(usr, "Left Mouse Button = Construct / Upgrade") + to_chat(usr, "Right Mouse Button = Deconstruct / Delete / Downgrade") + to_chat(usr, "Left Mouse Button + ctrl = R-Window") + to_chat(usr, "Left Mouse Button + alt = Airlock") + to_chat(usr, "") + to_chat(usr, "Use the button in the upper left corner to") + to_chat(usr, "change the direction of built objects.") + to_chat(usr, "***********************************************************") + + if(BUILDMODE_ADVANCED) + to_chat(usr, "***********************************************************") + to_chat(usr, "Right Mouse Button on buildmode button = Set object type") + to_chat(usr, "Middle Mouse Button on buildmode button= On/Off object type saying") + to_chat(usr, "Middle Mouse Button on turf/obj = Capture object type") + to_chat(usr, "Left Mouse Button on turf/obj = Place objects") + to_chat(usr, "Right Mouse Button = Delete objects") + to_chat(usr, "Mouse Button + ctrl = Copy object type") + to_chat(usr, "") + to_chat(usr, "Use the button in the upper left corner to") + to_chat(usr, "change the direction of built objects.") + to_chat(usr, "***********************************************************") + + if(BUILDMODE_EDIT) + to_chat(usr, "***********************************************************") + to_chat(usr, "Right Mouse Button on buildmode button = Select var(type) & value") + to_chat(usr, "Left Mouse Button on turf/obj/mob = Set var(type) & value") + to_chat(usr, "Right Mouse Button on turf/obj/mob = Reset var's value") + to_chat(usr, "***********************************************************") + + if(BUILDMODE_THROW) + to_chat(usr, "***********************************************************") + to_chat(usr, "Left Mouse Button on turf/obj/mob = Select") + to_chat(usr, "Right Mouse Button on turf/obj/mob = Throw") + to_chat(usr, "***********************************************************") + + if(BUILDMODE_ROOM) + to_chat(usr, "***********************************************************") + to_chat(usr, "Left Mouse Button on turf = Select as point A") + to_chat(usr, "Right Mouse Button on turf = Select as point B") + to_chat(usr, "Right Mouse Button on buildmode button = Change floor/wall type") + to_chat(usr, "***********************************************************") + + if(BUILDMODE_LADDER) + to_chat(usr, "***********************************************************") + to_chat(usr, "Left Mouse Button on turf = Set as upper ladder loc") + to_chat(usr, "Right Mouse Button on turf = Set as lower ladder loc") + to_chat(usr, "***********************************************************") + + if(BUILDMODE_CONTENTS) + to_chat(usr, "***********************************************************") + to_chat(usr, "Left Mouse Button on turf/obj/mob = Select") + to_chat(usr, "Right Mouse Button on turf/obj/mob = Move into selection") + to_chat(usr, "***********************************************************") + + if(BUILDMODE_LIGHTS) + to_chat(usr, "***********************************************************") + to_chat(usr, "Left Mouse Button on turf/obj/mob = Make it glow") + to_chat(usr, "Right Mouse Button on turf/obj/mob = Reset glowing") + to_chat(usr, "Right Mouse Button on buildmode button = Change glow properties") + to_chat(usr, "***********************************************************") + + if(BUILDMODE_AI) + to_chat(usr, "***********************************************************") + to_chat(usr, "Left Mouse Button drag box = Select only mobs in box") + to_chat(usr, "Left Mouse Button drag box + shift = Select additional mobs in area") + to_chat(usr, "Left Mouse Button on non-mob = Deselect all mobs") + to_chat(usr, "Left Mouse Button on AI mob = Select/Deselect mob") + to_chat(usr, "Left Mouse Button + alt on AI mob = Toggle hostility on mob") + to_chat(usr, "Left Mouse Button + shift on AI mob = Toggle AI (also resets)") + to_chat(usr, "Left Mouse Button + ctrl on AI mob = Copy mob faction") + to_chat(usr, "Right Mouse Button + ctrl on any mob = Paste mob faction copied with Left Mouse Button + shift") + to_chat(usr, "Right Mouse Button on enemy mob = Command selected mobs to attack mob") + to_chat(usr, "Right Mouse Button on allied mob = Command selected mobs to follow mob") + to_chat(usr, "Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction") + to_chat(usr, "Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)") + to_chat(usr, "Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be inturrupted by enemies)") + to_chat(usr, "Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf") + to_chat(usr, "***********************************************************") return 1 /obj/effect/bmode/buildquit @@ -159,6 +187,7 @@ var/obj/effect/bmode/buildquit/buildquit = null var/atom/movable/throw_atom = null var/list/selected_mobs = list() + var/copied_faction = null /obj/effect/bmode/buildholder/Destroy() qdel(builddir) @@ -184,7 +213,6 @@ selected_mobs -= unit C.images -= unit.selected_image - /obj/effect/bmode/buildmode icon_state = "buildmode1" screen_loc = "NORTH,WEST+2" @@ -207,48 +235,25 @@ if(pa.Find("middle")) switch(master.cl.buildmode) - if(2) + if(BUILDMODE_ADVANCED) objsay=!objsay - if(pa.Find("left")) - switch(master.cl.buildmode) - if(1) - master.cl.buildmode = 2 - src.icon_state = "buildmode2" - if(2) - master.cl.buildmode = 3 - src.icon_state = "buildmode3" - if(3) - master.cl.buildmode = 4 - src.icon_state = "buildmode4" - if(4) - master.cl.buildmode = 5 - src.icon_state = "buildmode5" - if(5) - master.cl.buildmode = 6 - src.icon_state = "buildmode6" - if(6) - master.cl.buildmode = 7 - src.icon_state = "buildmode7" - if(7) - master.cl.buildmode = 8 - src.icon_state = "buildmode8" - if(8) - master.cl.buildmode = 9 - src.icon_state = "buildmode9" - if(9) - master.cl.buildmode = 1 - src.icon_state = "buildmode1" + if(master.cl.buildmode == LAST_BUILDMODE) + master.cl.buildmode = 1 + else + master.cl.buildmode++ + src.icon_state = "buildmode[master.cl.buildmode]" else if(pa.Find("right")) switch(master.cl.buildmode) - if(1) // Basic Build + if(BUILDMODE_BASIC) + return 1 - if(2) // Adv. Build + if(BUILDMODE_ADVANCED) objholder = get_path_from_partial_text(/obj/structure/closet) - if(3) // Edit + if(BUILDMODE_EDIT) var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine") master.buildmode.varholder = input(usr,"Enter variable name:" ,"Name", "name") @@ -267,14 +272,16 @@ master.buildmode.valueholder = input(usr,"Enter variable value:" ,"Value") as obj in world if("turf-reference") master.buildmode.valueholder = input(usr,"Enter variable value:" ,"Value") as turf in world - if(5) // Room build + + if(BUILDMODE_ROOM) var/choice = alert("Would you like to change the floor or wall holders?","Room Builder", "Floor", "Wall") switch(choice) if("Floor") floor_holder = get_path_from_partial_text(/turf/simulated/floor/plating) if("Wall") wall_holder = get_path_from_partial_text(/turf/simulated/wall) - if(8) // Lights + + if(BUILDMODE_LIGHTS) var/choice = alert("Change the new light range, power, or color?", "Light Maker", "Range", "Power", "Color") switch(choice) if("Range") @@ -301,7 +308,7 @@ var/list/pa = params2list(params) switch(buildmode) - if(1) // Basic Build + if(BUILDMODE_BASIC) if(istype(object,/turf) && pa.Find("left") && !pa.Find("alt") && !pa.Find("ctrl") ) if(istype(object,/turf/space)) var/turf/T = object @@ -350,7 +357,8 @@ if(NORTHWEST) var/obj/structure/window/reinforced/WIN = new/obj/structure/window/reinforced(get_turf(object)) WIN.set_dir(NORTHWEST) - if(2) // Adv. Build + + if(BUILDMODE_ADVANCED) if(pa.Find("left") && !pa.Find("ctrl")) if(ispath(holder.buildmode.objholder,/turf)) var/turf/T = get_turf(object) @@ -363,27 +371,27 @@ qdel(object) else if(pa.Find("ctrl")) holder.buildmode.objholder = object.type - user << "[object]([object.type]) copied to buildmode." + to_chat(user, "[object]([object.type]) copied to buildmode.") if(pa.Find("middle")) holder.buildmode.objholder = text2path("[object.type]") - if(holder.buildmode.objsay) usr << "[object.type]" + if(holder.buildmode.objsay) + to_chat(usr, "[object.type]") - - if(3) // Edit + if(BUILDMODE_EDIT) if(pa.Find("left")) //I cant believe this shit actually compiles. if(object.vars.Find(holder.buildmode.varholder)) log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") object.vars[holder.buildmode.varholder] = holder.buildmode.valueholder else - user << "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'" + to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") if(pa.Find("right")) if(object.vars.Find(holder.buildmode.varholder)) log_admin("[key_name(usr)] modified [object.name]'s [holder.buildmode.varholder] to [holder.buildmode.valueholder]") object.vars[holder.buildmode.varholder] = initial(object.vars[holder.buildmode.varholder]) else - user << "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'" + to_chat(user, "[initial(object.name)] does not have a var called '[holder.buildmode.varholder]'") - if(4) // Throw + if(BUILDMODE_THROW) if(pa.Find("left")) if(istype(object, /atom/movable)) holder.throw_atom = object @@ -391,17 +399,18 @@ if(holder.throw_atom) holder.throw_atom.throw_at(object, 10, 1) log_admin("[key_name(usr)] threw [holder.throw_atom] at [object]") - if(5) // Room build + + if(BUILDMODE_ROOM) if(pa.Find("left")) holder.buildmode.coordA = get_turf(object) - user << "Defined [object] ([object.type]) as point A." + to_chat(user, "Defined [object] ([object.type]) as point A.") if(pa.Find("right")) holder.buildmode.coordB = get_turf(object) - user << "Defined [object] ([object.type]) as point B." + to_chat(user, "Defined [object] ([object.type]) as point B.") if(holder.buildmode.coordA && holder.buildmode.coordB) - user << "A and B set, creating rectangle." + to_chat(user, "A and B set, creating rectangle.") holder.buildmode.make_rectangle( holder.buildmode.coordA, holder.buildmode.coordB, @@ -410,17 +419,18 @@ ) holder.buildmode.coordA = null holder.buildmode.coordB = null - if(6) // Ladders + + if(BUILDMODE_LADDER) if(pa.Find("left")) holder.buildmode.coordA = get_turf(object) - user << "Defined [object] ([object.type]) as upper ladder location." + to_chat(user, "Defined [object] ([object.type]) as upper ladder location.") if(pa.Find("right")) holder.buildmode.coordB = get_turf(object) - user << "Defined [object] ([object.type]) as lower ladder location." + to_chat(user, "Defined [object] ([object.type]) as lower ladder location.") if(holder.buildmode.coordA && holder.buildmode.coordB) - user << "Ladder locations set, building ladders." + to_chat(user, "Ladder locations set, building ladders.") var/obj/structure/ladder/A = new /obj/structure/ladder/up(holder.buildmode.coordA) var/obj/structure/ladder/B = new /obj/structure/ladder(holder.buildmode.coordB) A.target_up = B @@ -429,7 +439,8 @@ B.update_icon() holder.buildmode.coordA = null holder.buildmode.coordB = null - if(7) // Move into contents + + if(BUILDMODE_CONTENTS) if(pa.Find("left")) if(istype(object, /atom)) holder.throw_atom = object @@ -437,23 +448,32 @@ if(holder.throw_atom && istype(object, /atom/movable)) object.forceMove(holder.throw_atom) log_admin("[key_name(usr)] moved [object] into [holder.throw_atom].") - if(8) // Lights + + if(BUILDMODE_LIGHTS) if(pa.Find("left")) if(object) object.set_light(holder.buildmode.new_light_range, holder.buildmode.new_light_intensity, holder.buildmode.new_light_color) if(pa.Find("right")) if(object) object.set_light(0, 0, "#FFFFFF") - if(9) // AI control + + if(BUILDMODE_AI) if(pa.Find("left")) if(isliving(object)) var/mob/living/L = object - // Reset processes. - if(pa.Find("ctrl")) - if(!isnull(L.get_AI_stance())) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on. + + // Pause/unpause AI + if(pa.Find("shift")) + var/stance = L.get_AI_stance() + if(!isnull(stance)) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on. var/datum/ai_holder/AI = L.ai_holder - AI.forget_everything() - to_chat(user, span("notice", "\The [L]'s AI has forgotten its target/movement destination/leader.")) + if(stance == STANCE_SLEEP) + AI.go_wake() + to_chat(user, span("notice", "\The [L]'s AI has been enabled.")) + else + AI.go_sleep() + to_chat(user, span("notice", "\The [L]'s AI has been disabled.")) + return else to_chat(user, span("warning", "\The [L] is not AI controlled.")) return @@ -468,6 +488,12 @@ to_chat(user, span("warning", "\The [L] is not AI controlled.")) return + // Copy faction + if(pa.Find("ctrl")) + holder.copied_faction = L.faction + to_chat(user, span("notice", "Copied faction '[holder.copied_faction]'.")) + return + // Select/Deselect if(!isnull(L.get_AI_stance())) if(L in holder.selected_mobs) @@ -476,10 +502,27 @@ else holder.select_AI_mob(user.client, L) to_chat(user, span("notice", "Selected \the [L].")) + return else to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + else //Not living + for(var/mob/living/unit in holder.selected_mobs) + holder.deselect_AI_mob(user.client, unit) + if(pa.Find("right")) + // Paste faction + if(pa.Find("ctrl") && isliving(object)) + if(!holder.copied_faction) + to_chat(user, span("warning", "LMB+Shift a mob to copy their faction before pasting.")) + return + else + var/mob/living/L = object + L.faction = holder.copied_faction + to_chat(user, span("notice", "Pasted faction '[holder.copied_faction]'.")) + return + if(istype(object, /atom)) // Force attack. var/atom/A = object @@ -490,6 +533,9 @@ AI.give_target(A) i++ to_chat(user, span("notice", "Commanded [i] mob\s to attack \the [A].")) + var/image/orderimage = image(buildmode_hud,A,"ai_targetorder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) return if(isliving(object)) // Follow or attack. @@ -514,16 +560,67 @@ if(j) message += "[j] mob\s to follow \the [L]." to_chat(user, span("notice", message)) + var/image/orderimage = image(buildmode_hud,L,"ai_targetorder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return if(isturf(object)) // Move or reposition. var/turf/T = object - var/i = 0 + var/forced = 0 + var/told = 0 for(var/mob/living/unit in holder.selected_mobs) var/datum/ai_holder/AI = unit.ai_holder - AI.give_destination(T, 1, pa.Find("shift")) // If shift is held, the mobs will not stop moving to attack a visible enemy. - i++ - to_chat(user, span("notice", "Commanded [i] mob\s to move to \the [T].")) + if(unit.get_AI_stance() == STANCE_SLEEP) + unit.forceMove(T) + forced++ + else + AI.give_destination(T, 1, pa.Find("shift")) // If shift is held, the mobs will not stop moving to attack a visible enemy. + told++ + to_chat(user, span("notice", "Commanded [told] mob\s to move to \the [T], and manually placed [forced] of them.")) + var/image/orderimage = image(buildmode_hud,T,"ai_turforder") + orderimage.plane = PLANE_BUILDMODE + flick_overlay(orderimage, list(user.client), 8, TRUE) + return +/proc/build_drag(var/client/user, buildmode, var/atom/fromatom, var/atom/toatom, var/atom/fromloc, var/atom/toloc, var/fromcontrol, var/tocontrol, params) + var/obj/effect/bmode/buildholder/holder = null + for(var/obj/effect/bmode/buildholder/H) + if(H.cl == user) + holder = H + break + if(!holder) return + var/list/pa = params2list(params) + + switch(buildmode) + if(BUILDMODE_AI) + + //Holding shift prevents the deselection of existing + if(!pa.Find("shift")) + for(var/mob/living/unit in holder.selected_mobs) + holder.deselect_AI_mob(user, unit) + + var/turf/c1 = get_turf(fromatom) + var/turf/c2 = get_turf(toatom) + if(!c1 || !c2) + return //Dragged outside window or something + + var/low_x = min(c1.x,c2.x) + var/low_y = min(c1.y,c2.y) + var/hi_x = max(c1.x,c2.x) + var/hi_y = max(c1.y,c2.y) + var/z = c1.z //Eh + + var/i = 0 + for(var/mob/living/L in living_mob_list) + if(L.z != z || L.client) + continue + if(L.x >= low_x && L.x <= hi_x && L.y >= low_y && L.y <= hi_y) + holder.select_AI_mob(user, L) + i++ + + to_chat(user, span("notice", "Band-selected [i] mobs.")) + return /obj/effect/bmode/buildmode/proc/get_path_from_partial_text(default_path) var/desired_path = input("Enter full or partial typepath.","Typepath","[default_path]") @@ -595,4 +692,15 @@ if(isturf(floor_type)) T.ChangeTurf(floor_type) else - new floor_type(T) \ No newline at end of file + new floor_type(T) + +#undef BUILDMODE_BASIC +#undef BUILDMODE_ADVANCED +#undef BUILDMODE_EDIT +#undef BUILDMODE_THROW +#undef BUILDMODE_ROOM +#undef BUILDMODE_LADDER +#undef BUILDMODE_CONTENTS +#undef BUILDMODE_LIGHTS +#undef BUILDMODE_AI +#undef LAST_BUILDMODE \ No newline at end of file diff --git a/code/modules/admin/verbs/custom_event.dm b/code/modules/admin/verbs/custom_event.dm index 987eb89c29..407843784a 100644 --- a/code/modules/admin/verbs/custom_event.dm +++ b/code/modules/admin/verbs/custom_event.dm @@ -19,10 +19,10 @@ custom_event_msg = input - world << "

Custom Event

" - world << "

A custom event is starting. OOC Info:

" - world << "[custom_event_msg]" - world << "
" + to_world("

Custom Event

") + to_world("

A custom event is starting. OOC Info:

") + to_world("[custom_event_msg]") + to_world("
") // normal verb for players to view info /client/verb/cmd_view_custom_event() diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 6a9405fef6..4500379ec5 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -303,7 +303,7 @@ set name = "Assume direct control" set desc = "Direct intervention" - if(!check_rights(R_DEBUG|R_ADMIN)) return + if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return if(M.ckey) if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes") return @@ -341,7 +341,7 @@ var/list/areas_with_intercom = list() var/list/areas_with_camera = list() - for(var/area/A in all_areas) + for(var/area/A in world) if(!(A.type in areas_all)) areas_all.Add(A.type) @@ -388,33 +388,33 @@ var/list/areas_without_intercom = areas_all - areas_with_intercom var/list/areas_without_camera = areas_all - areas_with_camera - world << "AREAS WITHOUT AN APC:" + to_world("AREAS WITHOUT AN APC:") for(var/areatype in areas_without_APC) - world << "* [areatype]" + to_world("* [areatype]") - world << "AREAS WITHOUT AN AIR ALARM:" + to_world("AREAS WITHOUT AN AIR ALARM:") for(var/areatype in areas_without_air_alarm) - world << "* [areatype]" + to_world("* [areatype]") - world << "AREAS WITHOUT A REQUEST CONSOLE:" + to_world("AREAS WITHOUT A REQUEST CONSOLE:") for(var/areatype in areas_without_RC) - world << "* [areatype]" + to_world("* [areatype]") - world << "AREAS WITHOUT ANY LIGHTS:" + to_world("AREAS WITHOUT ANY LIGHTS:") for(var/areatype in areas_without_light) - world << "* [areatype]" + to_world("* [areatype]") - world << "AREAS WITHOUT A LIGHT SWITCH:" + to_world("AREAS WITHOUT A LIGHT SWITCH:") for(var/areatype in areas_without_LS) - world << "* [areatype]" + to_world("* [areatype]") - world << "AREAS WITHOUT ANY INTERCOMS:" + to_world("AREAS WITHOUT ANY INTERCOMS:") for(var/areatype in areas_without_intercom) - world << "* [areatype]" + to_world("* [areatype]") - world << "AREAS WITHOUT ANY CAMERAS:" + to_world("AREAS WITHOUT ANY CAMERAS:") for(var/areatype in areas_without_camera) - world << "* [areatype]" + to_world("* [areatype]") /datum/admins/proc/cmd_admin_dress(input in getmobs()) set category = "Fun" @@ -539,7 +539,7 @@ Pump.air2.gas["nitrogen"] = 3750 //The contents of 2 canisters. Pump.air2.temperature = 50 Pump.air2.update_values() - Pump.use_power=1 + Pump.update_use_power(USE_POWER_IDLE) Pump.target_pressure = 4500 Pump.update_icon() @@ -638,13 +638,14 @@ return var/datum/planet/planet = input(usr, "Which planet do you want to modify the weather on?", "Change Weather") in SSplanets.planets - var/datum/weather/new_weather = input(usr, "What weather do you want to change to?", "Change Weather") as null|anything in planet.weather_holder.allowed_weather_types - if(new_weather) - planet.weather_holder.change_weather(new_weather) - planet.weather_holder.rebuild_forecast() - var/log = "[key_name(src)] changed [planet.name]'s weather to [new_weather]." - message_admins(log) - log_admin(log) + if(istype(planet)) + var/datum/weather/new_weather = input(usr, "What weather do you want to change to?", "Change Weather") as null|anything in planet.weather_holder.allowed_weather_types + if(new_weather) + planet.weather_holder.change_weather(new_weather) + planet.weather_holder.rebuild_forecast() + var/log = "[key_name(src)] changed [planet.name]'s weather to [new_weather]." + message_admins(log) + log_admin(log) /datum/admins/proc/change_time() set category = "Debug" @@ -655,20 +656,20 @@ return var/datum/planet/planet = input(usr, "Which planet do you want to modify time on?", "Change Time") in SSplanets.planets + if(istype(planet)) + var/datum/time/current_time_datum = planet.current_time + var/new_hour = input(usr, "What hour do you want to change to?", "Change Time", text2num(current_time_datum.show_time("hh"))) as null|num + if(!isnull(new_hour)) + var/new_minute = input(usr, "What minute do you want to change to?", "Change Time", text2num(current_time_datum.show_time("mm")) ) as null|num + if(!isnull(new_minute)) + var/type_needed = current_time_datum.type + var/datum/time/new_time = new type_needed() + new_time = new_time.add_hours(new_hour) + new_time = new_time.add_minutes(new_minute) + planet.current_time = new_time + spawn(1) + planet.update_sun() - var/datum/time/current_time_datum = planet.current_time - var/new_hour = input(usr, "What hour do you want to change to?", "Change Time", text2num(current_time_datum.show_time("hh"))) as null|num - if(!isnull(new_hour)) - var/new_minute = input(usr, "What minute do you want to change to?", "Change Time", text2num(current_time_datum.show_time("mm")) ) as null|num - if(!isnull(new_minute)) - var/type_needed = current_time_datum.type - var/datum/time/new_time = new type_needed() - new_time = new_time.add_hours(new_hour) - new_time = new_time.add_minutes(new_minute) - planet.current_time = new_time - spawn(1) - planet.update_sun() - - var/log = "[key_name(src)] changed [planet.name]'s time to [planet.current_time.show_time("hh:mm")]." - message_admins(log) - log_admin(log) \ No newline at end of file + var/log = "[key_name(src)] changed [planet.name]'s time to [planet.current_time.show_time("hh:mm")]." + message_admins(log) + log_admin(log) \ No newline at end of file diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 5199f8455f..d96fdae6bd 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -128,7 +128,7 @@ return if(!air_master) - usr << "Cannot find air_system" + to_chat(usr, "Cannot find air_system") return var/datum/air_group/dead_groups = list() for(var/datum/air_group/group in air_master.air_groups) @@ -150,7 +150,7 @@ return if(!air_master) - usr << "Cannot find air_system" + to_chat(usr, "Cannot find air_system") return var/turf/T = get_turf(usr) @@ -159,7 +159,7 @@ AG.next_check = 30 AG.group_processing = 0 else - usr << "Local airgroup is unsimulated!" + to_chat(usr, "Local airgroup is unsimulated!") feedback_add_details("admin_verb","KLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! */ @@ -168,9 +168,9 @@ set desc = "This spams all the active jobban entries for the current round to standard output." set category = "Debug" - usr << "Jobbans active in this round." + to_chat(usr, "Jobbans active in this round.") for(var/t in jobban_keylist) - usr << "[t]" + to_chat(usr, "[t]") /client/proc/print_jobban_old_filter() set name = "Search Jobban Log" @@ -181,7 +181,7 @@ if(!job_filter) return - usr << "Jobbans active in this round." + to_chat(usr, "Jobbans active in this round.") for(var/t in jobban_keylist) if(findtext(t, job_filter)) - usr << "[t]" + to_chat(usr, "[t]") diff --git a/code/modules/admin/verbs/dice.dm b/code/modules/admin/verbs/dice.dm index e5877a9da2..72e78be2b0 100644 --- a/code/modules/admin/verbs/dice.dm +++ b/code/modules/admin/verbs/dice.dm @@ -14,11 +14,11 @@ var/dice = num2text(sum) + "d" + num2text(side) if(alert("Do you want to inform the world about your game?",,"Yes", "No") == "Yes") - world << "

The dice have been rolled by Gods!

" + to_world("

The dice have been rolled by Gods!

") var/result = roll(dice) if(alert("Do you want to inform the world about the result?",,"Yes", "No") == "Yes") - world << "

Gods rolled [dice], result is [result]

" + to_world("

Gods rolled [dice], result is [result]

") message_admins("[key_name_admin(src)] rolled dice [dice], result is [result]", 1) \ No newline at end of file diff --git a/code/modules/admin/verbs/fps.dm b/code/modules/admin/verbs/fps.dm new file mode 100644 index 0000000000..fbafc004db --- /dev/null +++ b/code/modules/admin/verbs/fps.dm @@ -0,0 +1,23 @@ +//Merged Doohl's and the existing ticklag as they both had good elements about them ~ +//Replaces the old Ticklag verb, fps is easier to understand +/client/proc/set_server_fps() + set category = "Debug" + set name = "Set Server FPS" + set desc = "Sets game speed in frames-per-second. Can potentially break the game" + + if(!check_rights(R_DEBUG)) + return + + var/new_fps = round(input("Sets game frames-per-second. Can potentially break the game (default: [config.fps])", "FPS", world.fps) as num|null) + if(new_fps <= 0) + to_chat(src, "Error: set_server_fps(): Invalid world.fps value. No changes made.") + return + if(new_fps > config.fps * 1.5) + if(alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [config.fps]", "Warning!", "Confirm", "ABORT-ABORT-ABORT") != "Confirm") + return + + var/msg = "[key_name(src)] has modified world.fps to [new_fps]" + log_admin(msg, 0) + message_admins(msg, 0) + world.change_fps(new_fps) + feedback_add_details("admin_verb", "SETFPS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 7215c8dd32..2dcbc6e4a7 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -33,7 +33,7 @@ return target.verbs |= /client/proc/getruntimelog - target << "You have been granted access to runtime logs. Please use them responsibly or risk being banned." + to_chat(target, "You have been granted access to runtime logs. Please use them responsibly or risk being banned.") return @@ -100,7 +100,7 @@ set name = "Show Server Attack Log" set desc = "Shows today's server attack log." - to_chat(usr,"This verb doesn't actually do anything.") + to_chat(usr, "This verb doesn't actually do anything.") /* var/path = "data/logs/[time2text(world.realtime,"YYYY/MM-Month/DD-Day")] Attack.log" diff --git a/code/modules/admin/verbs/grief_fixers.dm b/code/modules/admin/verbs/grief_fixers.dm index 2d524b4c23..b8815876b2 100644 --- a/code/modules/admin/verbs/grief_fixers.dm +++ b/code/modules/admin/verbs/grief_fixers.dm @@ -2,7 +2,7 @@ set category = "Admin" set name = "Fix Atmospherics Grief" - if(!check_rights(R_ADMIN|R_DEBUG)) return + if(!check_rights(R_ADMIN|R_DEBUG|R_EVENT)) return if(alert("WARNING: Executing this command will perform a full reset of atmosphere. All pipelines will lose any gas that may be in them, and all zones will be reset to contain air mix as on roundstart. The supermatter engine will also be stopped (to prevent overheat due to removal of coolant). Do not use unless the station is suffering serious atmospheric issues due to grief or bug.", "Full Atmosphere Reboot", "No", "Yes") == "No") @@ -10,14 +10,14 @@ feedback_add_details("admin_verb","FA") log_and_message_admins("Full atmosphere reset initiated by [usr].") - world << "Initiating restart of atmosphere. The server may lag a bit." + to_world("Initiating restart of atmosphere. The server may lag a bit.") sleep(10) var/current_time = world.timeofday // Depower the supermatter, as it would quickly blow up once we remove all gases from the pipes. for(var/obj/machinery/power/supermatter/S in machines) S.power = 0 - usr << "\[1/5\] - Supermatter depowered" + to_chat(usr, "\[1/5\] - Supermatter depowered") // Remove all gases from all pipenets for(var/datum/pipe_network/PN in pipe_networks) @@ -25,27 +25,27 @@ G.gas = list() G.update_values() - usr << "\[2/5\] - All pipenets purged of gas." + to_chat(usr, "\[2/5\] - All pipenets purged of gas.") // Delete all zones. for(var/zone/Z in world) Z.c_invalidate() - usr << "\[3/5\] - All ZAS Zones removed." + to_chat(usr, "\[3/5\] - All ZAS Zones removed.") var/list/unsorted_overlays = list() for(var/id in gas_data.tile_overlay) unsorted_overlays |= gas_data.tile_overlay[id] - for(var/turf/simulated/T in turfs) + for(var/turf/simulated/T in world) T.air = null T.overlays.Remove(unsorted_overlays) T.zone = null - usr << "\[4/5\] - All turfs reset to roundstart values." + to_chat(usr, "\[4/5\] - All turfs reset to roundstart values.") SSair.RebootZAS() - usr << "\[5/5\] - ZAS Rebooted" - world << "Atmosphere restart completed in [(world.timeofday - current_time)/10] seconds." \ No newline at end of file + to_chat(usr, "\[5/5\] - ZAS Rebooted") + to_world("Atmosphere restart completed in [(world.timeofday - current_time)/10] seconds.") \ No newline at end of file diff --git a/code/modules/admin/verbs/lightning_strike.dm b/code/modules/admin/verbs/lightning_strike.dm index 1981e3f0d8..df28238177 100644 --- a/code/modules/admin/verbs/lightning_strike.dm +++ b/code/modules/admin/verbs/lightning_strike.dm @@ -1,6 +1,6 @@ /client/proc/admin_lightning_strike() set name = "Lightning Strike" - set desc = "Causes lightning to strike on your tile. This will hurt things on or nearby it severely." + set desc = "Causes lightning to strike on your tile. This can be made to hurt things on or nearby it severely." set category = "Fun" if(!check_rights(R_FUN)) @@ -28,7 +28,7 @@ // Do a lightning flash for the whole planet, if the turf belongs to a planet. var/datum/planet/P = null - P = SSplanets.z_to_planet[T.z] + P = LAZYACCESS(SSplanets.z_to_planet, T.z) if(P) var/datum/weather_holder/holder = P.weather_holder flick("lightning_flash", holder.special_visuals) @@ -64,7 +64,7 @@ // Otherwise only those on the current z-level will hear it. var/sound = get_sfx("thunder") for(var/mob/M in player_list) - if((P && M.z in P.expected_z_levels) || M.z == T.z) + if( (P && (M.z in P.expected_z_levels)) || M.z == T.z) if(M.is_preference_enabled(/datum/client_preference/weather_sounds)) M.playsound_local(get_turf(M), soundin = sound, vol = 70, vary = FALSE, is_global = TRUE) diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index f22a70827f..aa2b10365c 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -139,7 +139,7 @@ var/list/debug_verbs = list ( ,/client/proc/cmd_assume_direct_control ,/client/proc/jump_to_dead_group ,/client/proc/startSinglo - ,/client/proc/ticklag + ,/client/proc/set_server_fps ,/client/proc/cmd_admin_grantfullaccess ,/client/proc/kaboom ,/client/proc/cmd_admin_areatest @@ -164,7 +164,6 @@ var/list/debug_verbs = list ( ,/datum/admins/proc/setup_supermatter ,/client/proc/atmos_toggle_debug ,/client/proc/spawn_tanktransferbomb - ,/client/proc/debug_process_scheduler // VOREStation Edit - Nice ,/client/proc/take_picture ) @@ -221,7 +220,7 @@ var/list/debug_verbs = list ( var/turf/simulated/location = get_turf(usr) if(!istype(location, /turf/simulated)) // We're in space, let's not cause runtimes. - usr << "this debug tool cannot be used from space" + to_chat(usr, "this debug tool cannot be used from space") return var/icon/red = new('icons/misc/debug_group.dmi', "red") //created here so we don't have to make thousands of these. @@ -229,11 +228,11 @@ var/list/debug_verbs = list ( var/icon/blue = new('icons/misc/debug_group.dmi', "blue") if(!usedZAScolors) - usr << "ZAS Test Colors" - usr << "Green = Zone you are standing in" - usr << "Blue = Connected zone to the zone you are standing in" - usr << "Yellow = A zone that is connected but not one adjacent to your connected zone" - usr << "Red = Not connected" + to_chat(usr, "ZAS Test Colors") + to_chat(usr, "Green = Zone you are standing in") + to_chat(usr, "Blue = Connected zone to the zone you are standing in") + to_chat(usr, "Yellow = A zone that is connected but not one adjacent to your connected zone") + to_chat(usr, "Red = Not connected") usedZAScolors = 1 testZAScolors_zones += location.zone @@ -317,9 +316,9 @@ var/list/debug_verbs = list ( if(i*10+j <= atom_list.len) temp_atom = atom_list[i*10+j] line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " - world << line*/ + to_world(line)*/ - world << "There are [count] objects of type [type_path] on z-level [num_level]" + to_world("There are [count] objects of type [type_path] on z-level [num_level]") feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/count_objects_all() @@ -344,9 +343,9 @@ var/list/debug_verbs = list ( if(i*10+j <= atom_list.len) temp_atom = atom_list[i*10+j] line += " no.[i+10+j]@\[[temp_atom.x], [temp_atom.y], [temp_atom.z]\]; " - world << line*/ + to_world(line)*/ - world << "There are [count] objects of type [type_path] in the game world" + to_world("There are [count] objects of type [type_path] in the game world") feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -365,7 +364,7 @@ var/global/prevent_airgroup_regroup = 0 set category = "Mapping" set name = "Regroup All Airgroups Attempt" - usr << "Proc disabled." //Why not.. Delete the procs instead? + to_chat(usr, "Proc disabled.") //Why not.. Delete the procs instead? /*prevent_airgroup_regroup = 0 for(var/datum/air_group/AG in air_master.air_groups) @@ -376,7 +375,7 @@ var/global/prevent_airgroup_regroup = 0 set category = "Mapping" set name = "Kill pipe processing" - usr << "Proc disabled." + to_chat(usr, "Proc disabled.") /*pipe_processing_killed = !pipe_processing_killed if(pipe_processing_killed) @@ -388,7 +387,7 @@ var/global/prevent_airgroup_regroup = 0 set category = "Mapping" set name = "Kill air processing" - usr << "Proc disabled." + to_chat(usr, "Proc disabled.") /*air_processing_killed = !air_processing_killed if(air_processing_killed) @@ -402,7 +401,7 @@ var/global/say_disabled = 0 set category = "Mapping" set name = "Disable all communication verbs" - usr << "Proc disabled." + to_chat(usr, "Proc disabled.") /*say_disabled = !say_disabled if(say_disabled) @@ -417,7 +416,7 @@ var/global/movement_disabled_exception //This is the client that calls the proc, set category = "Mapping" set name = "Disable all movement" - usr << "Proc disabled." + to_chat(usr, "Proc disabled.") /*movement_disabled = !movement_disabled if(movement_disabled) diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 0f8f9ea3a1..3b3843efbd 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -61,7 +61,7 @@ var/list/sounds_cache = list() for(var/mob/living/carbon/human/CP in human_mob_list) if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") - CP << "Your body can't contain the rhumba beat" + to_chat(CP, "Your body can't contain the rhumba beat") CP.gib() diff --git a/code/modules/admin/verbs/possess.dm b/code/modules/admin/verbs/possess.dm index aa100623ff..5e4a3d0ce7 100644 --- a/code/modules/admin/verbs/possess.dm +++ b/code/modules/admin/verbs/possess.dm @@ -4,7 +4,7 @@ if(istype(O,/obj/singularity)) if(config.forbid_singulo_possession) - usr << "It is forbidden to possess singularities." + to_chat(usr, "It is forbidden to possess singularities.") return var/turf/T = get_turf(O) diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 5469367beb..aec9db7e81 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -3,7 +3,7 @@ set name = "Pray" if(say_disabled) //This is here to try to identify lag problems - usr << "Speech is currently admin-disabled." + to_chat(usr, "Speech is currently admin-disabled.") return msg = sanitize(msg) @@ -13,18 +13,18 @@ if(msg) client.handle_spam_prevention(MUTE_PRAY) if(usr.client.prefs.muted & MUTE_PRAY) - usr << " You cannot pray (muted)." + to_chat(usr, " You cannot pray (muted).") return var/image/cross = image('icons/obj/storage.dmi',"bible") - msg = "\icon[cross] PRAY: [key_name(src, 1)] (?) (PP) (VV) (SM) ([admin_jump_link(src, src)]) (CA) (SC) (SMITE): [msg]" + msg = "[bicon(cross)] PRAY: [key_name(src, 1)] (?) (PP) (VV) (SM) ([admin_jump_link(src, src)]) (CA) (SC) (SMITE): [msg]" for(var/client/C in admins) - if(R_ADMIN & C.holder.rights) + if(R_ADMIN|R_EVENT & C.holder.rights) if(C.is_preference_enabled(/datum/client_preference/admin/show_chat_prayers)) - C << msg + to_chat(C,msg) C << 'sound/effects/ding.ogg' - usr << "Your prayers have been received by the gods." + to_chat(usr, "Your prayers have been received by the gods.") feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //log_admin("HELP: [key_name(src)]: [msg]") @@ -32,13 +32,13 @@ /proc/CentCom_announce(var/msg, var/mob/Sender, var/iamessage) msg = "[uppertext(using_map.boss_short)]M[iamessage ? " IA" : ""]:[key_name(Sender, 1)] (PP) (VV) (SM) ([admin_jump_link(Sender, src)]) (CA) (BSA) (RPLY): [msg]" for(var/client/C in admins) - if(R_ADMIN & C.holder.rights) - C << msg + if(R_ADMIN|R_EVENT & C.holder.rights) + to_chat(C,msg) C << 'sound/machines/signal.ogg' /proc/Syndicate_announce(var/msg, var/mob/Sender) msg = "ILLEGAL:[key_name(Sender, 1)] (PP) (VV) (SM) ([admin_jump_link(Sender, src)]) (CA) (BSA) (RPLY): [msg]" for(var/client/C in admins) - if(R_ADMIN & C.holder.rights) - C << msg + if(R_ADMIN|R_EVENT & C.holder.rights) + to_chat(C,msg) C << 'sound/machines/signal.ogg' \ No newline at end of file diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 3367b98852..ace7d48e91 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -2,7 +2,6 @@ set category = null set name = "Drop Everything" if(!holder) - to_chat(src, "Only administrators may use this command.") return var/confirm = alert(src, "Make [M] drop everything?", "Message", "Yes", "No") @@ -20,8 +19,8 @@ set category = "Admin" set name = "Prison" if(!holder) - to_chat(src, "Only administrators may use this command.") return + if (ismob(M)) if(istype(M, /mob/living/silicon/ai)) alert("The AI can't be sent to prison you jerk!", null, null, null, null, null) @@ -38,7 +37,7 @@ prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/prison(prisoner), slot_w_uniform) prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) spawn(50) - M << "You have been sent to the prison station!" + to_chat(M, "You have been sent to the prison station!") log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -48,7 +47,7 @@ set category = "Admin" set name = "Check new Players" if(!holder) - to_chat(src, "Only staff members may use this command.") + return var/age = alert(src, "Age check", "Show accounts yonger then _____ days","7", "30" , "All") @@ -83,7 +82,6 @@ if(!ismob(M)) return if (!holder) - to_chat(src, "Only administrators may use this command.") return var/msg = sanitize(input("Message:", text("Subtle PM to [M.key]")) as text) @@ -93,7 +91,7 @@ if(usr) if (usr.client) if(usr.client.holder) - M << "You hear a voice in your head...[msg]" + to_chat(M, "You hear a voice in your head...[msg]") log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" @@ -106,14 +104,15 @@ set name = "Global Narrate" if (!holder) - to_chat(src, "Only administrators may use this command.") return - var/msg = sanitize(input("Message:", text("Enter the text you wish to appear to everyone:")) as text) + var/msg = input("Message:", text("Enter the text you wish to appear to everyone:")) as text + if(!(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. + msg = sanitize(msg) if (!msg) return - world << "[msg]" + to_world("[msg]") log_admin("GlobalNarrate: [key_name(usr)] : [msg]") message_admins(" GlobalNarrate: [key_name_admin(usr)] : [msg]
", 1) feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -123,7 +122,6 @@ set name = "Direct Narrate" if(!holder) - to_chat(src, "Only administrators and moderators may use this command.") return if(!M) @@ -132,7 +130,9 @@ if(!M) return - var/msg = sanitize(input("Message:", text("Enter the text you wish to appear to your target:")) as text) + var/msg = input("Message:", text("Enter the text you wish to appear to your target:")) as text + if(msg && !(msg[1] == "<" && msg[length(msg)] == ">")) //You can use HTML but only if the whole thing is HTML. Tries to prevent admin 'accidents'. + msg = sanitize(msg) if( !msg ) return @@ -147,11 +147,12 @@ /client/proc/cmd_admin_godmode(mob/M as mob in mob_list) set category = "Special Verbs" set name = "Godmode" + if(!holder) - to_chat(src, "Only administrators may use this command.") return + M.status_flags ^= GODMODE - usr << " Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]" + to_chat(usr, " Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") var/msg = "[key_name_admin(usr)] has toggled [ADMIN_LOOKUPFLW(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]" @@ -168,12 +169,12 @@ proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) if(!usr || !usr.client) return if(!usr.client.holder) - usr << "Error: cmd_admin_mute: You don't have permission to do this." + to_chat(usr, "Error: cmd_admin_mute: You don't have permission to do this.") return if(!M.client) - usr << "Error: cmd_admin_mute: This mob doesn't have a client tied to it." + to_chat(usr, "Error: cmd_admin_mute: This mob doesn't have a client tied to it.") if(M.client.holder) - usr << "Error: cmd_admin_mute: You cannot mute an admin/mod." + to_chat(usr, "Error: cmd_admin_mute: You cannot mute an admin/mod.") if(!M.client) return if(M.client.holder) @@ -196,7 +197,7 @@ proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) M.client.prefs.muted |= mute_type log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - M << "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin." + to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return @@ -209,15 +210,16 @@ proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - M << "You have been [muteunmute] from [mute_string]." + to_chat(M, "You have been [muteunmute] from [mute_string].") feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/cmd_admin_add_random_ai_law() set category = "Fun" set name = "Add Random AI Law" + if(!holder) - to_chat(src, "Only administrators may use this command.") return + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") if(confirm != "Yes") return log_admin("[key_name(src)] has added a random AI law.") @@ -264,8 +266,10 @@ Ccomp's first proc. set category = "Special Verbs" set name = "Allow player to respawn" set desc = "Let's the player bypass the wait to respawn or allow them to re-enter their corpse." + if(!holder) - to_chat(src, "Only administrators and moderators may use this command.") + return + var/list/ghosts= get_ghosts(1,1) var/target = input("Please, select a ghost!", "COME BACK TO LIFE!", null, null) as null|anything in ghosts @@ -295,7 +299,8 @@ Ccomp's first proc. set desc = "Toggles antagHUD usage for observers" if(!holder) - to_chat(src, "Only administrators may use this command.") + return + var/action="" if(config.antag_hud_allowed) for(var/mob/observer/dead/g in get_ghosts()) @@ -304,7 +309,7 @@ Ccomp's first proc. if(g.antagHUD) g.antagHUD = 0 // Disable it on those that have it enabled g.has_enabled_antagHUD = 2 // We'll allow them to respawn - g << "The Administrator has disabled AntagHUD " + to_chat(g, "The Administrator has disabled AntagHUD ") config.antag_hud_allowed = 0 to_chat(src, "AntagHUD usage has been disabled") action = "disabled" @@ -312,7 +317,7 @@ Ccomp's first proc. for(var/mob/observer/dead/g in get_ghosts()) if(!g.client.holder) // Add the verb back for all non-admin ghosts g.verbs += /mob/observer/dead/verb/toggle_antagHUD - g << "The Administrator has enabled AntagHUD " // Notify all observers they can now use AntagHUD + to_chat(g, "The Administrator has enabled AntagHUD ") // Notify all observers they can now use AntagHUD config.antag_hud_allowed = 1 action = "enabled" to_chat(src, "AntagHUD usage has been enabled") @@ -327,19 +332,21 @@ Ccomp's first proc. set category = "Server" set name = "Toggle antagHUD Restrictions" set desc = "Restricts players that have used antagHUD from being able to join this round." + if(!holder) - to_chat(src, "Only administrators may use this command.") + return + var/action="" if(config.antag_hud_restricted) for(var/mob/observer/dead/g in get_ghosts()) - g << "The administrator has lifted restrictions on joining the round if you use AntagHUD" + to_chat(g, "The administrator has lifted restrictions on joining the round if you use AntagHUD") action = "lifted restrictions" config.antag_hud_restricted = 0 to_chat(src, "AntagHUD restrictions have been lifted") else for(var/mob/observer/dead/g in get_ghosts()) - g << "The administrator has placed restrictions on joining the round if you use AntagHUD" - g << "Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions " + to_chat(g, "The administrator has placed restrictions on joining the round if you use AntagHUD") + to_chat(g, "Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ") g.antagHUD = 0 g.has_enabled_antagHUD = 0 action = "placed restrictions" @@ -358,16 +365,22 @@ Traitors and the like can also be revived with the previous role mostly intact. set category = "Special Verbs" set name = "Spawn Character" set desc = "(Re)Spawn a client's loaded character." + if(!holder) - to_chat(src, "Only administrators may use this command.") return - //I frontload all the questions so we don't have a half-done process while you're reading. var/client/picked_client = input(src, "Please specify which client's character to spawn.", "Client", "") as null|anything in GLOB.clients if(!picked_client) return - var/location = alert(src,"Please specify where to spawn them.", "Location", "Right Here", "Arrivals", "Cancel") + respawn_character_proper(picked_client) + +/client/proc/respawn_character_proper(client/picked_client) + if(!istype(picked_client)) + return + + //I frontload all the questions so we don't have a half-done process while you're reading. + var/location = alert(src, "Please specify where to spawn them.", "Location", "Right Here", "Arrivals", "Cancel") if(location == "Cancel" || !location) return @@ -438,15 +451,17 @@ Traitors and the like can also be revived with the previous role mostly intact. var/mob/living/carbon/human/new_character var/spawnloc + var/sparks //Where did you want to spawn them? switch(location) if("Right Here") //Spawn them on your turf - if(!src.mob) - to_chat(src, "You can't use 'Right Here' when you are not 'Right Anywhere'!") - return - spawnloc = get_turf(src.mob) + sparks = alert(src,"Sparks like they teleported in?", "Showy", "Yes", "No", "Cancel") + if(sparks == "Cancel") + return + if(sparks == "No") + sparks = FALSE if("Arrivals") //Spawn them at a latejoin spawnpoint spawnloc = pick(latejoin) @@ -461,6 +476,14 @@ Traitors and the like can also be revived with the previous role mostly intact. return new_character = new(spawnloc) + + if(sparks) + anim(spawnloc,new_character,'icons/mob/mob.dmi',,"phasein",,new_character.dir) + playsound(spawnloc, "sparks", 50, 1) + var/datum/effect/effect/system/spark_spread/spk = new(new_character) + spk.set_up(5, 0, new_character) + spk.attach(new_character) + spk.start() //We were able to spawn them, right? if(!new_character) @@ -502,12 +525,12 @@ Traitors and the like can also be revived with the previous role mostly intact. //If we're announcing their arrival if(announce) - AnnounceArrival(new_character, new_character.mind.assigned_role) + AnnounceArrival(new_character, new_character.mind.assigned_role, "Common", new_character.z) log_admin("[admin] has spawned [player_key]'s character [new_character.real_name].") message_admins("[admin] has spawned [player_key]'s character [new_character.real_name].", 1) - new_character << "You have been fully spawned. Enjoy the game." + to_chat(new_character, "You have been fully spawned. Enjoy the game.") feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -516,21 +539,22 @@ Traitors and the like can also be revived with the previous role mostly intact. /client/proc/cmd_admin_add_freeform_ai_law() set category = "Fun" set name = "Add Custom AI law" + if(!holder) - to_chat(src, "Only administrators may use this command.") return + var/input = sanitize(input(usr, "Please enter anything you want the AI to do. Anything. Serious.", "What?", "") as text|null) if(!input) return for(var/mob/living/silicon/ai/M in mob_list) if (M.stat == 2) - usr << "Upload failed. No signal is being detected from the AI." + to_chat(usr, "Upload failed. No signal is being detected from the AI.") else if (M.see_in_dark == 0) - usr << "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power." + to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") else M.add_ion_law(input) for(var/mob/living/silicon/ai/O in mob_list) - O << input + "... LAWS UPDATED!" + to_chat(O,input + "... LAWS UPDATED!") O.show_laws() log_admin("Admin [key_name(usr)] has added a new AI law - [input]") @@ -544,9 +568,10 @@ Traitors and the like can also be revived with the previous role mostly intact. /client/proc/cmd_admin_rejuvenate(mob/living/M as mob in mob_list) set category = "Special Verbs" set name = "Rejuvenate" + if(!holder) - to_chat(src, "Only administrators may use this command.") return + if(!mob) return if(!istype(M)) @@ -566,9 +591,10 @@ Traitors and the like can also be revived with the previous role mostly intact. /client/proc/cmd_admin_create_centcom_report() set category = "Special Verbs" set name = "Create Command Report" + if(!holder) - to_chat(src, "Only administrators may use this command.") return + var/input = sanitize(input(usr, "Please enter anything you want. Anything. Serious.", "What?", "") as message|null, extra = 0) var/customname = sanitizeSafe(input(usr, "Pick a title for the report.", "Title") as text|null) if(!input) @@ -583,7 +609,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if("Yes") command_announcement.Announce(input, customname, new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1); if("No") - world << "New [using_map.company_name] Update available at all communication consoles." + to_world("New [using_map.company_name] Update available at all communication consoles.") world << sound('sound/AI/commandreport.ogg') log_admin("[key_name(src)] has created a command report: [input]") @@ -595,8 +621,8 @@ Traitors and the like can also be revived with the previous role mostly intact. set name = "Delete" if (!holder) - to_chat(src, "Only administrators may use this command.") return + admin_delete(O) /client/proc/cmd_admin_list_open_jobs() @@ -604,8 +630,8 @@ Traitors and the like can also be revived with the previous role mostly intact. set name = "List free slots" if (!holder) - to_chat(src, "Only administrators may use this command.") return + if(job_master) for(var/datum/job/job in job_master.occupations) to_chat(src, "[job.title]: [job.total_positions]") @@ -690,6 +716,9 @@ Traitors and the like can also be revived with the previous role mostly intact. set name = "Gibself" set category = "Fun" + if(!holder) + return + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") if(confirm == "Yes") if (istype(mob, /mob/observer/dead)) // so they don't spam gibs everywhere @@ -732,9 +761,9 @@ Traitors and the like can also be revived with the previous role mostly intact. return if(M) AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) - M << "You have been banned by [usr.client.ckey].\nReason: [reason]." - M << "This is a temporary ban, it will be removed in [mins] minutes." - M << "To try to resolve this matter head to http://ss13.donglabs.com/forum/" + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") + to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=[mins]&server=[replacetext(config.server_name, "#", "")]") @@ -747,9 +776,9 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!reason) return AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) - M << "You have been banned by [usr.client.ckey].\nReason: [reason]." - M << "This is a permanent ban." - M << "To try to resolve this matter head to http://ss13.donglabs.com/forum/" + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a permanent ban.") + to_chat(M, "To try to resolve this matter head to http://ss13.donglabs.com/forum/") log_admin("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") message_admins("[usr.client.ckey] has banned [M.ckey].\nReason: [reason]\nThis is a permanent ban.") world.Export("http://216.38.134.132/adminlog.php?type=ban&key=[usr.client.key]&key2=[M.key]&msg=[html_decode(reason)]&time=perma&server=[replacetext(config.server_name, "#", "")]") @@ -767,9 +796,12 @@ Traitors and the like can also be revived with the previous role mostly intact. set name = "Check Contents" set popup_menu = FALSE //VOREStation Edit - Declutter. + if(!holder) + return + var/list/L = M.get_contents() for(var/t in L) - usr << "[t]" + to_chat(usr, "[t]") feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /* This proc is DEFERRED. Does not do anything. @@ -808,10 +840,15 @@ Traitors and the like can also be revived with the previous role mostly intact. set name = "Change View Range" set desc = "switches between 1x and custom views" + if(!holder) + return + + var/view = src.view if(view == world.view) view = input("Select view range:", "FUCK YE", 7) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) else view = world.view + mob.set_viewsize(view) log_admin("[key_name(usr)] changed their view range to [view].") //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI @@ -819,7 +856,6 @@ Traitors and the like can also be revived with the previous role mostly intact. feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/admin_call_shuttle() - set category = "Admin" set name = "Call Shuttle" @@ -887,9 +923,9 @@ Traitors and the like can also be revived with the previous role mostly intact. set category = "Special Verbs" set name = "Attack Log" - usr << text("Attack Log for []", mob) + to_chat(usr, "Attack Log for [mob]") for(var/t in M.attack_log) - usr << t + to_chat(usr,t) feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -901,13 +937,13 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!check_rights(R_FUN)) return if (ticker && ticker.mode) - usr << "Nope you can't do this, the game's already started. This only works before rounds!" + to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") return if(ticker.random_players) ticker.random_players = 0 message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) - usr << "Disabled." + to_chat(usr, "Disabled.") return @@ -919,9 +955,9 @@ Traitors and the like can also be revived with the previous role mostly intact. message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) if(notifyplayers == "Yes") - world << "Admin [usr.key] has forced the players to have completely random identities!" + to_world("Admin [usr.key] has forced the players to have completely random identities!") - usr << "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet." + to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") ticker.random_players = 1 feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -930,17 +966,17 @@ Traitors and the like can also be revived with the previous role mostly intact. /client/proc/toggle_random_events() set category = "Server" set name = "Toggle random events on/off" - set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" + if(!check_rights(R_SERVER)) return //VOREStation Edit if(!config.allow_random_events) config.allow_random_events = 1 - usr << "Random events enabled" + to_chat(usr, "Random events enabled") message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) else config.allow_random_events = 0 - usr << "Random events disabled" + to_chat(usr, "Random events disabled") message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -950,7 +986,7 @@ Traitors and the like can also be revived with the previous role mostly intact. set desc = "Removes a player from the round as if they'd cryo'd." set popup_menu = FALSE - if(!check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN|R_EVENT)) return if(!M) diff --git a/code/modules/admin/verbs/randomverbs_vr.dm b/code/modules/admin/verbs/randomverbs_vr.dm index 9fee7c58d0..dbf38882d9 100644 --- a/code/modules/admin/verbs/randomverbs_vr.dm +++ b/code/modules/admin/verbs/randomverbs_vr.dm @@ -2,8 +2,8 @@ set category = "Special Verbs" set name = "Spawn Character As Mob" set desc = "Spawn a specified ckey as a chosen mob." + if(!holder) - to_chat(src, "Only administrators may use this command.") return var/client/picked_client = input(src, "Who are we spawning as a mob?", "Client", "Cancel") as null|anything in GLOB.clients diff --git a/code/modules/admin/verbs/smite_vr.dm b/code/modules/admin/verbs/smite_vr.dm index b0d4b8aa8c..fb52f18b50 100644 --- a/code/modules/admin/verbs/smite_vr.dm +++ b/code/modules/admin/verbs/smite_vr.dm @@ -8,7 +8,7 @@ if(!istype(target)) return - var/list/smite_types = list(SMITE_SHADEKIN_ATTACK,SMITE_SHADEKIN_NOMF,SMITE_REDSPACE_ABDUCT) + var/list/smite_types = list(SMITE_SHADEKIN_ATTACK,SMITE_SHADEKIN_NOMF,SMITE_REDSPACE_ABDUCT,SMITE_AUTOSAVE,SMITE_AUTOSAVE_WIDE) var/smite_choice = input("Select the type of SMITE for [target]","SMITE Type Choice") as null|anything in smite_types if(!smite_choice) @@ -50,7 +50,7 @@ shadekin.death() if(SMITE_SHADEKIN_NOMF) - var/list/kin_types = list( + var/static/list/kin_types = list( "Red Eyes (Dark)" = /mob/living/simple_mob/shadekin/red/dark, "Red Eyes (Light)" = /mob/living/simple_mob/shadekin/red/white, "Red Eyes (Brown)" = /mob/living/simple_mob/shadekin/red/brown, @@ -122,6 +122,12 @@ if(SMITE_REDSPACE_ABDUCT) redspace_abduction(target, src) + if(SMITE_AUTOSAVE) + fake_autosave(target, src) + + if(SMITE_AUTOSAVE_WIDE) + fake_autosave(target, src, TRUE) + else return //Injection? Don't print any messages. @@ -211,3 +217,47 @@ var/redspace_abduction_z to_chat(user,"The mob has been moved. ([admin_jump_link(target,usr.client.holder)])") target.transforming = FALSE + +/proc/fake_autosave(var/mob/living/target, var/client/user, var/wide) + if(!istype(target) || !target.client) + to_chat(user, "Skipping [target] because they are not a /mob/living or have no client.") + return + + if(wide) + for(var/mob/living/L in orange(user.view, user.mob)) + fake_autosave(L, user) + return + + target.move_delay = 99999999 + + to_chat(target, "Autosaving your progress, please wait...") + target << 'sound/effects/ding.ogg' + + var/static/list/bad_tips = list( + "Did you know that black shoes protect you from electrocution while hacking?", + "Did you know that airlocks always have a wire that disables ID checks?", + "You can always find at least 3 pairs of glowing purple gloves in maint!", + "Phoron is not toxic if you've had a soda within 30 seconds of exposure!", + "Space Mountain Wind makes you immune to damage from space for 30 seconds!", + "A mask and air tank are all you need to be safe in space!", + "When exploring maintenance, wearing no shoes makes you move faster!", + "Did you know that the bartender's shotgun is loaded with harmless ammo?", + "Did you know that the tesla and singulo only need containment for 5 minutes?") + + var/tip = pick(bad_tips) + to_chat(target, "Tip of the day:
[tip]") + + var/obj/screen/loader = new(target) + loader.name = "Autosaving..." + loader.desc = "A disc icon that represents your game autosaving. Please wait." + loader.icon = 'icons/obj/discs_vr.dmi' + loader.icon_state = "quicksave" + loader.screen_loc = "NORTH-1, EAST-1" + target.client.screen += loader + + spawn(10 SECONDS) + if(target) + target.move_delay = 0 + to_chat(target, "Autosave complete!") + if(target.client) + target.client.screen -= loader diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm index 65d954fbc9..c0c64008c9 100644 --- a/code/modules/admin/verbs/striketeam.dm +++ b/code/modules/admin/verbs/striketeam.dm @@ -11,11 +11,11 @@ var/const/commandos_possible = 6 //if more Commandos are needed in the future return if(!ticker) - usr << "The game hasn't started yet!" + to_chat(usr, "The game hasn't started yet!") return if(world.time < 6000) - usr << "There are [(6000-world.time)/10] seconds remaining before it may be called." + to_chat(usr, "There are [(6000-world.time)/10] seconds remaining before it may be called.") return var/datum/antagonist/deathsquad/team @@ -33,7 +33,7 @@ var/const/commandos_possible = 6 //if more Commandos are needed in the future return if(team.deployed) - usr << "Someone is already sending a team." + to_chat(usr, "Someone is already sending a team.") return if(alert("Do you want to send in a strike team? Once enabled, this is irreversible.",,"Yes","No")!="Yes") @@ -49,7 +49,7 @@ var/const/commandos_possible = 6 //if more Commandos are needed in the future return if(team.deployed) - usr << "Looks like someone beat you to it." + to_chat(usr, "Looks like someone beat you to it.") return team.attempt_random_spawn() diff --git a/code/modules/admin/verbs/ticklag.dm b/code/modules/admin/verbs/ticklag.dm deleted file mode 100644 index 3c34f0f35a..0000000000 --- a/code/modules/admin/verbs/ticklag.dm +++ /dev/null @@ -1,24 +0,0 @@ -//Merged Doohl's and the existing ticklag as they both had good elements about them ~Carn - -/client/proc/ticklag() - set category = "Debug" - set name = "Set Ticklag" - set desc = "Sets a new tick lag. Recommend you don't mess with this too much! Stable, time-tested ticklag value is 0.9" - - if(!check_rights(R_DEBUG)) return - - var/newtick = input("Sets a new tick lag. Please don't mess with this too much! The stable, time-tested ticklag value is 0.9","Lag of Tick", world.tick_lag) as num|null - //I've used ticks of 2 before to help with serious singulo lags - if(newtick && newtick <= 2 && newtick > 0) - log_admin("[key_name(src)] has modified world.tick_lag to [newtick]", 0) - message_admins("[key_name(src)] has modified world.tick_lag to [newtick]", 0) - world.tick_lag = newtick - feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - switch(alert("Enable Tick Compensation?","Tick Comp is currently: [config.Tickcomp]","Yes","No")) - if("Yes") config.Tickcomp = 1 - else config.Tickcomp = 0 - else - to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") - - diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm index 8cf88aa127..eaf6dfa49f 100644 --- a/code/modules/admin/verbs/tripAI.dm +++ b/code/modules/admin/verbs/tripAI.dm @@ -3,20 +3,20 @@ set name = "Create AI Triumvirate" if(ticker.current_state > GAME_STATE_PREGAME) - usr << "This option is currently only usable during pregame. This may change at a later date." + to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") return if(job_master && ticker) var/datum/job/job = job_master.GetJob("AI") if(!job) - usr << "Unable to locate the AI job" + to_chat(usr, "Unable to locate the AI job") return if(ticker.triai) ticker.triai = 0 - usr << "Only one AI will be spawned at round start." + to_chat(usr, "Only one AI will be spawned at round start.") message_admins("[key_name_admin(usr)] has toggled off triple AIs at round start.", 1) else ticker.triai = 1 - usr << "There will be an AI Triumvirate at round start." + to_chat(usr, "There will be an AI Triumvirate at round start.") message_admins("[key_name_admin(usr)] has toggled on triple AIs at round start.", 1) return diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm index bcf097f813..97ce67e61b 100644 --- a/code/modules/admin/view_variables/debug_variables.dm +++ b/code/modules/admin/view_variables/debug_variables.dm @@ -8,7 +8,7 @@ name = DA[name] //name is really the index until this line else value = DA[name] - header = "
  • (E) (C) (-) " + header = "
  • (E) (C) (-) " else header = "
  • (E) (C) (M) " else diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm index 8878318fd6..9a999e1a75 100644 --- a/code/modules/admin/view_variables/modify_variables.dm +++ b/code/modules/admin/view_variables/modify_variables.dm @@ -97,9 +97,10 @@ GLOBAL_PROTECT(VVpixelmovement) L += var_value - switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) - if("Yes") - L[var_value] = mod_list_add_ass(O) //hehe + if(IS_VALID_ASSOC_KEY(var_value)) + switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) + if("Yes") + L[var_value] = mod_list_add_ass(O) //hehe if (O) if (O.vv_edit_var(objectvar, L) == FALSE) to_chat(src, "Your edit was rejected by the object.") @@ -179,12 +180,13 @@ GLOBAL_PROTECT(VVpixelmovement) if (index == null) return var/assoc = 0 - var/prompt = alert(src, "Do you want to edit the key or its assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") - if (prompt == "Cancel") - return - if (prompt == "Assigned Value") - assoc = 1 - assoc_key = L[index] + if(IS_VALID_ASSOC_KEY(L[index])) + var/prompt = alert(src, "Do you want to edit the key or its assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") + if (prompt == "Cancel") + return + if (prompt == "Assigned Value") + assoc = 1 + assoc_key = L[index] var/default var/variable var/old_assoc_value //EXPERIMENTAL - Keep old associated value while modifying key, if any @@ -193,9 +195,10 @@ GLOBAL_PROTECT(VVpixelmovement) else variable = L[index] //EXPERIMENTAL - Keep old associated value while modifying key, if any - var/found = L[variable] - if(!isnull(found)) - old_assoc_value = found + if(IS_VALID_ASSOC_KEY(variable)) + var/found = L[variable] + if(!isnull(found)) + old_assoc_value = found // default = vv_get_class(objectvar, variable) @@ -310,6 +313,13 @@ GLOBAL_PROTECT(VVpixelmovement) if(!variable) return + if(variable in GLOB.VVpixelmovement) + if(!check_rights(R_DEBUG)) + return + var/prompt = alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if (prompt != "Continue") + return + if(!O.can_vv_get(variable)) return diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 28eea3707b..43c51e65c6 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -4,8 +4,10 @@ if((usr.client != src) || !src.holder) return var/datum/target = locate(href_list["target"]) - if(target) + if(istype(target)) target.vv_do_topic(href_list) + else if(islist(target)) + vv_do_list(target, href_list) if(href_list["Vars"]) debug_variables(locate(href_list["Vars"])) @@ -16,7 +18,7 @@ var/mob/M = locate(href_list["rename"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return var/new_name = sanitize(input(usr,"What would you like to name this mob?","Input a name",M.real_name) as text|null, MAX_NAME_LEN) @@ -31,7 +33,7 @@ var/D = locate(href_list["datumedit"]) if(!istype(D,/datum) && !istype(D,/client)) - usr << "This can only be used on instances of types /client or /datum" + to_chat(usr, "This can only be used on instances of types /client or /datum") return modify_variables(D, href_list["varnameedit"], 1) @@ -41,7 +43,7 @@ var/D = locate(href_list["datumchange"]) if(!istype(D,/datum) && !istype(D,/client)) - usr << "This can only be used on instances of types /client or /datum" + to_chat(usr, "This can only be used on instances of types /client or /datum") return modify_variables(D, href_list["varnamechange"], 0) @@ -51,7 +53,7 @@ var/atom/A = locate(href_list["datummass"]) if(!istype(A)) - usr << "This can only be used on instances of type /atom" + to_chat(usr, "This can only be used on instances of type /atom") return cmd_mass_modify_object_variables(A, href_list["varnamemass"]) @@ -61,41 +63,41 @@ var/mob/M = locate(href_list["mob_player_panel"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return src.holder.show_player_panel(M) href_list["datumrefresh"] = href_list["mob_player_panel"] else if(href_list["give_spell"]) - if(!check_rights(R_ADMIN|R_FUN)) return + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return var/mob/M = locate(href_list["give_spell"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return src.give_spell(M) href_list["datumrefresh"] = href_list["give_spell"] else if(href_list["give_modifier"]) - if(!check_rights(R_ADMIN|R_FUN|R_DEBUG)) + if(!check_rights(R_ADMIN|R_FUN|R_DEBUG|R_EVENT)) return var/mob/living/M = locate(href_list["give_modifier"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob/living" + to_chat(usr, "This can only be used on instances of type /mob/living") return src.admin_give_modifier(M) href_list["datumrefresh"] = href_list["give_modifier"] else if(href_list["give_disease2"]) - if(!check_rights(R_ADMIN|R_FUN)) return + if(!check_rights(R_ADMIN|R_FUN|R_EVENT)) return var/mob/M = locate(href_list["give_disease2"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return src.give_disease2(M) @@ -106,7 +108,7 @@ var/mob/M = locate(href_list["godmode"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return src.cmd_admin_godmode(M) @@ -117,7 +119,7 @@ var/mob/M = locate(href_list["gib"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return src.cmd_admin_gib(M) @@ -127,18 +129,18 @@ var/mob/M = locate(href_list["build_mode"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return togglebuildmode(M) href_list["datumrefresh"] = href_list["build_mode"] else if(href_list["drop_everything"]) - if(!check_rights(R_DEBUG|R_ADMIN)) return + if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return var/mob/M = locate(href_list["drop_everything"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(usr.client) @@ -149,7 +151,7 @@ var/mob/M = locate(href_list["direct_control"]) if(!istype(M)) - usr << "This can only be used on instances of type /mob" + to_chat(usr, "This can only be used on instances of type /mob") return if(usr.client) @@ -160,7 +162,7 @@ var/mob/living/carbon/human/H = locate(href_list["make_skeleton"]) if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return H.ChangeToSkeleton() @@ -217,7 +219,7 @@ var/atom/A = locate(href_list["rotatedatum"]) if(!istype(A)) - usr << "This can only be done to instances of type /atom" + to_chat(usr, "This can only be done to instances of type /atom") return switch(href_list["rotatedir"]) @@ -230,12 +232,12 @@ var/mob/living/carbon/human/H = locate(href_list["makemonkey"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) @@ -244,12 +246,12 @@ var/mob/living/carbon/human/H = locate(href_list["makerobot"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return holder.Topic(href, list("makerobot"=href_list["makerobot"])) @@ -258,12 +260,12 @@ var/mob/living/carbon/human/H = locate(href_list["makealien"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return holder.Topic(href, list("makealien"=href_list["makealien"])) @@ -272,12 +274,12 @@ var/mob/living/carbon/human/H = locate(href_list["makeai"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") return if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return holder.Topic(href, list("makeai"=href_list["makeai"])) @@ -286,26 +288,26 @@ var/mob/living/carbon/human/H = locate(href_list["setspecies"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob/living/carbon/human" + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") return var/new_species = input("Please choose a new species.","Species",null) as null|anything in GLOB.all_species if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return if(H.set_species(new_species)) - usr << "Set species of [H] to [H.species]." + to_chat(usr, "Set species of [H] to [H.species].") else - usr << "Failed! Something went wrong." + to_chat(usr, "Failed! Something went wrong.") else if(href_list["addlanguage"]) if(!check_rights(R_SPAWN)) return var/mob/H = locate(href_list["addlanguage"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob" + to_chat(usr, "This can only be done to instances of type /mob") return var/new_language = input("Please choose a language to add.","Language",null) as null|anything in GLOB.all_languages @@ -314,24 +316,24 @@ return if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return if(H.add_language(new_language)) - usr << "Added [new_language] to [H]." + to_chat(usr, "Added [new_language] to [H].") else - usr << "Mob already knows that language." + to_chat(usr, "Mob already knows that language.") else if(href_list["remlanguage"]) if(!check_rights(R_SPAWN)) return var/mob/H = locate(href_list["remlanguage"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob" + to_chat(usr, "This can only be done to instances of type /mob") return if(!H.languages.len) - usr << "This mob knows no languages." + to_chat(usr, "This mob knows no languages.") return var/datum/language/rem_language = input("Please choose a language to remove.","Language",null) as null|anything in H.languages @@ -340,13 +342,13 @@ return if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return if(H.remove_language(rem_language.name)) - usr << "Removed [rem_language] from [H]." + to_chat(usr, "Removed [rem_language] from [H].") else - usr << "Mob doesn't know that language." + to_chat(usr, "Mob doesn't know that language.") else if(href_list["addverb"]) if(!check_rights(R_DEBUG)) return @@ -354,7 +356,7 @@ var/mob/living/H = locate(href_list["addverb"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob/living" + to_chat(usr, "This can only be done to instances of type /mob/living") return var/list/possibleverbs = list() possibleverbs += "Cancel" // One for the top... @@ -366,13 +368,13 @@ if(istype(H,/mob/living/silicon/ai)) possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/ai/proc,/mob/living/silicon/ai/verb) if(istype(H,/mob/living/simple_mob)) - possibleverbs += typesof(/mob/living/simple_mob/proc,/mob/living/simple_mob/verb) //VOREStation edit, Apparently polaris simplemobs have no verbs at all. + possibleverbs += typesof(/mob/living/simple_mob/proc) possibleverbs -= H.verbs possibleverbs += "Cancel" // ...And one for the bottom var/verb = input("Select a verb!", "Verbs",null) as anything in possibleverbs if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return if(!verb || verb == "Cancel") return @@ -385,11 +387,11 @@ var/mob/H = locate(href_list["remverb"]) if(!istype(H)) - usr << "This can only be done to instances of type /mob" + to_chat(usr, "This can only be done to instances of type /mob") return var/verb = input("Please choose a verb to remove.","Verbs",null) as null|anything in H.verbs if(!H) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return if(!verb) return @@ -401,18 +403,18 @@ var/mob/living/carbon/M = locate(href_list["addorgan"]) if(!istype(M)) - usr << "This can only be done to instances of type /mob/living/carbon" + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") return var/new_organ = input("Please choose an organ to add.","Organ",null) as null|anything in typesof(/obj/item/organ)-/obj/item/organ if(!new_organ) return if(!M) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return if(locate(new_organ) in M.internal_organs) - usr << "Mob already has that organ." + to_chat(usr, "Mob already has that organ.") return new new_organ(M) @@ -423,20 +425,20 @@ var/mob/living/carbon/M = locate(href_list["remorgan"]) if(!istype(M)) - usr << "This can only be done to instances of type /mob/living/carbon" + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") return var/obj/item/organ/rem_organ = input("Please choose an organ to remove.","Organ",null) as null|anything in M.internal_organs if(!M) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return if(!(locate(rem_organ) in M.internal_organs)) - usr << "Mob does not have that organ." + to_chat(usr, "Mob does not have that organ.") return - usr << "Removed [rem_organ] from [M]." + to_chat(usr, "Removed [rem_organ] from [M].") rem_organ.removed() qdel(rem_organ) @@ -446,13 +448,13 @@ var/mob/H = locate(href_list["fix_nano"]) if(!istype(H) || !H.client) - usr << "This can only be done on mobs with clients" + to_chat(usr, "This can only be done on mobs with clients") return - SSnanoui.send_resources(H.client) + H.client.send_resources() - usr << "Resource files sent" - H << "Your NanoUI Resource files have been refreshed" + to_chat(usr, "Resource files sent") + to_chat(H, "Your NanoUI Resource files have been refreshed") log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(H)] ") @@ -461,12 +463,12 @@ var/mob/M = locate(href_list["regenerateicons"]) if(!ismob(M)) - usr << "This can only be done to instances of type /mob" + to_chat(usr, "This can only be done to instances of type /mob") return M.regenerate_icons() else if(href_list["adjustDamage"] && href_list["mobToDamage"]) - if(!check_rights(R_DEBUG|R_ADMIN|R_FUN)) return + if(!check_rights(R_DEBUG|R_ADMIN|R_FUN|R_EVENT)) return var/mob/living/L = locate(href_list["mobToDamage"]) if(!istype(L)) return @@ -476,7 +478,7 @@ var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num if(!L) - usr << "Mob doesn't exist anymore" + to_chat(usr, "Mob doesn't exist anymore") return switch(Text) @@ -487,7 +489,7 @@ if("brain") L.adjustBrainLoss(amount) if("clone") L.adjustCloneLoss(amount) else - usr << "You caused an error. DEBUG: Text:[Text] Mob:[L]" + to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") return if(amount != 0) @@ -519,6 +521,6 @@ if(href_list["datumrefresh"]) var/datum/DAT = locate(href_list["datumrefresh"]) - if(istype(DAT, /datum) || istype(DAT, /client)) + if(istype(DAT, /datum) || istype(DAT, /client) || islist(DAT)) debug_variables(DAT) diff --git a/code/modules/admin/view_variables/topic_list.dm b/code/modules/admin/view_variables/topic_list.dm new file mode 100644 index 0000000000..c76023ec1f --- /dev/null +++ b/code/modules/admin/view_variables/topic_list.dm @@ -0,0 +1,44 @@ +//LISTS - CAN NOT DO VV_DO_TOPIC BECAUSE LISTS AREN'T DATUMS :( +/client/proc/vv_do_list(list/target, href_list) + if(!check_rights(R_VAREDIT)) + return + var/target_index = text2num(href_list["index"]) + if(target_index) + if(href_list[VV_HK_LIST_EDIT]) + mod_list(target, null, "list", "contents", target_index, autodetect_class = TRUE) + if(href_list[VV_HK_LIST_CHANGE]) + mod_list(target, null, "list", "contents", target_index, autodetect_class = FALSE) + if(href_list[VV_HK_LIST_REMOVE]) + var/variable = target[target_index] + var/prompt = alert("Do you want to remove item number [target_index] from list?", "Confirm", "Yes", "No") + if (prompt != "Yes") + return + target.Cut(target_index, target_index+1) + log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") + log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") + message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") + if(href_list[VV_HK_LIST_ADD]) + mod_list_add(target, null, "list", "contents") + if(href_list[VV_HK_LIST_ERASE_DUPES]) + uniqueList_inplace(target) + log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") + log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") + if(href_list[VV_HK_LIST_ERASE_NULLS]) + listclearnulls(target) + log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") + log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") + if(href_list[VV_HK_LIST_SET_LENGTH]) + var/value = vv_get_value(VV_NUM) + if (value["class"] != VV_NUM || value["value"] > max(5000, target.len)) //safety - would rather someone not put an extra 0 and erase the server's memory lmao. + return + target.len = value["value"] + log_world("### ListVarEdit by [src]: /list len: [target.len]") + log_admin("[key_name(src)] modified list's len: [target.len]") + message_admins("[key_name_admin(src)] modified list's len: [target.len]") + if(href_list[VV_HK_LIST_SHUFFLE]) + shuffle_inplace(target) + log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") + log_admin("[key_name(src)] modified list's contents: SHUFFLE") + message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index efa6f424f1..767d693abd 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -55,12 +55,12 @@ if (islist) dropdownoptions = list( "---", - "Add Item" = "?_src_=vars;listadd=[refid]", - "Remove Nulls" = "?_src_=vars;listnulls=[refid]", - "Remove Dupes" = "?_src_=vars;listdupes=[refid]", - "Set len" = "?_src_=vars;listlen=[refid]", - "Shuffle" = "?_src_=vars;listshuffle=[refid]", - "Show VV To Player" = "?_src_=vars;expose=[refid]" + "Add Item" = "?_src_=vars;[VV_HK_LIST_ADD]=TRUE;target=[refid]", + "Remove Nulls" = "?_src_=vars;[VV_HK_LIST_ERASE_NULLS]=TRUE;target=[refid]", + "Remove Dupes" = "?_src_=vars;[VV_HK_LIST_ERASE_DUPES]=TRUE;target=[refid]", + "Set len" = "?_src_=vars;[VV_HK_LIST_SET_LENGTH]=TRUE;target=[refid]", + "Shuffle" = "?_src_=vars;[VV_HK_LIST_SHUFFLE]=TRUE;target=[refid]", + // "Show VV To Player" = "?_src_=vars;[VV_HK_EXPOSE]=TRUE;target=[refid]" // TODO - Not yet implemented for lists ) autoconvert_dropdown = TRUE else @@ -88,7 +88,7 @@ for (var/i in 1 to L.len) var/key = L[i] var/value - if (IS_NORMAL_LIST(L) && !isnum(key)) + if (IS_NORMAL_LIST(L) && IS_VALID_ASSOC_KEY(key)) value = L[key] variable_html += debug_variable(i, value, 0, D) else diff --git a/code/modules/ai/_defines.dm b/code/modules/ai/_defines.dm index e94d26b3c3..7b49f18ddb 100644 --- a/code/modules/ai/_defines.dm +++ b/code/modules/ai/_defines.dm @@ -20,6 +20,11 @@ #define MOVEMENT_FAILED 0 // Move() returned false for whatever reason and the mob didn't move. #define MOVEMENT_SUCCESSFUL 1 // Move() returned true and the mob hopefully moved. +// Results of pre-attack checks +#define ATTACK_ON_COOLDOWN -1 // Recently attacked and needs to try again soon. +#define ATTACK_FAILED 0 // Something else went wrong! Maybe they moved away! +#define ATTACK_SUCCESSFUL 1 // We attacked (or tried to, misses count too) + // Reasons for targets to not be valid. Based on why, the AI responds differently. #define AI_TARGET_VALID 0 // We can fight them. #define AI_TARGET_INVIS 1 // They were in field of view but became invisible. Switch to STANCE_BLINDFIGHT if no other viable targets exist. diff --git a/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm index 45a3067a1b..a53906a7a3 100644 --- a/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm +++ b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm @@ -150,3 +150,40 @@ // Simple mobs that retaliate and support others in their faction who get attacked. /datum/ai_holder/simple_mob/retaliate/cooperative cooperative = TRUE + +// With all the bells and whistles +/datum/ai_holder/simple_mob/humanoid + intelligence_level = AI_SMART //Purportedly + retaliate = TRUE //If attacked, attack back + threaten = TRUE //Verbal threats + firing_lanes = TRUE //Avoid shooting allies + conserve_ammo = TRUE //Don't shoot when it can't hit target + can_breakthrough = TRUE //Can break through doors + violent_breakthrough = FALSE //Won't try to break through walls (humans can, but usually don't) + speak_chance = 2 //Babble chance + cooperative = TRUE //Assist each other + wander = TRUE //Wander around + returns_home = TRUE //But not too far + use_astar = TRUE //Path smartly + home_low_priority = TRUE //Following/helping is more important + +// The hostile subtype is implied to be trained combatants who use ""tactics"" +/datum/ai_holder/simple_mob/humanoid/hostile + var/run_if_this_close = 4 // If anything gets within this range, it'll try to move away. + hostile = TRUE //Attack! + +// Juke +/datum/ai_holder/simple_mob/humanoid/hostile/post_melee_attack(atom/A) + holder.IMove(get_step(holder, pick(alldirs))) + holder.face_atom(A) + +/datum/ai_holder/simple_mob/humanoid/hostile/post_ranged_attack(atom/A) + //Pick a random turf to step into + var/turf/T = get_step(holder, pick(alldirs)) + if(check_trajectory(A, T)) // Can we even hit them from there? + holder.IMove(T) + holder.face_atom(A) + + if(get_dist(holder, A) < run_if_this_close) + holder.IMove(get_step_away(holder, A)) + holder.face_atom(A) diff --git a/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm index 16609cc961..743e2b405c 100644 --- a/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm +++ b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm @@ -77,7 +77,7 @@ if(rabid) return var/justified = my_slime.is_justified_to_discipline() // This will also consider the AI-side of that proc. - lost_target() // Stop attacking. + remove_target() // Stop attacking. if(justified) obedience++ @@ -133,11 +133,11 @@ return rabid = TRUE my_slime.update_mood() - my_slime.visible_message(span("danger", "\The [src] enrages!")) + my_slime.visible_message(span("danger", "\The [my_slime] enrages!")) // Called when using a pacification agent (or it's Kendrick being initalized). /datum/ai_holder/simple_mob/xenobio_slime/proc/pacify() - lost_target() // So it stops trying to kill them. + remove_target() // So it stops trying to kill them. rabid = FALSE hostile = FALSE retaliate = FALSE @@ -167,7 +167,7 @@ return 1 // Melee (eat) the target if dead/dying, don't shoot it. return ..() -/datum/ai_holder/simple_mob/xenobio_slime/can_attack(atom/movable/AM) +/datum/ai_holder/simple_mob/xenobio_slime/can_attack(atom/movable/AM, var/vision_required = TRUE) . = ..() if(.) // Do some additional checks because we have Special Code(tm). if(ishuman(AM)) @@ -247,7 +247,7 @@ else delayed_say("Fine...", speaker) adjust_discipline(1, TRUE) // This must come before losing the target or it will be unjustified. - lost_target() + remove_target() if(leader) // We're being asked to stop following someone. diff --git a/code/modules/ai/ai_holder.dm b/code/modules/ai/ai_holder.dm index 4ef5507d5b..b5272ea996 100644 --- a/code/modules/ai/ai_holder.dm +++ b/code/modules/ai/ai_holder.dm @@ -9,6 +9,10 @@ /mob/living/Initialize() if(ai_holder_type) ai_holder = new ai_holder_type(src) + if(istype(src, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + H.hud_used = new /datum/hud(H) + H.instantiate_hud(H.hud_used) return ..() /mob/living/Destroy() @@ -46,8 +50,25 @@ home_turf = null return ..() +/datum/ai_holder/proc/update_stance_hud() + var/image/stanceimage = holder.grab_hud(LIFE_HUD) + stanceimage.icon_state = "ais_[stance]" + holder.apply_hud(LIFE_HUD, stanceimage) + +/datum/ai_holder/proc/update_paused_hud() + var/image/sleepingimage = holder.grab_hud(STATUS_HUD) + var/asleep = 0 + if(busy) + asleep = 2 + else if (stance == STANCE_SLEEP) + asleep = 1 + sleepingimage.icon_state = "ai_[asleep]" + holder.apply_hud(STATUS_HUD, sleepingimage) // Now for the actual AI stuff. +/datum/ai_holder/proc/set_busy(var/value = 0) + busy = value + update_paused_hud() // Makes this ai holder not get processed. // Called automatically when the host mob is killed. @@ -58,6 +79,7 @@ forget_everything() // If we ever wake up, its really unlikely that our current memory will be of use. set_stance(STANCE_SLEEP) SSai.processing -= src + update_paused_hud() // Reverses the above proc. // Revived mobs will wake their AI if they have one. @@ -68,6 +90,7 @@ return set_stance(STANCE_IDLE) SSai.processing += src + update_paused_hud() /datum/ai_holder/proc/should_wake() if(holder.client && !autopilot) @@ -80,21 +103,15 @@ /datum/ai_holder/proc/forget_everything() // Some of these might be redundant, but hopefully this prevents future bugs if that changes. lose_follow() - lose_target() - lose_target_position() - give_up_movement() + remove_target() // 'Tactical' processes such as moving a step, meleeing an enemy, firing a projectile, and other fairly cheap actions that need to happen quickly. /datum/ai_holder/proc/handle_tactics() - if(busy) - return handle_special_tactic() handle_stance_tactical() // 'Strategical' processes that are more expensive on the CPU and so don't get run as often as the above proc, such as A* pathfinding or robust targeting. /datum/ai_holder/proc/handle_strategicals() - if(busy) - return handle_special_strategical() handle_stance_strategical() @@ -103,39 +120,13 @@ /datum/ai_holder/proc/handle_special_strategical() -/* - //AI Actions - if(!ai_inactive) - //Stanceyness - handle_stance() - - //Movement - if(!stop_automated_movement && wander && !anchored) //Allowed to move? - handle_wander_movement() - - //Speaking - if(speak_chance && stance == STANCE_IDLE) // Allowed to chatter? - handle_idle_speaking() - - //Resisting out buckles - if(stance != STANCE_IDLE && incapacitated(INCAPACITATION_BUCKLED_PARTIALLY)) - handle_resist() - - //Resisting out of closets - if(istype(loc,/obj/structure/closet)) - var/obj/structure/closet/C = loc - if(C.welded) - resist() - else - C.open() -*/ - // For setting the stance WITHOUT processing it /datum/ai_holder/proc/set_stance(var/new_stance) ai_log("set_stance() : Setting stance from [stance] to [new_stance].", AI_LOG_INFO) stance = new_stance if(stance_coloring) // For debugging or really weird mobs. stance_color() + update_stance_hud() // This is called every half a second. /datum/ai_holder/proc/handle_stance_tactical() @@ -207,7 +198,8 @@ if(STANCE_REPOSITION) // This is the same as above but doesn't stop if an enemy is visible since its an 'in-combat' move order. ai_log("handle_stance_tactical() : STANCE_REPOSITION, going to walk_to_destination().", AI_LOG_TRACE) - walk_to_destination() + if(!target && !find_target()) + walk_to_destination() if(STANCE_FOLLOW) ai_log("handle_stance_tactical() : STANCE_FOLLOW, going to walk_to_leader().", AI_LOG_TRACE) @@ -233,12 +225,22 @@ ai_log("++++++++++ Slow Process Beginning ++++++++++", AI_LOG_TRACE) ai_log("handle_stance_strategical() : Called.", AI_LOG_TRACE) + //We got left around for some reason. Goodbye cruel world. + if(!holder) + qdel(src) + + ai_log("handle_stance_strategical() : LTT=[lose_target_time]", AI_LOG_TRACE) + if(lose_target_time && (lose_target_time + lose_target_timeout < world.time)) // We were tracking an enemy but they are gone. + ai_log("handle_stance_strategical() : Giving up a chase.", AI_LOG_DEBUG) + remove_target() + + if(stance in STANCES_COMBAT) + request_help() // Call our allies. + switch(stance) if(STANCE_IDLE) - if(speak_chance) // In the long loop since otherwise it wont shut up. handle_idle_speaking() - if(hostile) ai_log("handle_stance_strategical() : STANCE_IDLE, going to find_target().", AI_LOG_TRACE) find_target() @@ -246,6 +248,7 @@ if(target) ai_log("handle_stance_strategical() : STANCE_APPROACH, going to calculate_path([target]).", AI_LOG_TRACE) calculate_path(target) + walk_to_target() if(STANCE_MOVE) if(hostile && find_target()) // This will switch its stance. ai_log("handle_stance_strategical() : STANCE_MOVE, found target and was inturrupted.", AI_LOG_TRACE) @@ -255,6 +258,7 @@ else if(leader) ai_log("handle_stance_strategical() : STANCE_FOLLOW, going to calculate_path([leader]).", AI_LOG_TRACE) calculate_path(leader) + walk_to_leader() ai_log("handle_stance_strategical() : Exiting.", AI_LOG_TRACE) ai_log("++++++++++ Slow Process Ending ++++++++++", AI_LOG_TRACE) @@ -263,7 +267,7 @@ // Helper proc to turn AI 'busy' mode on or off without having to check if there is an AI, to simplify writing code. /mob/living/proc/set_AI_busy(value) if(ai_holder) - ai_holder.busy = value + ai_holder.set_busy(value) /mob/living/proc/is_AI_busy() if(!ai_holder) diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm index 63154e4fe4..075a9196d8 100644 --- a/code/modules/ai/ai_holder_combat.dm +++ b/code/modules/ai/ai_holder_combat.dm @@ -9,47 +9,23 @@ var/violent_breakthrough = TRUE // If false, the AI is not allowed to destroy things like windows or other structures in the way. Requires above var to be true. var/stand_ground = FALSE // If true, the AI won't try to get closer to an enemy if out of range. - - + // This does the actual attacking. /datum/ai_holder/proc/engage_target() ai_log("engage_target() : Entering.", AI_LOG_DEBUG) // Can we still see them? -// if(!target || !can_attack(target) || (!(target in list_targets())) ) if(!target || !can_attack(target)) ai_log("engage_target() : Lost sight of target.", AI_LOG_TRACE) - lose_target() // We lost them. - - if(!find_target()) // If we can't get a new one, then wait for a bit and then time out. - set_stance(STANCE_IDLE) - lost_target() - ai_log("engage_target() : No more targets. Exiting.", AI_LOG_DEBUG) + if(lose_target()) // We lost them (returns TRUE if we found something else to do) + ai_log("engage_target() : Pursuing other options (last seen, or a new target).", AI_LOG_TRACE) return - // if(lose_target_time + lose_target_timeout < world.time) - // ai_log("engage_target() : Unseen enemy timed out.", AI_LOG_TRACE) - // set_stance(STANCE_IDLE) // It must've been the wind. - // lost_target() - // ai_log("engage_target() : Exiting.", AI_LOG_DEBUG) - // return - - // // But maybe we do one last ditch effort. - // if(!target_last_seen_turf || intelligence_level < AI_SMART) - // ai_log("engage_target() : No last known position or is too dumb to fight unseen enemies.", AI_LOG_TRACE) - // set_stance(STANCE_IDLE) - // else - // ai_log("engage_target() : Fighting unseen enemy.", AI_LOG_TRACE) - // engage_unseen_enemy() - else - ai_log("engage_target() : Got new target ([target]).", AI_LOG_TRACE) var/distance = get_dist(holder, target) ai_log("engage_target() : Distance to target ([target]) is [distance].", AI_LOG_TRACE) holder.face_atom(target) last_conflict_time = world.time - request_help() // Call our allies. - // Do a 'special' attack, if one is allowed. // if(prob(special_attack_prob) && (distance >= special_attack_min_range) && (distance <= special_attack_max_range)) if(holder.ICheckSpecialAttack(target)) @@ -94,21 +70,21 @@ /datum/ai_holder/proc/melee_attack(atom/A) pre_melee_attack(A) . = holder.IAttack(A) - if(.) + if(. == ATTACK_SUCCESSFUL) post_melee_attack(A) // Ditto. /datum/ai_holder/proc/ranged_attack(atom/A) pre_ranged_attack(A) . = holder.IRangedAttack(A) - if(.) + if(. == ATTACK_SUCCESSFUL) post_ranged_attack(A) // Most mobs probably won't have this defined but we don't care. /datum/ai_holder/proc/special_attack(atom/movable/AM) pre_special_attack(AM) . = holder.ISpecialAttack(AM) - if(.) + if(. == ATTACK_SUCCESSFUL) post_special_attack(AM) // Called when within striking/shooting distance, however cooldown is not considered. @@ -198,12 +174,8 @@ // Make sure we can still chase/attack them. if(!target || !can_attack(target)) ai_log("walk_to_target() : Lost target.", AI_LOG_INFO) - if(!find_target()) - lost_target() - ai_log("walk_to_target() : Exiting.", AI_LOG_DEBUG) - return - else - ai_log("walk_to_target() : Found new target ([target]).", AI_LOG_INFO) + lose_target() + return // Find out where we're going. var/get_to = closest_distance(target) @@ -220,7 +192,6 @@ ai_log("walk_to_target() : Exiting.", AI_LOG_DEBUG) return - // Otherwise keep walking. if(!stand_ground) walk_path(target, get_to) @@ -247,7 +218,6 @@ var/dir_to_target = get_dir(holder, target_atom) holder.face_atom(target_atom) - ai_log("breakthrough() : Exiting", AI_LOG_DEBUG) // Sometimes the mob will try to hit something diagonally, and generally this fails. // So instead we will try two more times with some adjustments if the attack fails. @@ -303,32 +273,38 @@ for(var/obj/structure/window/W in problem_turf) if(W.dir == reverse_dir[holder.dir]) // So that windows get smashed in the right order ai_log("destroy_surroundings() : Attacking side window.", AI_LOG_INFO) - return holder.IAttack(W) + return melee_attack(W) else if(W.is_fulltile()) ai_log("destroy_surroundings() : Attacking full tile window.", AI_LOG_INFO) - return holder.IAttack(W) + return melee_attack(W) // Kill hull shields in the way. for(var/obj/effect/energy_field/shield in problem_turf) if(shield.density) // Don't attack shields that are already down. ai_log("destroy_surroundings() : Attacking hull shield.", AI_LOG_INFO) - return holder.IAttack(shield) + return melee_attack(shield) + + // Kill energy shields in the way. + for(var/obj/effect/shield/S in problem_turf) + if(S.density) // Don't attack shields that are already down. + ai_log("destroy_surroundings() : Attacking energy shield.", AI_LOG_INFO) + return melee_attack(S) // Kill common obstacle in the way like tables. var/obj/structure/obstacle = locate(/obj/structure, problem_turf) if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille)) ai_log("destroy_surroundings() : Attacking generic structure.", AI_LOG_INFO) - return holder.IAttack(obstacle) + return melee_attack(obstacle) for(var/obj/machinery/door/D in problem_turf) // Required since firelocks take up the same turf. if(D.density) ai_log("destroy_surroundings() : Attacking closed door.", AI_LOG_INFO) - return holder.IAttack(D) + return melee_attack(D) ai_log("destroy_surroundings() : Exiting due to nothing to attack.", AI_LOG_INFO) - return FALSE // Nothing to attack. + return ATTACK_FAILED // Nothing to attack. // Override for special behaviour. /datum/ai_holder/proc/can_violently_breakthrough() - return violent_breakthrough \ No newline at end of file + return violent_breakthrough diff --git a/code/modules/ai/ai_holder_combat_unseen.dm b/code/modules/ai/ai_holder_combat_unseen.dm index 0cb518f08c..c854377c89 100644 --- a/code/modules/ai/ai_holder_combat_unseen.dm +++ b/code/modules/ai/ai_holder_combat_unseen.dm @@ -2,21 +2,21 @@ // Used when a target is out of sight or invisible. /datum/ai_holder/proc/engage_unseen_enemy() + ai_log("engage_unseen_enemy() : Entering.", AI_LOG_TRACE) // Lets do some last things before giving up. - if(!ranged) - if(get_dist(holder, target_last_seen_turf > 1)) // We last saw them over there. + if(conserve_ammo || !holder.ICheckRangedAttack(target_last_seen_turf)) + if(get_dist(holder, target_last_seen_turf) > 1) // We last saw them over there. // Go to where you last saw the enemy. - give_destination(target_last_seen_turf, 1, TRUE) // This will set it to STANCE_REPOSITION. - else // We last saw them next to us, so do a blind attack on that tile. + give_destination(target_last_seen_turf, 1, TRUE) // Sets stance as well + else if(lose_target_time == world.time) // We last saw them next to us, so do a blind attack on that tile. melee_on_tile(target_last_seen_turf) - - else if(!conserve_ammo) + else + find_target() + else shoot_near_turf(target_last_seen_turf) // This shoots semi-randomly near a specific turf. /datum/ai_holder/proc/shoot_near_turf(turf/targeted_turf) - if(!ranged) - return // Can't shoot. if(get_dist(holder, targeted_turf) > max_range(targeted_turf)) return // Too far to shoot. @@ -32,6 +32,7 @@ // Attempts to attack something on a specific tile. // TODO: Put on mob/living? /datum/ai_holder/proc/melee_on_tile(turf/T) + ai_log("melee_on_tile() : Entering.", AI_LOG_TRACE) var/mob/living/L = locate() in T if(!L) T.visible_message("\The [holder] attacks nothing around \the [T].") diff --git a/code/modules/ai/ai_holder_communication.dm b/code/modules/ai/ai_holder_communication.dm index 4cfec6f7af..ef8fcf253d 100644 --- a/code/modules/ai/ai_holder_communication.dm +++ b/code/modules/ai/ai_holder_communication.dm @@ -15,7 +15,7 @@ /datum/ai_holder/proc/should_threaten() if(!threaten) return FALSE // We don't negotiate. - if(target in attackers) + if(check_attacker(target)) return FALSE // They (or someone like them) attacked us before, escalate immediately. if(!will_threaten(target)) return FALSE // Pointless to threaten an animal, a mindless drone, or an object. diff --git a/code/modules/ai/ai_holder_cooperation.dm b/code/modules/ai/ai_holder_cooperation.dm index 0f6b0bcfa2..dd6228f460 100644 --- a/code/modules/ai/ai_holder_cooperation.dm +++ b/code/modules/ai/ai_holder_cooperation.dm @@ -74,7 +74,7 @@ ai_log("request_help() : Exiting.", AI_LOG_DEBUG) -// What allies receive when someone else is calling for help. +// What allies receive when someone else is calling for help.1 /datum/ai_holder/proc/help_requested(mob/living/friend) ai_log("help_requested() : Entering.", AI_LOG_DEBUG) if(stance == STANCE_SLEEP) @@ -92,24 +92,26 @@ if(!holder.IIsAlly(friend)) // Extra sanity. ai_log("help_requested() : Help requested by [friend] but we hate them.", AI_LOG_INFO) return - if(friend.ai_holder && friend.ai_holder.target && !can_attack(friend.ai_holder.target)) - ai_log("help_requested() : Help requested by [friend] but we don't want to fight their target.", AI_LOG_INFO) - return - if(get_dist(holder, friend) <= follow_distance) - ai_log("help_requested() : Help requested by [friend] but we're already here.", AI_LOG_INFO) - return - if(get_dist(holder, friend) <= vision_range) // Within our sight. - ai_log("help_requested() : Help requested by [friend], and within target sharing range.", AI_LOG_INFO) - if(friend.ai_holder) // AI calling for help. - if(friend.ai_holder.target && can_attack(friend.ai_holder.target)) // Friend wants us to attack their target. - last_conflict_time = world.time // So we attack immediately and not threaten. - give_target(friend.ai_holder.target) // This will set us to the appropiate stance. - ai_log("help_requested() : Given target [target] by [friend]. Exiting", AI_LOG_DEBUG) - return + var/their_target = friend?.ai_holder?.target + if(their_target) // They have a target and aren't just shouting for no reason + if(!can_attack(their_target, vision_required = FALSE)) + ai_log("help_requested() : Help requested by [friend] but we don't want to fight their target.", AI_LOG_INFO) + return + if(get_dist(holder, friend) <= follow_distance) + ai_log("help_requested() : Help requested by [friend] but we're already here.", AI_LOG_INFO) + return + if(get_dist(holder, friend) <= vision_range) // Within our sight. + ai_log("help_requested() : Help requested by [friend], and within target sharing range.", AI_LOG_INFO) + last_conflict_time = world.time // So we attack immediately and not threaten. + give_target(their_target, urgent = TRUE) // This will set us to the appropiate stance. + ai_log("help_requested() : Given target [target] by [friend]. Exiting", AI_LOG_DEBUG) + return // Otherwise they're outside our sight, lack a target, or aren't AI controlled, but within call range. // So assuming we're AI controlled, we'll go to them and see whats wrong. ai_log("help_requested() : Help requested by [friend], going to go to friend.", AI_LOG_INFO) + if(their_target) + add_attacker(their_target) // We won't wait and 'warn' them while they're stabbing our ally set_follow(friend, 10 SECONDS) ai_log("help_requested() : Exiting.", AI_LOG_DEBUG) diff --git a/code/modules/ai/ai_holder_movement.dm b/code/modules/ai/ai_holder_movement.dm index 58b8c9d5ee..eb465dec5d 100644 --- a/code/modules/ai/ai_holder_movement.dm +++ b/code/modules/ai/ai_holder_movement.dm @@ -9,13 +9,14 @@ var/home_low_priority = FALSE // If true, the mob will not go home unless it has nothing better to do, e.g. its following someone. var/max_home_distance = 3 // How far the mob can go away from its home before being told to go_home(). // Note that there is a 'BYOND cap' of 14 due to limitations of get_/step_to(). - // Wandering. var/wander = FALSE // If true, the mob will randomly move in the four cardinal directions when idle. var/wander_delay = 0 // How many ticks until the mob can move a tile in handle_wander_movement(). var/base_wander_delay = 2 // What the above var gets set to when it wanders. Note that a tick happens every half a second. var/wander_when_pulled = FALSE // If the mob will refrain from wandering if someone is pulling it. + // Breakthrough + var/failed_breakthroughs = 0 // How many times we've failed to breakthrough something lately /datum/ai_holder/proc/walk_to_destination() ai_log("walk_to_destination() : Entering.",AI_LOG_TRACE) @@ -90,7 +91,9 @@ // step_to(holder, A) if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED) ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) - breakthrough(A) // We failed to move, time to smash things. + if(!breakthrough(A) && failed_breakthroughs++ >= 5) // We failed to move, time to smash things. + give_up_movement() + failed_breakthroughs = 0 return if(move_once() == FALSE) // Start walking the path. @@ -106,7 +109,9 @@ ai_log("walk_path() : Going to IMove().", AI_LOG_TRACE) if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED ) ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) - breakthrough(A) // We failed to move, time to smash things. + if(!breakthrough(A) && failed_breakthroughs++ >= 5) // We failed to move, time to smash things. + give_up_movement() + failed_breakthroughs = 0 ai_log("walk_path() : Exited.", AI_LOG_TRACE) diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm index e70e991d6e..f33906c662 100644 --- a/code/modules/ai/ai_holder_targeting.dm +++ b/code/modules/ai/ai_holder_targeting.dm @@ -26,8 +26,9 @@ // Step 1, find out what we can see. /datum/ai_holder/proc/list_targets() . = hearers(vision_range, holder) - holder // Remove ourselves to prevent suicidal decisions. ~ SRC is the ai_holder. + . -= dview_mob // Not the dview mob either, nerd. - var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha)) + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/structure/blob)) for(var/HM in typecache_filter_list(range(vision_range, holder), hostile_machines)) if(can_see(holder, HM, vision_range)) @@ -35,6 +36,7 @@ // Step 2, filter down possible targets to things we actually care about. /datum/ai_holder/proc/find_target(var/list/possible_targets, var/has_targets_list = FALSE) + ai_log("find_target() : Entered.", AI_LOG_TRACE) if(!hostile) // So retaliating mobs only attack the thing that hit it. return null . = list() @@ -70,13 +72,17 @@ return chosen_target // Step 4, give us our selected target. -/datum/ai_holder/proc/give_target(new_target) +/datum/ai_holder/proc/give_target(new_target, urgent = FALSE) + ai_log("give_target() : Given '[new_target]', urgent=[urgent].", AI_LOG_TRACE) target = new_target + if(target != null) - if(should_threaten()) + lose_target_time = 0 + track_target_position() + if(should_threaten() && !urgent) set_stance(STANCE_ALERT) else - set_stance(STANCE_APPROACH) + set_stance(STANCE_FIGHT) last_target_time = world.time return TRUE @@ -109,8 +115,8 @@ sorted_targets += A return sorted_targets -/datum/ai_holder/proc/can_attack(atom/movable/the_target) - if(!can_see_target(the_target)) +/datum/ai_holder/proc/can_attack(atom/movable/the_target, var/vision_required = TRUE) + if(!can_see_target(the_target) && vision_required) return FALSE if(istype(the_target, /mob/zshadow)) @@ -149,6 +155,11 @@ return FALSE // Turrets won't get hurt if they're still in their cover. return TRUE + if(istype(the_target, /obj/structure/blob)) // Blob mobs are always blob faction, but the blob can anger other things. + var/obj/structure/blob/Blob = the_target + if(holder.faction == Blob.faction) + return FALSE + return TRUE // return FALSE @@ -157,20 +168,37 @@ /datum/ai_holder/proc/found(atom/movable/the_target) return FALSE -//We can't see the target, go look or attack where they were last seen. +// 'Soft' loss of target. They may still exist, we still have some info about them maybe. /datum/ai_holder/proc/lose_target() + ai_log("lose_target() : Entering.", AI_LOG_TRACE) if(target) + ai_log("lose_target() : Had a target, setting to null and LTT.", AI_LOG_DEBUG) target = null lose_target_time = world.time give_up_movement() + if(target_last_seen_turf && intelligence_level >= AI_SMART) + ai_log("lose_target() : Going into 'engage unseen enemy' mode.", AI_LOG_INFO) + engage_unseen_enemy() + return TRUE //We're still working on it + else + ai_log("lose_target() : Can't chase target, so giving up.", AI_LOG_INFO) + remove_target() + return find_target() //Returns if we found anything else to do -//Target is no longer valid (?) -/datum/ai_holder/proc/lost_target() - set_stance(STANCE_IDLE) + return FALSE //Nothing new to do + +// 'Hard' loss of target. Clean things up and return to idle. +/datum/ai_holder/proc/remove_target() + ai_log("remove_target() : Entering.", AI_LOG_TRACE) + if(target) + target = null + + lose_target_time = 0 + give_up_movement() lose_target_position() - lose_target() + set_stance(STANCE_IDLE) // Check if target is visible to us. /datum/ai_holder/proc/can_see_target(atom/movable/the_target, view_range = vision_range) @@ -235,7 +263,7 @@ ai_log("react_to_attack() : Was attacked by [attacker], but we already have a target.", AI_LOG_TRACE) on_attacked(attacker) // So we attack immediately and not threaten. return FALSE - else if(attacker in attackers && world.time > last_target_time + 3 SECONDS) // Otherwise, let 'er rip + else if(check_attacker(attacker) && world.time > last_target_time + 3 SECONDS) // Otherwise, let 'er rip ai_log("react_to_attack() : Was attacked by [attacker]. Can retaliate, waited 3 seconds.", AI_LOG_INFO) on_attacked(attacker) // So we attack immediately and not threaten. return give_target(attacker) // Also handles setting the appropiate stance. @@ -246,15 +274,24 @@ ai_log("react_to_attack() : Was attacked by [attacker].", AI_LOG_INFO) on_attacked(attacker) // So we attack immediately and not threaten. - return give_target(attacker) // Also handles setting the appropiate stance. + return give_target(attacker, urgent = TRUE) // Also handles setting the appropiate stance. // Sets a few vars so mobs that threaten will react faster to an attacker or someone who attacked them before. /datum/ai_holder/proc/on_attacked(atom/movable/AM) - if(isliving(AM)) - var/mob/living/L = AM - if(!(L.name in attackers)) - attackers |= L.name - last_conflict_time = world.time + last_conflict_time = world.time + add_attacker(AM) + +// Checks to see if an atom attacked us lately +/datum/ai_holder/proc/check_attacker(var/atom/movable/A) + return (A in attackers) + +// We were attacked by this thing recently +/datum/ai_holder/proc/add_attacker(var/atom/movable/A) + attackers |= A.name + +// Forgive this attacker +/datum/ai_holder/proc/remove_attacker(var/atom/movable/A) + attackers -= A.name // Causes targeting to prefer targeting the taunter if possible. // This generally occurs if more than one option is within striking distance, including the taunter. diff --git a/code/modules/ai/interfaces.dm b/code/modules/ai/interfaces.dm index 59ffbeea72..6cf085c52e 100644 --- a/code/modules/ai/interfaces.dm +++ b/code/modules/ai/interfaces.dm @@ -8,15 +8,20 @@ /mob/living/simple_mob/IAttack(atom/A) if(!canClick()) // Still on cooldown from a "click". - return FALSE + return ATTACK_ON_COOLDOWN return attack_target(A) // This will set click cooldown. +/mob/living/carbon/human/IAttack(atom/A) + if(!canClick()) // Still on cooldown from a "click". + return FALSE + return ClickOn(A) // Except this is an actual fake "click". + /mob/living/proc/IRangedAttack(atom/A) return FALSE /mob/living/simple_mob/IRangedAttack(atom/A) if(!canClick()) // Still on cooldown from a "click". - return FALSE + return ATTACK_ON_COOLDOWN return shoot_target(A) // Test if the AI is allowed to attempt a ranged attack. diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm index 22b069093a..cbec4c25b6 100644 --- a/code/modules/alarm/alarm_handler.dm +++ b/code/modules/alarm/alarm_handler.dm @@ -47,16 +47,17 @@ existing.clear(source) return check_alarm_cleared(existing) -/datum/alarm_handler/proc/major_alarms() - return visible_alarms() +/datum/alarm_handler/proc/major_alarms(var/z) + return visible_alarms(z) -/datum/alarm_handler/proc/has_major_alarms() - if(alarms && alarms.len) - return 1 - return 0 +/datum/alarm_handler/proc/has_major_alarms(var/z) + if(!LAZYLEN(alarms)) + return 0 -/datum/alarm_handler/proc/minor_alarms() - return visible_alarms() + return LAZYLEN(major_alarms(z)) + +/datum/alarm_handler/proc/minor_alarms(var/z) + return visible_alarms(z) /datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm) if ((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len) @@ -101,9 +102,15 @@ for(var/listener in listeners) call(listener, listeners[listener])(src, alarm, was_raised) -/datum/alarm_handler/proc/visible_alarms() +/datum/alarm_handler/proc/visible_alarms(var/z) + if(!LAZYLEN(alarms)) + return list() + + var/list/map_levels = using_map.get_map_levels(z) + var/list/visible_alarms = new() for(var/datum/alarm/A in alarms) - if(!A.hidden) - visible_alarms.Add(A) + if(A.hidden || (z && !(A.origin?.z in map_levels))) + continue + visible_alarms.Add(A) return visible_alarms \ No newline at end of file diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm index fc0fe92ee4..f19044a6b7 100644 --- a/code/modules/alarm/atmosphere_alarm.dm +++ b/code/modules/alarm/atmosphere_alarm.dm @@ -1,16 +1,22 @@ /datum/alarm_handler/atmosphere category = "Atmosphere Alarms" -/datum/alarm_handler/atmosphere/major_alarms() +/datum/alarm_handler/atmosphere/major_alarms(var/z) var/list/major_alarms = new() + var/list/map_levels = using_map.get_map_levels(z) for(var/datum/alarm/A in visible_alarms()) + if(z && (z && !(A.origin?.z in map_levels))) + continue if(A.max_severity() > 1) major_alarms.Add(A) return major_alarms -/datum/alarm_handler/atmosphere/minor_alarms() +/datum/alarm_handler/atmosphere/minor_alarms(var/z) var/list/minor_alarms = new() + var/list/map_levels = using_map.get_map_levels(z) for(var/datum/alarm/A in visible_alarms()) + if(z && (z && !(A.origin?.z in map_levels))) + continue if(A.max_severity() == 1) minor_alarms.Add(A) return minor_alarms diff --git a/code/modules/artifice/deadringer.dm b/code/modules/artifice/deadringer.dm index bdac793daf..0906eda63e 100644 --- a/code/modules/artifice/deadringer.dm +++ b/code/modules/artifice/deadringer.dm @@ -19,13 +19,13 @@ START_PROCESSING(SSobj, src) /obj/item/weapon/deadringer/Destroy() //just in case some smartass tries to stay invisible by destroying the watch - uncloak() + reveal() STOP_PROCESSING(SSobj, src) ..() /obj/item/weapon/deadringer/dropped() if(timer > 20) - uncloak() + reveal() watchowner = null return @@ -67,7 +67,7 @@ if(timer > 0) timer-- if(timer == 20) - uncloak() + reveal() if(corpse) new /obj/effect/effect/smoke/chem(corpse.loc) qdel(corpse) @@ -86,7 +86,7 @@ makeacorpse(watchowner) return -/obj/item/weapon/deadringer/proc/uncloak() +/obj/item/weapon/deadringer/proc/reveal() if(watchowner) watchowner.alpha = 255 playsound(get_turf(src), 'sound/effects/uncloak.ogg', 35, 1, -1) diff --git a/code/modules/artifice/telecube.dm b/code/modules/artifice/telecube.dm index bb2af4b83d..581b1791f8 100644 --- a/code/modules/artifice/telecube.dm +++ b/code/modules/artifice/telecube.dm @@ -43,11 +43,8 @@ var/image/glow = null var/image/charge = null - var/shell_color = "#FFFFFF" - var/image/shell = null - var/cooldown_time = 30 SECONDS - var/last_teleport = 0 + var/ready = TRUE // How far the cube will search for things to teleport. 0 = only contacting objects / mobs. var/teleport_range = 0 // For all that is holy, do not change this unless you know what you're doing. @@ -56,14 +53,11 @@ /obj/item/weapon/telecube/Initialize() . = ..() - START_PROCESSING(SSobj, src) - last_teleport = world.time - glow = image(icon = icon, icon_state = "[icon_state]-ready") - glow.plane = PLANE_LIGHTING_ABOVE - charge = image(icon = icon, icon_state = "[icon_state]-charging") - charge.plane = PLANE_LIGHTING_ABOVE - shell = image(icon = icon, icon_state = "[icon_state]") + glow = image("[icon_state]-ready") + glow.appearance_flags = KEEP_APART + charge = image("[icon_state]-charging") + charge.appearance_flags = KEEP_APART if(teleport_range) description_info += "
    " @@ -71,49 +65,42 @@ if(randomize_colors) glow_color = rgb(rand(0, 255),rand(0, 255),rand(0, 255)) - shell_color = rgb(rand(0, 255),rand(0, 255),rand(0, 255)) + color = rgb(rand(30, 255),rand(30, 255),rand(30, 255)) if(start_paired) mate = new(src.loc) if(mirror_colors) - mate.glow_color = shell_color - mate.shell_color = glow_color + mate.glow_color = color + mate.color = glow_color else mate.glow_color = glow_color - mate.shell_color = shell_color + mate.color = color mate.pair_cube(src) - glow.color = glow_color - charge.color = glow_color - shell.color = shell_color - - return - -/obj/item/weapon/telecube/process() - ..() update_icon() /obj/item/weapon/telecube/update_icon() . = ..() - glow.color = glow_color - charge.color = glow_color - shell.color = shell_color - if(shell.color != initial(shell.color)) - cut_overlay(shell) - add_overlay(shell) + if(isturf(loc)) + glow.plane = PLANE_LIGHTING_ABOVE + charge.plane = PLANE_LIGHTING_ABOVE + else //So it shows up in inventory looking ok + glow.plane = initial(glow.plane) + charge.plane = initial(glow.plane) - if(world.time < (last_teleport + cooldown_time)) - cut_overlay(charge) - cut_overlay(glow) + if(glow_color != glow.color) + glow.color = glow_color + charge.color = glow_color + + if(!ready) + cut_overlays() add_overlay(charge) else - cut_overlay(glow) - cut_overlay(charge) + cut_overlays() add_overlay(glow) /obj/item/weapon/telecube/Destroy() - STOP_PROCESSING(SSobj, src) if(mate) var/turf/T = get_turf(mate) mate.visible_message("\The [mate] collapses into itself!") @@ -121,7 +108,15 @@ mate = null explosion(T,1,3,7) - ..() + return ..() + +/obj/item/weapon/telecube/equipped() + . = ..() + update_icon() + +/obj/item/weapon/telecube/dropped() + . = ..() + update_icon() /obj/item/weapon/telecube/proc/pair_cube(var/obj/item/weapon/telecube/M) if(mate) @@ -139,19 +134,24 @@ if(A == src || A == mate) A.visible_message("\The [A] distorts and fades, before popping back into existence.") + animate_out(A) + animate_in(A) return . var/mob/living/L = src.loc if(istype(L)) + L << 'sound/effects/singlebeat.ogg' L.drop_from_inventory(src) forceMove(get_turf(src)) - if(world.time < (last_teleport + cooldown_time)) + if(!ready) return . if((A.anchored && !omniteleport) || !mate) A.visible_message("\The [A] distorts for a moment, before reforming in the same position.") + animate_out(A) + animate_in(A) return . var/turf/TLocate = get_turf(mate) @@ -160,7 +160,9 @@ if(T1) A.visible_message("\The [A] fades out of existence.") + animate_out(A) A.forceMove(T1) + animate_in(A) . = TRUE A.visible_message("\The [A] fades into existence.") else @@ -188,27 +190,58 @@ . = TRUE return . +/obj/item/weapon/telecube/proc/cooldown(var/mate_too = FALSE) + if(!ready) + return + + ready = FALSE + update_icon() + addtimer(CALLBACK(src, .proc/ready), cooldown_time) + if(mate_too && mate) + mate.cooldown(mate_too = FALSE) //No infinite recursion pls + +/obj/item/weapon/telecube/proc/ready() + ready = TRUE + update_icon() + +/obj/item/weapon/telecube/proc/animate_out(var/atom/movable/AM) + //See atom cloak/uncloak animations for comments + var/atom/movable/target = AM + var/our_filter_index = target.filters.len+1 + AM.filters += filter(type="blur", size = 0) + + animate(target, alpha = 0, time = 5) //Out + animate(target.filters[our_filter_index], size = 2, time = 5, flags = ANIMATION_PARALLEL) + sleep(5) + target.filters -= filter(type="blur", size = 2) + +/obj/item/weapon/telecube/proc/animate_in(var/atom/movable/AM) + //See atom cloak/uncloak animations for comments + var/atom/movable/target = AM + var/our_filter_index = target.filters.len+1 + AM.filters += filter(type="blur", size = 2) + + animate(target, alpha = 255, time = 5) //In + animate(target.filters[our_filter_index], size = 0, time = 5, flags = ANIMATION_PARALLEL) + sleep(5) + target.filters -= filter(type="blur", size = 0) + /obj/item/weapon/telecube/CtrlClick(mob/user) - if(Adjacent(user)) - if(teleport_to_mate(user)) - last_teleport = world.time - return + if(Adjacent(user) && teleport_to_mate(user)) + cooldown(mate_too = FALSE) /obj/item/weapon/telecube/AltClick(mob/user) - if(Adjacent(user)) - if(swap_with_mate()) - last_teleport = world.time - mate.last_teleport = world.time - return + if(Adjacent(user) && swap_with_mate()) + cooldown(mate_too = TRUE) -/obj/item/weapon/telecube/Bump(atom/movable/AM) +/obj/item/weapon/telecube/Bump(var/atom/movable/AM) if(teleport_to_mate(AM)) - last_teleport = world.time + cooldown(mate_too = FALSE) . = ..() -/obj/item/weapon/telecube/Bumped(atom/movable/M as mob|obj) +/obj/item/weapon/telecube/Bumped(var/atom/movable/M) if(teleport_to_mate(M)) - last_teleport = world.time + cooldown(mate_too = FALSE) . = ..() // Subtypes @@ -224,7 +257,7 @@ /obj/item/weapon/telecube/precursor glow_color = "#FF1D8E" - shell_color = "#2F1B26" + color = "#2F1B26" /obj/item/weapon/telecube/precursor/mated start_paired = TRUE diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 04dadddc04..b5851ab4c6 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -13,7 +13,7 @@ var/secured = 1 var/list/attached_overlays = null var/obj/item/device/assembly_holder/holder = null - var/cooldown = 0//To prevent spam + var/cooldown = FALSE //To prevent spam var/wires = WIRE_RECEIVE | WIRE_PULSE var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() @@ -22,78 +22,45 @@ var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message -/obj/item/device/assembly/proc/activate() //What the device does when turned on +/obj/item/device/assembly/proc/holder_movement() return -/obj/item/device/assembly/proc/pulsed(var/radio = 0) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs - return +/obj/item/device/assembly/proc/process_cooldown() + if(cooldown) + return FALSE + cooldown = TRUE + VARSET_IN(src, cooldown, FALSE, 2 SECONDS) + return TRUE -/obj/item/device/assembly/proc/pulse(var/radio = 0) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct - return - -/obj/item/device/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here - return - -/obj/item/device/assembly/proc/attach_assembly(var/obj/A, var/mob/user) //Called when an assembly is attacked by another - return - -/obj/item/device/assembly/proc/process_cooldown() //Called via spawn(10) to have it count down the cooldown var - return - -/obj/item/device/assembly/proc/holder_movement() //Called when the holder is moved - return - -/obj/item/device/assembly/interact(mob/user as mob) //Called when attack_self is called - return - - -/obj/item/device/assembly/process_cooldown() - cooldown-- - if(cooldown <= 0) return 0 - spawn(10) - process_cooldown() - return 1 - - -/obj/item/device/assembly/pulsed(var/radio = 0) +/obj/item/device/assembly/proc/pulsed(var/radio = 0) if(holder && (wires & WIRE_RECEIVE)) activate() if(radio && (wires & WIRE_RADIO_RECEIVE)) activate() return 1 - -/obj/item/device/assembly/pulse(var/radio = 0) +/obj/item/device/assembly/proc/pulse(var/radio = 0) if(holder && (wires & WIRE_PULSE)) holder.process_activation(src, 1, 0) if(holder && (wires & WIRE_PULSE_SPECIAL)) holder.process_activation(src, 0, 1) -// if(radio && (wires & WIRE_RADIO_PULSE)) - //Not sure what goes here quite yet send signal? return 1 +/obj/item/device/assembly/proc/activate() + if(!secured || !process_cooldown()) + return FALSE + return TRUE -/obj/item/device/assembly/activate() - if(!secured || (cooldown > 0)) return 0 - cooldown = 2 - spawn(10) - process_cooldown() - return 1 - - -/obj/item/device/assembly/toggle_secure() +/obj/item/device/assembly/proc/toggle_secure() secured = !secured update_icon() return secured - -/obj/item/device/assembly/attach_assembly(var/obj/item/device/assembly/A, var/mob/user) +/obj/item/device/assembly/proc/attach_assembly(var/obj/item/device/assembly/A, var/mob/user) holder = new/obj/item/device/assembly_holder(get_turf(src)) if(holder.attach(A,src,user)) to_chat(user, "You attach \the [A] to \the [src]!") - return 1 - return 0 - + return TRUE /obj/item/device/assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) if(isassembly(W)) @@ -107,14 +74,10 @@ else to_chat(user, "\The [src] can now be attached!") return - ..() - return - + return ..() /obj/item/device/assembly/process() - STOP_PROCESSING(SSobj, src) - return - + return PROCESS_KILL /obj/item/device/assembly/examine(mob/user) ..(user) @@ -132,7 +95,6 @@ interact(user) return 1 - /obj/item/device/assembly/interact(mob/user as mob) return //HTML MENU FOR WIRES GOES HERE @@ -140,41 +102,3 @@ if(istype(loc, /obj/item/device/assembly_holder)) return loc.nano_host() return ..() - -/* - var/small_icon_state = null//If this obj will go inside the assembly use this for icons - var/list/small_icon_state_overlays = null//Same here - var/obj/holder = null - var/cooldown = 0//To prevent spam - - proc - Activate()//Called when this assembly is pulsed by another one - Process_cooldown()//Call this via spawn(10) to have it count down the cooldown var - Attach_Holder(var/obj/H, var/mob/user)//Called when an assembly holder attempts to attach, sets src's loc in here - - - Activate() - if(cooldown > 0) - return 0 - cooldown = 2 - spawn(10) - Process_cooldown() - //Rest of code here - return 0 - - - Process_cooldown() - cooldown-- - if(cooldown <= 0) return 0 - spawn(10) - Process_cooldown() - return 1 - - - Attach_Holder(var/obj/H, var/mob/user) - if(!H) return 0 - if(!H.IsAssemblyHolder()) return 0 - //Remember to have it set its loc somewhere in here - - -*/ diff --git a/code/modules/assembly/helpers.dm b/code/modules/assembly/helpers.dm index bfecbd8a41..cc3100f772 100644 --- a/code/modules/assembly/helpers.dm +++ b/code/modules/assembly/helpers.dm @@ -27,18 +27,3 @@ if(istype(O, /obj/item/device/assembly/timer)) return 1 return 0 - -/* -Name: IsSpecialAssembly -Desc: If true is an object that can be attached to an assembly holder but is a special thing like a phoron can or door -*/ - -/obj/proc/IsSpecialAssembly() - return 0 - -/* -Name: IsAssemblyHolder -Desc: If true is an object that can hold an assemblyholder object -*/ -/obj/proc/IsAssemblyHolder() - return 0 \ No newline at end of file diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 7db2d4e827..c95d2869e9 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -14,76 +14,48 @@ var/obj/item/device/assembly/a_right = null var/obj/special_assembly = null -/obj/item/device/assembly_holder/proc/attach(var/obj/item/device/D, var/obj/item/device/D2, var/mob/user) - return +/obj/item/device/assembly_holder/proc/attach(var/obj/item/device/assembly/D, var/obj/item/device/assembly/D2, var/mob/user) + if(!D || !D2) + return FALSE + + if(!istype(D) || !istype(D2)) + return FALSE -/obj/item/device/assembly_holder/proc/attach_special(var/obj/O, var/mob/user) - return - -/obj/item/device/assembly_holder/proc/process_activation(var/obj/item/device/D) - return - -/obj/item/device/assembly_holder/proc/detached() - return - - -/obj/item/device/assembly_holder/IsAssemblyHolder() - return 1 - - -/obj/item/device/assembly_holder/attach(var/obj/item/device/D, var/obj/item/device/D2, var/mob/user) - if((!D)||(!D2)) return 0 - if((!isassembly(D))||(!isassembly(D2))) return 0 - if((D:secured)||(D2:secured)) return 0 + if(D.secured || D2.secured) + return FALSE + if(user) user.remove_from_mob(D) user.remove_from_mob(D2) - D:holder = src - D2:holder = src - D.loc = src - D2.loc = src + + D.holder = src + D2.holder = src + D.forceMove(src) + D2.forceMove(src) a_left = D a_right = D2 name = "[D.name]-[D2.name] assembly" update_icon() - usr.put_in_hands(src) + user.put_in_hands(src) - return 1 + return TRUE - -/obj/item/device/assembly_holder/attach_special(var/obj/O, var/mob/user) - if(!O) return - if(!O.IsSpecialAssembly()) return 0 - -/* if(O:Attach_Holder()) - special_assembly = O - update_icon() - src.name = "[a_left.name] [a_right.name] [special_assembly.name] assembly" -*/ +/obj/item/device/assembly_holder/proc/detached() return - /obj/item/device/assembly_holder/update_icon() - overlays.Cut() + cut_overlays() if(a_left) - overlays += "[a_left.icon_state]_left" + add_overlay("[a_left.icon_state]_left") for(var/O in a_left.attached_overlays) - overlays += "[O]_l" + add_overlay("[O]_l") if(a_right) - src.overlays += "[a_right.icon_state]_right" + add_overlay("[a_right.icon_state]_right") for(var/O in a_right.attached_overlays) - overlays += "[O]_r" + add_overlay("[O]_r") if(master) master.update_icon() -/* if(special_assembly) - special_assembly.update_icon() - if(special_assembly:small_icon_state) - src.overlays += special_assembly:small_icon_state - for(var/O in special_assembly:small_icon_state_overlays) - src.overlays += O -*/ - /obj/item/device/assembly_holder/examine(mob/user) ..(user) if ((in_range(src, user) || src.loc == user)) @@ -91,65 +63,40 @@ to_chat(user, "\The [src] is ready!") else to_chat(user, "\The [src] can be attached!") - return - /obj/item/device/assembly_holder/HasProximity(atom/movable/AM as mob|obj) if(a_left) a_left.HasProximity(AM) if(a_right) a_right.HasProximity(AM) - if(special_assembly) - special_assembly.HasProximity(AM) - /obj/item/device/assembly_holder/Crossed(atom/movable/AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(AM.is_incorporeal()) + return if(a_left) a_left.Crossed(AM) if(a_right) a_right.Crossed(AM) - if(special_assembly) - special_assembly.Crossed(AM) - /obj/item/device/assembly_holder/on_found(mob/finder as mob) if(a_left) a_left.on_found(finder) if(a_right) a_right.on_found(finder) - if(special_assembly) - if(istype(special_assembly, /obj/item)) - var/obj/item/S = special_assembly - S.on_found(finder) - /obj/item/device/assembly_holder/Move() ..() if(a_left && a_right) a_left.holder_movement() a_right.holder_movement() -// if(special_assembly) -// special_assembly:holder_movement() - return - /obj/item/device/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess if(a_left && a_right) a_left.holder_movement() a_right.holder_movement() -// if(special_assembly) -// special_assembly:Holder_Movement() ..() - return - -/obj/item/device/assembly_holder/attackby(obj/item/weapon/W as obj, mob/user as mob) +/obj/item/device/assembly_holder/attackby(var/obj/item/weapon/W, var/mob/user) if(W.is_screwdriver()) if(!a_left || !a_right) to_chat(user, " BUG:Assembly part missing, please report this!") @@ -163,14 +110,10 @@ to_chat(user, "\The [src] can now be taken apart!") update_icon() return - else if(W.IsSpecialAssembly()) - attach_special(W, user) else ..() - return - -/obj/item/device/assembly_holder/attack_self(mob/user as mob) +/obj/item/device/assembly_holder/attack_self(var/mob/user) src.add_fingerprint(user) if(src.secured) if(!a_left || !a_right) @@ -188,22 +131,21 @@ a_right.attack_self(user) else var/turf/T = get_turf(src) - if(!T) return 0 + if(!T) + return 0 if(a_left) - a_left:holder = null - a_left.loc = T + a_left.holder = null + a_left.forceMove(T) if(a_right) - a_right:holder = null - a_right.loc = T - spawn(0) - qdel(src) - return + a_right.holder = null + a_right.forceMove(T) + qdel(src) - -/obj/item/device/assembly_holder/process_activation(var/obj/D, var/normal = 1, var/special = 1) - if(!D) return 0 +/obj/item/device/assembly_holder/proc/process_activation(var/obj/D, var/normal = 1) + if(!D) + return 0 if(!secured) - visible_message("\icon[src] *beep* *beep*", "*beep* *beep*") + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") if((normal) && (a_right) && (a_left)) if(a_right != D) a_right.pulsed(0) @@ -211,42 +153,36 @@ a_left.pulsed(0) if(master) master.receive_signal() -// if(special && special_assembly) -// if(!special_assembly == D) -// special_assembly.dothings() return 1 - /obj/item/device/assembly_holder/hear_talk(mob/living/M as mob, msg, verb, datum/language/speaking) if(a_right) a_right.hear_talk(M,msg,verb,speaking) if(a_left) a_left.hear_talk(M,msg,verb,speaking) - - - /obj/item/device/assembly_holder/timer_igniter name = "timer-igniter assembly" - New() - ..() +/obj/item/device/assembly_holder/timer_igniter/New() + ..() - var/obj/item/device/assembly/igniter/ign = new(src) - ign.secured = 1 - ign.holder = src - var/obj/item/device/assembly/timer/tmr = new(src) - tmr.time=5 - tmr.secured = 1 - tmr.holder = src - START_PROCESSING(SSobj, tmr) - a_left = tmr - a_right = ign - secured = 1 - update_icon() - name = initial(name) + " ([tmr.time] secs)" + var/obj/item/device/assembly/igniter/ign = new(src) + ign.secured = 1 + ign.holder = src - loc.verbs += /obj/item/device/assembly_holder/timer_igniter/verb/configure + var/obj/item/device/assembly/timer/tmr = new(src) + tmr.time = 5 + tmr.secured = 1 + tmr.holder = src + + a_left = tmr + a_right = ign + secured = 1 + update_icon() + name = initial(name) + " ([tmr.time] secs)" + + loc.verbs += /obj/item/device/assembly_holder/timer_igniter/verb/configure /obj/item/device/assembly_holder/timer_igniter/detached() loc.verbs -= /obj/item/device/assembly_holder/timer_igniter/verb/configure diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 88845d0651..5344d4b15b 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -9,7 +9,8 @@ wires = WIRE_RECEIVE /obj/item/device/assembly/igniter/activate() - if(!..()) return 0//Cooldown check + if(!..()) + return FALSE if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc @@ -28,13 +29,12 @@ s.set_up(3, 1, src) s.start() - return 1 + return TRUE -/obj/item/device/assembly/igniter/attack_self(mob/user as mob) +/obj/item/device/assembly/igniter/attack_self(var/mob/user) activate() add_fingerprint(user) - return /obj/item/device/assembly/igniter/is_hot() return TRUE \ No newline at end of file diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index afec31665d..2f10a91fda 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -13,124 +13,118 @@ var/on = 0 var/visible = 0 - var/obj/effect/beam/i_beam/first = null + var/list/i_beams = null /obj/item/device/assembly/infra/activate() - if(!..()) return 0//Cooldown check - on = !on - update_icon() - return 1 - + if(!..()) + return FALSE + on = !on + update_icon() + return TRUE /obj/item/device/assembly/infra/toggle_secure() secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - on = 0 - if(first) qdel(first) - STOP_PROCESSING(SSobj, src) + if(!secured) + toggle_state(FALSE) update_icon() return secured +/obj/item/device/assembly/infra/proc/toggle_state(var/picked) + if(!isnull(picked)) + on = picked + else + on = !on + + if(secured && on) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + QDEL_LIST_NULL(i_beams) + return on /obj/item/device/assembly/infra/update_icon() - overlays.Cut() - attached_overlays = list() + cut_overlays() + LAZYCLEARLIST(attached_overlays) if(on) - overlays += "infrared_on" - attached_overlays += "infrared_on" + add_overlay("infrared_on") + LAZYADD(attached_overlays, "infrared_on") if(holder) - holder.update_icon() - return + holder.update_icon(2) +/obj/item/device/assembly/infra/process() + if(!on && i_beams) + QDEL_LIST_NULL(i_beams) + return -/obj/item/device/assembly/infra/process()//Old code - if(!on) - if(first) - qdel(first) - return + if(!i_beams && secured && (istype(loc, /turf) || (holder && istype(holder.loc, /turf)))) + create_beams() - if((!(first) && (secured && (istype(loc, /turf) || (holder && istype(holder.loc, /turf)))))) - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam((holder ? holder.loc : loc) ) +/obj/item/device/assembly/infra/proc/create_beams(var/limit = 8) + var/current_spot = get_turf(src) + for(var/i = 1 to limit) + var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(current_spot) I.master = src I.density = 1 I.set_dir(dir) - step(I, I.dir) - if(I) - I.density = 0 - first = I - I.vis_spread(visible) - spawn(0) - if(I) - //world << "infra: setting limit" - I.limit = 8 - //world << "infra: processing beam \ref[I]" - I.process() - return - return - + if(!step(I, I.dir)) //Try to take a step in that direction + return //Couldn't, oh well, we hit a wall or something. Beam should qdel itself in it's Bump(). + I.density = 0 + i_beams |= I + I.visible = visible /obj/item/device/assembly/infra/attack_hand() - qdel(first) + QDEL_LIST_NULL(i_beams) ..() - return - /obj/item/device/assembly/infra/Move() var/t = dir ..() set_dir(t) - qdel(first) - return - + QDEL_LIST_NULL(i_beams) /obj/item/device/assembly/infra/holder_movement() - if(!holder) return 0 -// set_dir(holder.dir) - qdel(first) - return 1 - + if(!holder) + return FALSE + QDEL_LIST_NULL(i_beams) + return TRUE /obj/item/device/assembly/infra/proc/trigger_beam() - if((!secured)||(!on)||(cooldown > 0)) return 0 + if(!process_cooldown()) + return FALSE pulse(0) + QDEL_LIST_NULL(i_beams) //They will get recreated next process() if the situation is still appropriate if(!holder) - visible_message("\icon[src] *beep* *beep*") - cooldown = 2 - spawn(10) - process_cooldown() - return - + visible_message("[bicon(src)] *beep* *beep*") /obj/item/device/assembly/infra/interact(mob/user as mob)//TODO: change this this to the wire control panel - if(!secured) return + if(!secured) + return user.set_machine(src) var/dat = text("Infrared Laser\nStatus: []
    \nVisibility: []
    \n
    ", (on ? text("On", src) : text("Off", src)), (src.visible ? text("Visible", src) : text("Invisible", src))) dat += "

    Refresh" dat += "

    Close" user << browse(dat, "window=infra") onclose(user, "infra") - return - /obj/item/device/assembly/infra/Topic(href, href_list, state = deep_inventory_state) - if(..()) return 1 + if(..()) + return 1 + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) usr << browse(null, "window=infra") onclose(usr, "infra") return if(href_list["state"]) - on = !(on) - update_icon() + toggle_state() if(href_list["visible"]) visible = !(visible) - spawn(0) - if(first) - first.vis_spread(visible) + for(var/ibeam in i_beams) + var/obj/effect/beam/i_beam/I = ibeam + I.visible = visible + CHECK_TICK if(href_list["close"]) usr << browse(null, "window=infra") @@ -139,18 +133,12 @@ if(usr) attack_self(usr) - return - - /obj/item/device/assembly/infra/verb/rotate_clockwise() set name = "Rotate Infrared Laser Clockwise" set category = "Object" set src in usr - src.set_dir(turn(src.dir, 270)) - return - - + set_dir(turn(dir, 270)) /***************************IBeam*********************************/ @@ -158,106 +146,37 @@ name = "i beam" icon = 'icons/obj/projectiles.dmi' icon_state = "ibeam" - var/obj/effect/beam/i_beam/next = null var/obj/item/device/assembly/infra/master = null - var/limit = null - var/visible = 0.0 - var/left = null - anchored = 1.0 + var/visible = 0 + anchored = 1 +/obj/effect/beam/i_beam/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/effect/beam/i_beam/Destroy() + STOP_PROCESSING(SSobj, src) + master = null + return ..() /obj/effect/beam/i_beam/proc/hit() - if(master) - master.trigger_beam() + master?.trigger_beam() qdel(src) - return - -/obj/effect/beam/i_beam/proc/vis_spread(v) - //world << "i_beam \ref[src] : vis_spread" - visible = v - spawn(0) - if(next) - //world << "i_beam \ref[src] : is next [next.type] \ref[next], calling spread" - next.vis_spread(v) - return - return /obj/effect/beam/i_beam/process() - - if((loc && loc.density) || !master) + if(loc?.density || !master) qdel(src) return - if(left > 0) - left-- - if(left < 1) - if(!(visible)) - invisibility = 101 - else - invisibility = 0 - else - invisibility = 0 - - - //world << "now [src.left] left" - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(loc) - I.master = master - I.density = 1 - I.set_dir(dir) - //world << "created new beam \ref[I] at [I.x] [I.y] [I.z]" - step(I, I.dir) - - if(I) - //world << "step worked, now at [I.x] [I.y] [I.z]" - if(!(next)) - //world << "no next" - I.density = 0 - //world << "spreading" - I.vis_spread(visible) - next = I - spawn(0) - //world << "limit = [limit] " - if((I && limit > 0)) - I.limit = limit - 1 - //world << "calling next process" - I.process() - return - else - //world << "is a next: \ref[next], deleting beam \ref[I]" - qdel(I) - else - //world << "step failed, deleting \ref[next]" - qdel(next) - spawn(10) - process() - return - return - /obj/effect/beam/i_beam/Bump() qdel(src) - return /obj/effect/beam/i_beam/Bumped() hit() - return -/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/effect/beam/i_beam/Crossed(var/atom/movable/AM) + if(AM.is_incorporeal()) + return if(istype(AM, /obj/effect/beam)) return - spawn(0) - hit() - return - return - -/obj/effect/beam/i_beam/Destroy() - . = ..() - if(master.first == src) - master.first = null - if(next && !next.gc_destroyed) - QDEL_NULL(next) + hit() \ No newline at end of file diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index b54df46b54..c2fb202984 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -7,7 +7,7 @@ var/armed = 0 -/obj/item/device/assembly/mousetrap/examine(mob/user) +/obj/item/device/assembly/mousetrap/examine(var/mob/user) ..(user) if(armed) to_chat(user, "It looks like it's armed.") @@ -20,7 +20,7 @@ if(holder) holder.update_icon() -/obj/item/device/assembly/mousetrap/proc/triggered(mob/target as mob, var/type = "feet") +/obj/item/device/assembly/mousetrap/proc/triggered(var/mob/target, var/type = "feet") if(!armed) return var/obj/item/organ/external/affecting = null @@ -49,10 +49,9 @@ update_icon() pulse(0) - -/obj/item/device/assembly/mousetrap/attack_self(mob/living/user as mob) +/obj/item/device/assembly/mousetrap/attack_self(var/mob/living/user) if(!armed) - user << "You arm [src]." + to_chat(user, "You arm [src].") else if((CLUMSY in user.mutations) && prob(50)) var/which_hand = "l_hand" @@ -68,8 +67,7 @@ update_icon() playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) - -/obj/item/device/assembly/mousetrap/attack_hand(mob/living/user as mob) +/obj/item/device/assembly/mousetrap/attack_hand(var/mob/living/user) if(armed) if((CLUMSY in user.mutations) && prob(50)) var/which_hand = "l_hand" @@ -81,14 +79,9 @@ return ..() - -/obj/item/device/assembly/mousetrap/Crossed(AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/obj/item/device/assembly/mousetrap/Crossed(var/atom/movable/AM) + if(AM.is_incorporeal()) + return if(armed) if(ishuman(AM)) var/mob/living/carbon/H = AM @@ -100,8 +93,7 @@ triggered(AM) ..() - -/obj/item/device/assembly/mousetrap/on_found(mob/living/finder as mob) +/obj/item/device/assembly/mousetrap/on_found(var/mob/living/finder) if(armed) finder.visible_message("[finder] accidentally sets off [src], breaking their fingers.", \ "You accidentally trigger [src]!") @@ -109,19 +101,16 @@ return 1 //end the search! return 0 - -/obj/item/device/assembly/mousetrap/hitby(A as mob|obj) +/obj/item/device/assembly/mousetrap/hitby(var/atom/movable/A) if(!armed) return ..() visible_message("[src] is triggered by [A].") triggered(null) - /obj/item/device/assembly/mousetrap/armed icon_state = "mousetraparmed" armed = 1 - /obj/item/device/assembly/mousetrap/verb/hide_under() set src in oview(1) set name = "Hide" @@ -130,5 +119,5 @@ if(usr.stat) return - layer = TURF_LAYER+0.2 + layer = HIDING_LAYER to_chat(usr, "You hide [src].") \ No newline at end of file diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index a03130ea92..a93bda4809 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -16,16 +16,16 @@ var/range = 2 /obj/item/device/assembly/prox_sensor/activate() - if(!..()) return 0//Cooldown check + if(!..()) + return FALSE timing = !timing update_icon() - return 0 - + return FALSE /obj/item/device/assembly/prox_sensor/toggle_secure() secured = !secured if(secured) - START_PROCESSING(SSobj, src) + START_PROCESSING(SSobj, src) else scanning = 0 timing = 0 @@ -33,29 +33,22 @@ update_icon() return secured - /obj/item/device/assembly/prox_sensor/HasProximity(atom/movable/AM as mob|obj) if(!istype(AM)) log_debug("DEBUG: HasProximity called with [AM] on [src] ([usr]).") return - if (istype(AM, /obj/effect/beam)) return - if (!isobserver(AM) && AM.move_speed < 12) sense() - return - + if (istype(AM, /obj/effect/beam)) + return + if (!isobserver(AM) && AM.move_speed < 12) + sense() /obj/item/device/assembly/prox_sensor/proc/sense() + if((!holder && !secured) || !scanning || !process_cooldown()) + return FALSE var/turf/mainloc = get_turf(src) -// if(scanning && cooldown <= 0) -// mainloc.visible_message("\icon[src] *boop* *boop*", "*boop* *boop*") - if((!holder && !secured)||(!scanning)||(cooldown > 0)) return 0 pulse(0) if(!holder) - mainloc.visible_message("\icon[src] *beep* *beep*", "*beep* *beep*") - cooldown = 2 - spawn(10) - process_cooldown() - return - + mainloc.visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") /obj/item/device/assembly/prox_sensor/process() if(scanning) @@ -69,46 +62,35 @@ if(timing && time <= 0) timing = 0 toggle_scan() - time = 10 - return - + time = initial(time) /obj/item/device/assembly/prox_sensor/dropped() - spawn(0) - sense() - return - return - + sense() /obj/item/device/assembly/prox_sensor/proc/toggle_scan() - if(!secured) return 0 + if(!secured) + return FALSE scanning = !scanning update_icon() - return - /obj/item/device/assembly/prox_sensor/update_icon() - overlays.Cut() - attached_overlays = list() + cut_overlays() + LAZYCLEARLIST(attached_overlays) if(timing) - overlays += "prox_timing" - attached_overlays += "prox_timing" + add_overlay("prox_timing") + LAZYADD(attached_overlays, "prox_timing") if(scanning) - overlays += "prox_scanning" - attached_overlays += "prox_scanning" + add_overlay("prox_scanning") + LAZYADD(attached_overlays, "prox_scanning") if(holder) holder.update_icon() if(holder && istype(holder.loc,/obj/item/weapon/grenade/chem_grenade)) var/obj/item/weapon/grenade/chem_grenade/grenade = holder.loc grenade.primed(scanning) - return - /obj/item/device/assembly/prox_sensor/Move() ..() sense() - return - /obj/item/device/assembly/prox_sensor/interact(mob/user as mob)//TODO: Change this to the wires thingy if(!secured) @@ -123,11 +105,11 @@ dat += "

    Close" user << browse(dat, "window=prox") onclose(user, "prox") - return - /obj/item/device/assembly/prox_sensor/Topic(href, href_list, state = deep_inventory_state) - if(..()) return 1 + if(..()) + return TRUE + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) usr << browse(null, "window=prox") onclose(usr, "prox") @@ -156,6 +138,3 @@ if(usr) attack_self(usr) - - - return diff --git a/code/modules/assembly/shock_kit.dm b/code/modules/assembly/shock_kit.dm index 55616a2b91..d3f518bbc8 100644 --- a/code/modules/assembly/shock_kit.dm +++ b/code/modules/assembly/shock_kit.dm @@ -13,7 +13,7 @@ ..() return -/obj/item/assembly/shock_kit/attackby(obj/item/weapon/W as obj, mob/user as mob) +/obj/item/assembly/shock_kit/attackby(var/obj/item/weapon/W, var/mob/user) if(W.is_wrench() && !status) var/turf/T = loc if(ismob(T)) diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index a8859e7c1c..3d45fb4e62 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -17,33 +17,22 @@ var/datum/radio_frequency/radio_connection var/deadman = FALSE -/obj/item/device/assembly/signaler/New() - ..() - spawn(40) - set_frequency(frequency) - return - +/obj/item/device/assembly/signaler/Initialize() + . = ..() + set_frequency(frequency) /obj/item/device/assembly/signaler/activate() - if(cooldown > 0) return FALSE - cooldown = 2 - spawn(10) - process_cooldown() - + if(!process_cooldown()) + return FALSE signal() return TRUE /obj/item/device/assembly/signaler/update_icon() if(holder) holder.update_icon() - return -/obj/item/device/assembly/signaler/interact(mob/user as mob, flag1) +/obj/item/device/assembly/signaler/interact(var/mob/user) var/t1 = "-------" -// if ((src.b_stat && !( flag1 ))) -// t1 = text("-------
    \nGreen Wire: []
    \nRed Wire: []
    \nBlue Wire: []
    \n", (src.wires & 4 ? text("Cut Wire", src) : text("Mend Wire", src)), (src.wires & 2 ? text("Cut Wire", src) : text("Mend Wire", src)), (src.wires & 1 ? text("Cut Wire", src) : text("Mend Wire", src))) -// else -// t1 = "-------" Speaker: [src.listening ? "Engaged" : "Disengaged"]
    var/dat = {" @@ -66,8 +55,6 @@ Code: "} user << browse(dat, "window=radio") onclose(user, "radio") - return - /obj/item/device/assembly/signaler/Topic(href, href_list, state = deep_inventory_state) if(..()) @@ -97,9 +84,7 @@ Code: if(usr) attack_self(usr) - return - -/obj/item/device/assembly/signaler/attackby(obj/item/weapon/W, mob/user, params) +/obj/item/device/assembly/signaler/attackby(var/obj/item/weapon/W, mob/user, params) if(issignaler(W)) var/obj/item/device/assembly/signaler/signaler2 = W if(secured && signaler2.secured) @@ -120,8 +105,6 @@ Code: signal.encryption = code signal.data["message"] = "ACTIVATE" radio_connection.post_signal(src, signal) - return - /obj/item/device/assembly/signaler/pulse(var/radio = 0) if(is_jammed(src)) @@ -134,7 +117,6 @@ Code: ..(radio) return TRUE - /obj/item/device/assembly/signaler/receive_signal(datum/signal/signal) if(!signal) return FALSE @@ -148,9 +130,7 @@ Code: if(!holder) for(var/mob/O in hearers(1, src.loc)) - O.show_message(text("\icon[] *beep* *beep*", src), 3, "*beep* *beep*", 2) - return - + O.show_message("[bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) /obj/item/device/assembly/signaler/proc/set_frequency(new_frequency) if(!frequency) @@ -163,7 +143,6 @@ Code: radio_controller.remove_object(src, frequency) frequency = new_frequency radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) - return /obj/item/device/assembly/signaler/process() if(!deadman) @@ -176,7 +155,6 @@ Code: STOP_PROCESSING(SSobj, src) else if(prob(5)) M.visible_message("[M]'s finger twitches a bit over [src]'s signal button!") - return /obj/item/device/assembly/signaler/verb/deadman_it() set src in usr diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index 488083d11a..1bc653662b 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -14,45 +14,43 @@ /obj/item/device/assembly/timer/activate() - if(!..()) return 0//Cooldown check + if(!..()) + return FALSE - timing = !timing + set_state(!timing) update_icon() return 0 - /obj/item/device/assembly/timer/toggle_secure() secured = !secured if(secured) - START_PROCESSING(SSobj, src) + START_PROCESSING(SSobj, src) else timing = 0 STOP_PROCESSING(SSobj, src) update_icon() return secured +/obj/item/device/assembly/timer/proc/set_state(var/state) + if(state && !timing) //Not running, starting though + START_PROCESSING(SSobj, src) + else if(timing && !state) //Running, stopping though + STOP_PROCESSING(SSobj, src) + timing = state /obj/item/device/assembly/timer/proc/timer_end() - if(!secured) return 0 + if(!secured) + return 0 pulse(0) if(!holder) - visible_message("\icon[src] *beep* *beep*", "*beep* *beep*") - cooldown = 2 - spawn(10) - process_cooldown() - return - + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") /obj/item/device/assembly/timer/process() - if(timing && (time > 0)) - time-- - if(timing && time <= 0) - timing = 0 + if(timing && time-- <= 0) + set_state(0) timer_end() time = 10 - return - /obj/item/device/assembly/timer/update_icon() overlays.Cut() @@ -87,7 +85,8 @@ return if(href_list["time"]) - timing = text2num(href_list["time"]) + var/new_timing = text2num(href_list["time"]) + set_state(new_timing) update_icon() if(href_list["tp"]) diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index 7e8c89cc79..8ebf8278a8 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -12,7 +12,7 @@ recorded = msg listening = 0 var/turf/T = get_turf(src) //otherwise it won't work in hand - T.visible_message("\icon[src] beeps, \"Activation message is '[recorded]'.\"") + T.visible_message("[bicon(src)] beeps, \"Activation message is '[recorded]'.\"") else if(findtext(msg, recorded)) pulse(0) @@ -22,14 +22,14 @@ if(!holder) listening = !listening var/turf/T = get_turf(src) - T.visible_message("\icon[src] beeps, \"[listening ? "Now" : "No longer"] recording input.\"") + T.visible_message("[bicon(src)] beeps, \"[listening ? "Now" : "No longer"] recording input.\"") /obj/item/device/assembly/voice/attack_self(mob/user) - if(!user) return 0 + if(!user) + return FALSE activate() - return 1 - + return TRUE /obj/item/device/assembly/voice/toggle_secure() . = ..() diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index b6695e39cb..987d395992 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -26,7 +26,7 @@ /obj/machinery/gateway/centerstation density = 1 icon_state = "offcenter" - use_power = 1 + use_power = USE_POWER_IDLE //warping vars var/list/linked = list() @@ -85,13 +85,13 @@ obj/machinery/gateway/centerstation/process() if(linked.len != 8) return if(!powered()) return if(!awaygate) - user << "Error: No destination found. Please program gateway." + to_chat(user, "Error: No destination found. Please program gateway.") return if(world.time < wait) - user << "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes." + to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") return if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) //VOREStation Edit - user << "Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update." + to_chat(user, "Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.") return for(var/obj/machinery/gateway/G in linked) @@ -133,6 +133,14 @@ obj/machinery/gateway/centerstation/process() M.set_dir(SOUTH) return else + //VOREStation Addition Start: Prevent taurriding abuse + if(istype(M, /mob/living)) + var/mob/living/L = M + if(LAZYLEN(L.buckled_mobs)) + var/datum/riding/R = L.riding_datum + for(var/rider in L.buckled_mobs) + R.force_dismount(rider) + //VOREStation Addition End: Prevent taurriding abuse var/obj/effect/landmark/dest = pick(awaydestinations) if(dest) M.forceMove(dest.loc) @@ -144,11 +152,11 @@ obj/machinery/gateway/centerstation/process() if(!awaygate) awaygate = locate(/obj/machinery/gateway/centeraway) if(!awaygate) // We still can't find the damn thing because there is no destination. - user << "Error: Programming failed. No destination found." + to_chat(user, "Error: Programming failed. No destination found.") return - user << "Startup programming successful!: A destination in another point of space and time has been detected." + to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") else - user << "The gate is already calibrated, there is no work for you to do here." + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") return /////////////////////////////////////Away//////////////////////// @@ -157,7 +165,7 @@ obj/machinery/gateway/centerstation/process() /obj/machinery/gateway/centeraway density = 1 icon_state = "offcenter" - use_power = 0 + use_power = USE_POWER_OFF var/calibrated = 1 var/list/linked = list() //a list of the connected gateway chunks var/ready = 0 @@ -205,7 +213,7 @@ obj/machinery/gateway/centerstation/process() if(!ready) return if(linked.len != 8) return if(!stationgate || !calibrated) // Vorestation edit. Not like Polaris ever touches this anyway. - user << "Error: No destination found. Please calibrate gateway." + to_chat(user, "Error: No destination found. Please calibrate gateway.") return for(var/obj/machinery/gateway/G in linked) @@ -239,7 +247,7 @@ obj/machinery/gateway/centerstation/process() if(istype(M, /mob/living/carbon)) for(var/obj/item/weapon/implant/exile/E in M)//Checking that there is an exile implant in the contents if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket - M << "The station gate has detected your exile implant and is blocking your entry." + to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") return M.forceMove(get_step(stationgate.loc, SOUTH)) M.set_dir(SOUTH) @@ -250,16 +258,16 @@ obj/machinery/gateway/centerstation/process() /obj/machinery/gateway/centeraway/attackby(obj/item/device/W as obj, mob/user as mob) if(istype(W,/obj/item/device/multitool)) if(calibrated && stationgate) - user << "The gate is already calibrated, there is no work for you to do here." + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") return else // VOREStation Add stationgate = locate(/obj/machinery/gateway/centerstation) if(!stationgate) - user << "Error: Recalibration failed. No destination found... That can't be good." + to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") return // VOREStation Add End else - user << "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target." + to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") calibrated = 1 return diff --git a/code/modules/awaymissions/trigger.dm b/code/modules/awaymissions/trigger.dm index 4afaf4a135..d34763ab02 100644 --- a/code/modules/awaymissions/trigger.dm +++ b/code/modules/awaymissions/trigger.dm @@ -4,7 +4,7 @@ /obj/effect/step_trigger/message/Trigger(mob/M as mob) if(M.client) - M << "[message]" + to_chat(M, "[message]") if(once) qdel(src) diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm index bf1165d2ea..dc4ad7ecd6 100644 --- a/code/modules/awaymissions/zlevel.dm +++ b/code/modules/awaymissions/zlevel.dm @@ -38,12 +38,12 @@ proc/createRandomZlevel() admin_notice("Loading away mission...", R_DEBUG) var/map = pick(potentialRandomZlevels) - world.log << "Away mission picked: [map]" //VOREStation Add for debugging + to_world_log("Away mission picked: [map]") //VOREStation Add for debugging var/file = file(map) if(isfile(file)) var/datum/map_template/template = new(file, "away mission") template.load_new_z() - world.log << "away mission loaded: [map]" + to_world_log("away mission loaded: [map]") /* VOREStation Removal - We do this in the special landmark init instead. for(var/obj/effect/landmark/L in landmarks_list) if (L.name != "awaystart") diff --git a/code/modules/blob2/_defines.dm b/code/modules/blob2/_defines.dm index 1ad0b73893..85f9d39115 100644 --- a/code/modules/blob2/_defines.dm +++ b/code/modules/blob2/_defines.dm @@ -7,4 +7,7 @@ #define BLOB_DIFFICULTY_EASY 0 #define BLOB_DIFFICULTY_MEDIUM 1 #define BLOB_DIFFICULTY_HARD 2 -#define BLOB_DIFFICULTY_SUPERHARD 3 \ No newline at end of file +#define BLOB_DIFFICULTY_SUPERHARD 3 + +#define BLOB_CHUNK_CONSTANT 0 +#define BLOB_CHUNK_TOGGLE 1 diff --git a/code/modules/blob2/announcement.dm b/code/modules/blob2/announcement.dm deleted file mode 100644 index 6eceeec569..0000000000 --- a/code/modules/blob2/announcement.dm +++ /dev/null @@ -1,18 +0,0 @@ -/proc/level_seven_blob_announcement(var/obj/structure/blob/core/B) - if(!B || !B.overmind) - return - var/datum/blob_type/blob = B.overmind.blob_type // Shortcut so we don't need to delve into three variables every time. - var/list/lines = list() - - lines += "Confirmed outbreak of level [7 + blob.difficulty] biohazard aboard [station_name()]. All personnel must contain the outbreak." - - if(blob.difficulty >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough. - lines += "The biohazard has been identified as a '[blob.name]'." - - if(blob.difficulty >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster. - lines += "It is suspected to have originated from \the [get_area(B)]." - - if(blob.difficulty >= BLOB_DIFFICULTY_SUPERHARD) - lines += "Extreme caution is advised." - - command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') \ No newline at end of file diff --git a/code/modules/blob2/blobs/base_blob.dm b/code/modules/blob2/blobs/base_blob.dm index 767cf4c783..2e04c36b95 100644 --- a/code/modules/blob2/blobs/base_blob.dm +++ b/code/modules/blob2/blobs/base_blob.dm @@ -1,4 +1,4 @@ -var/list/blobs = list() +GLOBAL_LIST_EMPTY(all_blobs) /obj/structure/blob name = "blob" @@ -17,22 +17,23 @@ var/list/blobs = list() var/heal_timestamp = 0 //we got healed when? var/mob/observer/blob/overmind = null var/base_name = "blob" // The name that gets appended along with the blob_type's name. + var/faction = "blob" -/obj/structure/blob/New(var/newloc, var/new_overmind) - ..(newloc) +/obj/structure/blob/Initialize(newloc, new_overmind) if(new_overmind) overmind = new_overmind update_icon() if(!integrity) integrity = max_integrity set_dir(pick(cardinal)) - blobs += src + GLOB.all_blobs += src consume_tile() + return ..() /obj/structure/blob/Destroy() playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Expand() is no longer broken, no check necessary. - blobs -= src + GLOB.all_blobs -= src overmind = null return ..() @@ -56,6 +57,8 @@ var/list/blobs = list() return TRUE else if(istype(mover, /obj/item/projectile)) var/obj/item/projectile/P = mover + if(istype(P.firer, /obj/structure/blob)) + return TRUE if(istype(P.firer) && P.firer.faction == "blob") return TRUE return FALSE @@ -112,7 +115,7 @@ var/list/blobs = list() if(overmind) expand_probablity *= overmind.blob_type.spread_modifier if(overmind.blob_type.slow_spread_with_size) - expand_probablity /= (blobs.len / 10) + expand_probablity /= (GLOB.all_blobs.len / 10) if(distance <= expand_range) var/can_expand = TRUE @@ -218,13 +221,97 @@ var/list/blobs = list() qdel(src) return B +/obj/structure/blob/attack_generic(var/mob/user, var/damage, var/attack_verb) + visible_message("[user] [attack_verb] the [src]!") + playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) + user.do_attack_animation(src) + if(overmind) + damage *= overmind.blob_type.brute_multiplier + else + damage *= 2 + + if(overmind) + damage = overmind.blob_type.on_received_damage(src, damage, BRUTE, user) + + adjust_integrity(-damage) + + return + +/obj/structure/blob/attack_hand(mob/living/M as mob) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + H.setClickCooldown(H.get_attack_speed()) + var/datum/unarmed_attack/attack = H.get_unarmed_attack(src, BP_TORSO) + if(!attack) + return FALSE + + if(attack.unarmed_override(H, src, BP_TORSO)) + return FALSE + + H.do_attack_animation(src) + H.visible_message("[H] strikes \the [src]!") + + var/real_damage = rand(3,6) + var/hit_dam_type = attack.damage_type + real_damage += attack.get_unarmed_damage(H) + if(H.gloves) + if(istype(H.gloves, /obj/item/clothing/gloves)) + var/obj/item/clothing/gloves/G = H.gloves + real_damage += G.punch_force + hit_dam_type = G.punch_damtype + if(HULK in H.mutations) + real_damage *= 2 // Hulks do twice the damage + + real_damage = max(1, real_damage) + + var/damage_mult_burn = 1 + var/damage_mult_brute = 1 + + if(hit_dam_type == SEARING) + damage_mult_burn *= 0.3 + damage_mult_brute *= 0.6 + + else if(hit_dam_type == BIOACID) + damage_mult_burn *= 0.6 + damage_mult_brute = 0 + + else if(hit_dam_type in list(ELECTROCUTE, BURN)) + damage_mult_brute = 0 + + else if(hit_dam_type in list(BRUTE, CLONE)) + damage_mult_burn = 0 + + else if(hit_dam_type != HALLOSS) // Tox, Oxy, or something new. Half damage split to the organism. + damage_mult_burn = 0.25 + damage_mult_brute = 0.25 + + else + damage_mult_brute = 0.25 + damage_mult_burn = 0 + + var/burn_dam = real_damage * damage_mult_burn + var/brute_dam = real_damage * damage_mult_brute + + if(overmind) + if(brute_dam) + brute_dam = overmind.blob_type.on_received_damage(src, brute_dam, BRUTE, M) + if(burn_dam) + burn_dam = overmind.blob_type.on_received_damage(src, burn_dam, BURN, M) + + real_damage = burn_dam + brute_dam + + adjust_integrity(-real_damage) + + else + attack_generic(M, rand(1,10), "bashed") + /obj/structure/blob/attackby(var/obj/item/weapon/W, var/mob/user) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) playsound(loc, 'sound/effects/attackblob.ogg', 50, 1) visible_message("\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]") var/damage = W.force switch(W.damtype) - if(BURN) + if(BURN, BIOACID, ELECTROCUTE, OXY) if(overmind) damage *= overmind.blob_type.burn_multiplier else @@ -234,7 +321,7 @@ var/list/blobs = list() playsound(src.loc, 'sound/items/welder.ogg', 100, 1) else playsound(src, 'sound/weapons/tap.ogg', 50, 1) - if(BRUTE) + if(BRUTE, SEARING, TOX, CLONE) if(overmind) damage *= overmind.blob_type.brute_multiplier else diff --git a/code/modules/blob2/blobs/core.dm b/code/modules/blob2/blobs/core.dm index f43ec7ad6f..b3633b406e 100644 --- a/code/modules/blob2/blobs/core.dm +++ b/code/modules/blob2/blobs/core.dm @@ -12,6 +12,7 @@ var/list/blob_cores = list() health_regen = 0 //we regen in Life() instead of when pulsed var/datum/blob_type/desired_blob_type = null // If this is set, the core always creates an overmind possessing this blob type. var/difficulty_threshold = null // Otherwise if this is set, it picks a random blob_type that is equal or lower in difficulty. + var/difficulty_floor = null // Related to the above var, acts as a floor value to the above, inclusive. var/core_regen = 2 var/overmind_get_delay = 0 //we don't want to constantly try to find an overmind, this var tracks when we'll try to get an overmind again var/resource_delay = 0 @@ -23,14 +24,18 @@ var/list/blob_cores = list() ai_controlled = FALSE // Spawn these if you want a semi-random blob. +// Can give a random easy blob. /obj/structure/blob/core/random_easy difficulty_threshold = BLOB_DIFFICULTY_EASY +// Can give a random easy or medium blob. /obj/structure/blob/core/random_medium difficulty_threshold = BLOB_DIFFICULTY_MEDIUM +// Can give a random medium or hard blob. /obj/structure/blob/core/random_hard difficulty_threshold = BLOB_DIFFICULTY_HARD + difficulty_floor = BLOB_DIFFICULTY_MEDIUM // Spawn these if you want a specific blob. /obj/structure/blob/core/blazing_oil @@ -78,11 +83,20 @@ var/list/blob_cores = list() /obj/structure/blob/core/volatile_alluvium desired_blob_type = /datum/blob_type/volatile_alluvium +/obj/structure/blob/core/ravenous_macrophage + desired_blob_type = /datum/blob_type/ravenous_macrophage + +/obj/structure/blob/core/roiling_mold + desired_blob_type = /datum/blob_type/roiling_mold + +/obj/structure/blob/core/ectoplasmic_horror + desired_blob_type = /datum/blob_type/ectoplasmic_horror + /obj/structure/blob/core/classic desired_blob_type = /datum/blob_type/classic -/obj/structure/blob/core/New(var/newloc, var/client/new_overmind = null, new_rate = 2, placed = 0) - ..(newloc) +/obj/structure/blob/core/Initialize(newloc, client/new_overmind = null, new_rate = 2, placed = 0) + . = ..(newloc) blob_cores += src START_PROCESSING(SSobj, src) update_icon() //so it atleast appears @@ -93,6 +107,9 @@ var/list/blob_cores = list() point_rate = new_rate /obj/structure/blob/core/Destroy() + var/turf/T = get_turf(src) + new /obj/item/weapon/blobcore_chunk(T, overmind.blob_type) + blob_cores -= src if(overmind) overmind.blob_core = null @@ -130,6 +147,8 @@ var/list/blob_cores = list() if(prob(5)) B.change_to(/obj/structure/blob/shield/core, overmind) + overmind.blob_type.on_core_process(src) + /obj/structure/blob/core/proc/create_overmind(client/new_overmind, override_delay) if(overmind_get_delay > world.time && !override_delay) return @@ -180,7 +199,9 @@ var/list/blob_cores = list() var/list/valid_types = list() for(var/thing in subtypesof(/datum/blob_type)) var/datum/blob_type/BT = thing - if(initial(BT.difficulty) > difficulty_threshold) + if(initial(BT.difficulty) > difficulty_threshold) // Too hard. + continue + if(initial(BT.difficulty) < difficulty_floor) // Too easy. continue valid_types += BT - return pick(valid_types) \ No newline at end of file + return pick(valid_types) diff --git a/code/modules/blob2/blobs/factory.dm b/code/modules/blob2/blobs/factory.dm index 6ee41ccd53..f9415a6e66 100644 --- a/code/modules/blob2/blobs/factory.dm +++ b/code/modules/blob2/blobs/factory.dm @@ -42,6 +42,8 @@ if(overmind.blob_type.ranged_spores) S.projectiletype = overmind.blob_type.spore_projectile S.projectilesound = overmind.blob_type.spore_firesound + S.projectile_accuracy = overmind.blob_type.spore_accuracy + S.projectile_dispersion = overmind.blob_type.spore_dispersion else //Other mobs don't add themselves in New. Ew. S.nest = src spores += S @@ -51,3 +53,8 @@ name = "sluggish factory blob" max_spores = 4 spore_cooldown = 16 SECONDS + +/obj/structure/blob/factory/turret // Produces a single spore slowly, but is intended to be used as a 'mortar' by the blob type. + name = "volatile factory blob" + max_spores = 1 + spore_cooldown = 10 SECONDS diff --git a/code/modules/blob2/blobs/node.dm b/code/modules/blob2/blobs/node.dm index aa1dcb3e8f..5f54b6b80a 100644 --- a/code/modules/blob2/blobs/node.dm +++ b/code/modules/blob2/blobs/node.dm @@ -33,4 +33,6 @@ var/list/blob_nodes = list() /obj/structure/blob/node/process() set waitfor = FALSE if(overmind) // This check is so that if the core is killed, the nodes stop. - pulse_area(overmind, 10, BLOB_NODE_PULSE_RANGE, BLOB_NODE_EXPAND_RANGE) \ No newline at end of file + pulse_area(overmind, 10, BLOB_NODE_PULSE_RANGE, BLOB_NODE_EXPAND_RANGE) + + overmind.blob_type.on_node_process(src) diff --git a/code/modules/blob2/blobs/resource.dm b/code/modules/blob2/blobs/resource.dm index 189abfd520..0cd3f06f10 100644 --- a/code/modules/blob2/blobs/resource.dm +++ b/code/modules/blob2/blobs/resource.dm @@ -9,10 +9,10 @@ var/resource_delay = 0 var/resource_cooldown = 4 SECONDS -/obj/structure/blob/resource/New(var/newloc, var/new_overmind) - ..(newloc, new_overmind) +/obj/structure/blob/resource/Initialize(newloc, new_overmind) if(overmind) overmind.resource_blobs += src + return ..() /obj/structure/blob/resource/Destroy() if(overmind) diff --git a/code/modules/blob2/core_chunk.dm b/code/modules/blob2/core_chunk.dm new file mode 100644 index 0000000000..8a5795aae9 --- /dev/null +++ b/code/modules/blob2/core_chunk.dm @@ -0,0 +1,99 @@ + +/obj/item/weapon/blobcore_chunk + name = "core chunk" + desc = "The remains of some strange life-form. It smells awful." + description_info = "Some blob types will have core effects when the chunk is used in-hand, toggled with an alt click, or constantly active." + icon = 'icons/mob/blob.dmi' + icon_state = "blobcore" + var/datum/blob_type/blob_type // The blob type this dropped from. + + var/active_ability_cooldown = 20 SECONDS + var/last_active_use = 0 + + var/should_tick = TRUE // Incase it's a toggle. + + var/passive_ability_cooldown = 5 SECONDS + var/last_passive_use = 0 + + drop_sound = 'sound/effects/slime_squish.ogg' + +/obj/item/weapon/blobcore_chunk/New(var/atom/newloc, var/datum/blob_type/parentblob = null) + ..(newloc) + + setup_blobtype(parentblob) + +/obj/item/weapon/blobcore_chunk/Destroy() + STOP_PROCESSING(SSobj, src) + + blob_type = null + + ..() + +/obj/item/weapon/blobcore_chunk/proc/setup_blobtype(var/datum/blob_type/parentblob = null) + if(!parentblob) + name = "inert [initial(name)]" + + else + blob_type = parentblob + name = "[blob_type.name] [initial(name)]" + + if(blob_type) + color = blob_type.color + if(LAZYLEN(blob_type.core_tech)) + origin_tech = blob_type.core_tech.Copy() + + if(blob_type.chunk_active_type == BLOB_CHUNK_CONSTANT) + should_tick = TRUE + else if(blob_type.chunk_active_type == BLOB_CHUNK_TOGGLE) + should_tick = FALSE + + active_ability_cooldown = blob_type.chunk_active_ability_cooldown + passive_ability_cooldown = blob_type.chunk_passive_ability_cooldown + + blob_type.chunk_setup(src) + + START_PROCESSING(SSobj, src) + +/obj/item/weapon/blobcore_chunk/proc/call_chunk_unique() + if(blob_type) + blob_type.chunk_unique(src, args) + return + +/obj/item/weapon/blobcore_chunk/proc/get_carrier(var/atom/target) + var/atom/A = target ? target.loc : src + if(!istype(A, /mob/living)) + A = get_carrier(A) + + if(isturf(A) || isarea(A)) // Something has gone horribly wrong if the second is true. + return FALSE // No mob is carrying us. + + return A + +/obj/item/weapon/blobcore_chunk/blob_act(obj/structure/blob/B) + if(B.overmind && !blob_type) + setup_blobtype(B.overmind.blob_type) + + return + +/obj/item/weapon/blobcore_chunk/attack_self(var/mob/user) + if(blob_type && world.time > active_ability_cooldown + last_active_use) + last_active_use = world.time + to_chat(user, "\icon [src] \The [src] gesticulates.") + blob_type.on_chunk_use(src, user) + else + to_chat(user, "\The [src] doesn't seem to respond.") + ..() + +/obj/item/weapon/blobcore_chunk/process() + if(blob_type && should_tick && world.time > passive_ability_cooldown + last_passive_use) + last_passive_use = world.time + blob_type.on_chunk_tick(src) + +/obj/item/weapon/blobcore_chunk/AltClick(mob/living/carbon/user) + if(blob_type &&blob_type.chunk_active_type == BLOB_CHUNK_TOGGLE) + should_tick = !should_tick + + if(should_tick) + to_chat(user, "\The [src] shudders with life.") + else + to_chat(user, "\The [src] stills, returning to a death-like state.") diff --git a/code/modules/blob2/overmind/overmind.dm b/code/modules/blob2/overmind/overmind.dm index 223cb41e60..ffcd347dd8 100644 --- a/code/modules/blob2/overmind/overmind.dm +++ b/code/modules/blob2/overmind/overmind.dm @@ -23,7 +23,7 @@ var/list/overminds = list() var/ai_controlled = TRUE var/auto_pilot = FALSE // If true, and if a client is attached, the AI routine will continue running. -/mob/observer/blob/New(var/newloc, pre_placed = 0, starting_points = 60, desired_blob_type = null) +/mob/observer/blob/Initialize(newloc, pre_placed = 0, starting_points = 60, desired_blob_type = null) blob_points = starting_points if(pre_placed) //we already have a core! placed = 1 @@ -40,12 +40,11 @@ var/list/overminds = list() color = blob_type.complementary_color if(blob_core) blob_core.update_icon() - level_seven_blob_announcement(blob_core) - ..(newloc) + return ..(newloc) /mob/observer/blob/Destroy() - for(var/BL in blobs) + for(var/BL in GLOB.all_blobs) var/obj/structure/blob/B = BL if(B && B.overmind == src) B.overmind = null @@ -66,7 +65,7 @@ var/list/overminds = list() if(blob_core) stat(null, "Core Health: [blob_core.integrity]") stat(null, "Power Stored: [blob_points]/[max_blob_points]") - stat(null, "Total Blobs: [blobs.len]") + stat(null, "Total Blobs: [GLOB.all_blobs.len]") /mob/observer/blob/Move(NewLoc, Dir = 0) if(placed) diff --git a/code/modules/blob2/overmind/powers.dm b/code/modules/blob2/overmind/powers.dm index 360bfbb10b..f5727e94bf 100644 --- a/code/modules/blob2/overmind/powers.dm +++ b/code/modules/blob2/overmind/powers.dm @@ -22,6 +22,9 @@ to_chat(src, "There is no blob here!") return + if(B.overmind != src) + to_chat(src, span("warning", "This blob isn't controlled by you.")) + if(!istype(B, /obj/structure/blob/normal)) to_chat(src, "Unable to use this blob, find a normal one.") return @@ -66,7 +69,7 @@ return FALSE var/obj/structure/blob/B = null - var/list/potential_blobs = blobs.Copy() + var/list/potential_blobs = GLOB.all_blobs.Copy() while(potential_blobs.len) var/obj/structure/blob/temp = pick(potential_blobs) if(!(locate(/obj/structure/blob/node) in range(temp, BLOB_NODE_PULSE_RANGE) ) && !(locate(/obj/structure/blob/core) in range(temp, BLOB_CORE_PULSE_RANGE) )) @@ -77,6 +80,8 @@ potential_blobs -= temp // Don't take up the core's shield spot. else if(!istype(temp, /obj/structure/blob/normal)) potential_blobs -= temp // Not a normal blob. + else if(temp.overmind != src) + potential_blobs -= temp // Not our blob. else B = temp break @@ -107,7 +112,7 @@ return FALSE var/obj/structure/blob/B = null - var/list/potential_blobs = blobs.Copy() + var/list/potential_blobs = GLOB.all_blobs.Copy() while(potential_blobs.len) var/obj/structure/blob/temp = pick(potential_blobs) if(!(locate(/obj/structure/blob/node) in range(temp, BLOB_NODE_PULSE_RANGE) ) && !(locate(/obj/structure/blob/core) in range(temp, BLOB_CORE_PULSE_RANGE) )) @@ -118,6 +123,8 @@ potential_blobs -= temp // Don't take up the core's shield spot. else if(!istype(temp, /obj/structure/blob/normal)) potential_blobs -= temp // Not a normal blob. + else if(temp.overmind != src) + potential_blobs -= temp // Not our blob. else B = temp break @@ -149,7 +156,7 @@ return FALSE var/obj/structure/blob/B = null - var/list/potential_blobs = blobs.Copy() + var/list/potential_blobs = GLOB.all_blobs.Copy() while(potential_blobs.len) var/obj/structure/blob/temp = pick(potential_blobs) if(locate(/obj/structure/blob/node) in range(temp, 5) ) @@ -158,6 +165,8 @@ potential_blobs -= temp else if(!istype(temp, /obj/structure/blob/normal)) potential_blobs -= temp + else if(temp.overmind != src) + potential_blobs -= temp // Not our blob. else B = temp break @@ -184,7 +193,7 @@ other_T = get_step(T, direction) if(other_T) B = locate(/obj/structure/blob) in other_T - if(B) + if(B && B.overmind == src) break if(!B) @@ -216,7 +225,7 @@ for(var/direction in cardinal) var/turf/T = get_step(L, direction) B = locate(/obj/structure/blob) in T - if(B) + if(B && B.overmind == src) break if(!B) continue diff --git a/code/modules/blob2/overmind/types.dm b/code/modules/blob2/overmind/types.dm index 24ffbd6b1b..79df541aae 100644 --- a/code/modules/blob2/overmind/types.dm +++ b/code/modules/blob2/overmind/types.dm @@ -37,12 +37,19 @@ var/spore_firesound = 'sound/effects/slime_squish.ogg' var/spore_range = 7 // The range the spore can fire. var/spore_projectile = /obj/item/projectile/energy/blob + var/spore_accuracy = 0 // Projectile accuracy + var/spore_dispersion = 0 // Dispersion. var/factory_type = /obj/structure/blob/factory var/resource_type = /obj/structure/blob/resource var/node_type = /obj/structure/blob/node var/shield_type = /obj/structure/blob/shield + var/list/core_tech = list(TECH_BIO = 4, TECH_MATERIAL = 3) // Tech for the item created when a core is destroyed. + var/chunk_active_type = BLOB_CHUNK_TOGGLE + var/chunk_active_ability_cooldown = 20 SECONDS + var/chunk_passive_ability_cooldown = 5 SECONDS + // Called when a blob receives damage. This needs to return the final damage or blobs will be immortal. /datum/blob_type/proc/on_received_damage(var/obj/structure/blob/B, damage, damage_type) return damage @@ -63,6 +70,14 @@ /datum/blob_type/proc/on_pulse(var/obj/structure/blob/B) return +// Called when the core processes. +/datum/blob_type/proc/on_core_process(var/obj/structure/blob/B) + return + +// Called when a node processes. +/datum/blob_type/proc/on_node_process(var/obj/structure/blob/B) + return + // Called when hit by EMP. /datum/blob_type/proc/on_emp(obj/structure/blob/B, severity) return @@ -71,574 +86,26 @@ /datum/blob_type/proc/on_water(obj/structure/blob/B, amount) return -// Spore things +// Spore death /datum/blob_type/proc/on_spore_death(mob/living/simple_mob/blob/spore/S) return +// Spore handle_special call. +/datum/blob_type/proc/on_spore_lifetick(mob/living/simple_mob/blob/spore/S) + return -// Subtypes +// Blob core chunk process. +/datum/blob_type/proc/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + return -// Super fast spreading, but weak to EMP. -/datum/blob_type/grey_goo - name = "grey tide" - desc = "A swarm of self replicating nanomachines. Extremely illegal and dangerous, the EIO was meant to prevent this from showing up a second time." - effect_desc = "Spreads much faster than average, but is harmed greatly by electromagnetic pulses." - ai_desc = "genocidal" - difficulty = BLOB_DIFFICULTY_SUPERHARD // Fastest spread of them all and has snowballing capabilities. - color = "#888888" - complementary_color = "#CCCCCC" - spread_modifier = 1.0 - slow_spread_with_size = FALSE - ai_aggressiveness = 80 - can_build_resources = TRUE - attack_message = "The tide tries to swallow you" - attack_message_living = ", and you feel your skin dissolve" - attack_message_synth = ", and your external plating dissolves" +// Blob core chunk use in-hand. +/datum/blob_type/proc/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/user) + return -/datum/blob_type/grey_goo/on_emp(obj/structure/blob/B, severity) - B.adjust_integrity(-(20 / severity)) +// Proc that is unique to the blob type. +/datum/blob_type/proc/chunk_unique(obj/item/weapon/blobcore_chunk/B, var/list/extra_args = null) + return -// Slow, tanky blobtype which uses not spores, but hivebots, as its soldiers. -/datum/blob_type/fabrication_swarm - name = "iron tide" - desc = "A swarm of self replicating construction nanites. Incredibly illegal, but only mildly dangerous." - effect_desc = "Slow-spreading, but incredibly resiliant. It has a chance to harden itself against attacks automatically for no resource cost, and uses cheaply-constructed hivebots as soldiers." - ai_desc = "defensive" - difficulty = BLOB_DIFFICULTY_MEDIUM // Emitters are okay, EMP is great. - color = "#666666" - complementary_color = "#B7410E" - spread_modifier = 0.2 - can_build_factories = TRUE - can_build_resources = TRUE - attack_message = "The tide tries to shove you away" - attack_message_living = ", and your skin itches" - attack_message_synth = ", and your external plating dulls" - attack_verb = "shoves" - armor_pen = 40 - damage_lower = 10 - damage_upper = 25 - brute_multiplier = 0.25 - burn_multiplier = 0.6 - ai_aggressiveness = 50 //Really doesn't like you near it. - spore_type = /mob/living/simple_mob/mechanical/hivebot/swarm - -/datum/blob_type/fabrication_swarm/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) - if(istype(B, /obj/structure/blob/normal)) - if(damage > 0) - var/reinforce_probability = min(damage, 70) - if(prob(reinforce_probability)) - B.visible_message("The [name] quakes, before rapidly hardening!") - new/obj/structure/blob/shield(get_turf(B), B.overmind) - qdel(B) - return ..() - -/datum/blob_type/fabrication_swarm/on_emp(obj/structure/blob/B, severity) - B.adjust_integrity(-(30 / severity)) - -// A blob meant to be fought like a fire. -/datum/blob_type/blazing_oil - name = "blazing oil" - desc = "A strange, extremely vicious liquid that seems to burn endlessly." - ai_desc = "aggressive" - effect_desc = "Cannot be harmed by burning weapons, and ignites entities it attacks. It will also gradually heat up the area it is in. Water harms it greatly." - difficulty = BLOB_DIFFICULTY_MEDIUM // Emitters don't work but extinguishers are fairly common. Might need fire/atmos suits. - color = "#B68D00" - complementary_color = "#BE5532" - spread_modifier = 0.5 - ai_aggressiveness = 50 - damage_type = BURN - burn_multiplier = 0 // Fire immunity - attack_message = "The blazing oil splashes you with its burning oil" - attack_message_living = ", and you feel your skin char and melt" - attack_message_synth = ", and your external plating melts" - attack_verb = "splashes" - -/datum/blob_type/blazing_oil/on_attack(obj/structure/blob/B, mob/living/victim) - victim.fire_act() // Burn them. - -/datum/blob_type/blazing_oil/on_water(obj/structure/blob/B, amount) - spawn(1) - B.adjust_integrity(-(amount * 5)) - -/datum/blob_type/blazing_oil/on_pulse(var/obj/structure/blob/B) - var/turf/T = get_turf(B) - if(!T) - return - var/datum/gas_mixture/env = T.return_air() - if(env) - env.add_thermal_energy(10 * 1000) - - -// Mostly a classic blob. No nodes, no other blob types. -/datum/blob_type/classic - name = "lethargic blob" - desc = "A mass that seems bound to its core." - ai_desc = "unambitious" - effect_desc = "Will not create any nodes. Has average strength and resistances." - difficulty = BLOB_DIFFICULTY_EASY // Behaves almost like oldblob, and as such is about as easy as oldblob. - color = "#AAFF00" - complementary_color = "#57787B" - can_build_nodes = FALSE - spread_modifier = 1.0 - ai_aggressiveness = 0 - - -// Makes robots cry. Really weak to brute damage. -/datum/blob_type/electromagnetic_web - name = "electromagnetic web" - desc = "A gooy mesh that generates an electromagnetic field. Electronics will likely be ruined if nearby." - ai_desc = "balanced" - effect_desc = "Causes an EMP on attack, and will EMP upon death. It is also more fragile than average, especially to brute force." - difficulty = BLOB_DIFFICULTY_MEDIUM // Rough for robots but otherwise fragile and can be fought at range like most blobs anyways. - color = "#83ECEC" - complementary_color = "#EC8383" - damage_type = BURN - damage_lower = 10 - damage_upper = 20 - brute_multiplier = 3 - burn_multiplier = 2 - ai_aggressiveness = 60 - attack_message = "The web lashes you" - attack_message_living = ", and you hear a faint buzzing" - attack_message_synth = ", and your electronics get badly damaged" - attack_verb = "lashes" - -/datum/blob_type/electromagnetic_web/on_death(obj/structure/blob/B) - empulse(B.loc, 0, 1, 2) - -/datum/blob_type/electromagnetic_web/on_attack(obj/structure/blob/B, mob/living/victim) - victim.emp_act(2) - - -// Makes spores that spread the blob and infest dead people. -/datum/blob_type/fungal_bloom - name = "fungal bloom" - desc = "A massive network of rapidly expanding mycelium. Large spore-like particles can be seen spreading from it." - ai_desc = "swarming" - effect_desc = "Creates floating spores that attack enemies from specialized blobs, and will spread the blob if killed. The spores can also \ - infest deceased biological humanoids. It is vulnerable to fire." - difficulty = BLOB_DIFFICULTY_MEDIUM // The spores are more of an annoyance but can be difficult to contain. - color = "#AAAAAA" - complementary_color = "#FFFFFF" - damage_type = TOX - damage_lower = 15 - damage_upper = 25 - spread_modifier = 0.3 // Lower, since spores will do a lot of the spreading. - burn_multiplier = 3 - ai_aggressiveness = 40 - can_build_factories = TRUE - spore_type = /mob/living/simple_mob/blob/spore/infesting - -/datum/blob_type/fungal_bloom/on_spore_death(mob/living/simple_mob/blob/spore/S) - if(S.is_infesting) - return // Don't make blobs if they were on someone's head. - var/turf/T = get_turf(S) - var/obj/structure/blob/B = locate(/obj/structure/blob) in T - if(B) // Is there already a blob here? If so, just heal it. - B.adjust_integrity(10) - else - B = new /obj/structure/blob/normal(T, S.overmind) // Otherwise spread it. - B.visible_message("\A [B] forms on \the [T] as \the [S] bursts!") - -// Makes tons of weak spores whenever it spreads. -/datum/blob_type/fulminant_organism - name = "fulminant organism" - desc = "A self expanding mass of living biomaterial, that appears to produce entities to defend it, much like a living organism's immune system." - ai_desc = "swarming" - effect_desc = "Creates weak floating spores that attack enemies from specialized blobs, has a chance to also create a spore when \ - it spreads onto a new tile, and has a chance to create a spore when a blob tile is destroyed. It is more fragile than average to all types of damage." - difficulty = BLOB_DIFFICULTY_HARD // Loads of spores that can overwhelm, and spreads quickly. - color = "#FF0000" // Red - complementary_color = "#FFCC00" // Orange-ish - damage_type = TOX - damage_lower = 10 - damage_upper = 20 - spread_modifier = 0.7 - burn_multiplier = 1.5 - brute_multiplier = 1.5 - ai_aggressiveness = 30 // The spores do most of the fighting. - can_build_factories = TRUE - spore_type = /mob/living/simple_mob/blob/spore/weak - -/datum/blob_type/fulminant_organism/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O) - if(prob(10)) // 10% chance to make a weak spore when expanding. - var/mob/living/simple_mob/blob/spore/S = new spore_type(T) - if(istype(S)) - S.overmind = O - O.blob_mobs.Add(S) - else - S.faction = "blob" - S.update_icons() - -/datum/blob_type/fulminant_organism/on_death(obj/structure/blob/B) - if(prob(33)) // 33% chance to make a spore when dying. - var/mob/living/simple_mob/blob/spore/S = new spore_type(get_turf(B)) - B.visible_message("\The [S] floats free from the [name]!") - if(istype(S)) - S.overmind = B.overmind - B.overmind.blob_mobs.Add(S) - else - S.faction = "blob" - S.update_icons() - - -// Auto-retaliates against melee attacks. Weak to projectiles. -/datum/blob_type/reactive_spines - name = "reactive spines" - desc = "An ever-growing lifeform with a large amount of sharp, powerful looking spines. They look like they could pierce most armor." - ai_desc = "defensive" - effect_desc = "When attacked by a melee weapon, it will automatically retaliate, striking the attacker with an armor piercing attack. \ - The blob itself is rather weak to all forms of attacks regardless, and lacks automatic realitation from ranged attacks." - difficulty = BLOB_DIFFICULTY_EASY // Potentially deadly to people not knowing the mechanics, but otherwise fairly tame, due to its slow spread and weakness. - color = "#9ACD32" - complementary_color = "#FFA500" - damage_type = BRUTE - damage_lower = 30 - damage_upper = 40 - armor_pen = 50 // Even with riot armor and tactical jumpsuit, you'd have 90 armor, reduced by 50, totaling 40. Getting hit for around 21 damage is still rough. - burn_multiplier = 2.0 - brute_multiplier = 2.0 - spread_modifier = 0.35 // Ranged projectiles tend to have a higher material cost, so ease up on the spreading. - ai_aggressiveness = 40 - attack_message = "The blob stabs you" - attack_message_living = ", and you feel sharp spines pierce your flesh" - attack_message_synth = ", and your external plating is pierced by sharp spines" - attack_verb = "stabs" - -// Even if the melee attack is enough to one-shot this blob, it gets to retaliate at least once. -/datum/blob_type/reactive_spines/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) - if(damage > 0 && attacker && get_dist(B, attacker) <= 1) - B.visible_message("The [name] retaliates, lashing out at \the [attacker]!") - B.blob_attack_animation(attacker, B.overmind) - attacker.blob_act(B) - return ..() - - -// Spreads damage to nearby blobs, and attacks with the force of all nearby blobs. -/datum/blob_type/synchronous_mesh - name = "synchronous mesh" - desc = "A mesh that seems strongly interconnected to itself. It moves slowly, but with purpose." - ai_desc = "defensive" - effect_desc = "When damaged, spreads the damage to nearby blobs. When attacking, damage is increased based on how many blobs are near the target. It is resistant to burn damage." - difficulty = BLOB_DIFFICULTY_EASY // Mostly a tank and spank. - color = "#65ADA2" - complementary_color = "#AD6570" - damage_type = BRUTE - damage_lower = 10 - damage_upper = 15 - brute_multiplier = 0.5 - burn_multiplier = 0.2 // Emitters do so much damage that this will likely not matter too much. - spread_modifier = 0.3 // Since the blob spreads damage, it takes awhile to actually kill, so spread is reduced. - ai_aggressiveness = 60 - attack_message = "The mesh synchronously strikes you" - attack_verb = "synchronously strikes" - var/synchronously_attacking = FALSE - -/datum/blob_type/synchronous_mesh/on_attack(obj/structure/blob/B, mob/living/victim) - if(synchronously_attacking) - return - synchronously_attacking = TRUE // To avoid infinite loops. - for(var/obj/structure/blob/C in orange(1, victim)) - if(victim) // Some things delete themselves when dead... - C.blob_attack_animation(victim) - victim.blob_act(C) - synchronously_attacking = FALSE - -/datum/blob_type/synchronous_mesh/on_received_damage(var/obj/structure/blob/B, damage, damage_type) - var/list/blobs_to_hurt = list() // Maximum split is 9, reducing the damage each blob takes to 11.1% but doing that damage to 9 blobs. - for(var/obj/structure/blob/C in range(1, B)) - if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && (C.overmind == B.overmind) ) //if it doesn't have the same 'ownership' or is a core or node, don't split damage to it - blobs_to_hurt += C - - for(var/thing in blobs_to_hurt) - var/obj/structure/blob/C = thing - if(C == B) - continue // We'll damage this later. - - C.adjust_integrity(-(damage / blobs_to_hurt.len)) - - return damage / max(blobs_to_hurt.len, 1) // To hurt the blob that got hit. - - -/datum/blob_type/shifting_fragments - name = "shifting fragments" - desc = "A collection of fragments that seem to shuffle around constantly." - ai_desc = "evasive" - effect_desc = "Swaps places with nearby blobs when hit or when expanding." - difficulty = BLOB_DIFFICULTY_EASY - color = "#C8963C" - complementary_color = "#3C6EC8" - damage_type = BRUTE - damage_lower = 20 - damage_upper = 30 - brute_multiplier = 0.5 - burn_multiplier = 0.5 - spread_modifier = 0.5 - ai_aggressiveness = 30 - attack_message = "A fragment strikes you" - attack_verb = "strikes" - -/datum/blob_type/shifting_fragments/on_received_damage(var/obj/structure/blob/B, damage, damage_type) - if(damage > 0 && prob(60)) - var/list/available_blobs = list() - for(var/obj/structure/blob/OB in orange(1, B)) - if((istype(OB, /obj/structure/blob/normal) || (istype(OB, /obj/structure/blob/shield) && prob(25))) && OB.overmind && OB.overmind == B.overmind) - available_blobs += OB - if(available_blobs.len) - var/obj/structure/blob/targeted = pick(available_blobs) - var/turf/T = get_turf(targeted) - targeted.forceMove(get_turf(B)) - B.forceMove(T) // Swap places. - return ..() - -/datum/blob_type/shifting_fragments/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O) - if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25))) - new_B.forceMove(get_turf(B)) - B.forceMove(T) - -// A very cool blob, literally. -/datum/blob_type/cryogenic_goo - name = "cryogenic goo" - desc = "A mass of goo that freezes anything it touches." - ai_desc = "balanced" - effect_desc = "Lowers the temperature of the room passively, and will also greatly lower the temperature of anything it attacks." - difficulty = BLOB_DIFFICULTY_MEDIUM - color = "#8BA6E9" - complementary_color = "#7D6EB4" - damage_type = BURN - damage_lower = 15 - damage_upper = 25 - brute_multiplier = 0.25 - burn_multiplier = 1.2 - spread_modifier = 0.5 - ai_aggressiveness = 50 - attack_message = "The goo stabs you" - attack_message_living = ", and you feel an intense chill from within" - attack_message_synth = ", and your system reports lower internal temperatures" - attack_verb = "stabs" - -/datum/blob_type/cryogenic_goo/on_attack(obj/structure/blob/B, mob/living/victim) - if(ishuman(victim)) - var/mob/living/carbon/human/H = victim - var/protection = H.get_cold_protection(50) - if(protection < 1) - var/temp_change = 80 // Each hit can reduce temperature by up to 80 kelvin. - var/datum/species/baseline = GLOB.all_species["Human"] - var/temp_cap = baseline.cold_level_3 - 5 // Can't go lower than this. - - var/cold_factor = abs(protection - 1) - temp_change *= cold_factor // If protection was at 0.5, then they only lose 40 kelvin. - - H.bodytemperature = max(H.bodytemperature - temp_change, temp_cap) - else // Just do some extra burn for mobs who don't process bodytemp - victim.adjustFireLoss(20) - -/datum/blob_type/cryogenic_goo/on_pulse(var/obj/structure/blob/B) - var/turf/simulated/T = get_turf(B) - if(!istype(T)) - return - T.freeze_floor() - var/datum/gas_mixture/env = T.return_air() - if(env) - env.add_thermal_energy(-10 * 1000) - -// Electric blob that stuns. -/datum/blob_type/energized_jelly - name = "energized jelly" - desc = "A substance that seems to generate electricity." - ai_desc = "suppressive" - effect_desc = "When attacking an entity, it will shock them with a strong electric shock. Repeated attacks can stun the target." - difficulty = BLOB_DIFFICULTY_MEDIUM - color = "#EFD65A" - complementary_color = "#00E5B1" - damage_type = BURN - damage_lower = 5 - damage_upper = 10 - brute_multiplier = 0.5 - burn_multiplier = 0.5 - spread_modifier = 0.35 - ai_aggressiveness = 80 - attack_message = "The jelly prods you" - attack_message_living = ", and your flesh burns as electricity arcs into you" - attack_message_synth = ", and your internal circuity is overloaded as electricity arcs into you" - attack_verb = "prods" - -/datum/blob_type/energized_jelly/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) - victim.electrocute_act(10, src, 1, def_zone) - victim.stun_effect_act(0, 40, BP_TORSO, src) - - -// A blob with area of effect attacks. -/datum/blob_type/explosive_lattice - name = "explosive lattice" - desc = "A very unstable lattice that looks quite explosive." - ai_desc = "aggressive" - effect_desc = "When attacking an entity, it will cause a small explosion, hitting things near the target. It is somewhat resilient, but weaker to brute damage." - difficulty = BLOB_DIFFICULTY_MEDIUM - color = "#8B2500" - complementary_color = "#00668B" - damage_type = BURN - damage_lower = 25 - damage_upper = 35 - armor_check = "bomb" - armor_pen = 5 // This is so blob hits still hurt just slightly when wearing a bomb suit (100 bomb resist). - brute_multiplier = 0.75 - burn_multiplier = 0.5 - spread_modifier = 0.4 - ai_aggressiveness = 75 - attack_message = "The lattice blasts you" - attack_message_living = ", and your flesh burns from the blast wave" - attack_message_synth = ", and your plating burns from the blast wave" - attack_verb = "blasts" - var/exploding = FALSE - -/datum/blob_type/explosive_lattice/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) // This doesn't use actual bombs since they're too strong and it would hurt the blob. - if(exploding) // We're busy, don't infinite loop us. - return - - exploding = TRUE - for(var/mob/living/L in range(get_turf(victim), 1)) // We don't use orange(), in case there is more than one mob on the target tile. - if(L == victim) // Already hit. - continue - if(L.faction == "blob") // No friendly fire - continue - L.blob_act() - - // Visual effect. - var/datum/effect/system/explosion/E = new/datum/effect/system/explosion/smokeless() - var/turf/T = get_turf(victim) - E.set_up(T) - E.start() - - // Now for sounds. - playsound(T, "explosion", 75, 1) // Local sound. - - for(var/mob/M in player_list) // For everyone else. - if(M.z == T.z && get_dist(M, T) > world.view && !M.ear_deaf && !istype(M.loc,/turf/space)) - M << 'sound/effects/explosionfar.ogg' - - exploding = FALSE - - -// A blob that slips and drowns you. -/datum/blob_type/pressurized_slime - name = "pressurized slime" - desc = "A large mass that seems to leak slippery fluid everywhere." - ai_desc = "drowning" - effect_desc = "Wets the floor when expanding and when hit. Tries to drown its enemies when attacking. It forces itself past internals. Resistant to burn damage." - difficulty = BLOB_DIFFICULTY_HARD - color = "#AAAABB" - complementary_color = "#BBBBAA" - damage_type = OXY - damage_lower = 5 - damage_upper = 15 - armor_check = null - brute_multiplier = 0.6 - burn_multiplier = 0.2 - spread_modifier = 0.4 - ai_aggressiveness = 75 - attack_message = "The slime splashes into you" - attack_message_living = ", and you gasp for breath" - attack_message_synth = ", and the fluid wears down on your components" - attack_verb = "splashes" - -/datum/blob_type/pressurized_slime/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) - victim.water_act(5) - var/turf/simulated/T = get_turf(victim) - if(T) - T.wet_floor() - -/datum/blob_type/pressurized_slime/on_received_damage(var/obj/structure/blob/B, damage, damage_type) - wet_surroundings(B, damage) - return ..() - -/datum/blob_type/pressurized_slime/on_pulse(var/obj/structure/blob/B) - var/turf/simulated/T = get_turf(B) - if(!istype(T)) - return - T.wet_floor() - -/datum/blob_type/pressurized_slime/on_death(obj/structure/blob/B) - B.visible_message("The blob ruptures, spraying the area with liquid!") - wet_surroundings(B, 50) - -/datum/blob_type/pressurized_slime/proc/wet_surroundings(var/obj/structure/blob/B, var/probability = 50) - for(var/turf/simulated/T in range(1, B)) - if(prob(probability)) - T.wet_floor() - for(var/atom/movable/AM in T) - AM.water_act(2) - - -// A blob that irradiates everything. -/datum/blob_type/radioactive_ooze - name = "radioactive ooze" - desc = "A goopy mess that glows with an unhealthy aura." - ai_desc = "radical" - effect_desc = "Irradiates the surrounding area, and inflicts toxic attacks. Weak to brute damage." - difficulty = BLOB_DIFFICULTY_MEDIUM - color = "#33CC33" - complementary_color = "#99FF66" - damage_type = TOX - damage_lower = 20 - damage_upper = 30 - armor_check = "rad" - brute_multiplier = 0.75 - burn_multiplier = 0.2 - spread_modifier = 0.8 - ai_aggressiveness = 50 - attack_message = "The ooze splashes you" - attack_message_living = ", and you feel warm" - attack_message_synth = ", and your internal systems are bombarded by ionizing radiation" - attack_verb = "splashes" - -/datum/blob_type/radioactive_ooze/on_pulse(var/obj/structure/blob/B) - SSradiation.radiate(B, 200) - -/datum/blob_type/volatile_alluvium - name = "volatile alluvium" - desc = "A churning, earthy mass that moves in waves." - ai_desc = "earthen" - effect_desc = "Moves slowly, producing weak ranged spores to defend itself, and inflicts brute attacks. Attempts to disarm nearby attackers. Weak to water." - difficulty = BLOB_DIFFICULTY_HARD //Slow-starting, but can be overwhelming if left alone. - color = "#6B481E" - complementary_color = "#7F471F" - damage_lower = 10 - damage_upper = 20 - armor_pen = 40 - brute_multiplier = 0.7 - burn_multiplier = 0.5 - spread_modifier = 0.5 - ai_aggressiveness = 50 - attack_message = "The alluvium crashes against you" - attack_verb = "crashes against" - can_build_factories = TRUE - can_build_resources = TRUE - spore_type = /mob/living/simple_mob/blob/spore/weak - ranged_spores = TRUE - spore_range = 3 - spore_projectile = /obj/item/projectile/energy/blob/splattering - factory_type = /obj/structure/blob/factory/sluggish - resource_type = /obj/structure/blob/resource/sluggish - -/datum/blob_type/volatile_alluvium/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) - if(damage > 0 && attacker && get_dist(B, attacker) <= 2 && prob(min(damage, 70)) && istype(attacker, /mob/living/carbon/human)) // Melee weapons of any type carried by a human will have a high chance of being stolen. - var/mob/living/carbon/human/H = attacker - var/obj/item/I = H.get_active_hand() - H.drop_item() - if(I) - if((I.sharp || I.edge) && !istype(I, /obj/item/weapon/gun)) - I.forceMove(get_turf(B)) // Disarmed entirely. - B.visible_message("The [name] heaves, \the [attacker]'s weapon becoming stuck in the churning mass!") - else - I.throw_at(B, 2, 4) // Just yoinked. - B.visible_message("The [name] heaves, pulling \the [attacker]'s weapon from their hands!") - B.blob_attack_animation(attacker, B.overmind) - return ..() - -/datum/blob_type/volatile_alluvium/on_water(obj/structure/blob/B, amount) - spawn(1) - var/damage = amount * 2 - B.adjust_integrity(-(damage)) - if(B && prob(damage)) - B.visible_message("The [name] begins to crumble!") +// Set up the blob type for the chunk. +/datum/blob_type/proc/chunk_setup(obj/item/weapon/blobcore_chunk/B) + return diff --git a/code/modules/blob2/overmind/types/blazing_oil.dm b/code/modules/blob2/overmind/types/blazing_oil.dm new file mode 100644 index 0000000000..094083e778 --- /dev/null +++ b/code/modules/blob2/overmind/types/blazing_oil.dm @@ -0,0 +1,45 @@ +// A blob meant to be fought like a fire. +/datum/blob_type/blazing_oil + name = "blazing oil" + desc = "A strange, extremely vicious liquid that seems to burn endlessly." + ai_desc = "aggressive" + effect_desc = "Cannot be harmed by burning weapons, and ignites entities it attacks. It will also gradually heat up the area it is in. Water harms it greatly." + difficulty = BLOB_DIFFICULTY_MEDIUM // Emitters don't work but extinguishers are fairly common. Might need fire/atmos suits. + color = "#B68D00" + complementary_color = "#BE5532" + spread_modifier = 0.5 + ai_aggressiveness = 50 + damage_type = BURN + burn_multiplier = 0 // Fire immunity + chunk_active_ability_cooldown = 4 MINUTES + attack_message = "The blazing oil splashes you with its burning oil" + attack_message_living = ", and you feel your skin char and melt" + attack_message_synth = ", and your external plating melts" + attack_verb = "splashes" + +/datum/blob_type/blazing_oil/on_attack(obj/structure/blob/B, mob/living/victim) + victim.fire_act() // Burn them. + +/datum/blob_type/blazing_oil/on_water(obj/structure/blob/B, amount) + spawn(1) + B.adjust_integrity(-(amount * 5)) + +/datum/blob_type/blazing_oil/on_pulse(var/obj/structure/blob/B) + var/turf/T = get_turf(B) + if(!T) + return + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(10 * 1000) + +/datum/blob_type/blazing_oil/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/turf/T = get_turf(B) + if(!T) + return + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(10 * 1000) + +/datum/blob_type/blazing_oil/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) + user.add_modifier(/datum/modifier/exothermic, 5 MINUTES) + return \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/classic.dm b/code/modules/blob2/overmind/types/classic.dm new file mode 100644 index 0000000000..450324b493 --- /dev/null +++ b/code/modules/blob2/overmind/types/classic.dm @@ -0,0 +1,37 @@ +// Mostly a classic blob. No nodes, no other blob types. +/datum/blob_type/classic + name = "lethargic blob" + desc = "A mass that seems bound to its core." + ai_desc = "unambitious" + effect_desc = "Will not create any nodes. Has average strength and resistances." + difficulty = BLOB_DIFFICULTY_EASY // Behaves almost like oldblob, and as such is about as easy as oldblob. + color = "#AAFF00" + complementary_color = "#57787B" + can_build_nodes = FALSE + spread_modifier = 1.0 + ai_aggressiveness = 0 + +/datum/blob_type/classic/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) + var/turf/T = get_turf(B) + + to_chat(user, "\The [B] produces a soothing ooze!") + + T.visible_message("\The [B] shudders at \the [user]'s touch, before disgorging a disgusting ooze.") + + for(var/turf/simulated/floor/F in view(2, T)) + spawn() + var/obj/effect/effect/water/splash = new(T) + splash.create_reagents(15) + splash.reagents.add_reagent("blood", 10,list("blood_colour" = color)) + splash.set_color() + + splash.set_up(F, 2, 3) + + var/obj/effect/decal/cleanable/chemcoating/blood = locate() in T + if(!istype(blood)) + blood = new(T) + blood.reagents.add_reagent("blood", 10,list("blood_colour" = color)) + blood.reagents.add_reagent("tricorlidaze", 5) + blood.update_icon() + + return \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/cryogenic_goo.dm b/code/modules/blob2/overmind/types/cryogenic_goo.dm new file mode 100644 index 0000000000..32684ed58f --- /dev/null +++ b/code/modules/blob2/overmind/types/cryogenic_goo.dm @@ -0,0 +1,59 @@ +// A very cool blob, literally. +/datum/blob_type/cryogenic_goo + name = "cryogenic goo" + desc = "A mass of goo that freezes anything it touches." + ai_desc = "balanced" + effect_desc = "Lowers the temperature of the room passively, and will also greatly lower the temperature of anything it attacks." + difficulty = BLOB_DIFFICULTY_MEDIUM + color = "#8BA6E9" + complementary_color = "#7D6EB4" + damage_type = BURN + damage_lower = 15 + damage_upper = 25 + brute_multiplier = 0.25 + burn_multiplier = 1.2 + spread_modifier = 0.5 + ai_aggressiveness = 50 + chunk_active_ability_cooldown = 4 MINUTES + attack_message = "The goo stabs you" + attack_message_living = ", and you feel an intense chill from within" + attack_message_synth = ", and your system reports lower internal temperatures" + attack_verb = "stabs" + +/datum/blob_type/cryogenic_goo/on_attack(obj/structure/blob/B, mob/living/victim) + if(ishuman(victim)) + var/mob/living/carbon/human/H = victim + var/protection = H.get_cold_protection(50) + if(protection < 1) + var/temp_change = 80 // Each hit can reduce temperature by up to 80 kelvin. + var/datum/species/baseline = GLOB.all_species["Human"] + var/temp_cap = baseline.cold_level_3 - 5 // Can't go lower than this. + + var/cold_factor = abs(protection - 1) + temp_change *= cold_factor // If protection was at 0.5, then they only lose 40 kelvin. + + H.bodytemperature = max(H.bodytemperature - temp_change, temp_cap) + else // Just do some extra burn for mobs who don't process bodytemp + victim.adjustFireLoss(20) + +/datum/blob_type/cryogenic_goo/on_pulse(var/obj/structure/blob/B) + var/turf/simulated/T = get_turf(B) + if(!istype(T)) + return + T.freeze_floor() + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(-10 * 1000) + +/datum/blob_type/cryogenic_goo/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/turf/simulated/T = get_turf(B) + if(!istype(T)) + return + T.freeze_floor() + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(-10 * 1000) + +/datum/blob_type/cryogenic_goo/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) + user.add_modifier(/datum/modifier/endothermic, 5 MINUTES) + return diff --git a/code/modules/blob2/overmind/types/ectoplasmic_horror.dm b/code/modules/blob2/overmind/types/ectoplasmic_horror.dm new file mode 100644 index 0000000000..f7689847cd --- /dev/null +++ b/code/modules/blob2/overmind/types/ectoplasmic_horror.dm @@ -0,0 +1,128 @@ +// A blob that drains energy from nearby mobs in order to fuel itself, and 'negates' some attacks extradimensionally. +/datum/blob_type/ectoplasmic_horror + name = "ectoplasmic horror" + desc = "A disgusting translucent slime that feels out of place." + ai_desc = "dodging" + effect_desc = "Drains energy from nearby life-forms in order to expand itself. Weak to all damage." + difficulty = BLOB_DIFFICULTY_MEDIUM + color = "#72109eaa" + complementary_color = "#1a9de8" + damage_type = HALLOSS + damage_lower = 10 + damage_upper = 30 + armor_check = "energy" + brute_multiplier = 1.5 + burn_multiplier = 1.5 + spread_modifier = 0.9 + ai_aggressiveness = 50 + attack_message = "The horror strikes you" + attack_message_living = ", and you feel a wave of exhaustion" + attack_message_synth = ", and your systems begin to slow" + attack_verb = "strikes" + can_build_factories = TRUE + factory_type = /obj/structure/blob/factory/sluggish + spore_type = /mob/living/simple_mob/blob/spore/weak + + var/list/active_beams = list() + +/datum/blob_type/ectoplasmic_horror/on_pulse(var/obj/structure/blob/B) + if(B.type == /obj/structure/blob && (locate(/obj/structure/blob/node) in oview(2, get_turf(B)))) + B.visible_message("The [name] quakes, before hardening.") + new/obj/structure/blob/shield(get_turf(B), B.overmind) + qdel(B) + + if(istype(B, /obj/structure/blob/factory)) + listclearnulls(active_beams) + var/atom/movable/beam_origin = B + for(var/mob/living/L in oview(world.view, B)) + if(L.stat == DEAD || L.faction == "blob") + continue + if(prob(5)) + var/beamtarget_exists = FALSE + + if(active_beams.len) + for(var/datum/beam/Beam in active_beams) + if(Beam.target == L) + beamtarget_exists = TRUE + break + + if(!beamtarget_exists && GetAnomalySusceptibility(L) >= 0.5) + B.visible_message("\The [B] lashes out at \the [L]!") + var/datum/beam/drain_beam = beam_origin.Beam(L, icon_state = "drain_life", time = 10 SECONDS) + active_beams |= drain_beam + spawn(9 SECONDS) + if(B && drain_beam) + B.visible_message("\The [B] siphons energy from \the [L]") + L.add_modifier(/datum/modifier/berserk_exhaustion, 60 SECONDS) + B.overmind.add_points(rand(10,30)) + if(!QDELETED(drain_beam)) + qdel(drain_beam) + +/datum/blob_type/ectoplasmic_horror/on_received_damage(var/obj/structure/blob/B, damage, damage_type) + if(prob(round(damage * 0.5))) + B.visible_message("\The [B] shimmers, distorting through some unseen dimension.") + var/initial_alpha = B.alpha + spawn() + animate(B,alpha = initial_alpha, alpha = 10, time = 10) + animate(B,alpha = 10, alpha = initial_alpha, time = 10) + return 0 + return ..() + +/datum/blob_type/ectoplasmic_horror/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/mob/living/carrier = B.get_carrier() + + if(!carrier) + return + + var/list/nearby_mobs = list() + for(var/mob/living/L in oview(world.view, carrier)) + if(L.stat != DEAD) + nearby_mobs |= L + + if(nearby_mobs.len) + listclearnulls(active_beams) + for(var/mob/living/L in nearby_mobs) + if(L.stat == DEAD || L.faction == "blob") + continue + if(prob(5)) + var/beamtarget_exists = FALSE + + if(active_beams.len) + for(var/datum/beam/Beam in active_beams) + if(Beam.target == L) + beamtarget_exists = TRUE + break + + if(!beamtarget_exists && GetAnomalySusceptibility(L) >= 0.5) + carrier.visible_message("\icon [B] \The [B] lashes out at \the [L]!") + var/datum/beam/drain_beam = carrier.Beam(L, icon_state = "drain_life", time = 10 SECONDS) + active_beams |= drain_beam + spawn(9 SECONDS) + if(B && drain_beam) + carrier.visible_message("\The [B] siphons energy from \the [L]") + L.add_modifier(/datum/modifier/berserk_exhaustion, 30 SECONDS) + var/total_heal = 0 + + if(carrier.getBruteLoss()) + carrier.adjustBruteLoss(-5) + total_heal += 5 + + if(carrier.getFireLoss()) + carrier.adjustFireLoss(-5) + total_heal += 5 + + if(carrier.getToxLoss()) + carrier.adjustToxLoss(-5) + total_heal += 5 + + if(carrier.getOxyLoss()) + carrier.adjustOxyLoss(-5) + total_heal += 5 + + if(carrier.getCloneLoss()) + carrier.adjustCloneLoss(-5) + total_heal += 5 + + carrier.add_modifier(/datum/modifier/berserk_exhaustion, total_heal SECONDS) + if(!QDELETED(drain_beam)) + qdel(drain_beam) diff --git a/code/modules/blob2/overmind/types/electromagnetic_web.dm b/code/modules/blob2/overmind/types/electromagnetic_web.dm new file mode 100644 index 0000000000..9831998587 --- /dev/null +++ b/code/modules/blob2/overmind/types/electromagnetic_web.dm @@ -0,0 +1,34 @@ +// Makes robots cry. Really weak to brute damage. +/datum/blob_type/electromagnetic_web + name = "electromagnetic web" + desc = "A gooy mesh that generates an electromagnetic field. Electronics will likely be ruined if nearby." + ai_desc = "balanced" + effect_desc = "Causes an EMP on attack, and will EMP upon death. It is also more fragile than average, especially to brute force." + difficulty = BLOB_DIFFICULTY_MEDIUM // Rough for robots but otherwise fragile and can be fought at range like most blobs anyways. + color = "#83ECEC" + complementary_color = "#EC8383" + damage_type = BURN + damage_lower = 10 + damage_upper = 20 + brute_multiplier = 3 + burn_multiplier = 2 + ai_aggressiveness = 60 + chunk_active_type = BLOB_CHUNK_CONSTANT + attack_message = "The web lashes you" + attack_message_living = ", and you hear a faint buzzing" + attack_message_synth = ", and your electronics get badly damaged" + attack_verb = "lashes" + +/datum/blob_type/electromagnetic_web/on_death(obj/structure/blob/B) + empulse(B.loc, 0, 1, 2) + +/datum/blob_type/electromagnetic_web/on_attack(obj/structure/blob/B, mob/living/victim) + victim.emp_act(2) + +/datum/blob_type/electromagnetic_web/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/turf/T = get_turf(B) + if(!T) + return + + for(var/mob/living/L in view(2, T)) + L.add_modifier(/datum/modifier/faraday, 30 SECONDS) \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/energized_jelly.dm b/code/modules/blob2/overmind/types/energized_jelly.dm new file mode 100644 index 0000000000..33c570f24f --- /dev/null +++ b/code/modules/blob2/overmind/types/energized_jelly.dm @@ -0,0 +1,39 @@ +// Electric blob that stuns. +/datum/blob_type/energized_jelly + name = "energized jelly" + desc = "A substance that seems to generate electricity." + ai_desc = "suppressive" + effect_desc = "When attacking an entity, it will shock them with a strong electric shock. Repeated attacks can stun the target." + difficulty = BLOB_DIFFICULTY_MEDIUM + color = "#EFD65A" + complementary_color = "#00E5B1" + damage_type = BURN + damage_lower = 5 + damage_upper = 10 + brute_multiplier = 0.5 + burn_multiplier = 0.5 + spread_modifier = 0.35 + ai_aggressiveness = 80 + attack_message = "The jelly prods you" + attack_message_living = ", and your flesh burns as electricity arcs into you" + attack_message_synth = ", and your internal circuity is overloaded as electricity arcs into you" + attack_verb = "prods" + spore_projectile = /obj/item/projectile/beam/shock + +/datum/blob_type/energized_jelly/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) + victim.electrocute_act(10, src, 1, def_zone) + victim.stun_effect_act(0, 40, BP_TORSO, src) + +/datum/blob_type/energized_jelly/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + for(var/mob/living/L in oview(world.view, get_turf(B))) + var/mob/living/carrier = B.get_carrier() + + if(istype(carrier) && carrier == L) + continue + + var/obj/item/projectile/P = new spore_projectile(get_turf(B)) + + carrier.visible_message("\The [B] discharges energy toward \the [L]!") + P.launch_projectile(L, BP_TORSO, carrier) + + return \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/explosive_lattice.dm b/code/modules/blob2/overmind/types/explosive_lattice.dm new file mode 100644 index 0000000000..d0484c3725 --- /dev/null +++ b/code/modules/blob2/overmind/types/explosive_lattice.dm @@ -0,0 +1,58 @@ +// A blob with area of effect attacks. +/datum/blob_type/explosive_lattice + name = "explosive lattice" + desc = "A very unstable lattice that looks quite explosive." + ai_desc = "aggressive" + effect_desc = "When attacking an entity, it will cause a small explosion, hitting things near the target. It is somewhat resilient, but weaker to brute damage." + difficulty = BLOB_DIFFICULTY_MEDIUM + color = "#8B2500" + complementary_color = "#00668B" + damage_type = BURN + damage_lower = 25 + damage_upper = 35 + armor_check = "bomb" + armor_pen = 5 // This is so blob hits still hurt just slightly when wearing a bomb suit (100 bomb resist). + brute_multiplier = 0.75 + burn_multiplier = 0.5 + spread_modifier = 0.4 + ai_aggressiveness = 75 + attack_message = "The lattice blasts you" + attack_message_living = ", and your flesh burns from the blast wave" + attack_message_synth = ", and your plating burns from the blast wave" + attack_verb = "blasts" + var/exploding = FALSE + +/datum/blob_type/explosive_lattice/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) // This doesn't use actual bombs since they're too strong and it would hurt the blob. + if(exploding) // We're busy, don't infinite loop us. + return + + exploding = TRUE + for(var/mob/living/L in range(get_turf(victim), 1)) // We don't use orange(), in case there is more than one mob on the target tile. + if(L == victim) // Already hit. + continue + if(L.faction == "blob") // No friendly fire + continue + L.blob_act() + + // Visual effect. + var/datum/effect/system/explosion/E = new/datum/effect/system/explosion/smokeless() + var/turf/T = get_turf(victim) + E.set_up(T) + E.start() + + // Now for sounds. + playsound(T, "explosion", 75, 1) // Local sound. + + for(var/mob/M in player_list) // For everyone else. + if(M.z == T.z && get_dist(M, T) > world.view && !M.ear_deaf && !istype(M.loc,/turf/space)) + M << 'sound/effects/explosionfar.ogg' + + exploding = FALSE + +/datum/blob_type/explosive_lattice/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/turf/T = get_turf(B) + if(!T) + return + + for(var/mob/living/L in view(1, T)) + L.add_modifier(/datum/modifier/blastshield, 30 SECONDS) \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/fabrication_swarm.dm b/code/modules/blob2/overmind/types/fabrication_swarm.dm new file mode 100644 index 0000000000..7e267558d9 --- /dev/null +++ b/code/modules/blob2/overmind/types/fabrication_swarm.dm @@ -0,0 +1,44 @@ +// Slow, tanky blobtype which uses not spores, but hivebots, as its soldiers. +/datum/blob_type/fabrication_swarm + name = "iron tide" + desc = "A swarm of self replicating construction nanites. Incredibly illegal, but only mildly dangerous." + effect_desc = "Slow-spreading, but incredibly resiliant. It has a chance to harden itself against attacks automatically for no resource cost, and uses cheaply-constructed hivebots as soldiers." + ai_desc = "defensive" + difficulty = BLOB_DIFFICULTY_MEDIUM // Emitters are okay, EMP is great. + color = "#666666" + complementary_color = "#B7410E" + spread_modifier = 0.2 + can_build_factories = TRUE + can_build_resources = TRUE + attack_message = "The tide tries to shove you away" + attack_message_living = ", and your skin itches" + attack_message_synth = ", and your external plating dulls" + attack_verb = "shoves" + armor_pen = 40 + damage_lower = 10 + damage_upper = 25 + brute_multiplier = 0.25 + burn_multiplier = 0.6 + ai_aggressiveness = 50 //Really doesn't like you near it. + spore_type = /mob/living/simple_mob/mechanical/hivebot/swarm + +/datum/blob_type/fabrication_swarm/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) + if(istype(B, /obj/structure/blob/normal)) + if(damage > 0) + var/reinforce_probability = min(damage, 70) + if(prob(reinforce_probability)) + B.visible_message("The [name] quakes, before rapidly hardening!") + new/obj/structure/blob/shield(get_turf(B), B.overmind) + qdel(B) + return ..() + +/datum/blob_type/fabrication_swarm/on_emp(obj/structure/blob/B, severity) + B.adjust_integrity(-(30 / severity)) + +/datum/blob_type/fabrication_swarm/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/turf/T = get_turf(B) + for(var/mob/living/L in view(world.view, T)) + if(L.stat != DEAD && L.isSynthetic()) + L.adjustBruteLoss(-1) + L.adjustFireLoss(-1) + return \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/fulminant_organism.dm b/code/modules/blob2/overmind/types/fulminant_organism.dm new file mode 100644 index 0000000000..2d940f9359 --- /dev/null +++ b/code/modules/blob2/overmind/types/fulminant_organism.dm @@ -0,0 +1,50 @@ +// Makes tons of weak spores whenever it spreads. +/datum/blob_type/fulminant_organism + name = "fulminant organism" + desc = "A self expanding mass of living biomaterial, that appears to produce entities to defend it, much like a living organism's immune system." + ai_desc = "swarming" + effect_desc = "Creates weak floating spores that attack enemies from specialized blobs, has a chance to also create a spore when \ + it spreads onto a new tile, and has a chance to create a spore when a blob tile is destroyed. It is more fragile than average to all types of damage." + difficulty = BLOB_DIFFICULTY_HARD // Loads of spores that can overwhelm, and spreads quickly. + color = "#FF0000" // Red + complementary_color = "#FFCC00" // Orange-ish + damage_type = TOX + damage_lower = 10 + damage_upper = 20 + spread_modifier = 0.7 + burn_multiplier = 1.5 + brute_multiplier = 1.5 + ai_aggressiveness = 30 // The spores do most of the fighting. + can_build_factories = TRUE + spore_type = /mob/living/simple_mob/blob/spore/weak + chunk_active_ability_cooldown = 60 SECONDS + +/datum/blob_type/fulminant_organism/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O) + if(prob(10)) // 10% chance to make a weak spore when expanding. + var/mob/living/simple_mob/blob/spore/S = new spore_type(T) + if(istype(S)) + S.overmind = O + O.blob_mobs.Add(S) + else + S.faction = "blob" + S.update_icons() + +/datum/blob_type/fulminant_organism/on_death(obj/structure/blob/B) + if(prob(33)) // 33% chance to make a spore when dying. + var/mob/living/simple_mob/blob/spore/S = new spore_type(get_turf(B)) + B.visible_message("\The [S] floats free from the [name]!") + if(istype(S)) + S.overmind = B.overmind + B.overmind.blob_mobs.Add(S) + else + S.faction = "blob" + S.update_icons() + +/datum/blob_type/fulminant_organism/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) + for(var/I = 1 to rand(3,4)) + var/mob/living/simple_mob/blob/spore/S = new spore_type(get_turf(B)) + S.faction = user.faction + S.blob_type = src + S.update_icons() + S.ai_holder.forget_everything() + S.add_modifier(/datum/modifier/doomed, 2 MINUTES) diff --git a/code/modules/blob2/overmind/types/fungal_bloom.dm b/code/modules/blob2/overmind/types/fungal_bloom.dm new file mode 100644 index 0000000000..6bf20b99d3 --- /dev/null +++ b/code/modules/blob2/overmind/types/fungal_bloom.dm @@ -0,0 +1,37 @@ +// Makes spores that spread the blob and infest dead people. +/datum/blob_type/fungal_bloom + name = "fungal bloom" + desc = "A massive network of rapidly expanding mycelium. Large spore-like particles can be seen spreading from it." + ai_desc = "swarming" + effect_desc = "Creates floating spores that attack enemies from specialized blobs, and will spread the blob if killed. The spores can also \ + infest deceased biological humanoids. It is vulnerable to fire." + difficulty = BLOB_DIFFICULTY_MEDIUM // The spores are more of an annoyance but can be difficult to contain. + color = "#AAAAAA" + complementary_color = "#FFFFFF" + damage_type = TOX + damage_lower = 15 + damage_upper = 25 + spread_modifier = 0.3 // Lower, since spores will do a lot of the spreading. + burn_multiplier = 3 + ai_aggressiveness = 40 + can_build_factories = TRUE + spore_type = /mob/living/simple_mob/blob/spore/infesting + chunk_active_ability_cooldown = 2 MINUTES + +/datum/blob_type/fungal_bloom/on_spore_death(mob/living/simple_mob/blob/spore/S) + if(S.is_infesting) + return // Don't make blobs if they were on someone's head. + var/turf/T = get_turf(S) + var/obj/structure/blob/B = locate(/obj/structure/blob) in T + if(B) // Is there already a blob here? If so, just heal it. + B.adjust_integrity(10) + else + B = new /obj/structure/blob/normal(T, S.overmind) // Otherwise spread it. + B.visible_message("\A [B] forms on \the [T] as \the [S] bursts!") + +/datum/blob_type/fungal_bloom/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) + var/mob/living/simple_mob/blob/spore/S = new spore_type(get_turf(B)) + S.faction = user.faction + S.blob_type = src + S.update_icons() + S.ai_holder.forget_everything() \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/grey_goo.dm b/code/modules/blob2/overmind/types/grey_goo.dm new file mode 100644 index 0000000000..5c93fb46c8 --- /dev/null +++ b/code/modules/blob2/overmind/types/grey_goo.dm @@ -0,0 +1,27 @@ +// Super fast spreading, but weak to EMP. +/datum/blob_type/grey_goo + name = "grey tide" + desc = "A swarm of self replicating nanomachines. Extremely illegal and dangerous, the EIO was meant to prevent this from showing up a second time." + effect_desc = "Spreads much faster than average, but is harmed greatly by electromagnetic pulses." + ai_desc = "genocidal" + difficulty = BLOB_DIFFICULTY_SUPERHARD // Fastest spread of them all and has snowballing capabilities. + color = "#888888" + complementary_color = "#CCCCCC" + spread_modifier = 1.0 + slow_spread_with_size = FALSE + ai_aggressiveness = 80 + can_build_resources = TRUE + attack_message = "The tide tries to swallow you" + attack_message_living = ", and you feel your skin dissolve" + attack_message_synth = ", and your external plating dissolves" + +/datum/blob_type/grey_goo/on_emp(obj/structure/blob/B, severity) + B.adjust_integrity(-(20 / severity)) + +/datum/blob_type/grey_goo/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/turf/T = get_turf(B) + for(var/mob/living/L in view(world.view, T)) + if(L.stat != DEAD) + L.adjustBruteLoss(-1) + L.adjustFireLoss(-1) + return diff --git a/code/modules/blob2/overmind/types/pressurized_slime.dm b/code/modules/blob2/overmind/types/pressurized_slime.dm new file mode 100644 index 0000000000..6b2ff5800f --- /dev/null +++ b/code/modules/blob2/overmind/types/pressurized_slime.dm @@ -0,0 +1,56 @@ +// A blob that slips and drowns you. +/datum/blob_type/pressurized_slime + name = "pressurized slime" + desc = "A large mass that seems to leak slippery fluid everywhere." + ai_desc = "drowning" + effect_desc = "Wets the floor when expanding and when hit. Tries to drown its enemies when attacking. It forces itself past internals. Resistant to burn damage." + difficulty = BLOB_DIFFICULTY_HARD + color = "#AAAABB" + complementary_color = "#BBBBAA" + damage_type = OXY + damage_lower = 5 + damage_upper = 15 + armor_check = null + brute_multiplier = 0.6 + burn_multiplier = 0.2 + spread_modifier = 0.4 + ai_aggressiveness = 75 + attack_message = "The slime splashes into you" + attack_message_living = ", and you gasp for breath" + attack_message_synth = ", and the fluid wears down on your components" + attack_verb = "splashes" + +/datum/blob_type/pressurized_slime/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) + victim.water_act(5) + var/turf/simulated/T = get_turf(victim) + if(T) + T.wet_floor() + +/datum/blob_type/pressurized_slime/on_received_damage(var/obj/structure/blob/B, damage, damage_type) + wet_surroundings(B, damage) + return ..() + +/datum/blob_type/pressurized_slime/on_pulse(var/obj/structure/blob/B) + var/turf/simulated/T = get_turf(B) + if(!istype(T)) + return + T.wet_floor() + +/datum/blob_type/pressurized_slime/on_death(obj/structure/blob/B) + B.visible_message("The blob ruptures, spraying the area with liquid!") + wet_surroundings(B, 50) + +/datum/blob_type/pressurized_slime/proc/wet_surroundings(var/obj/structure/blob/B, var/probability = 50) + for(var/turf/simulated/T in range(1, get_turf(B))) + if(prob(probability)) + T.wet_floor() + for(var/atom/movable/AM in T) + AM.water_act(2) + +/datum/blob_type/pressurized_slime/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + wet_surroundings(B, 10) + +/datum/blob_type/pressurized_slime/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) // Drenches you in water. + if(user) + user.ExtinguishMob() + user.fire_stacks = CLAMP(user.fire_stacks - 1, -25, 25) \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/radioactive_ooze.dm b/code/modules/blob2/overmind/types/radioactive_ooze.dm new file mode 100644 index 0000000000..f3226f2b5b --- /dev/null +++ b/code/modules/blob2/overmind/types/radioactive_ooze.dm @@ -0,0 +1,27 @@ +// A blob that irradiates everything. +/datum/blob_type/radioactive_ooze + name = "radioactive ooze" + desc = "A goopy mess that glows with an unhealthy aura." + ai_desc = "radical" + effect_desc = "Irradiates the surrounding area, and inflicts toxic attacks. Weak to brute damage." + difficulty = BLOB_DIFFICULTY_MEDIUM + color = "#33CC33" + complementary_color = "#99FF66" + damage_type = TOX + damage_lower = 20 + damage_upper = 30 + armor_check = "rad" + brute_multiplier = 0.75 + burn_multiplier = 0.2 + spread_modifier = 0.8 + ai_aggressiveness = 50 + attack_message = "The ooze splashes you" + attack_message_living = ", and you feel warm" + attack_message_synth = ", and your internal systems are bombarded by ionizing radiation" + attack_verb = "splashes" + +/datum/blob_type/radioactive_ooze/on_pulse(var/obj/structure/blob/B) + SSradiation.radiate(B, 200) + +/datum/blob_type/radioactive_ooze/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + SSradiation.radiate(B, rand(25,100)) \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/ravenous_macrophage.dm b/code/modules/blob2/overmind/types/ravenous_macrophage.dm new file mode 100644 index 0000000000..0a73501c34 --- /dev/null +++ b/code/modules/blob2/overmind/types/ravenous_macrophage.dm @@ -0,0 +1,50 @@ +// A blob that produces noxious smoke-clouds and recycles its dying parts. +/datum/blob_type/ravenous_macrophage + name = "ravenous macrophage" + desc = "A disgusting gel that reeks of death." + ai_desc = "resourceful" + effect_desc = "Produces noxious fumes, and melts prey with acidic attacks. Weak to brute damage." + difficulty = BLOB_DIFFICULTY_MEDIUM + color = "#639b3f" + complementary_color = "#d1ec3c" + damage_type = BIOACID + damage_lower = 20 + damage_upper = 30 + armor_check = "bio" + armor_pen = 50 + brute_multiplier = 0.8 + burn_multiplier = 0.3 + spread_modifier = 0.8 + ai_aggressiveness = 70 + attack_message = "The macrophage splashes you" + attack_message_living = ", and you feel a horrible burning" + attack_message_synth = ", and your body begins to corrode" + attack_verb = "splashes" + +/datum/blob_type/ravenous_macrophage/on_pulse(var/obj/structure/blob/B) + var/mob/living/L = locate() in range(world.view, B) + if(prob(1) && L.mind && !L.stat) // There's some active living thing nearby, produce offgas. + var/turf/T = get_turf(B) + var/datum/effect/effect/system/smoke_spread/noxious/BS = new /datum/effect/effect/system/smoke_spread/noxious + BS.attach(T) + BS.set_up(3, 0, T) + playsound(T, 'sound/effects/smoke.ogg', 50, 1, -3) + BS.start() + +/datum/blob_type/ravenous_macrophage/on_death(obj/structure/blob/B) + var/obj/structure/blob/other = locate() in oview(2, B) + if(other) + B.visible_message("The dying mass is rapidly consumed by the nearby [other]!") + if(other.overmind) + other.overmind.add_points(rand(1,4)) + +/datum/blob_type/ravenous_macrophage/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/mob/living/L = locate() in range(world.view, B) + if(prob(5) && !L.stat) // There's some active living thing nearby, produce offgas. + B.visible_message("\icon [B] \The [B] disgorches a cloud of noxious gas!") + var/turf/T = get_turf(B) + var/datum/effect/effect/system/smoke_spread/noxious/BS = new /datum/effect/effect/system/smoke_spread/noxious + BS.attach(T) + BS.set_up(3, 0, T) + playsound(T, 'sound/effects/smoke.ogg', 50, 1, -3) + BS.start() \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/reactive_spines.dm b/code/modules/blob2/overmind/types/reactive_spines.dm new file mode 100644 index 0000000000..0d0021f243 --- /dev/null +++ b/code/modules/blob2/overmind/types/reactive_spines.dm @@ -0,0 +1,59 @@ +// Auto-retaliates against melee attacks. Weak to projectiles. +/datum/blob_type/reactive_spines + name = "reactive spines" + desc = "An ever-growing lifeform with a large amount of sharp, powerful looking spines. They look like they could pierce most armor." + ai_desc = "defensive" + effect_desc = "When attacked by a melee weapon, it will automatically retaliate, striking the attacker with an armor piercing attack. \ + The blob itself is rather weak to all forms of attacks regardless, and lacks automatic realitation from ranged attacks." + difficulty = BLOB_DIFFICULTY_EASY // Potentially deadly to people not knowing the mechanics, but otherwise fairly tame, due to its slow spread and weakness. + color = "#9ACD32" + complementary_color = "#FFA500" + damage_type = BRUTE + damage_lower = 30 + damage_upper = 40 + armor_pen = 50 // Even with riot armor and tactical jumpsuit, you'd have 90 armor, reduced by 50, totaling 40. Getting hit for around 21 damage is still rough. + burn_multiplier = 2.0 + brute_multiplier = 2.0 + spread_modifier = 0.35 // Ranged projectiles tend to have a higher material cost, so ease up on the spreading. + ai_aggressiveness = 40 + chunk_passive_ability_cooldown = 0.5 SECONDS + attack_message = "The blob stabs you" + attack_message_living = ", and you feel sharp spines pierce your flesh" + attack_message_synth = ", and your external plating is pierced by sharp spines" + attack_verb = "stabs" + spore_projectile = /obj/item/projectile/bullet/thorn + +// Even if the melee attack is enough to one-shot this blob, it gets to retaliate at least once. +/datum/blob_type/reactive_spines/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) + if(damage > 0 && attacker && get_dist(B, attacker) <= 1) + B.visible_message("The [name] retaliates, lashing out at \the [attacker]!") + B.blob_attack_animation(attacker, B.overmind) + attacker.blob_act(B) + return ..() + +// We're expecting 1 to be a target, 2 to be an old move loc, and 3 to be a new move loc. +/datum/blob_type/reactive_spines/chunk_unique(obj/item/weapon/blobcore_chunk/B, var/list/extra_data = null) + if(!LAZYLEN(extra_data)) + return + + var/atom/movable/A = extra_data[1] + + if(istype(A, /mob/living) && world.time > (B.last_passive_use + B.passive_ability_cooldown) && B.should_tick) + B.last_passive_use = world.time + var/mob/living/L = A + + var/mob/living/carrier = B.get_carrier() + + if(!istype(carrier) || L.z != carrier.z || L == carrier || get_dist(L, carrier) > 3) + return + + var/obj/item/projectile/P = new spore_projectile(get_turf(B)) + + carrier.visible_message("\The [B] fires a spine at \the [L]!") + P.launch_projectile(L, BP_TORSO, carrier) + + return + +/datum/blob_type/reactive_spines/chunk_setup(obj/item/weapon/blobcore_chunk/B) + GLOB.moved_event.register_global(B, /obj/item/weapon/blobcore_chunk/proc/call_chunk_unique) + return \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/roiling_mold.dm b/code/modules/blob2/overmind/types/roiling_mold.dm new file mode 100644 index 0000000000..dd42be30ac --- /dev/null +++ b/code/modules/blob2/overmind/types/roiling_mold.dm @@ -0,0 +1,63 @@ +// Blob that fires biological mortar shells from its factories. +/datum/blob_type/roiling_mold + name = "roiling mold" + desc = "A bubbling, creeping mold." + ai_desc = "bombarding" + effect_desc = "Bombards nearby organisms with toxic spores. Weak to all damage." + difficulty = BLOB_DIFFICULTY_MEDIUM + color = "#571509" + complementary_color = "#ec4940" + damage_type = BRUTE + damage_lower = 5 + damage_upper = 20 + armor_check = "melee" + brute_multiplier = 1.2 + burn_multiplier = 1.2 + spread_modifier = 0.8 + can_build_factories = TRUE + ai_aggressiveness = 50 + attack_message = "The mold whips you" + attack_message_living = ", and you feel a searing pain" + attack_message_synth = ", and your shell buckles" + attack_verb = "lashes" + spore_projectile = /obj/item/projectile/arc/spore + +/datum/blob_type/roiling_mold/proc/find_target(var/obj/structure/blob/B, var/tries = 0, var/list/previous_targets = null) + if(tries > 3) + return + var/mob/living/L = locate() in (view(world.view + 3, get_turf(B)) - view(2,get_turf(B)) - previous_targets) // No adjacent mobs. + + if(!check_trajectory(L, B, PASSTABLE)) + if(!LAZYLEN(previous_targets)) + previous_targets = list() + + previous_targets |= L + + L = find_target(B, tries + 1, previous_targets) + + return L + +/datum/blob_type/roiling_mold/on_pulse(var/obj/structure/blob/B) + var/mob/living/L = find_target(B) + + if(!istype(L)) + return + + if(istype(B, /obj/structure/blob/factory) && L.stat != DEAD && prob(ai_aggressiveness) && L.faction != "blob") + var/obj/item/projectile/arc/spore/P = new(get_turf(B)) + P.launch_projectile(L, BP_TORSO, B) + +/datum/blob_type/roiling_mold/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) + for(var/mob/living/L in oview(world.view, get_turf(B))) + if(istype(user) && user == L) + continue + + if(!check_trajectory(L, B, PASSTABLE)) // Can't fire at things on the other side of walls / windows. + continue + + var/obj/item/projectile/P = new spore_projectile(get_turf(B)) + + user.visible_message("\icon [B] \The [B] discharges energy toward \the [L]!") + P.launch_projectile(L, BP_TORSO, user) + + return \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/shifting_fragments.dm b/code/modules/blob2/overmind/types/shifting_fragments.dm new file mode 100644 index 0000000000..ffca17760b --- /dev/null +++ b/code/modules/blob2/overmind/types/shifting_fragments.dm @@ -0,0 +1,40 @@ +/datum/blob_type/shifting_fragments + name = "shifting fragments" + desc = "A collection of fragments that seem to shuffle around constantly." + ai_desc = "evasive" + effect_desc = "Swaps places with nearby blobs when hit or when expanding." + difficulty = BLOB_DIFFICULTY_EASY + color = "#C8963C" + complementary_color = "#3C6EC8" + damage_type = BRUTE + damage_lower = 20 + damage_upper = 30 + brute_multiplier = 0.5 + burn_multiplier = 0.5 + spread_modifier = 0.5 + ai_aggressiveness = 30 + chunk_active_ability_cooldown = 3 MINUTES + attack_message = "A fragment strikes you" + attack_verb = "strikes" + +/datum/blob_type/shifting_fragments/on_received_damage(var/obj/structure/blob/B, damage, damage_type) + if(damage > 0 && prob(60)) + var/list/available_blobs = list() + for(var/obj/structure/blob/OB in orange(1, B)) + if((istype(OB, /obj/structure/blob/normal) || (istype(OB, /obj/structure/blob/shield) && prob(25))) && OB.overmind && OB.overmind == B.overmind) + available_blobs += OB + if(available_blobs.len) + var/obj/structure/blob/targeted = pick(available_blobs) + var/turf/T = get_turf(targeted) + targeted.forceMove(get_turf(B)) + B.forceMove(T) // Swap places. + return ..() + +/datum/blob_type/shifting_fragments/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O) + if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25))) + new_B.forceMove(get_turf(B)) + B.forceMove(T) + +/datum/blob_type/shifting_fragments/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) + user.add_modifier(/datum/modifier/sprinting, 2 MINUTES) + return \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/synchronous_mesh.dm b/code/modules/blob2/overmind/types/synchronous_mesh.dm new file mode 100644 index 0000000000..e234756b2d --- /dev/null +++ b/code/modules/blob2/overmind/types/synchronous_mesh.dm @@ -0,0 +1,73 @@ +// Spreads damage to nearby blobs, and attacks with the force of all nearby blobs. +/datum/blob_type/synchronous_mesh + name = "synchronous mesh" + desc = "A mesh that seems strongly interconnected to itself. It moves slowly, but with purpose." + ai_desc = "defensive" + effect_desc = "When damaged, spreads the damage to nearby blobs. When attacking, damage is increased based on how many blobs are near the target. It is resistant to burn damage." + difficulty = BLOB_DIFFICULTY_EASY // Mostly a tank and spank. + color = "#65ADA2" + complementary_color = "#AD6570" + damage_type = BRUTE + damage_lower = 10 + damage_upper = 15 + brute_multiplier = 0.5 + burn_multiplier = 0.2 // Emitters do so much damage that this will likely not matter too much. + spread_modifier = 0.3 // Since the blob spreads damage, it takes awhile to actually kill, so spread is reduced. + ai_aggressiveness = 60 + attack_message = "The mesh synchronously strikes you" + attack_verb = "synchronously strikes" + var/synchronously_attacking = FALSE + +/datum/blob_type/synchronous_mesh/on_attack(obj/structure/blob/B, mob/living/victim) + if(synchronously_attacking) + return + synchronously_attacking = TRUE // To avoid infinite loops. + for(var/obj/structure/blob/C in orange(1, victim)) + if(victim) // Some things delete themselves when dead... + C.blob_attack_animation(victim) + victim.blob_act(C) + synchronously_attacking = FALSE + +/datum/blob_type/synchronous_mesh/on_received_damage(var/obj/structure/blob/B, damage, damage_type) + var/list/blobs_to_hurt = list() // Maximum split is 9, reducing the damage each blob takes to 11.1% but doing that damage to 9 blobs. + for(var/obj/structure/blob/C in range(1, B)) + if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && (C.overmind == B.overmind) ) //if it doesn't have the same 'ownership' or is a core or node, don't split damage to it + blobs_to_hurt += C + + for(var/thing in blobs_to_hurt) + var/obj/structure/blob/C = thing + if(C == B) + continue // We'll damage this later. + + C.adjust_integrity(-(damage / blobs_to_hurt.len)) + + return damage / max(blobs_to_hurt.len, 1) // To hurt the blob that got hit. + +/datum/blob_type/synchronous_mesh/on_chunk_tick(obj/item/weapon/blobcore_chunk/B) + var/mob/living/carrier = B.get_carrier() + + if(!carrier) + return + + var/list/nearby_mobs = list() + for(var/mob/living/L in oview(world.view, carrier)) + if(L.stat != DEAD) + nearby_mobs |= L + + if(nearby_mobs.len) + for(var/mob/living/victim in nearby_mobs) + var/need_beam = FALSE + + if(carrier.getBruteLoss()) + need_beam = TRUE + victim.adjustBruteLoss(3 / nearby_mobs.len) + carrier.adjustBruteLoss(-3 / nearby_mobs.len) + + if(carrier.getFireLoss()) + need_beam = TRUE + victim.adjustFireLoss(3 / nearby_mobs.len) + carrier.adjustFireLoss(-3 / nearby_mobs.len) + + if(need_beam) + carrier.visible_message("\icon [B] \The [B] sends noxious spores toward \the [victim]!") + carrier.Beam(victim, icon_state = "lichbeam", time = 2 SECONDS) \ No newline at end of file diff --git a/code/modules/blob2/overmind/types/volatile_alluvium.dm b/code/modules/blob2/overmind/types/volatile_alluvium.dm new file mode 100644 index 0000000000..a689bb856f --- /dev/null +++ b/code/modules/blob2/overmind/types/volatile_alluvium.dm @@ -0,0 +1,55 @@ +// A blob that steals your weapon. +/datum/blob_type/volatile_alluvium + name = "volatile alluvium" + desc = "A churning, earthy mass that moves in waves." + ai_desc = "earthen" + effect_desc = "Moves slowly, producing weak ranged spores to defend itself, and inflicts brute attacks. Attempts to disarm nearby attackers. Weak to water." + difficulty = BLOB_DIFFICULTY_HARD //Slow-starting, but can be overwhelming if left alone. + color = "#6B481E" + complementary_color = "#7F471F" + damage_lower = 10 + damage_upper = 20 + armor_pen = 40 + brute_multiplier = 0.7 + burn_multiplier = 0.5 + spread_modifier = 0.5 + ai_aggressiveness = 50 + attack_message = "The alluvium crashes against you" + attack_verb = "crashes against" + can_build_factories = TRUE + can_build_resources = TRUE + spore_type = /mob/living/simple_mob/blob/spore/weak + ranged_spores = TRUE + spore_range = 3 + spore_projectile = /obj/item/projectile/energy/blob/splattering + spore_accuracy = 15 + spore_dispersion = 45 + factory_type = /obj/structure/blob/factory/sluggish + resource_type = /obj/structure/blob/resource/sluggish + chunk_active_ability_cooldown = 2 MINUTES + +/datum/blob_type/volatile_alluvium/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) + if(damage > 0 && attacker && get_dist(B, attacker) <= 2 && prob(min(damage, 70)) && istype(attacker, /mob/living/carbon/human)) // Melee weapons of any type carried by a human will have a high chance of being stolen. + var/mob/living/carbon/human/H = attacker + var/obj/item/I = H.get_active_hand() + H.drop_item() + if(I) + if((I.sharp || I.edge) && !istype(I, /obj/item/weapon/gun)) + I.forceMove(get_turf(B)) // Disarmed entirely. + B.visible_message("The [name] heaves, \the [attacker]'s weapon becoming stuck in the churning mass!") + else + I.throw_at(B, 2, 4) // Just yoinked. + B.visible_message("The [name] heaves, pulling \the [attacker]'s weapon from their hands!") + B.blob_attack_animation(attacker, B.overmind) + return ..() + +/datum/blob_type/volatile_alluvium/on_water(obj/structure/blob/B, amount) + spawn(1) + var/damage = amount * 4 + B.adjust_integrity(-(damage)) + if(B && prob(damage)) + B.visible_message("The [name] begins to crumble!") + +/datum/blob_type/volatile_alluvium/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user) + if(user) + user.add_modifier(/datum/modifier/fortify, 60 SECONDS) diff --git a/code/modules/busy_space_yw/air_traffic.dm b/code/modules/busy_space_yw/air_traffic.dm index f7b5a5aab2..360e186cba 100644 --- a/code/modules/busy_space_yw/air_traffic.dm +++ b/code/modules/busy_space_yw/air_traffic.dm @@ -6,7 +6,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller var/delay_min = 30 MINUTES //How long between ATC traffic, min. Increased to 30 for YW due to round length. var/delay_max = 60 MINUTES //How long between ATC traffic, max. Increased to 60 for YW due to round length. var/backoff_delay = 5 MINUTES //How long to back off if we can't talk and want to. Default is 5 mins. - var/next_message //When the next message should happen in world.time + var/next_message = 30 MINUTES //When the next message should happen in world.time - Making it default to min value var/force_chatter_type //Force a specific type of messages var/squelched = 0 //If ATC is squelched currently @@ -56,9 +56,9 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller squelched = 0 /datum/lore/atc_controller/proc/shift_ending(var/evac = 0) - msg("NAS |Faraday|, this is [using_map.dock_name] Control, you are cleared to complete routine transfer from [using_map.station_name] to [using_map.dock_name].") //YW Edit - Shuttle, tho. + msg("[using_map.shuttle_name], this is [using_map.dock_name] Control, you are cleared to complete routine transfer from [using_map.station_name] to [using_map.dock_name].") sleep(5 SECONDS) - msg("NAS |Faraday| copies, [using_map.dock_name] Control. Departing [using_map.dock_name] for [using_map.station_name] on routine transfer route. Estimated time to arrival: ten minutes.","NAS Faraday") //YW Edit - Shuttle, tho. also flip the locs so it makes sense + msg("[using_map.shuttle_name] copies, [using_map.dock_name] Control. Departing [using_map.dock_name] for [using_map.station_name] on routine transfer route. Estimated time to arrival: ten minutes.","[using_map.shuttle_name]") /datum/lore/atc_controller/proc/random_convo() var/one = pick(loremaster.organizations) //These will pick an index, not an instance @@ -94,7 +94,9 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller var/alt_atc_names = list("[using_map.dock_name] Traffic Control","[using_map.dock_name] TraCon","[using_map.dock_name] System Control","[using_map.dock_name] Star Control","[using_map.dock_name] SysCon","[using_map.dock_name] Tower","[using_map.dock_name] Control","[using_map.dock_name] STC","[using_map.dock_name] StarCon") var/mission_noun = pick(source.flight_types) //pull from a list of owner-specific flight ops, to allow an extra dash of flavor - + if(source.complex_tasks) //if our source has the complex_tasks flag, regenerate with a two-stage assignment + mission_noun = "[pick(source.task_types)] [pick(source.flight_types)]" + //First response is 'yes', second is 'no' var/requests = list( "special flight rules" = list("authorizing special flight rules", "denying special flight rules, not allowed for your traffic class"), @@ -111,7 +113,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller if(force_chatter_type) chatter_type = force_chatter_type else if(law_abiding && !system_defense) //I have to offload this from the chatter_type switch below and do it here, otherwise BYOND throws a shitfit for no discernable reason - chatter_type = pick(5;"emerg",25;"policescan",25;"traveladvisory",30;"dockingrequestgeneric",30;"dockingrequestsupply",30;"dockingrequestrepair",30;"dockingrequestmedical",30;"dockingrequestsecurity",30;"undockingrequest","normal",30;"undockingdenied",30;"undockingdelayed") + chatter_type = pick(5;"emerg",25;"policescan",25;"traveladvisory",30;"dockingrequestgeneric",30;"dockingrequestdenied",30;"dockingrequestdelayed",30;"dockingrequestsupply",30;"dockingrequestrepair",30;"dockingrequestmedical",30;"dockingrequestsecurity",30;"undockingrequest","normal",30;"undockingdenied",30;"undockingdelayed") //the following filters *always* fire their 'unique' event when they're tripped, simply because the conditions behind them are quite rare to begin with else if(name == "Smugglers" && !system_defense2) //just straight up funnel smugglers into always being caught chatter_type = "policeflee" @@ -125,13 +127,13 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller chatter_type = "hostiledetected" //SDF-specific events that need to filter based on the second party (basically just the following SDF-unique list with the soft-result ship scan thrown in) else if(system_defense && law_abiding2 && !system_defense2) //let's see if we can narrow this down, I didn't see many ship-to-ship scans - chatter_type = pick(75;"policeshipscan","sdfpatrolupdate",75;"sdfendingpatrol",30;"dockingrequestgeneric",30;"dockingrequestsupply",30;"dockingrequestrepair",30;"dockingrequestmedical",30;"dockingrequestsecurity",20;"undockingrequest",75;"sdfbeginpatrol","normal") + chatter_type = pick(75;"policeshipscan","sdfpatrolupdate",75;"sdfendingpatrol",30;"dockingrequestgeneric",30;"dockingrequestdelayed",30;"dockingrequestsupply",30;"dockingrequestrepair",30;"dockingrequestmedical",30;"dockingrequestsecurity",20;"undockingrequest",75;"sdfbeginpatrol",50;"normal") //SDF-specific events that don't require the secondary at all, in the event that we manage to roll SDF + hostile/smuggler or something else if(system_defense) - chatter_type = pick("sdfpatrolupdate",60;"sdfendingpatrol",30;"dockingrequestgeneric",30;"dockingrequestsupply",30;"dockingrequestrepair",30;"dockingrequestmedical",30;"dockingrequestsecurity",20;"undockingrequest",80;"sdfbeginpatrol","normal") + chatter_type = pick("sdfpatrolupdate",60;"sdfendingpatrol",30;"dockingrequestgeneric",30;"dockingrequestdelayed",30;"dockingrequestsupply",30;"dockingrequestrepair",30;"dockingrequestmedical",30;"dockingrequestsecurity",20;"undockingrequest",80;"sdfbeginpatrol","normal") //if we somehow don't match any of the other existing filters once we've run through all of them else - chatter_type = pick(5;"emerg",25;"policescan",25;"traveladvisory",30;"pathwarning",30;"dockingrequestgeneric",30;"dockingrequestdenied",30;"dockingrequestsupply",30;"dockingrequestrepair",30;"dockingrequestmedical",30;"dockingrequestsecurity",30;"undockingrequest",30;"undockingdenied",30;"undockingdelayed","normal") + chatter_type = pick(5;"emerg",25;"policescan",25;"traveladvisory",30;"pathwarning",30;"dockingrequestgeneric",30;"dockingrequestdelayed",30;"dockingrequestdenied",30;"dockingrequestsupply",30;"dockingrequestrepair",30;"dockingrequestmedical",30;"dockingrequestsecurity",30;"undockingrequest",30;"undockingdenied",30;"undockingdelayed","normal") var/yes = prob(90) //Chance for them to say yes vs no @@ -153,7 +155,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller //Control scan event: soft outcome if("policescan") var/confirm = pick("Understood","Roger that","Affirmative") - var/complain = pick("I hope this doesn't take too long.","Can we hurry this up?","Make it quick.","This better not take too long.") + var/complain = pick("I hope this doesn't take too long.","Can we hurry this up?","Make it quick.","This better not take too long.","Is this really necessary?") var/completed = pick("You're free to proceed.","Everything looks fine, carry on.","You're clear, move along.","Apologies for the delay, you're clear.","Switch to channel [sdfchannel] and await further instruction.") msg("[combined_first_name], this is [using_map.dock_name] Control, your [pick("ship","vessel","starship")] has been flagged for routine inspection. Hold position and prepare to be scanned.") sleep(5 SECONDS) @@ -166,16 +168,16 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Scan complete. [completed]") //Control scan event: hard outcome if("policeflee") - var/uhoh = pick("No can do chief, we got places to be.","Sorry but we've got places to be.","Not happening.","Ah fuck, who ratted us out this time?!","You'll never take me alive!","Hey, I have a cloaking device! You can't see me!","I'm going to need to ask for a refund on that stealth drive...","I'm afraid I can't do that, Control.") + var/uhoh = pick("No can do chief, we got places to be.","Sorry but we've got places to be.","Not happening.","Ah fuck, who ratted us out this time?!","You'll never take me alive!","Hey, I have a cloaking device! You can't see me!","I'm going to need to ask for a refund on that stealth drive...","I'm afraid I can't do that, Control.","Ah hell.","Fuck!","This isn't the ship you're looking for.","Well. This is awkward.","Uh oh.","I surrender!") msg("Unknown [pick("ship","vessel","starship")], this is [using_map.dock_name] Control, identify yourself and submit to a full inspection. You are in violation of system regulations.") sleep(5 SECONDS) - msg("[uhoh]","??? [shipname]") + msg("[uhoh]","[shipname]") sleep(5 SECONDS) msg("This is [using_map.starsys_name] Defense Control to all local assets: vector to interdict and detain [combined_first_name]. Control out.","[using_map.starsys_name] Defense Control") //SDF scan event: soft outcome if("policeshipscan") var/confirm = pick("Understood","Roger that","Affirmative") - var/complain = pick("I hope this doesn't take too long.","Can we hurry this up?","Make it quick.","This better not take too long.") + var/complain = pick("I hope this doesn't take too long.","Can we hurry this up?","Make it quick.","This better not take too long.","Is this really necessary?") var/completed = pick("You're free to proceed.","Everything looks fine, carry on.","You're clear. Move along.","Apologies for the delay, you're clear.","Switch to channel [sdfchannel] and await further instruction.") msg("[combined_second_name], this is [combined_first_name], your [pick("ship","vessel","starship")] has been flagged for routine inspection. Hold position and prepare to be scanned.","[prefix] [shipname]") sleep(5 SECONDS) @@ -188,14 +190,14 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_second_name], this is [combined_first_name]. Scan complete. [completed]","[prefix] [shipname]") //SDF scan event: hard outcome if("policeshipflee") - var/uhoh = pick("No can do chief, we got places to be.","Sorry but we've got places to be.","Not happening.","Ah fuck, who ratted us out this time?!","You'll never take me alive!","Hey, I have a cloaking device! You can't see me!","I'm going to need to ask for a refund on that stealth drive...","I'm afraid I can't do that, |[shipname]|.") - msg("Unknown [pick("ship","vessel","starship")], this is [combined_first_name], identify yourself and submit to a full inspection. You are in violation of system regulations.","[secondprefix] [secondshipname]") + var/uhoh = pick("No can do chief, we got places to be.","Sorry but we've got places to be.","Not happening.","Ah fuck, who ratted us out this time?!","You'll never take me alive!","Hey, I have a cloaking device! You can't see me!","I'm going to need to ask for a refund on that stealth drive...","I'm afraid I can't do that, |[shipname]|.","Ah hell.","Fuck!","This isn't the ship you're looking for.","Well. This is awkward.","Uh oh.","I surrender!") + msg("Unknown [pick("ship","vessel","starship")], this is [combined_second_name], identify yourself and submit to a full inspection. You are in violation of system regulations.","[secondprefix] [secondshipname]") sleep(5 SECONDS) - msg("[uhoh]","??? [shipname]") + msg("[uhoh]","[shipname]") sleep(5 SECONDS) - msg("[using_map.starsys_name] Defense Control, this is [combined_first_name], we have a situation here, please advise.","[secondprefix] [secondshipname]") + msg("[using_map.starsys_name] Defense Control, this is [combined_second_name], we have a situation here, please advise.","[secondprefix] [secondshipname]") sleep(5 SECONDS) - msg("Defense Control copies, [combined_first_name], reinforcements are en route. Switch further communications to encrypted band [sdfchannel].","[using_map.starsys_name] Defense Control") + msg("Defense Control copies, [combined_second_name], reinforcements are en route. Switch further communications to encrypted band [sdfchannel].","[using_map.starsys_name] Defense Control") //SDF scan event: engage primary in combat! very rare since it needs a pirate/vox + SDF roll if("policeshipcombat") var/battlestatus = pick("requesting reinforcements.","we need backup! Now!","holding steady.","we're holding our own for now.","we have them on the run.","they're trying to make a run for it!","we have them right where we want them.","we're badly outgunned!","we have them outgunned.","we're outnumbered here!","we have them outnumbered.","this'll be a cakewalk.",10;"notify their next of kin.") @@ -204,7 +206,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[using_map.starsys_name] Defense Control copies, [combined_second_name]. Keep us updated.","[using_map.starsys_name] Defense Control") //SDF event: patrol update if("sdfpatrolupdate") - var/statusupdate = pick("nothing unusual so far","nothing to report","everything looks clear so far","ran off some [pick("pirates","marauders")] near route [pick(1,100)], [pick("no","minor")] damage sustained","situation normal, no suspicious activity yet","minor incident on route [pick(1,100)]","Code 7-X on route [pick(1,100)], situation is under control","a lot of traffic on route [pick(1,100)]","caught a couple of smugglers on route [pick(1,100)]") + var/statusupdate = pick("nothing unusual so far","nothing of note","everything looks clear so far","ran off some [pick("pirates","marauders")] near route [pick(1,100)], [pick("no","minor")] damage sustained, continuing patrol","situation normal, no suspicious activity yet","minor incident on route [pick(1,100)]","Code 7-X on route [pick(1,100)], situation is under control","seeing a lot of traffic on route [pick(1,100)]","caught a couple of smugglers on route [pick(1,100)]","sustained some damage in a skirmish just now, we're heading back for repairs") msg("[using_map.starsys_name] Defense Control, this is [combined_first_name] reporting in, [statusupdate], over.","[prefix] [shipname]") sleep(5 SECONDS) msg("[using_map.starsys_name] Defense Control copies, [combined_first_name]. Keep us updated, out.","[using_map.starsys_name] Defense Control") @@ -223,20 +225,22 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("This is [using_map.starsys_name] Defense Control to all SDF assets. Priority update follows.","[using_map.starsys_name] Defense Control") sleep(5 SECONDS) msg("Be on the lookout for [combined_first_name], last sighted near route [rand(1,100)]. [orders]. DefCon, out.","[using_map.starsys_name] Defense Control") - //new event: distress call + //Ship event: distress call, under attack if("distress") msg("+Mayday, mayday, mayday!+ This is [combined_first_name] declaring an emergency! We are under attack by [combined_second_name]! Requesting immediate assistance!","[prefix] [shipname]") sleep(5 SECONDS) msg("[combined_first_name], this is [using_map.starsys_name] Defense Control, copy. SDF is en route, contact on [sdfchannel].") sleep(5 SECONDS) msg("Understood [using_map.starsys_name] Defense Control, switching now.","[prefix] [shipname]") + //Control event: travel advisory if("traveladvisory") var/flightwarning = pick("Solar flare activity is spiking and expected to cause issues along main flight lanes [rand(1,33)], [rand(34,67)], and [rand(68,100)]","Pirate activity is on the rise, stay close to System Defense vessels","We're seeing a rise in illegal salvage operations, please report any unusual activity to the nearest SDF vessel via channel [sdfchannel]","Vox Marauder activity is higher than usual, report any unusual activity to the nearest System Defense vessel","A quarantined [pick("fleet","convoy")] is passing through the system along route [rand(1,100)], please observe minimum safe distance","A prison [pick("fleet","convoy")] is passing through the system along route [rand(1,100)], please observe minimum safe distance","Traffic volume is higher than normal, expect processing delays","Anomalous bluespace activity detected along route [rand(1,100)], exercise caution","Smugglers have been particularly active lately, expect increased security scans","Depots are currently experiencing a fuel shortage, expect delays and higher rates","Asteroid mining has displaced debris dangerously close to main flight lanes on route [rand(1,100)], watch for potential impactors","[pick("Pirate","Vox Marauder")] and System Defense forces are currently engaged in skirmishes throughout the system, please steer clear of any active combat zones","A [pick("fuel tanker","cargo liner","passenger liner","freighter","transport ship")] has collided with a [pick("fuel tanker","cargo liner","passenger liner","freighter","transport ship")] near route [rand(1,100)], watch for debris and do not impede emergency service vessels","A [pick("fuel tanker","cargo liner","passenger liner","freighter","transport ship")] on route [rand(1,100)] has experienced total engine failure. Emergency response teams are en route, please observe minimum safe distances and do not impede emergency service vessels","Transit routes have been recalculated to adjust for planetary drift. Please synch your astronav computers as soon as possible to avoid delays and difficulties","[pick("Bounty hunters","System Defense officers","Mercenaries")] are currently searching for a wanted fugitive, report any sightings of suspicious activity to System Defense via channel [sdfchannel]","Mercenary contractors are currently conducting aggressive [pick("piracy","marauder")] suppression operations",10;"It's space carp breeding season. [pick("Stars","Gods","God","Goddess")] have mercy on you all, because the carp won't") msg("This is [using_map.dock_name] Control to all vessels in the [using_map.starsys_name] system. Priority travel advisory follows.") sleep(5 SECONDS) msg("[flightwarning]. Control out.") + //Control event: warning to a specific vessel if("pathwarning") - var/navhazard = pick ("a pocket of intense radiation","a pocket of unstable gas","a debris field","a secure installation","an active combat zone","a quarantined ship","a quarantined installation","a quarantined sector","a live-fire SDF training exercise","an ongoing Search & Rescue operation") + var/navhazard = pick("a pocket of intense radiation","a pocket of unstable gas","a debris field","a secure installation","an active combat zone","a quarantined ship","a quarantined installation","a quarantined sector","a live-fire SDF training exercise","an ongoing Search & Rescue operation") var/confirm = pick("Understood","Roger that","Affirmative","Our bad","Thanks for the heads up") var/safetravels = pick("Fly safe out there","Good luck","Safe travels","Godspeed","Stars guide you","Don't let it happen again") msg("[combined_first_name], this is [using_map.dock_name] Control, your [pick("ship","vessel","starship")] is approaching [navhazard], observe minimum safe distance and adjust your heading appropriately.") @@ -244,6 +248,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[confirm] [using_map.dock_name] Control, adjusting course.","[prefix] [shipname]") sleep(5 SECONDS) msg("Your compliance is appreciated, [combined_first_name]. [safetravels].") + //Ship event: docking request (generic) if("dockingrequestgeneric") var/appreciation = pick("Much appreciated","Many thanks","Understood","Cheers") var/dockingplan = pick("Starting final approach now.","Commencing docking procedures.","Autopilot engaged.","Approach vector locked in.","In the pipe, five by five.") @@ -252,13 +257,31 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Permission granted, proceed to docking bay [bay]. Follow the green lights on your way in.") sleep(5 SECONDS) msg("[appreciation], [using_map.dock_name] Control. [dockingplan]","[prefix] [shipname]") + //Ship event: docking request (denied) if("dockingrequestdenied") - var/reason = pick("we don't have any free landing pads right now","we don't have any free landing pads large enough for your vessel","we don't have the necessary facilities for your vessel type or class","we can't verify your credentials","you're too far away, please close to ten thousand meters and resubmit your request") + var/reason = pick("we don't have any docking bays large enough for your vessel","we don't have the necessary facilities for your vessel type or class") msg("[callname], this is [combined_first_name], [pick("stopping by","passing through")] on our way to [destname], requesting permission to dock.","[prefix] [shipname]") sleep(5 SECONDS) msg("[combined_first_name], this is [using_map.dock_name] Control. Request denied, [reason].") sleep(5 SECONDS) msg("Understood, [using_map.dock_name] Control.","[prefix] [shipname]") + //Ship event: docking request (delayed) + if("dockingrequestdelayed") + var/reason = pick("we don't have any free docking bays right now","you're too far away, please close to ten thousand meters and resubmit your request","we're seeing heavy traffic around the docking ports right now","we're currently cleaning up a fuel cloud","there are loose containers in the traffic lanes, stand by whilst we secure them") + var/appreciation = pick("Much appreciated","Many thanks","Understood","Perfect, thank you","Excellent, thanks","Great","Copy that") + var/dockingplan = pick("Starting final approach now.","Commencing docking procedures.","Autopilot engaged.","Approach vector locked in.","In the pipe, five by five.") + msg("[callname], this is [combined_first_name], [pick("stopping by","passing through")] on our way to [destname], requesting permission to dock.","[prefix] [shipname]") + sleep(5 SECONDS) + msg("[combined_first_name], this is [using_map.dock_name] Control. Request denied, [reason]. Please resubmit your request in three minutes.") + sleep(5 SECONDS) + msg("Understood, [using_map.dock_name] Control.","[prefix] [shipname]") + sleep(180 SECONDS) + msg("[callname], this is [combined_first_name], resubmitting docking request.","[prefix] [shipname]") + sleep (5 SECONDS) + msg("[combined_first_name], this is [using_map.dock_name] Control. Everything appears to be in order now, permission granted, proceed to docking bay [bay]. Follow the green lights on your way in.") + sleep(5 SECONDS) + msg("[appreciation], [using_map.dock_name] Control. [dockingplan]","[prefix] [shipname]") + //Ship event: docking request (resupply) if("dockingrequestsupply") var/preintensifier = pick(75;"getting ",75;"running ","") //whitespace hack, sometimes they'll add a preintensifier, but not always var/intensifier = pick("very","pretty","critically","extremely","dangerously","desperately","kinda","a little","a bit","rather","sorta") @@ -270,6 +293,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Permission granted, proceed to docking bay [bay]. Follow the green lights on your way in.") sleep(5 SECONDS) msg("[appreciation], [using_map.dock_name] Control. [dockingplan]","[prefix] [shipname]") + //Ship event: docking request (repair/maint) if("dockingrequestrepair") var/damagestate = pick("We've experienced some hull damage","We're suffering minor system malfunctions","We're having some technical issues","We're overdue maintenance","We have several minor space debris impacts","We've got some battle damage here","Our reactor output is fluctuating","We're hearing some weird noises from the [pick("engines","pipes","ducting","HVAC")]","Our artificial gravity generator has failed","Our life support is failing","Our environmental controls are busted","Our water recycling system has shorted out","Our navcomp is freaking out","Our systems are glitching out","We just got caught in a solar flare","We had a close call with an asteroid","We have a minor [pick("fuel","water","oxygen","gas")] leak","We have depressurized compartments","We have a hull breach","Our shield generator is on the fritz","Our RCS is acting up","One of our [pick("hydraulic","pneumatic")] systems has depressurized","Our repair bots are malfunctioning") var/appreciation = pick("Much appreciated","Many thanks","Understood","You're a lifesaver","We owe you one","I owe you one","Perfect, thank you") @@ -279,6 +303,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Permission granted, proceed to docking bay [bay]. Follow the green lights on your way in. Repair crews are standing by, contact them on channel [engchannel].") sleep(5 SECONDS) msg("[appreciation], [using_map.dock_name] Control. [dockingplan]","[prefix] [shipname]") + //Ship event: docking request (medical) if("dockingrequestmedical") var/medicalstate = pick("multiple casualties","several cases of radiation sickness","an unknown virus","an unknown infection","a critically injured VIP","sick refugees","multiple cases of food poisoning","injured passengers","sick passengers","injured engineers","wounded marines","a delicate situation","a pregnant passenger","injured castaways","recovered escape pods","unknown escape pods") var/appreciation = pick("Much appreciated","Many thanks","Understood","You're a lifesaver","We owe you one","I owe you one","Perfect, thank you") @@ -288,6 +313,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Permission granted, proceed to docking bay [bay]. Follow the green lights on your way in. Medtechs are standing by, contact them on channel [medchannel].") sleep(5 SECONDS) msg("[appreciation], [using_map.dock_name] Control. [dockingplan]","[prefix] [shipname]") + //Ship event: docking request (security) if("dockingrequestsecurity") var/species = pick("human","unathi","lizard","tajaran","feline","skrell","akula","promethean","sergal","synthetic","robotic","teshari","avian","vulpkanin","canine","vox","zorren","hybrid","mixed-species","vox","grey","alien") var/securitystate = pick("several [species] convicts","a captured pirate","a wanted criminal","[species] stowaways","incompetent [species] shipjackers","a delicate situation","a disorderly passenger","disorderly [species] passengers","ex-mutineers","a captured vox marauder","captured vox marauders","stolen goods","a container full of confiscated contraband","containers full of confiscated contraband",5;"a very lost shadekin",5;"a raging case of [pick("spiders","crabs")]") //gotta have a little something to lighten the mood now and then @@ -298,6 +324,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Permission granted, proceed to docking bay [bay]. Follow the green lights on your way in. Security teams are standing by, contact them on channel [secchannel].") sleep(5 SECONDS) msg("[appreciation], [using_map.dock_name] Control. [dockingplan]","[prefix] [shipname]") + //Ship event: undocking request if("undockingrequest") var/safetravels = pick("Fly safe out there","Good luck","Safe travels","See you next week","Godspeed","Stars guide you") var/thanks = pick("Appreciated","Thanks","Don't worry about us","We'll be fine","You too","So long") @@ -306,6 +333,7 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Permission granted. Docking clamps released. [safetravels].") sleep(5 SECONDS) msg("[thanks], [using_map.dock_name] Control. This is [combined_first_name] setting course for [destname], out.","[prefix] [shipname]") + //SDF event: starting patrol if("sdfbeginpatrol") var/safetravels = pick("Fly safe out there","Good luck","Good hunting","Safe travels","Godspeed","Stars guide you") var/thanks = pick("Appreciated","Thanks","Don't worry about us","We'll be fine","You too") @@ -314,11 +342,13 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Permission granted. Docking clamps released. [safetravels].") sleep(5 SECONDS) msg("[thanks], [using_map.dock_name] Control. This is [combined_first_name] beginning system patrol, out.","[prefix] [shipname]") + //Ship event: undocking request (denied) if("undockingdenied") var/denialreason = pick("Security is requesting a full cargo inspection","Your ship has been impounded for multiple [pick("security","safety")] violations","Your ship is currently under quarantine lockdown","We have reason to believe there's an issue with your papers","Security personnel are currently searching for a fugitive and have ordered all outbound ships remain grounded until further notice") msg("[callname], this is [combined_first_name], requesting permission to depart from docking bay [bay].","[prefix] [shipname]") sleep(5 SECONDS) msg("Negative [combined_first_name], request denied. [denialreason].") + //Ship event: undocking request (delayed) if("undockingdelayed") var/denialreason = pick("Docking clamp malfunction, please hold","Fuel lines have not been secured","Ground crew are still on the pad","Loose containers are on the pad","Exhaust deflectors are not yet in position, please hold","There's heavy traffic right now, it's not safe for your vessel to launch","Another vessel has aerospace priority at this moment","Port officials are still aboard") var/safetravels = pick("Fly safe out there","Good luck","Safe travels","See you next week","Godspeed","Stars guide you") @@ -332,7 +362,8 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg("[combined_first_name], this is [using_map.dock_name] Control. Everything appears to be in order now, permission granted. Docking clamps released. [safetravels].") sleep(5 SECONDS) msg("[thanks], [using_map.dock_name] Control. This is [combined_first_name] setting course for [destname], out.","[prefix] [shipname]") - if("friendlyencounter") //new rare event for matched rolls, which can occasionally happen + //Matched Corps event: friendly encounter [CURRENTLY DISABLED: INCOMPLETE] + if("friendlyencounter") var/greeting = pick("Hey |[secondshipname]|, long time no see!","Well if it ain't |[secondshipname]|. Fancy seeing you here.") var/ack = pick("No way! |[shipname]|!","Been a while, hasn't it?","Kept you waiting, huh?") var/query = pick("How's business on your end?","Everything going well these days?") diff --git a/code/modules/busy_space_yw/organizations.dm b/code/modules/busy_space_yw/organizations.dm index e806cfa69b..c7d1108ac8 100644 --- a/code/modules/busy_space_yw/organizations.dm +++ b/code/modules/busy_space_yw/organizations.dm @@ -10,6 +10,15 @@ var/motto = "" // A motto/jingle/whatever, if they have one. Currently unused. var/list/ship_prefixes = list() //Some might have more than one! Like NanoTrasen. Value is the mission they perform, e.g. ("ABC" = "mission desc") + var/complex_tasks = FALSE //enables complex task generation + var/list/task_types = list( //special purpose addition for certain groups, but generally unused + "logistics", + "patrol", + "training", + "peacekeeping", + "escort", + "search and rescue" + ) var/list/flight_types = list( //operations and flights - we can override this if we want to remove the military-sounding ones or add our own "flight", "mission", @@ -58,7 +67,20 @@ "Defender", "Ranger", "River", - "Jubilee" + "Jubilee", + "Gumdrop", + "Spider", + "Columbia", + "Eagle", + "Intrepid", + "Odyssey", + "Aquarius", + "Kitty Hawk", + "Antares", + "Falcon", + "Casper", + "Orion", + "Challenger" ) var/list/destination_names = list() //Names of static holdings that the organization's ships visit regularly. @@ -73,16 +95,255 @@ var/i = rand(15, 30) //significantly increased from original values due to the greater length of rounds on YW //known planets and exoplanets, plus fictional ones - var/list/planets = list("Earth", "Luna", "Mars", "Titan", "Europa", "Sif", "Kara", "Rota", "Root", "Toledo, New Ohio", "Meralar", "Adhomai", "Arion", "Arkas", "Orbitar", "Galileo", "Brahe", "Janssen", "Harriot", "Aegir", "Amateru", "Dagon", "Meztli", "Hypatia", "Dulcinea", "Rocinante", "Sancho", "Thestias", "Saffar", "Samh", "Majriti", "Draugr", "Binma", "Kishar", "Anshar", "Nisp", "Elysium", "Sophia, El", "New Kyoto", "Angessa's Pearl", "Oasis", "Saint Columbia", "Love") + var/list/planets = list( + /* real planets */ + "Earth", + "Luna", + "Mars", + "Titan", + "Europa", + /* named exoplanets */ + "Spe", + "Arion", + "Arkas", + "Orbitar", + "Dimidium", + "Galileo", + "Brahe", + "Lipperhey", + "Janssen", + "Harriot", + "Aegir", + "Amateru", + "Dagon", + "Meztli", + "Smertrios", + "Hypatia", + "Quijote", + "Dulcinea", + "Rocinante", + "Sancho", + "Thestias", + "Saffar", + "Samh", + "Majriti", + "Fortitudo", + "Draugr", + "Arber", + "Tassili", + "Madriu", + "Naqaya", + "Bocaprins", + "Yanyan", + "Sissi", + "Tondra", + "Eburonia", + "Drukyul", + "Yvaga", + "Naron", + "Guarani", + "Mastika", + "Bendida", + "Nakanbe", + "Awasis", + "Caleuche", + "Wangshu", + "Melquiades", + "Pipitea", + "Ditso", + "Asye", + "Veles", + "Finlay", + "Onasilos", + "Makropolus", + "Surt", + "Boinayel", + "Eyeke", + "Cayahuanca", + "Hamarik", + "Abol", + "Hiisi", + "Belisama", + "Mintome", + "Neri", + "Toge", + "Iolaus", + "Koyopa", + "Independance", + "Ixbalanque", + "Magor", + "Fold", + "Santamasa", + "Noifasui", + "Kavian", + "Babylonia", + "Bran", + "Alef", + "Lete", + "Chura", + "Wadirum", + "Buru", + "Umbaasaa", + "Vytis", + "Peitruss", + "Trimobe", + "Baiduri", + "Ggantija", + "Cuptor", + "Xolotl", + "Isli", + "Hairu", + "Bagan", + "Laligurans", + "Kereru", + "Equiano", + "Albmi", + "Perwana", + "Pollera", + "Tumearandu", + "Sumajmajta", + "Haik", + "Leklsullun", + "Pirx", + "Viriato", + "Aumatex", + "Negoiu", + "Teberda", + "Dopere", + "Vlasina", + "Viculus", + "Kralomoc", + "Iztok", + "Krotoa", + "Halla", + "Riosar", + "Samagiya", + "Isagel", + "Eiger", + "Ugarit", + "Sazum", + "Maeping", + "Agouto", + "Ramajay", + "Khomsa", + "Gokturk", + "Barajeel", + "Cruinlagh", + "Mulchatria", + "Ibirapita", + "Madalitso", + /* fictional planets */ + "Sif", + "Kara", + "Rota", + "Root", + "Toledo, New Ohio", + "Meralar", + "Adhomai", + "Binma", + "Kishar", + "Anshar", + "Nisp", + "Elysium", + "Sophia, El", + "New Kyoto", + "Angessa's Pearl, Exalt's Light", + "Oasis", + "Love" + ) //existing systems, pruned for duplicates, includes systems that contain suspected or confirmed exoplanets var/list/systems = list( - "Sol", "Alpha Centauri", "Sirius", "Vega", "Tau Ceti", "Altair", "Zhu Que", "Oasis", "Vir", "Gavel", "Ganesha", "Saint Columbia", "Altair", "Sidhe", "New Ohio", "Parvati", "Mahi-Mahi", "Nyx", "New Seoul", "Kess-Gendar", "Raphael", "Phact", "El", "Eutopia", "Qerr'valis", "Harr'Qak", "Qerrna-Lakirr", "Rarkajar", "Vazzend", "Thoth", "Jahan's Post", "Kauq'xum", "Silk", "New Singapore", "Stove", "Viola", "Love", "Isavau's Gamble", "deep space", "Epsilon Eridani", "Fomalhaut", "Mu Arae", "Pollux", "Wolf 359", "Ross 128", "Gliese 1061", "Luyten's Star", "Teegarden's Star", "Kapteyn", "Wolf 1061", "Aldebaran", "Proxima Centauri", "Kepler-90", "HD 10180", "HR 8832", "TRAPPIST-1", "55 Cancri", "Gliese 876", "Upsilon Andromidae", "Mu Arae", "WASP-47", "82 G. Eridani", "Rho Coronae Borealis", "Pi Mensae", "Beta Pictoris", "Gamma Librae", "Gliese 667 C", "Kapteyn", "LHS 1140", "New Ohio", "Samsara", "Vounna", "Relan") + /* real solar systems */ + "Sol", + "Alpha Centauri", + "Sirius", + "Vega", + "Tau Ceti", + "Altair", + "Epsilon Eridani", + "Fomalhaut", + "Mu Arae", + "Pollux", + "Wolf 359", + "Ross 128", + "Gliese 1061", + "Luyten's Star", + "Teegarden's Star", + "Kapteyn", + "Wolf 1061", + "Aldebaran", + "Proxima Centauri", + "Kepler-90", + "HD 10180", + "HR 8832", + "TRAPPIST-1", + "55 Cancri", + "Gliese 876", + "Upsilon Andromidae", + "Mu Arae", + "WASP-47", + "82 G. Eridani", + "Rho Coronae Borealis", + "Pi Mensae", + "Beta Pictoris", + "Gamma Librae", + "Gliese 667 C", + "LHS 1140", + "Phact", + /* fictional systems from Polaris and other sources*/ + "Zhu Que", + "Oasis", + "Vir", + "Gavel", + "Ganesha", + "Sidhe", + "New Ohio", + "Parvati", + "Mahi-Mahi", + "Nyx", + "New Seoul", + "Kess-Gendar", + "Raphael", + "El", + "Eutopia", + /* skrell */ + "Qerr'valis", + "Harr'Qak", + "Qerrna-Lakirr", + "Kauq'xum", + /* tajaran */ + "Rarkajar", + "Arrakthiir", + "Mesomori", + /* other */ + "Vazzend", + "Thoth", + "Jahan's Post", + "Silk", + "New Singapore", + "Stove", + "Viola", + "Love", + "Isavau's Gamble", + "Samsara", + "Vounna", + "Relan", + "Whythe", + "Exalt's Light", + /* generic territories */ + "deep space", + "USG Territory", + "Independent Space", + "Almach Territory", + "Skrell Territories", + "Hegemony Space" + ) var/list/owners = list("a government", "a civilian", "a corporate", "a private", "an independent", "a mercenary", "a military") var/list/purpose = list("an exploration", "a trade", "a research", "a survey", "a military", "a mercenary", "a corporate", "a civilian", "an independent") //unique or special locations - var/list/unique = list("the Jovian subcluster","Isavau International Spaceport","Terminus Station","Casini's Reach","the Shelf flotilla","the Ue'Orsi flotilla","|Heaven| Orbital Complex, Alpha Centauri") + var/list/unique = list("the Jovian subcluster","Isavau International Spaceport","Terminus Station","Casini's Reach","the Shelf flotilla","the Ue'Orsi flotilla","|Heaven| Orbital Complex, Alpha Centauri","the |Saint Columbia| Complex") var/list/orbitals = list("[pick(owners)] shipyard","[pick(owners)] dockyard","[pick(owners)] station","[pick(owners)] vessel","a habitat","[pick(owners)] refinery","[pick(owners)] research facility","an industrial platform","[pick(owners)] installation") var/list/surface = list("a colony","a settlement","a trade outpost","[pick(owners)] supply depot","a fuel depot","[pick(owners)] installation","[pick(owners)] research facility") @@ -197,7 +458,7 @@ headquarters = "Luna, Sol" motto = "" - ship_prefixes = list("HIV" = "a general operations", "HTV" = "a freight", "HLV" = "a munitions resupply", "HDV" = "an asset protection", "HDV" = "a preemptive deployment") + ship_prefixes = list("HCV" = "a general operations", "HTV" = "a freight", "HLV" = "a munitions resupply", "HDV" = "an asset protection", "HDV" = "a preemptive deployment") //War God Theme, updated ship_names = list( "Anhur", @@ -289,7 +550,7 @@ acronym = "VM" desc = "Vey-Med is one of the newer TSCs on the block and is notable for being largely owned and operated by Skrell. \ Despite the suspicion and prejudice leveled at them for their alien origin, Vey-Med has obtained market dominance in \ - the sale of medical equipment-- from surgical tools to large medical devices to the Oddyseus trauma response mecha \ + the sale of medical equipment-- from surgical tools to large medical devices to the Odysseus trauma response mecha \ and everything in between. Their equipment tends to be top-of-the-line, most obviously shown by their incredibly \ human-like FBP designs. Vey's rise to stardom came from their introduction of resurrective cloning, although in \ recent years they've been forced to diversify as their patents expired and NanoTrasen-made medications became \ @@ -335,8 +596,8 @@ short_name = "Zeng-Hu " acronym = "ZH" desc = "Zeng-Hu is an old TSC, based in the Sol system. Until the discovery of Phoron, Zeng-Hu maintained a stranglehold \ - on the market for medications, and many household names are patentted by Zeng-Hu-- Bicaridyne, Dylovene, Tricordrizine, \ - and Dexalin all came from a Zeng-Hu medical laboratory. Zeng-Hu's fortunes have been in decline as Nanotrasen's near monopoly \ + on the market for medications, and many household names are patentted by Zeng-Hu-- Bicaridine, Dylovene, Tricordrazine, \ + and Dexalin all came from Zeng-Hu medical laboratories. Zeng-Hu's fortunes have been in decline as Nanotrasen's near monopoly \ on phoron research cuts into their R&D and Vey-Med's superior medical equipment effectively decimated their own equipment \ interests. The three-way rivalry between these companies for dominance in the medical field is well-known and a matter of \ constant economic speculation. \ @@ -467,12 +728,12 @@ name = "Bishop Cybernetics" short_name = "Bishop " acronym = "BC" - desc = "Bishop's focus is on high-class, stylish cybernetics. A favorite among transhumanists (and a bête noire for \ + desc = "Bishop's focus is on high-class, stylish cybernetics. A favorite among transhumanists (and an easy target for \ bioconservatives), Bishop manufactures not only prostheses but also brain augmentation, synthetic organ replacements, \ and odds and ends like implanted wrist-watches. Their business model tends towards smaller, boutique operations, giving \ it a reputation for high price and luxury, with Bishop cyberware often rivalling Vey-Med's for cost. Bishop's reputation \ for catering towards the interests of human augmentation enthusiasts instead of positronics have earned it ire from the \ - Positronic Rights Group and puts it in ideological (but not economic) comptetition with Morpheus Cyberkinetics. \ + Positronic Rights Group and puts it in ideological (but not economic) competition with Morpheus Cyberkinetics. \

    \ Each vessel in Bishop's sleek and stylish fleet is intended to advertise the corporate style, and bears the name of a famous mechanical engineer." history = "" @@ -973,7 +1234,7 @@ acronym = "SFI" desc = "Founded in 2437 by Astara Junea, StarFlight Incorporated is now one of the biggest passenger liner businesses in human-occupied space and has even begun breaking into alien markets - all despite a rocky start, and several high-profile ship disappearances and shipjackings. With space traffic at an all-time high, it's a depressing reality that SFI's incidents are just a tiny drop in the bucket compared to everything else going on. \

    \ - SFI's fleet is, somewhat endearingly, named after various species of bird, though the designation Pigeon was removed from the lineup after a particularly unusual chain of events involving a business liner." + SFI's fleet is, somewhat endearingly, named after various species of bird, though the designation Pigeon was removed from the lineup after a particularly unusual chain of events involving a business liner. For reasons that have continued to remain unclear since the company's foundation, SFI vessels are permitted to use the same high-level identifier pattern as governmental vessels." history = "" work = "luxury, business, and economy passenger flights" headquarters = "Spin Aerostat, Jupiter" @@ -1027,7 +1288,213 @@ "an arctic retreat" ) -/datum/lore/organization/tsc/independent +/datum/lore/organization/tsc/oculum + name = "Oculum Broadcasting Network" + short_name = "Oculus " + acronym = "OBN" + desc = "Oculum owns approximately 30% of Sol-wide news networks, including microblogging aggregate sites, network and comedy news, and even old-fashioned newspapers. Staunchly apolitical, they specialize in delivering the most popular news available- which means telling people what they already want to hear. Oculum is a specialist in branding, and most people don't know that the reactionary Daedalus Dispatch newsletter and the radically transhuman Liquid Steel webcrawler are controlled by the same organization." + history = "" + work = "news media" + headquarters = "" + motto = "News from all across the spectrum" + + ship_prefixes = list("OBV" = "an investigation", "OBV" = "a distribution", "OBV" = "a journalism", "OBV" = "a general operations") + destination_names = list( + "Oculus HQ" + ) + +/datum/lore/organization/tsc/centauriprovisions + name = "Centauri Provisions" + short_name = "Centauri " + acronym = "ACP" + desc = "Headquartered in Alpha Centauri, Centauri Provisions made a name in the snack-food industry primarily by being the first to focus on colonial holdings. The various brands of Centauri snackfoods are now household names, from SkrellSnaks to Space Mountain Wind to the ubiquitous and edible Bread Tube. Their staying power is legendary, and many spacers have grown up on a mix of their cheap crap and protein shakes." + history = "" + work = "catering, food, drinks" + headquarters = "Alpha Centauri" + motto = "The largest brands of food and drink - most of them are Centauri." + + ship_prefixes = list("CPTV" = "a transport", "CPCV" = "a catering", "CPRV" = "a resupply", "CPV" = "a general operations") + destination_names = list( + "Centauri Provisions HQ", + "a Centauri Provisions depot", + "a Centauri Provisions warehouse" + ) + +/datum/lore/organization/tsc/einstein + name = "Einstein Engines" + short_name = "Einstein " + acronym = "EEN" + desc = "Einstein is an old company that has survived through rampant respecialization. In the age of phoron-powered exotic engines and ubiquitous solar power, Einstein makes its living through the sale of engine designs for power sources it has no access to and emergency fission or hydrocarbon power supplies. Accusations of corporate espionage against research-heavy corporations like NanoTrasen and its chief rival Focal Point are probably unfounded." + history = "" + work = "catering, food, drinks" + headquarters = "" + motto = "Engine designs, emergency generators, and old memories" + + ship_prefixes = list("EETV" = "a transport", "EERV" = "a research", "EEV" = "a general operations") + destination_names = list( + "Einstein HQ" + ) + +/datum/lore/organization/tsc/wulf + name = "Wulf Aeronautics" + short_name = "Wulf Aero " + acronym = "WUFA" + desc = "Wulf Aeronautics is the chief producer of transport and hauling spacecraft. A favorite contractor of the USG and USDF, Wulf manufactures most of their diplomatic and logistics craft, and does a brisk business with most other TSCs. The quiet reliance of the economy on their craft has kept them out of the spotlight and uninvolved in other corporations' back-room dealings." + history = "" + work = "starship construction" + headquarters = "" + motto = "We build it - you fly it" + + ship_prefixes = list("WATV" = "a transport", "WARV" = "a repair", "WAV" = "a general operations") + destination_names = list( + "Wulf Aeronautics HQ", + "a Wulf Aeronautics supply depot", + "a Wulf Aeronautics Shipyard" + ) + +/datum/lore/organization/tsc/gilthari + name = "Gilthari Exports" + short_name = "Gilthari " + acronym = "GEX" + desc = "Gilthari is Sol's premier supplier of luxury goods, specializing in extracting money from the rich and successful. Their largest holdings are in gambling, but they maintain subsidiaries in everything from VR equipment to luxury watches. Their holdings in mass media are a smaller but still important part of their empire. Gilthari is known for treating its positronic employees very well, sparking a number of conspiracy theories. The gorgeous FBP model that Gilthari provides them is a symbol of the corporation's wealth and reach ludicrous prices when available on the black market, with legal ownership of the chassis limited, by contract, to employees." + history = "" + work = "luxury goods" + headquarters = "" + motto = "" + + ship_prefixes = list("GETV" = "a transport", "GECV" = "a luxury catering", "GEV" = "a general operations") + //precious stones + ship_names = list( + "Afghanite", + "Agate", + "Alexandrite", + "Amazonite", + "Amber", + "Amethyst", + "Ametrine", + "Andalusite", + "Aquamarine", + "Azurite", + "Benitoite", + "Beryl", + "Carnelian", + "Chalcedony", + "Chrysoberyl", + "Chrysoprase", + "Citrine", + "Coral", + "Danburite", + "Diamond", + "Emerald", + "Fluorite", + "Garnet", + "Heliodor", + "Iolite", + "Jade", + "Jasper", + "Lapis Lazuli", + "Malachite", + "Moldavite", + "Moonstone", + "Obsidian", + "Onyx", + "Orthoclase", + "Pearl", + "Peridot", + "Quartz", + "Ruby", + "Sapphire", + "Scapolite", + "Selenite", + "Serpentine", + "Sphalerite", + "Sphene", + "Spinel", + "Sunstone", + "Tanzanite", + "Topaz", + "Tourmaline", + "Turquoise", + "Zircon" + ) + destination_names = list( + "Gilthari HQ", + "a GE supply depot", + "a GE warehouse", + "a GE-owned luxury resort" + ) + +/datum/lore/organization/tsc/coyotecorp + name = "Coyote Salvage Corp." + short_name = "Coyote " + acronym = "CSC" + desc = "The threat of Kessler Syndrome ever looms in this age of spaceflight, and it's only thanks to the dedication and hard work of unionized salvage groups like the Coyote Salvage Corporation that the spacelanes are kept clear and free of wrecks and debris. Painted in that distinctive industrial yellow, their fleets of roaming scrappers are contracted throughout civilized space and the frontier alike to clean up space debris. Some may look down on them for handling what would be seen as garbage and discarded scraps, but as far as the CSC is concerned everything would grind to a halt (or more accurately, rapidly expand in a cloud of red-hot scrap metal) without their tender care and watchful eyes.\ +

    \ + Many spacers turn to join the ranks of the Salvage Corps when times are lean, or when they need a quick buck. The work is dangerous and the hours are long, but the benefits are generous and you're paid by what you salvage so if you've an eye for appraising scrap you can turn a good profit. For those who dedicate their lives to the work, they can become kings of the scrapheap and live like royalty. \ +

    \ + CSC Contractors are no strangers to conflict either, often having to deal with claimjumpers and illegal salvage operations - or worse, the vox." + history = "" + work = "salvage and shipbreaking" + headquarters = "N/A" + motto = "one man's trash is another man's treasure" + + ship_prefixes = list("CSV" = "a salvage", "CRV" = "a recovery", "CTV" = "a transport", "CSV" = "a shipbreaking", "CHV" = "a towing") + //mostly-original, maybe some references, and more than a few puns + ship_names = list( + "Road Hog", + "Mine, Not Yours", + "Legal Salvage", + "Barely Legal", + "One Man's Trash", + "Held Together By Tape And Dreams", + "Ventilated Vagrant", + "Half A Wing And A Prayer", + "Scrap King", + "Make Or Break", + "Lead Into Gold", + "Under New Management", + "Pride of Centauri", + "Long Haul", + "Argonaut", + "Desert Nomad", + "Non-Prophet Organization", + "Rest In Pieces", + "Sweep Dreams", + "Home Sweep Home", + "Atomic Broom", + "Ship Broken", + "Rarely Sober", + "Barely Coherent", + "Piece Of Mind", + "War And Pieces", + "Bits 'n' Bobs", + "Home Wrecker", + "T-Wrecks", + "Dust Bunny", + "No Gears No Glory", + "Three Drinks In", + "The Almighty Janitor", + "Wreckless Endangerment", + "Scarab" + ) + //remove a couple types, add the more down-to-earth 'job' to reflect some personality + flight_types = list( + "job", + "op", + "operation", + "assignment", + "contract" + ) + destination_names = list ( + "a frontier scrapyard", + "a trashbelt", + "a local salvage yard", + "a nearby system" + ) + +// Other + +/datum/lore/organization/other/independent name = "Independent Pilots Association" short_name = "" //using the same whitespace hack as USDF acronym = "IPA" @@ -1051,20 +1518,19 @@ destination_names = list() //we have no hqs or facilities of our own //ship names: blank, because we use the universal list -// Other - //SPACE LAW /datum/lore/organization/other/sysdef name = "System Defense Force" short_name = "" //whitespace hack again acronym = "SDF" - desc = "Localized militias are used to secure systems throughout inhabited space, but are especially common on the frontier. By levying and maintaining these local militia forces, governments can use their fleets for more important matters. System Defense Forces tend to be fairly poorly trained and modestly equipped compared to genuine military fleets, but are more than capable of contending with small-time pirates and can generally stall greater threats long enough for reinforcements to arrive. They're also typically responsible for most search-and-rescue operations in their system.\ + desc = "Localized militias are used to secure systems throughout inhabited space, but are especially common on the frontier; by levying and maintaining these militia forces, larger governments can use their primary fleets (like the USG's USDF) for more important matters and smaller ones can give travellers in their space some peace of mind given the ever-present threat of pirates and vox marauders whilst also helping cut down on smuggling (narcotic substances remain as popular in this century as they have throughout the last few millennia). System Defense Forces tend to be fairly poorly trained and modestly equipped compared to genuine military fleets, but are more than capable of contending with equally ramshackle pirate vessels and can generally stall greater threats long enough for reinforcements to arrive. They're also typically responsible for most search-and-rescue operations in their system.\

    \ - SDF ships are traditionally named after various forms of melee weapon; as their founding members tend to be veterans of other SDF services which used this system, this tradition has slowly propagated.\ + SDF ships are traditionally named after various forms of historical weaponry; as their founding members tend to be veterans of other SDF services which used this system, this tradition has slowly propagated.\

    \ Common SDF ship designations include;
    \ SDF = System Defense Fleet
    \ SDV/SDB = System Defense Vessel/Boat
    \ + SAR = Search And Rescue (Emergency Services)
    \ SDT = System Defense Tender (Mobile Refuel & Resupply)
    \ SDJ = Prisoner Transport" history = "" @@ -1072,9 +1538,9 @@ headquarters = "" motto = "Serve, Protect, Survive" sysdef = TRUE //we're the space law, we don't impersonate people and stuff - autogenerate_destination_names = FALSE + autogenerate_destination_names = FALSE //don't add extra destinations to our pool, or else we leave the system which makes no sense - ship_prefixes = list ("SDB" = "a patrol", "SDF" = "a patrol", "SDV" = "a patrol", "SDB" = "an escort", "SDF" = "an escort", "SDV" = "an escort", "SAR" = "a search and rescue", "SDT" = "a logistics", "SDT" = "a resupply", "SDJ" = "a prisoner transport") //b = boat, f = fleet, v = vessel, t = tender + ship_prefixes = list ("SDB" = "a patrol", "SDF" = "a patrol", "SDV" = "a patrol", "SDB" = "an escort", "SDF" = "an escort", "SDV" = "an escort", "SAR" = "a search and rescue", "SDT" = "a logistics", "SDT" = "a resupply", "SDJ" = "a prisoner transport") //b = boat, f = fleet (generic), v = vessel, t = tender //ship names: weapons ship_names = list( "Sword", @@ -1108,10 +1574,12 @@ "Truncheon", "Hammer", "Arbalest", + "Ballista", "Catapult", "Trebuchet", "Longbow", "Pike", + "Javelin", "Glaive", "Halberd", "Scythe", @@ -1144,8 +1612,11 @@ "Waypoint Chi", "Waypoint Psi", "Waypoint Omega", + "System Defense Control", "an SDF correctional facility", "an SDF processing center", + "an SDF supply depot", + "an SDF Rapid Response Hub", "an SDF outpost" ) @@ -1154,12 +1625,16 @@ name = "Smugglers" short_name = "" //whitespace hack again acronym = "ISC" - desc = "Illegal smuggling operations are rife on the frontier, as independent pilots attempt to sneak all manner of illegal goods past system authorities." + desc = "Where there's a market, there need to be merchants, and where there are buyers, there need to be suppliers. Most of all, wherever there's governments, there'll be somebody trying to control what people are and aren't allowed to do with their bodies. For those seeking goods deemed illegal (for good reasons or otherwise) they need to turn to smugglers and the fine art of sneaking things past the authorities.\ +

    \ + The most common goods smuggled throughout space are narcotics, firearms, and occasionally slaves; whilst firearm ownership laws vary from location to location, most governments also take fairly hard stances on hard drugs, and slavery is consistently outlawed and punished viciously throughout the vast majority of civilized space.\ +

    \ + Still, contrary to many conceptions, not all smuggling is nefarious. Entertainment media within human territories loves to paint romantic images of heroic smugglers sneaking aid supplies to refugees or even helping oppressed minorities escape the grasp of xenophobic regimes." history = "" work = "" headquarters = "" - motto = "PROFIT!" - lawful = FALSE //if it wasn't obvious, these guys are actually criminals + motto = "" + lawful = FALSE //if it wasn't obvious, these guys are usually criminals hostile = FALSE //but they're not aggressive ones sysdef = FALSE autogenerate_destination_names = TRUE //the events we get called for don't fire a destination, but we need entries to avoid runtimes. @@ -1327,7 +1802,11 @@ name = "Pirates" short_name = "" //whitespace hack again acronym = "IPG" - desc = "Arr matey!" + desc = "Where there's prey, predators are sure to follow. In space, the prey are civilian merchants, and the predators are opportunistic pirates. This is about where the analogy breaks down, but the basic concept remains the same; civilian ships are usually full of valuable goods or important people, which can be sold on black markets or ransomed back for a healthy sum. Pirates seek to seize the assets of others to get rich, rather than make an honest thaler themselves.\ +

    \ + In contrast to the colorful Ue-Katish and sneaky Vox, common pirates tend to be rough, practical, and brutally efficient in their work. System Defense units practice rapid response drills, and in most systems it's only a matter of minutes before The Law arrives unless the pirate is able to isolate their target and prevent them from sending a distress signal.\ +

    \ + Complicating matters is the infrequent use of privateers by various minor colonial governments, mercenaries turning to piracy during hard times, and illegal salvage operations." history = "" work = "" headquarters = "" @@ -1336,7 +1815,7 @@ hostile = TRUE autogenerate_destination_names = TRUE //the events we get called for don't fire a destination, but we need entries to avoid runtimes. - ship_prefixes = list ("known pirate" = "a piracy", "suspected pirate" = "a piracy", "rogue privateer" = "a piracy", "Cartel enforcer" = "a piracy", "known outlaw" = "a piracy", "bandit" = "a piracy", "roving corsair" = "a piracy") //as assigned by control, second part shouldn't even come up + ship_prefixes = list ("known pirate" = "a piracy", "suspected pirate" = "a piracy", "rogue privateer" = "a piracy", "Cartel enforcer" = "a piracy", "known outlaw" = "a piracy", "bandit" = "a piracy", "roving corsair" = "a piracy", "illegal salvager" = "an illegal salvage", "rogue mercenary" = "a mercenary") //as assigned by control, second part shouldn't even come up ship_names = list( "Morally Bankrupt", "Bucket of Bolts", @@ -1493,12 +1972,47 @@ destination_names = list( ) */ + +/datum/lore/organization/other/uekatish + name = "Ue-Katish Pirates" + short_name = "" + acronym = "UEK" + desc = "Contrasting with the Qerr-Glia is a vibrant community of Ue-Katish pirates, who live in cargo flotillas on the edge of Skrellian space (especially on the Human-Skrell border). Ue-Katish ships have no caste system even for the truecaste Skrell and aliens who live there, although they are regimented by rank and role in the ship's functioning. Ue-Katish ships are floating black markets where everything is available for the right price, including some of the galaxy's most well-connected information brokers and most skilled guns-for-hire. The Ue-Katish present the greatest Skrellian counterculture and feature heavily in romanticized human media, although at their hearts they are still bandits and criminals, and the black markets are filled with goods plundered from human and Skrellian trade ships. Many of the Ue-Katish ships themselves bear the scars of the battle that brought them under the pirate flag.\ +

    \ + Ue-Katish pirate culture is somewhat similar to many human countercultures, gleefully reclaiming slurs and subverting expectations for the sheer shock value. Nonetheless, Ue-Katish are still Skrell, and still organize in neat hierarchies under each ship's Captain. The Captain's word is absolute, and unlike the Qerr-Katish they lack any sort of anti-corruption institutions." + history = "" + work = "" + motto = "" + lawful = FALSE + hostile = TRUE + autogenerate_destination_names = TRUE + + ship_prefixes = list("Ue-Katish pirate" = "a raiding", "Ue-Katish bandit" = "a raiding", "Ue-Katish raider" = "a raiding", "Ue-Katish enforcer" = "an enforcement") + ship_names = list( + "Keqxuer'xeu's Prize", + "Xaeker'qux' Bounty", + "Teq'ker'qerr's Mercy", + "Ke'teq's Thunder", + "Xumxerr's Compass", + "Xue'qux' Greed", + "Xaexuer's Slave", + "Xue'taq's Dagger", + "Teqxae's Madness", + "Taeqtaq'kea's Pride", + "Keqxae'xeu's Saber", + "Xueaeq's Disgrace", + "Xum'taq'qux' Star", + "Ke'xae'xe's Scream", + "Keq'keax' Blade" + ) /datum/lore/organization/other/marauders - name = "Marauders" + name = "Vox Marauders" short_name = "" //whitespace hack again acronym = "VOX" - desc = "Hand over shinies dustlung, yaya!" + desc = "Whilst rarely as directly threatening as 'common' pirates, the phoron-breathing vox nevertheless pose a constant nuisance for shipping; as far as vox are concerned, only vox and vox matters matter, and everyone else is a 'treeless dustlung'. Unlike sometimes over-confident pirates, the vox rarely engage in open combat, preferring to steal and raid through stealth rather than brute force. Vox raiders will only commit to an attack if they're confident that they can quickly overwhelm and subdue their victims, then swiftly get away in the event that any alarms are tripped or reinforcements are called for.\ +

    \ + As Vox ship names are generally impossible for the vast majority of other species to pronounce, System Defense tends to tag marauders with a designation based on the ancient NATO Phonetic Alphabet." history = "Unknown" work = "Looting and raiding" headquarters = "Nowhere" @@ -1507,7 +2021,7 @@ hostile = TRUE autogenerate_destination_names = TRUE //the events we get called for don't fire a destination, but we need *some* entries to avoid runtimes. - ship_prefixes = list ("vox marauder" = "a marauding", "vox raider" = "a raiding", "vox ravager" = "a raiding", "vox corsair" = "a raiding") //as assigned by control, second part shouldn't even come up + ship_prefixes = list("vox marauder" = "a marauding", "vox raider" = "a raiding", "vox ravager" = "a raiding", "vox corsair" = "a raiding") //as assigned by control, second part shouldn't even come up //blank out our shipnames for redesignation ship_names = list( ) @@ -1659,122 +2173,69 @@ "a classified location" ) // autogen will add a lot of other places as well. + +/datum/lore/organization/gov/almachi + name = "Almach Association" + short_name = "Almachi " + acronym = "ALM" + desc = "The Almach Association, or Almach, is a Mercurial governmental entity with major population centers in Shelf, Angessa's Pearl, Vounna, and Relan, and extending throughout the nearby region.\ +

    \ + Almach is primarily a military union designed to protect the constituent systems from an attack by SolGov, although some argue that it gives large constituents an unacceptable level of influence over their neighbors. Almach also conducts shared trade and diplomacy missions, but differs from a confederacy by its lack of legislative powers. All contributions to the Association are voluntary. Most constituent systems are organized as direct democracies, although it also counts workers' unions, republics and even theocracies among its members.\ +

    \ + Almach's military forces are given broad autonomy to respond to unfolding situations as they will, with a heavily decentralized strategic model unique to the Mercurial government." + history = "" + work = "" + headquarters = "Shelf, Angessa's Pearl, Vounna, and Relan" + motto = "" + autogenerate_destination_names = TRUE -/* Space foxes: deleted. Keeping them commented for historical reasons. -YW -/datum/lore/organization/gov/kitsuhana - name = "Kitsuhana Heavy Industries" - short_name = "Kitsuhana" - desc = "A large post-scarcity amalgamation of races, Kitsuhana is no longer a company but rather a loose association of 'members' \ - who only share the KHI name and their ideals in common. Kitsuhana accepts interviews to join their ranks, and though they have no \ - formal structure with regards to government or law, the concept of 'consent' drives most of the large decision making. Kitsuhanans \ - pride themselves on their ability to avoid consequence, essentially preferring to live care-free lives. Their post-scarcity allows \ - them to rebuild, regrow, and replenish almost any lost asset or resource nearly instantly. It leads to many of the Kitsuhana \ - 'members' treating everything with frivolity and lends them a care-free demeanor." - history = "Originally a heavy industrial equipment and space mining company. During a forced evacuation of their homeworld, \ - they were they only organization with enough ship capacity to relocate any significant portion of the population, starting with \ - their own employees. After the resulting slowship travel to nearby starsystems, most of the population decided to keep the moniker \ - of the company name. Over the years, Kitsuhana developed into a post-scarcity anarchy where virtually nothing has consequences and \ - Kitsuhana 'members' can live their lives as they see fit, often in isolation." - work = "utopian anarchy" - headquarters = "Kitsuhana Prime" - motto = "Do what you want. We know we will." - - //Culture ship names! - ship_prefixes = list("KHI" = "personal") //Everybody's out for themselves, yanno. - ship_names = list("Nervous Energy", - "Prosthetic Conscience", - "Revisionist", - "Trade Surplus", - "Flexible Demeanour", - "Just Read The Instructions", - "Limiting Factor", - "Cargo Cult", - "Gunboat Diplomat", - "A Ship With A View", - "Cantankerous", - "I Thought He Was With You", - "Never Talk To Strangers", - "Sacrificial Victim", - "Unwitting Accomplice", - "Bad For Business", - "Just Testing", - "Size Isn't Everything", - "Yawning Angel", - "Liveware Problem", - "Very Little Gravitas Indeed", - "Zero Gravitas", - "Gravitas Free Zone", - "Absolutely No You-Know-What") - destination_names = list("Kitsuhana Prime", - "Kitsuhana Beta", - "Kitsuhana Gamma", - "the Kitsuhana Forge", - "a Kitsuhanan's home", - "a Kitsuhana ringworld in Pleis Ceti V", - "a Kitsuhana ringworld in Lund VI", - "a Kitsuhana ringworld in Dais IX", - "a Kitsuhana ringworld in Leibert II-b") -*/ + ship_prefixes = list("ALM-A" = "an administrative", "ALM-T" = "a transportation", "ALM-D" = "a diplomatic", "ALM-F" = "a freight", "ALM-J" = "a prisoner transfer") + //TODO: better ship names + /* + ship_names = list( + "", + ) + */ + destination_names = list( + "an Association member colony", + "the Xia subcluster", + "the Matigab subcluster", + "the Zegev subcluster", + "|Prometheus| Station, Vounna", + "an Association embassy", + "a classified location" + ) /* setup for tajaran + other xeno groups, but allakai never came forth with useful resources for me. leaving my framework in place for later updating. -/datum/lore/organization/gov/tajara_rakar - name = "Rakar Empire" - short_name = "Rakar " - acronym = "RKE" - desc = "" +/datum/lore/organization/gov/alarjir_iniye + name = "Alarjir Iniye" + short_name = "Coalition " + acronym = "ALI" + desc = "The Alarjir Iniye, or Pearlshield Coalition, is a tenuous alliance of several tajaran nations and other groups who seek to solidify their people's hold on the few territories they presently hold. Whilst externally fairly well-organzed, the Coalition is internally a hotbed of intrigue and competition." history = "" work = "" - headquarters = "" + headquarters = "Rarkajar" motto = "" autogenerate_destination_names = FALSE //big list of own holdings to come - ship_prefixes = list("RKE-D" = "a diplomatic", "RKE-M" = "a peacekeeping") + ship_prefixes = list("ALI-A" = "an administrative", "ALI-D" = "a diplomatic", "ALI-M" = "a peacekeeping", "ALI-F" = "a transportation", "ALI-J" = "a prisoner transfer", "ALI-R" = "a research") //TODO: better ship names ship_names = list( "", ) destination_names = list( - "", - ) - -/datum/lore/organization/gov/tajara_selem - name = "Selem Hegemony" - short_name = "Selem " - acronym = "TSH" - desc = "" - history = "" - work = "" - headquarters = "" - motto = "" - autogenerate_destination_names = FALSE //big list of own holdings to come - - ship_prefixes = list("TSH-D" = "a diplomatic", "TSH-M" = "a peacekeeping") - //TODO: better ship names - ship_names = list( - "", - ) - destination_names = list( - "", - ) - -/datum/lore/organization/gov/tajara_najrir - name = "Najrir Republic" - short_name = "Republic " - acronym = "TNR" - desc = "" - history = "" - work = "" - headquarters = "" - motto = "" - autogenerate_destination_names = FALSE //big list of own holdings to come - - ship_prefixes = list("TNR-D" = "a diplomatic", "TNR-M" = "a peacekeeping") - //TODO: better ship names - ship_names = list( - "", - ) - destination_names = list( - "", + "Rarkajar, Meralar", + "a Coalition outpost in Meralar", + "a Coalition shipyard", + "a Coalition supply depot", + "a Coalition embassy", + "a Coalition research base", + "Alar-Selna, Arrakthiir", + "a minor colony in Arrakthiir", + "a Coalition outpost in Arrakthiir", + "a tajaran colony in Mesomori", + "a Coalition outpost in Mesomori", + "a mining base in Mesomori" ) /datum/lore/organization/gov/unathi @@ -1788,13 +2249,20 @@ motto = "" autogenerate_destination_names = FALSE //big list of own holdings to come - ship_prefixes = list("MGH-D" = "a diplomatic", "MGH-U" = "a Unity-mandated", "MGH-M" = "a peacekeeping") + ship_prefixes = list("MGH-D" = "a diplomatic", "MGH-U" = "a Unity-mandated", "MGH-M" = "a peacekeeping", "MGH-F" = "a transportation", "MGH-J" = "a prisoner transfer", "MGH-R" = "a Redemptionist") //TODO: better ship names ship_names = list( "", ) destination_names = list( - "", + "Moghes, Uueoa-Esa", + "Kharet, Uueoa-Esa", + "a gas-mining facility over Yeora", + "a mining outpost in the Yoos belt", + "a Hegemony resuply depot", + "a Hegemony shipyard", + "a Hegemony embassy", + "a Hegemony outpost near Abel's Rest" ) */ @@ -1855,10 +2323,17 @@ history = "" work = "peacekeeping and piracy suppression" headquarters = "Paris, Earth" - motto = "Si Vis Pacem Para Bellum" + motto = "Si Vis Pacem Para Bellum" //if you wish for peace, prepare for war autogenerate_destination_names = TRUE ship_prefixes = list ("USDF" = "a logistical", "USDF" = "a training", "USDF" = "a patrol", "USDF" = "a piracy suppression", "USDF" = "a peacekeeping", "USDF" = "a relief", "USDF" = "an escort", "USDF" = "a search and rescue", "USDF" = "a classified") + flight_types = list( + "mission", + "operation", + "exercise", + "assignment", + "deployment" + ) ship_names = list( "Aphrodite", "Apollo", @@ -1929,12 +2404,65 @@ ) destination_names = list( "USDF HQ", - "a USDF staging facility on the edge of SolGov territory", - "a USDF resupply depot", + "a USDF staging facility on the edge of USG territory", + "a USDF supply depot", + "a USDF rally point", + "a USDF forward base", + "a USDF repair facility", "a USDF shipyard in Sol", "a classified location" ) +/datum/lore/organization/mil/almachivanguard + name = "Almach Revolutionary Vanguard" + short_name = "Vanguard " + acronym = "ARV" + desc = "The Association Fleet is a metaphorical knife to be aimed at SolGov at all times. Contributed to and commanded by constituent interests, the elements of the Fleet vary wildly in composition, construction, and doctrine. Generally, the Association has adopted more unconventional tactics in their standing doctrine than the USDF, a necessity given the minescule size of their fleet and population compared to the latter. While sometimes deployed to protect trade convoys from the heavy Vox presence in the uninhabited region between the Rim and the outer Skrell colonies, the Fleet's primary purpose is discouraging a direct attack from any who might covet the Association's holdings." + history = "" + work = "Almach Association Security" + headquarters = "Classified" + motto = "" + autogenerate_destination_names = TRUE + complex_tasks = TRUE + + ship_prefixes = list("AFV" = "a formalist", "ATV" = "a telops", "AAV" = "an ascendancy") + flight_types = list( + "mission", + "operation", + "exercise", + "assignment", + "deployment" + ) + ship_names = list( + "Bulwark of the Free", + "Dawnstar", + "Fiery Justice", + "Freedom", + "Gauntlet", + "Hero of the Revolution", + "Liberty", + "Memory of the Fallen", + "People's Fist", + "Pride of Liberty", + "Star of Tiamat", + "Torch of Freedom", + "Vanguard's Defiance", + "Transhumanist", + "Beacon of Progress" + ) + destination_names = list( + "a Vanguard resupply hub", + "a Vanguard staging facility on the edge of Association territory", + "an Association colony in need of assistance", + "a Vanguard supply depot", + "a Vanguard taskforce", + "a Vanguard rally point", + "a Vanguard forward base", + "a Vanguard vac-dock", + "a Vanguard repair facility", + "a classified location" + ) + /datum/lore/organization/mil/pcrc name = "Proxima Centauri Risk Control" short_name = "Proxima Centauri " @@ -2078,9 +2606,9 @@ name = "Stealth Assault Enterprises" short_name = "" acronym = "SAARE" - desc = "SAARE have consistently the worst reputation of any TSC. This is because they are a paramilitary group specializing in deniability and secrecy. Although publically they work in asset recovery, they have a substantiated reputation for info-theft and piracy that has lead to them butting heads with the law on more than one occasion. Nonetheless, they are an invaluable part of the Solar economy, and other TSCs and small colonial governments keep them in business.\ + desc = "SAARE consistently have the worst reputation of any paramilitary group. This is because they specialize in deniability and secrecy. Although publically they work in asset recovery, they have a substantiated reputation for info-theft and piracy that has lead to them butting heads with the law on more than one occasion. Nonetheless, they are an invaluable part of the Solar economy, and other TSCs and small colonial governments keep them in business.\

    \ - For the purposes of plausible deniability, SAARE designates their ships using a series of rotating identifiers that tend to only be used for a single operation." + For the purposes of plausible deniability, SAARE designates their ships using a series of rotating identifiers, with ships on a specific operation or in a particular area all using the same initial designation (e.g. 'Sledgehammer') and having a different numerical identifier, with the most important ships involved bearing a unique additional codename (such as 'Actual' for Command \& Control ships). As ships are shuffled in and out of operating areas, it can be difficult to pin down exactly which ship in SAARE's fleet was responsible for which act. SAARE's misdirection is multilayered, including elements such as extensive use of repainting, false IFFs, bribes, forged documents, intimidation, camouflage, and all manner of other underhanded tactics." history = "" work = "mercenary contractors" headquarters = "" @@ -2145,7 +2673,12 @@ "Maverick", "Nighthawk", "Redshift", - "Challenger" + "Challenger", + "Starlight", + "Sunray", + "Ironside", + "Holdfast", + "Foxhound" ) var/list/numbersone = list( "Zero", @@ -2172,8 +2705,8 @@ "Niner" ) while(i) - ship_names.Add("[pick(letters)] [pick(numbersone)]-[pick(numberstwo)]") + ship_names.Add("[pick(letters)] [pick(40;"Actual","[pick(numbersone)]-[pick(numberstwo)]")]") i-- - //ex: "Phalanx One-Niner", "Kodiak Seven-Four", "Tomahawk Two-Zero" + //ex: "Phalanx One-Niner", "Sledgehammer Actual" (CO/VIP), "Kodiak Seven-Four", "Tomahawk Two-Zero" //probably a more elegant (read: fancier) way to do the second part but fuck it, this works just fine diff --git a/code/modules/catalogue/catalogue_data_vr.dm b/code/modules/catalogue/catalogue_data_vr.dm index 0c97e9ce00..4a117f018c 100644 --- a/code/modules/catalogue/catalogue_data_vr.dm +++ b/code/modules/catalogue/catalogue_data_vr.dm @@ -40,26 +40,11 @@ desc = "" value = CATALOGUER_REWARD_TRIVIAL -/datum/category_item/catalogue/fauna/xenohybrid - name = "Sapients - Xenomorph Hybrid" - desc = "" - value = CATALOGUER_REWARD_TRIVIAL - /datum/category_item/catalogue/fauna/zorren name = "Sapients - Zorren" desc = "" value = CATALOGUER_REWARD_TRIVIAL -/datum/category_item/catalogue/fauna/highzorren - name = "Sapients - Highlander Zorren" - desc = "" - value = CATALOGUER_REWARD_TRIVIAL - -/datum/category_item/catalogue/fauna/flatzorren - name = "Sapients - Flatlander Zorren" - desc = "" - value = CATALOGUER_REWARD_TRIVIAL - /datum/category_item/catalogue/fauna/shadekin name = "Sapients - Shadekin" desc = "" diff --git a/code/modules/catalogue/catalogue_data_yw.dm b/code/modules/catalogue/catalogue_data_yw.dm new file mode 100644 index 0000000000..569314f7b4 --- /dev/null +++ b/code/modules/catalogue/catalogue_data_yw.dm @@ -0,0 +1,34 @@ +/datum/category_item/catalogue/fauna/grey + name = "Sapients - Grey" + desc = "The Grey are a species of genderless grey skinned aliens.\ + Greys lack a language of their own, communicating with their kin telepathically. Though their formal names used through\ + telepathic communication lack a written counterpart, many Grey adopt written names for the purposes of interacting with\ + other species or record keeping. Due to their lack of a spoken language, however, these names do not normally follow a\ + scheme or pattern, varying greatly from individual to individual.\ +

    \ + It's believed that Greys have originated\ + outside of the milky way, on another galaxy. From observing the anatomy of various Greys, xenobiologists have come to the\ + conclusion that Greys likely did not go through a natural process of evolution, but were instead engineered,\ + likely by another master species, in a similar fashion to the Vox Primalis commonly found aboard Nanotrasen stations.\ + The true origin of the species appears to be unknown to even the Greys themselves, who are believed to have arrived from\ + a distant galaxy cluster after a long period of extended cryosleep. Due to these peculiar circumstances many\ + Grey have elected that Mauna-b, which has a high population of Greys, act as a stand in for their home planet.\ +

    \ + Grey physiology is unique in several ways. While they have similar respiratory and vascular systems to humans - breathing\ + oxygen and expelling carbon dioxide - it is important to note that instead of producing hemoglobin to transport oxygen,\ + Greys produce hemerythrin, which causes their blood to take on a violet colour when oxygenated. Hemerythrin is\ + unfortunately much less effective at distributing oxygen throughout the bloodstream, which lead to issues of\ + hypoxemia in the Grey population aboard non-Grey facilities.Furthermore, due to high salt concentrations,\ + the cells in a Grey's body are known rapidly undergo cytolysis when brought into contact with water - a reaction\ + which can prove immediately fatal.\ + Grey eyes are almost triple the size of those of most other species, and their sclera, iris, and pupil are completely\ + indistinct from one another. The large size of Grey eyes is believed to be an adaptation to shield the ocular nerve\ + against ultraviolet radiation with their unusually monotone complexion providing a similar degree of protection.\ + This has lead scholars to believe that in addition to likely being low gravity, the original homeworld of the Greys\ + likely had a poor magnetosphere or was unusually close to its star.\ + the Grey population appears to be wholly sustained via a form of advanced cloning; due to this abnormality Grey\ + family structure typically consists of a single parent. Greys are often cited as being highly-reserved and independent,\ + and appear to be able to go with little to no social interaction for extended periods of time without suffering any\ + ill-effects. Though Greys are not sexually dimorphic, they often adopt male or female accoutrements for the sake of\ + clarifying communications with other species, or even make gene-mods in some cases." + value = CATALOGUER_REWARD_TRIVIAL diff --git a/code/modules/catalogue/cataloguer.dm b/code/modules/catalogue/cataloguer.dm index f1c5756443..e4e148c230 100644 --- a/code/modules/catalogue/cataloguer.dm +++ b/code/modules/catalogue/cataloguer.dm @@ -25,6 +25,7 @@ GLOBAL_LIST_EMPTY(all_cataloguers) w_class = ITEMSIZE_NORMAL origin_tech = list(TECH_MATERIAL = 2, TECH_DATA = 3, TECH_MAGNET = 3) force = 0 + slot_flags = SLOT_BELT var/points_stored = 0 // Amount of 'exploration points' this device holds. var/scan_range = 3 // How many tiles away it can scan. Changing this also changes the box size. var/credit_sharing_range = 14 // If another person is within this radius, they will also be credited with a successful scan. diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm new file mode 100644 index 0000000000..ef64cd4acd --- /dev/null +++ b/code/modules/client/asset_cache.dm @@ -0,0 +1,272 @@ +/* +Asset cache quick users guide: + +Make a datum at the bottom of this file with your assets for your thing. +The simple subsystem will most like be of use for most cases. +Then call get_asset_datum() with the type of the datum you created and store the return +Then call .send(client) on that stored return value. + +You can set verify to TRUE if you want send() to sleep until the client has the assets. +*/ + + +// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping. +// This is doubled for the first asset, then added per asset after +#define ASSET_CACHE_SEND_TIMEOUT 7 + +//When sending mutiple assets, how many before we give the client a quaint little sending resources message +#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8 + +//When passively preloading assets, how many to send at once? Too high creates noticable lag where as too low can flood the client's cache with "verify" files +#define ASSET_CACHE_PRELOAD_CONCURRENT 3 + +/client + var/list/cache = list() // List of all assets sent to this client by the asset cache. + var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement. + var/list/sending = list() + var/last_asset_job = 0 // Last job done. + +//This proc sends the asset to the client, but only if it needs it. +//This proc blocks(sleeps) unless verify is set to false +/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE) + client = CLIENT_FROM_VAR(client) // Will get client from a mob, or accept a client, or return null + if(!istype(client)) + return 0 + + if(client.cache.Find(asset_name) || client.sending.Find(asset_name)) + return 0 + + client << browse_rsc(SSassets.cache[asset_name], asset_name) + if(!verify) // Can't access the asset cache browser, rip. + client.cache += asset_name + return 1 + + client.sending |= asset_name + var/job = ++client.last_asset_job + + client << browse({" + + "}, "window=asset_cache_browser") + + var/t = 0 + var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT + while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() + sleep(1) // Lock up the caller until this is received. + t++ + + if(client) + client.sending -= asset_name + client.cache |= asset_name + client.completed_asset_jobs -= job + + return 1 + +//This proc blocks(sleeps) unless verify is set to false +/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE) + client = CLIENT_FROM_VAR(client) // Will get client from a mob, or accept a client, or return null + if(!istype(client)) + return 0 + + var/list/unreceived = asset_list - (client.cache + client.sending) + if(!unreceived || !unreceived.len) + return 0 + if(unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT) + to_chat(client, "Sending Resources...") + for(var/asset in unreceived) + if(asset in SSassets.cache) + client << browse_rsc(SSassets.cache[asset], asset) + + if(!verify) // Can't access the asset cache browser, rip. + client.cache += unreceived + return 1 + + client.sending |= unreceived + var/job = ++client.last_asset_job + + client << browse({" + + "}, "window=asset_cache_browser") + + var/t = 0 + var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len + while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() + sleep(1) // Lock up the caller until this is received. + t++ + + if(client) + client.sending -= unreceived + client.cache |= unreceived + client.completed_asset_jobs -= job + + return 1 + +//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start. +//The proc calls procs that sleep for long times. +/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) + var/concurrent_tracker = 1 + for(var/file in files) + if(!client) + break + if(register_asset) + register_asset(file, files[file]) + if(concurrent_tracker >= ASSET_CACHE_PRELOAD_CONCURRENT) + concurrent_tracker = 1 + send_asset(client, file) + else + concurrent_tracker++ + send_asset(client, file, verify = FALSE) + sleep(0) //queuing calls like this too quickly can cause issues in some client versions + +//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. +//if it's an icon or something be careful, you'll have to copy it before further use. +/proc/register_asset(var/asset_name, var/asset) + SSassets.cache[asset_name] = asset + +//These datums are used to populate the asset cache, the proc "register()" does this. + +//all of our asset datums, used for referring to these later +/var/global/list/asset_datums = list() + +//get a assetdatum or make a new one +/proc/get_asset_datum(var/type) + if(!(type in asset_datums)) + return new type() + return asset_datums[type] + +/datum/asset/New() + asset_datums[type] = src + +/datum/asset/proc/register() + return + +/datum/asset/proc/send(client) + return + +//If you don't need anything complicated. +/datum/asset/simple + var/assets = list() + var/verify = FALSE + +/datum/asset/simple/register() + for(var/asset_name in assets) + register_asset(asset_name, assets[asset_name]) +/datum/asset/simple/send(client) + send_asset_list(client,assets,verify) + + +//DEFINITIONS FOR ASSET DATUMS START HERE. +/datum/asset/simple/pda + assets = list( + "pda_atmos.png" = 'icons/pda_icons/pda_atmos.png', + "pda_back.png" = 'icons/pda_icons/pda_back.png', + "pda_bell.png" = 'icons/pda_icons/pda_bell.png', + "pda_blank.png" = 'icons/pda_icons/pda_blank.png', + "pda_boom.png" = 'icons/pda_icons/pda_boom.png', + "pda_bucket.png" = 'icons/pda_icons/pda_bucket.png', + "pda_crate.png" = 'icons/pda_icons/pda_crate.png', + "pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png', + "pda_eject.png" = 'icons/pda_icons/pda_eject.png', + "pda_exit.png" = 'icons/pda_icons/pda_exit.png', + "pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png', + "pda_honk.png" = 'icons/pda_icons/pda_honk.png', + "pda_mail.png" = 'icons/pda_icons/pda_mail.png', + "pda_medical.png" = 'icons/pda_icons/pda_medical.png', + "pda_menu.png" = 'icons/pda_icons/pda_menu.png', + "pda_mule.png" = 'icons/pda_icons/pda_mule.png', + "pda_notes.png" = 'icons/pda_icons/pda_notes.png', + "pda_power.png" = 'icons/pda_icons/pda_power.png', + "pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png', + "pda_reagent.png" = 'icons/pda_icons/pda_reagent.png', + "pda_refresh.png" = 'icons/pda_icons/pda_refresh.png', + "pda_scanner.png" = 'icons/pda_icons/pda_scanner.png', + "pda_signaler.png" = 'icons/pda_icons/pda_signaler.png', + "pda_status.png" = 'icons/pda_icons/pda_status.png' + ) + +/datum/asset/simple/generic + assets = list( + "search.js" = 'html/search.js', + "panels.css" = 'html/panels.css', + "loading.gif" = 'html/images/loading.gif', + "ntlogo.png" = 'html/images/ntlogo.png', + "sglogo.png" = 'html/images/sglogo.png', + "talisman.png" = 'html/images/talisman.png', + "paper_bg.png" = 'html/images/paper_bg.png', + "no_image32.png" = 'html/images/no_image32.png', + "sos_1.png" = 'icons/spideros_icons/sos_1.png', + "sos_2.png" = 'icons/spideros_icons/sos_2.png', + "sos_3.png" = 'icons/spideros_icons/sos_3.png', + "sos_4.png" = 'icons/spideros_icons/sos_4.png', + "sos_5.png" = 'icons/spideros_icons/sos_5.png', + "sos_6.png" = 'icons/spideros_icons/sos_6.png', + "sos_7.png" = 'icons/spideros_icons/sos_7.png', + "sos_8.png" = 'icons/spideros_icons/sos_8.png', + "sos_9.png" = 'icons/spideros_icons/sos_9.png', + "sos_10.png" = 'icons/spideros_icons/sos_10.png', + "sos_11.png" = 'icons/spideros_icons/sos_11.png', + "sos_12.png" = 'icons/spideros_icons/sos_12.png', + "sos_13.png" = 'icons/spideros_icons/sos_13.png', + "sos_14.png" = 'icons/spideros_icons/sos_14.png' + ) + +/datum/asset/simple/changelog + assets = list( + "88x31.png" = 'html/88x31.png', + "bug-minus.png" = 'html/bug-minus.png', + "cross-circle.png" = 'html/cross-circle.png', + "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png', + "image-minus.png" = 'html/image-minus.png', + "image-plus.png" = 'html/image-plus.png', + "map-pencil.png" = 'html/map-pencil.png', + "music-minus.png" = 'html/music-minus.png', + "music-plus.png" = 'html/music-plus.png', + "tick-circle.png" = 'html/tick-circle.png', + "wrench-screwdriver.png" = 'html/wrench-screwdriver.png', + "spell-check.png" = 'html/spell-check.png', + "burn-exclamation.png" = 'html/burn-exclamation.png', + "chevron.png" = 'html/chevron.png', + "chevron-expand.png" = 'html/chevron-expand.png', + "changelog.css" = 'html/changelog.css', + "changelog.js" = 'html/changelog.js', + "changelog.html" = 'html/changelog.html' + ) + +/datum/asset/nanoui + var/list/common = list() + + var/list/common_dirs = list( + "nano/css/", + "nano/images/", + "nano/images/modular_computers/", + "nano/js/" + ) + var/list/uncommon_dirs = list( + "nano/templates/" + ) + +/datum/asset/nanoui/register() + // Crawl the directories to find files. + for(var/path in common_dirs) + var/list/filenames = flist(path) + for(var/filename in filenames) + if(copytext(filename, length(filename)) != "/") // Ignore directories. + if(fexists(path + filename)) + common[filename] = fcopy_rsc(path + filename) + register_asset(filename, common[filename]) + for(var/path in uncommon_dirs) + var/list/filenames = flist(path) + for(var/filename in filenames) + if(copytext(filename, length(filename)) != "/") // Ignore directories. + if(fexists(path + filename)) + register_asset(filename, fcopy_rsc(path + filename)) + +/datum/asset/nanoui/send(client, uncommon) + if(!islist(uncommon)) + uncommon = list(uncommon) + + send_asset_list(client, uncommon) + send_asset_list(client, common) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index d61b6fef9a..2331f5b7a6 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -24,8 +24,12 @@ var/area = null var/time_died_as_mouse = null //when the client last died as a mouse var/datum/tooltip/tooltips = null + var/datum/chatOutput/chatOutput + var/chatOutputLoadedAt var/adminhelped = 0 + var/examine_text_mode = 0 // Just examine text, include usage (description_info), switch to examine panel. + /////////////// //SOUND STUFF// diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index cbc389bcda..7df78ea617 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -27,16 +27,21 @@ return #if defined(TOPIC_DEBUGGING) - world << "[src]'s Topic: [href] destined for [hsrc]." + to_world("[src]'s Topic: [href] destined for [hsrc].") if(href_list["nano_err"]) //nano throwing errors - world << "## NanoUI, Subject [src]: " + html_decode(href_list["nano_err"]) //NANO DEBUG HOOK + to_world("## NanoUI, Subject [src]: " + html_decode(href_list["nano_err")]) //NANO DEBUG HOOK #endif + if(href_list["asset_cache_confirm_arrival"]) + var/job = text2num(href_list["asset_cache_confirm_arrival"]) + completed_asset_jobs += job + return + //search the href for script injection if( findtext(href,"You are no longer able to use this, it's been more then 10 minutes since an admin on IRC has responded to you" + to_chat(usr, "You are no longer able to use this, it's been more then 10 minutes since an admin on IRC has responded to you") return if(mute_irc) - usr << "" + to_chat(usr, "") return send2adminirc(href_list["irc_msg"]) return - - //Logs all hrefs if(config && config.log_hrefs && href_logfile) - href_logfile << "[src] (usr:[usr]) || [hsrc ? "[hsrc] " : ""][href]" + WRITE_LOG(href_logfile, "[src] (usr:[usr]) || [hsrc ? "[hsrc] " : ""][href]") switch(href_list["_src_"]) if("holder") hsrc = holder if("usr") hsrc = mob if("prefs") return prefs.process_link(usr,href_list) if("vars") return view_var_Topic(href,href_list,hsrc) + if("chat") return chatOutput.Topic(href, href_list) + + switch(href_list["action"]) + if("openLink") + src << link(href_list["link"]) ..() //redirect to hsrc.Topic() @@ -106,8 +114,14 @@ del(src) return - to_chat(src, "If the title screen is black, resources are still downloading. Please be patient until the title screen appears.") + chatOutput = new /datum/chatOutput(src) //veechat + chatOutput.send_resources() + spawn() + chatOutput.start() + //Only show this if they are put into a new_player mob. Otherwise, "what title screen?" + if(isnewplayer(src.mob)) + to_chat(src, "If the title screen is black, resources are still downloading. Please be patient until the title screen appears.") GLOB.clients += src GLOB.directory[ckey] = src @@ -137,6 +151,8 @@ to_chat(src, "[custom_event_msg]") to_chat(src, "
    ") + if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them. + to_chat(src, "Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.") if(holder) add_admin_verbs() @@ -153,21 +169,20 @@ log_client_to_db() send_resources() - SSnanoui.send_resources(src) if(!void) void = new() void.MakeGreed() screen += void - if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. + if((prefs.lastchangelog != changelog_hash) && isnewplayer(src.mob)) //bolds the changelog button on the interface so we know there are updates. to_chat(src, "You have unread updates in the changelog.") winset(src, "rpane.changelog", "background-color=#eaeaea;font-style=bold") if(config.aggressive_changelog) src.changes() hook_vr("client_new",list(src)) //VOREStation Code - + if(config.paranoia_logging) var/alert = FALSE //VOREStation Edit start. if(isnum(player_age) && player_age == 0) @@ -298,11 +313,11 @@ //Take action if required if(config.ipr_block_bad_ips && config.ipr_allow_existing) //We allow players of an age, but you don't meet it - to_chat(src,"Sorry, we only allow VPN/Proxy/Tor usage for players who have spent at least [config.ipr_minimum_age] days on the server. If you are unable to use the internet without your VPN/Proxy/Tor, please contact an admin out-of-game to let them know so we can accommodate this.") + to_chat(src, "Sorry, we only allow VPN/Proxy/Tor usage for players who have spent at least [config.ipr_minimum_age] days on the server. If you are unable to use the internet without your VPN/Proxy/Tor, please contact an admin out-of-game to let them know so we can accommodate this.") qdel(src) return 0 else if(config.ipr_block_bad_ips) //We don't allow players of any particular age - to_chat(src,"Sorry, we do not accept connections from users via VPN/Proxy/Tor connections.") + to_chat(src, "Sorry, we do not accept connections from users via VPN/Proxy/Tor connections.") qdel(src) return 0 else @@ -341,6 +356,14 @@ if(inactivity > duration) return inactivity return 0 +//Called when the client performs a drag-and-drop operation. +/client/MouseDrop(start_object,end_object,start_location,end_location,start_control,end_control,params) + if(buildmode && start_control == "mapwindow.map" && start_control == end_control) + build_drag(src,buildmode,start_object,end_object,start_location,end_location,start_control,end_control,params) + else + . = ..() + + // Byond seemingly calls stat, each tick. // Calling things each tick can get expensive real quick. // So we slow this down a little. @@ -357,56 +380,9 @@ //send resources to the client. It's here in its own proc so we can move it around easiliy if need be /client/proc/send_resources() - - getFiles( - 'html/search.js', - 'html/panels.css', - 'html/images/loading.gif', - 'html/images/ntlogo.png', - 'html/images/sglogo.png', - 'html/images/talisman.png', - 'html/images/paper_bg.png', - 'html/images/no_image32.png', - 'icons/pda_icons/pda_atmos.png', - 'icons/pda_icons/pda_back.png', - 'icons/pda_icons/pda_bell.png', - 'icons/pda_icons/pda_blank.png', - 'icons/pda_icons/pda_boom.png', - 'icons/pda_icons/pda_bucket.png', - 'icons/pda_icons/pda_crate.png', - 'icons/pda_icons/pda_cuffs.png', - 'icons/pda_icons/pda_eject.png', - 'icons/pda_icons/pda_exit.png', - 'icons/pda_icons/pda_flashlight.png', - 'icons/pda_icons/pda_honk.png', - 'icons/pda_icons/pda_mail.png', - 'icons/pda_icons/pda_medical.png', - 'icons/pda_icons/pda_menu.png', - 'icons/pda_icons/pda_mule.png', - 'icons/pda_icons/pda_notes.png', - 'icons/pda_icons/pda_power.png', - 'icons/pda_icons/pda_rdoor.png', - 'icons/pda_icons/pda_reagent.png', - 'icons/pda_icons/pda_refresh.png', - 'icons/pda_icons/pda_scanner.png', - 'icons/pda_icons/pda_signaler.png', - 'icons/pda_icons/pda_status.png', - 'icons/spideros_icons/sos_1.png', - 'icons/spideros_icons/sos_2.png', - 'icons/spideros_icons/sos_3.png', - 'icons/spideros_icons/sos_4.png', - 'icons/spideros_icons/sos_5.png', - 'icons/spideros_icons/sos_6.png', - 'icons/spideros_icons/sos_7.png', - 'icons/spideros_icons/sos_8.png', - 'icons/spideros_icons/sos_9.png', - 'icons/spideros_icons/sos_10.png', - 'icons/spideros_icons/sos_11.png', - 'icons/spideros_icons/sos_12.png', - 'icons/spideros_icons/sos_13.png', - 'icons/spideros_icons/sos_14.png' - ) - + spawn (10) //removing this spawn causes all clients to not get verbs. + //Precache the client with all other assets slowly, so as to not block other browse() calls + getFilesSlow(src, SSassets.preload, register_asset = FALSE) mob/proc/MayRespawn() return 0 @@ -442,6 +418,27 @@ client/verb/character_setup() return FALSE return ..() +/client/verb/reload_vchat() + set name = "Reload VChat" + set category = "OOC" + + //Timing + if(src.chatOutputLoadedAt > (world.time - 10 SECONDS)) + alert(src, "You can only try to reload VChat every 10 seconds at most.") + return + + //Log, disable + log_debug("[key_name(src)] reloaded VChat.") + winset(src, null, "outputwindow.htmloutput.is-visible=false;outputwindow.oldoutput.is-visible=false;outputwindow.chatloadlabel.is-visible=true") + + //The hard way + qdel_null(src.chatOutput) + chatOutput = new /datum/chatOutput(src) //veechat + chatOutput.send_resources() + spawn() + chatOutput.start() + + //This is for getipintel.net. //You're welcome to replace this proc with your own that does your own cool stuff. //Just set the client's ip_reputation var and make sure it makes sense with your config settings (higher numbers are worse results) @@ -450,9 +447,9 @@ client/verb/character_setup() var/http[] = world.Export(request) /* Debug - world.log << "Requested this: [request]" + to_world_log("Requested this: [request]") for(var/entry in http) - world.log << "[entry] : [http[entry]]" + to_world_log("[entry] : [http[entry]]") */ if(!http || !islist(http)) //If we couldn't check, the service might be down, fail-safe. diff --git a/code/modules/client/client procs_vr.dm b/code/modules/client/client procs_vr.dm new file mode 100644 index 0000000000..830521f20d --- /dev/null +++ b/code/modules/client/client procs_vr.dm @@ -0,0 +1,100 @@ +//Uses a couple different services +/client/update_ip_reputation() + var/scores[] = list("GII" = ipr_getipintel(), "IPQS" = ipr_ipqualityscore()) + + var/log_output = "IP Reputation [key] from [address]" + var/worst = 0 + + for(var/service in scores) + var/score = scores[service] + if(score > worst) + worst = score + log_output += " - [service] ([num2text(score)])" + + log_admin(log_output) + ip_reputation = worst + return TRUE + +//Service returns a single float in html body +/client/proc/ipr_getipintel() + if(!config.ipr_email) + return -1 + + var/request = "http://check.getipintel.net/check.php?ip=[address]&contact=[config.ipr_email]" + var/http[] = world.Export(request) + + if(!http || !islist(http)) //If we couldn't check, the service might be down, fail-safe. + log_admin("Couldn't connect to getipintel.net to check [address] for [key]") + return -1 + + //429 is rate limit exceeded + if(text2num(http["STATUS"]) == 429) + log_and_message_admins("getipintel.net reports HTTP status 429. IP reputation checking is now disabled. If you see this, let a developer know.") + config.ip_reputation = FALSE + return -1 + + var/content = file2text(http["CONTENT"]) //world.Export actually returns a file object in CONTENT + var/score = text2num(content) + if(isnull(score)) + return -1 + + //Error handling + if(score < 0) + var/fatal = TRUE + var/ipr_error = "getipintel.net IP reputation check error while checking [address] for [key]: " + switch(score) + if(-1) + ipr_error += "No input provided" + if(-2) + fatal = FALSE + ipr_error += "Invalid IP provided" + if(-3) + fatal = FALSE + ipr_error += "Unroutable/private IP (spoofing?)" + if(-4) + fatal = FALSE + ipr_error += "Unable to reach database" + if(-5) + ipr_error += "Our IP is banned or otherwise forbidden" + if(-6) + ipr_error += "Missing contact info" + + log_and_message_admins(ipr_error) + if(fatal) + config.ip_reputation = FALSE + log_and_message_admins("With this error, IP reputation checking is disabled for this shift. Let a developer know.") + return -1 + + //Went fine + else + return score + +//Service returns JSON in html body +/client/proc/ipr_ipqualityscore() + if(!config.ipqualityscore_apikey) + return -1 + + var/request = "http://www.ipqualityscore.com/api/json/ip/[config.ipqualityscore_apikey]/[address]?strictness=1&fast=true&byond_key=[key]" + var/http[] = world.Export(request) + + if(!http || !islist(http)) //If we couldn't check, the service might be down, fail-safe. + log_admin("Couldn't connect to ipqualityscore.com to check [address] for [key]") + return -1 + + var/content = file2text(http["CONTENT"]) //world.Export actually returns a file object in CONTENT + var/response = json_decode(content) + if(isnull(response)) + return -1 + + //Error handling + if(!response["success"]) + log_admin("IPQualityscore.com returned an error while processing [key] from [address]: " + response["message"]) + return -1 + + var/score = 0 + if(response["proxy"]) + score = 100 + else + score = response["fraud_score"] + + return score/100 //To normalize with the 0.0 to 1.0 scores. \ No newline at end of file diff --git a/code/modules/client/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm index 12c3aef2f5..0dfb61c8e5 100644 --- a/code/modules/client/preference_setup/general/01_basic.dm +++ b/code/modules/client/preference_setup/general/01_basic.dm @@ -88,7 +88,7 @@ datum/preferences/proc/set_biological_gender(var/gender) pref.real_name = new_name return TOPIC_REFRESH else - user << "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and ." + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") return TOPIC_NOACTION else if(href_list["random_name"]) @@ -107,7 +107,7 @@ datum/preferences/proc/set_biological_gender(var/gender) pref.nickname = new_nickname return TOPIC_REFRESH else - user << "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and ." + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") return TOPIC_NOACTION else if(href_list["bio_gender"]) diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 0472f6cd46..e0a462db5e 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -40,6 +40,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.preview_icon = null S["bgstate"] >> pref.bgstate S["body_descriptors"] >> pref.body_descriptors + S["Wingdings"] >> pref.wingdings //YWadd /datum/category_item/player_setup_item/general/body/save_character(var/savefile/S) S["species"] << pref.species @@ -70,6 +71,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O S["synth_markings"] << pref.synth_markings S["bgstate"] << pref.bgstate S["body_descriptors"] << pref.body_descriptors + S["Wingdings"] << pref.wingdings //YWadd /datum/category_item/player_setup_item/general/body/sanitize_character(var/savefile/S) if(!pref.species || !(pref.species in GLOB.playable_species)) @@ -125,6 +127,8 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O character.g_synth = pref.g_synth character.b_synth = pref.b_synth character.synth_markings = pref.synth_markings + if(pref.species == "Grey")//YWadd START + character.wingdings = pref.wingdings //YWadd END // Destroy/cyborgize organs and limbs. for(var/name in list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO)) @@ -203,6 +207,10 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O . += "Needs Glasses: [pref.disabilities & NEARSIGHTED ? "Yes" : "No"]
    " . += "Limbs: Adjust Reset
    " . += "Internal Organs: Adjust
    " + //YW Edit ADD + if(pref.species == "Grey") + . += "Speak Wingdings: [pref.wingdings ? "Yes" : "No"]
    " + //YW Edit End //display limbs below var/ind = 0 @@ -398,7 +406,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else return TOPIC_NOACTION - if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) //VOREStation Edit: selectability + if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) //VOREStation Edit: selectability return TOPIC_NOACTION var/prev_species = pref.species @@ -741,7 +749,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O organ = O_STOMACH if("Brain") if(pref.organ_data[BP_HEAD] != "cyborg") - user << "You may only select a cybernetic or synthetic brain if you have a full prosthetic body." + to_chat(user, "You may only select a cybernetic or synthetic brain if you have a full prosthetic body.") return organ = "brain" @@ -807,6 +815,12 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.bgstate = next_in_list(pref.bgstate, pref.bgstate_options) return TOPIC_REFRESH_UPDATE_PREVIEW + //YW Add Start + else if(href_list["wingdings"]) + pref.wingdings = !pref.wingdings + return TOPIC_REFRESH_UPDATE_PREVIEW + //YW Add End + return ..() /datum/category_item/player_setup_item/general/body/proc/reset_limbs() @@ -894,7 +908,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O dat += "You cannot play as this species.
    If you wish to be whitelisted, you can make an application post on the forums.

    " else if(restricted == 2) dat += "You cannot play as this species.
    This species is not available for play as a station race..

    " - if(!restricted || check_rights(R_ADMIN, 0) || current_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE) //VOREStation Edit: selectability + if(!restricted || check_rights(R_ADMIN|R_EVENT, 0) || current_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE) //VOREStation Edit: selectability dat += "\[select\]" dat += "" diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/general/04_equipment.dm index 19159fd69a..36f315b1c2 100644 --- a/code/modules/client/preference_setup/general/04_equipment.dm +++ b/code/modules/client/preference_setup/general/04_equipment.dm @@ -42,7 +42,7 @@ pref.backbag = 1 //Same as above character.backbag = pref.backbag - if(pref.pdachoice > 4 || pref.pdachoice < 1) + if(pref.pdachoice > 5 || pref.pdachoice < 1) pref.pdachoice = 1 character.pdachoice = pref.pdachoice diff --git a/code/modules/client/preference_setup/global/01_ui.dm b/code/modules/client/preference_setup/global/01_ui.dm index 5b1314d05f..ed66138da5 100644 --- a/code/modules/client/preference_setup/global/01_ui.dm +++ b/code/modules/client/preference_setup/global/01_ui.dm @@ -93,4 +93,4 @@ return ..() /datum/category_item/player_setup_item/player_global/ui/proc/can_select_ooc_color(var/mob/user) - return config.allow_admin_ooccolor && check_rights(R_ADMIN, 0, user) + return config.allow_admin_ooccolor && check_rights(R_ADMIN|R_EVENT|R_FUN, 0, user) diff --git a/code/modules/client/preference_setup/global/04_ooc.dm b/code/modules/client/preference_setup/global/04_ooc.dm index 0150554250..e695bee677 100644 --- a/code/modules/client/preference_setup/global/04_ooc.dm +++ b/code/modules/client/preference_setup/global/04_ooc.dm @@ -33,7 +33,7 @@ if(player_to_ignore) player_to_ignore = sanitize(ckey(player_to_ignore)) if(player_to_ignore == user.ckey) - user <<"You can't ignore yourself." + to_chat(user, "You can't ignore yourself.") return TOPIC_REFRESH pref.ignored_players |= player_to_ignore return TOPIC_REFRESH diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm index ac1a203e4b..8fefa8c929 100644 --- a/code/modules/client/preference_setup/global/setting_datums.dm +++ b/code/modules/client/preference_setup/global/setting_datums.dm @@ -98,6 +98,12 @@ var/list/_client_preferences_by_type key = "DIGEST_NOISES" enabled_description = "Noisy" disabled_description = "Silent" + +/datum/client_preference/emote_noises + description = "Emote Noises" //MERP + key = "EMOTE_NOISES" + enabled_description = "Noisy" + disabled_description = "Silent" //VOREStation Add End /datum/client_preference/weather_sounds description ="Weather sounds" @@ -135,11 +141,17 @@ var/list/_client_preferences_by_type enabled_description = "Show" disabled_description = "Hide" -/datum/client_preference/air_pump_noise - description ="Air Pump Ambient Noise" - key = "SOUND_AIRPUMP" - enabled_description = "Audible" - disabled_description = "Silent" +/datum/client_preference/air_pump_noise + description ="Air Pump Ambient Noise" + key = "SOUND_AIRPUMP" + enabled_description = "Audible" + disabled_description = "Silent" + +/datum/client_preference/drop_sounds + description = "Dropped Item Sounds" + key = "SOUND_DROPPED" + enabled_description = "Enabled" + disabled_description = "Disabled" /datum/client_preference/mob_tooltips description ="Mob tooltips" @@ -153,6 +165,12 @@ var/list/_client_preferences_by_type enabled_description = "Show" disabled_description = "Hide" +/datum/client_preference/precision_placement + description ="Precision Placement" + key = "PRECISE_PLACEMENT" + enabled_description = "Active" + disabled_description = "Inactive" + /datum/client_preference/hotkeys_default description ="Hotkeys Default" key = "HUD_HOTKEYS" @@ -230,11 +248,17 @@ var/list/_client_preferences_by_type description ="Hear In-game Instruments" key = "SOUND_INSTRUMENT" +/datum/client_preference/vchat_enable + description = "Enable/Disable VChat" + key = "VCHAT_ENABLE" + enabled_description = "Enabled" + disabled_description = "Disabled" + /******************** * Staff Preferences * ********************/ /datum/client_preference/admin/may_toggle(var/mob/preference_mob) - return check_rights(R_ADMIN, 0, preference_mob) + return check_rights(R_ADMIN|R_EVENT, 0, preference_mob) /datum/client_preference/mod/may_toggle(var/mob/preference_mob) return check_rights(R_MOD|R_ADMIN, 0, preference_mob) diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index 93242d4fdf..ae7826511d 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -92,16 +92,16 @@ var/list/gear_datums = list() var/total_cost = 0 for(var/gear_name in pref.gear) if(!gear_datums[gear_name]) - preference_mob << "You cannot have more than one of the \the [gear_name]" + to_chat(preference_mob, "You cannot have more than one of the \the [gear_name]") pref.gear -= gear_name else if(!(gear_name in valid_gear_choices())) - preference_mob << "You cannot take \the [gear_name] as you are not whitelisted for the species or item." //Vorestation Edit + to_chat(preference_mob, "You cannot take \the [gear_name] as you are not whitelisted for the species or item.") //Vorestation Edit pref.gear -= gear_name else var/datum/gear/G = gear_datums[gear_name] if(total_cost + G.cost > MAX_GEAR_COST) pref.gear -= gear_name - preference_mob << "You cannot afford to take \the [gear_name]" + to_chat(preference_mob, "You cannot afford to take \the [gear_name]") else total_cost += G.cost @@ -272,12 +272,12 @@ var/list/gear_datums = list() /datum/gear/proc/spawn_item(var/location, var/metadata) var/datum/gear_data/gd = new(path, location) - for(var/datum/gear_tweak/gt in gear_tweaks) - if(gear_tweaks.len) + if(length(gear_tweaks) && metadata) + for(var/datum/gear_tweak/gt in gear_tweaks) gt.tweak_gear_data(metadata["[gt]"], gd) var/item = new gd.path(gd.location) - for(var/datum/gear_tweak/gt in gear_tweaks) - if(gear_tweaks.len) + if(length(gear_tweaks) && metadata) + for(var/datum/gear_tweak/gt in gear_tweaks) gt.tweak_item(item, metadata["[gt]"]) var/mob/M = location if(istype(M) && exploitable) //Update exploitable info records for the mob without creating a duplicate object at their feet. diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes_yw.dm b/code/modules/client/preference_setup/loadout/loadout_eyes_yw.dm index ec84428bd1..2b1ec0f025 100644 --- a/code/modules/client/preference_setup/loadout/loadout_eyes_yw.dm +++ b/code/modules/client/preference_setup/loadout/loadout_eyes_yw.dm @@ -9,7 +9,7 @@ allowed_roles = list("Station Engineer","Chief Engineer","Atmospheric Technician") /datum/gear/eyes/arglasses/med - display_name = "AR-M glasses (Med, S&R)" + display_name = "AR-M glasses (Med, SAR)" path = /obj/item/clothing/glasses/omnihud/med allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Search and Rescue") diff --git a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm index c0dc19841f..27f553827c 100644 --- a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm @@ -1,3 +1,7 @@ +// Note for newly added fluff items: Ckeys should not contain any spaces, underscores or capitalizations, +// or else the item will not be usable. +// Example: Someone whose username is "Master Pred_Man" should be written as "masterpredman" instead + /datum/gear/fluff path = /obj/item sort_category = "Fluff Items" @@ -14,6 +18,14 @@ allowed_roles = list("Station Engineer") */ + +/datum/gear/fluff/collar //Use this as a base path for collars if you'd like to set tags in loadout. Make sure you don't use apostrophes in the display name or this breaks! + slot = slot_tie + +/datum/gear/fluff/collar/New() + ..() + gear_tweaks = list(gear_tweak_collar_tag) + // 0-9 CKEYS /datum/gear/fluff/malady_crop path = /obj/item/weapon/material/twohanded/fluff/riding_crop/malady @@ -25,6 +37,7 @@ /datum/gear/fluff/lethe_helmet path = /obj/item/clothing/head/helmet/hos/fluff/lethe display_name = "Lethe's Helmet" + slot = slot_head ckeywhitelist = list("adk09") character_name = list("Lethe") @@ -40,6 +53,12 @@ ckeywhitelist = list("aegisoa") character_name = list("Xander Bevin") +/datum/gear/fluff/xander_medal_2 + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Xander's Unity Medal" + ckeywhitelist = list("aegisoa") + character_name = list("Xander Bevin") + /datum/gear/fluff/lynn_penlight path = /obj/item/device/flashlight/pen/fluff/lynn display_name = "Lynn's Penlight" @@ -50,20 +69,30 @@ path = /obj/item/clothing/under/solgov/utility/sifguard/officer/medical display_name = "centcom medical uniform" description = "A medical uniform straight from Central Command." + slot = slot_w_uniform ckeywhitelist = list("arokha") - character_name = list("Aronai Kadigan") + character_name = list("Aronai Sieyes") /datum/gear/fluff/aronai_ccmedjacket path = /obj/item/clothing/suit/storage/service/sifguard/medical/command display_name = "centcom medical jacket" description = "A medical jacket straight from Central Command." + slot = slot_wear_suit ckeywhitelist = list("arokha") - character_name = list("Aronai Kadigan") + character_name = list("Aronai Sieyes") + +/datum/gear/fluff/collar/azura + path = /obj/item/clothing/accessory/collar/azura + display_name = "collar, Azura" + description = "For the kobold's pet." + ckeywhitelist = list("azurachitin") + character_name = list("Azura Chitin") // B CKEYS /datum/gear/fluff/yuuko_kimono path = /obj/item/clothing/under/fluff/sakura_hokkaido_kimono display_name = "Yuuko's Kimono" + slot = slot_w_uniform ckeywhitelist = list("benemuel") character_name = list("Yuuko Shimmerpond") @@ -76,12 +105,14 @@ /datum/gear/fluff/kilano_dress path = /obj/item/clothing/under/dress/fluff/kilano display_name = "Kilano's Dress" + slot = slot_w_uniform ckeywhitelist = list("beyondmylife") character_name = list("Kilano Soryu") /datum/gear/fluff/kilano_gloves path = /obj/item/clothing/gloves/fluff/kilano display_name = "Kilano's Gloves" + slot = slot_gloves ckeywhitelist = list("beyondmylife") character_name = list("Kilano Soryu") @@ -100,9 +131,10 @@ /datum/gear/fluff/xin_sovietuniform path = /obj/item/clothing/under/soviet display_name = "Xin's Soviet Uniform" + description = "This soviet uniform has seen considerable use over the years, it's rather worn in some places, frayed in others and the stomach region has signs of being stretched out repeatedly." + slot = slot_w_uniform ckeywhitelist = list("britishrabbit") character_name = list("Xin Xiao") - description = "This soviet uniform has seen considerable use over the years, it's rather worn in some places, frayed in others and the stomach region has signs of being stretched out repeatedly." /datum/gear/fluff/tasald_box path = /obj/item/weapon/storage/box/fluff/tasald @@ -113,18 +145,21 @@ /datum/gear/fluff/tasald_cartographer_jumpsuit path = /obj/item/clothing/under/solgov/utility/sifguard/officer/exploration display_name = "Tasald's Cartographer's uniform - jumpsuit" + slot = slot_w_uniform ckeywhitelist = list("bwoincognito") character_name = list("Tasald Corlethian") /datum/gear/fluff/tasald_cartographer_jacket path = /obj/item/clothing/suit/storage/service/sifguard/command display_name = "Tasald's Cartographer's uniform - jacket" + slot = slot_wear_suit ckeywhitelist = list("bwoincognito") character_name = list("Tasald Corlethian") /datum/gear/fluff/tasald_cartographer_beret path = /obj/item/clothing/head/beret/sol/expedition/command display_name = "Tasald's Cartographer's uniform - beret" + slot = slot_head ckeywhitelist = list("bwoincognito") character_name = list("Tasald Corlethian") @@ -137,10 +172,24 @@ /datum/gear/fluff/jayda_meduniform path = /obj/item/clothing/under/solgov/utility/sifguard/medical/fluff display_name = "Jayda's Uniform" + slot = slot_w_uniform ckeywhitelist = list("burritojustice") character_name = list("Jayda Wilson") +/datum/gear/fluff/star_hoodie + path = /obj/item/clothing/suit/storage/star + display_name = "Star hoodie" + slot = slot_wear_suit + ckeywhitelist = list("bacon12366") + character_name = list("Elly Brown") + // C CKEYS +/datum/gear/fluff/cappy_watch + path = /obj/item/clothing/accessory/watch + display_name = "Cappy's Pocket Watch" + ckeywhitelist = list("cappycat") + character_name = list("Cappy Fuzzlyfeathers") + /datum/gear/fluff/james_disk path = /obj/item/weapon/disk/data display_name = "James' Disk" @@ -156,18 +205,21 @@ /datum/gear/fluff/diana_robe path = /obj/item/clothing/suit/fluff/purp_robes display_name = "Diana's Robes" + slot = slot_wear_suit ckeywhitelist = list("cameron653") character_name = list("Diana Kuznetsova") /datum/gear/fluff/diana_tiara path = /obj/item/clothing/head/fluff/pink_tiara display_name = "Diana's Tiara" + slot = slot_head ckeywhitelist = list("cameron653") character_name = list("Diana Kuznetsova") /datum/gear/fluff/aika_coat path = /obj/item/clothing/suit/fluff/blue_trimmed_coat display_name = "Aika's Coat" + slot = slot_wear_suit ckeywhitelist = list("chaoko99") character_name = list("Aika Hisakawa") @@ -183,6 +235,18 @@ ckeywhitelist = list("crossexonar") character_name = list("Sari-E") +/datum/gear/fluff/alexandria_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Alexandria's Unity Medal" + ckeywhitelist = list("cgr") + character_name = list("Alexandria Kozetto") + +/datum/gear/fluff/hisako_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Hisako's Unity Medal" + ckeywhitelist = list("chaleur") + character_name = list("Hisako Arato") + // D CKEYS /datum/gear/fluff/dhaeleena_medal path = /obj/item/clothing/accessory/medal/silver/security/fluff/dhael @@ -219,18 +283,21 @@ /datum/gear/fluff/serkii_slippers path = /obj/item/clothing/shoes/slippers display_name = "Serkii's Slippers" + slot = slot_shoes ckeywhitelist = list("eekasqueak") character_name = list("Serkii Miishy") /datum/gear/fluff/serkii_skirt path = /obj/item/clothing/under/skirt/fluff/serkii display_name = "Serkii's Skirt" + slot = slot_w_uniform ckeywhitelist = list("eekasqueak") character_name = list("Serkii Miishy") /datum/gear/fluff/jessie_coat path = /obj/item/clothing/suit/storage/hooded/wintercoat/jessie display_name = "Jessie's Coat" + slot = slot_wear_suit ckeywhitelist = list("epiccharger") character_name = list("Jessie Mare") @@ -247,6 +314,13 @@ character_name = list("Erik Ramadwood") // F CKEYS +/datum/gear/fluff/yikatihaki_armor + path = /obj/item/clothing/suit/armor/vox_scrap + display_name = "Yikatihaki's Armor" + slot = slot_wear_suit + ckeywhitelist = list("flaktual") + character_name = list("Yikatihaki") + allowed_roles = list("Explorer") // G CKEYS /datum/gear/fluff/eldi_implant @@ -296,9 +370,22 @@ /datum/gear/fluff/ivy_backpack path = /obj/item/weapon/storage/backpack/messenger/sec/fluff/ivymoomoo display_name = "Ivy's Backpack" + slot = slot_back ckeywhitelist = list("ivymoomoo") character_name = list("Ivy Baladeva") +/datum/gear/fluff/farren_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Farren's Unity Medal" + ckeywhitelist = list("itsaket") + character_name = list("Farren Fruit") + +/datum/gear/fluff/myse_monk + path = /obj/item/clothing/suit/imperium_monk + display_name = "Imperium monk" + ckeywhitelist = list("itsaket") + character_name = list("Myse Topazian IV") + // J CKEYS /datum/gear/fluff/mor_box path = /obj/item/weapon/storage/box/fluff/morxaina @@ -309,6 +396,7 @@ /datum/gear/fluff/areax_staff path = /obj/item/weapon/storage/backpack/fluff/stunstaff display_name = "Areax's Stun Staff" + slot = slot_back ckeywhitelist = list("jacknoir413") character_name = list("Areax Third") allowed_roles = list("Security Officer, Warden, Head of Security") @@ -316,12 +404,14 @@ /datum/gear/fluff/earthen_uniform path = /obj/item/clothing/under/fluff/earthenbreath display_name = "Earthen's Uniform" + slot = slot_w_uniform ckeywhitelist = list("jacobdragon") character_name = list("Earthen Breath") /datum/gear/fluff/earthen_hairpin path = /obj/item/clothing/head/fluff/hairflowerpin display_name = "Earthen's Flower Pin" + slot = slot_head ckeywhitelist = list("jacobdragon") character_name = list("Earthen Breath") @@ -334,33 +424,10 @@ /datum/gear/fluff/jemli_fedora path = /obj/item/clothing/head/fedora/fluff/jemli display_name = "Jemli's Fedora" + slot = slot_head ckeywhitelist = list("jemli") character_name = list("Jemli") -//TFF 28/11/19 - Expired permit removal -/* -/datum/gear/fluff/jeremiah_permit - path = /obj/item/clothing/accessory/permit/gun/fluff/ace - display_name = "Ace's Gun Permit" - ckeywhitelist = list("jertheace") - character_name = list("Jeremiah Acacius") - allowed_roles = list("Colony Director", "Warden", "Head of Security") - -/datum/gear/fluff/jeremiah_gun - path = /obj/item/weapon/gun/projectile/p92x/large/preban/hp - display_name = "Ace's Gun" - ckeywhitelist = list("jertheace") - character_name = list("Jeremiah Acacius") - allowed_roles = list("Colony Director", "Warden", "Head of Security") - -/datum/gear/fluff/jeremiah_ammo - path = /obj/item/ammo_magazine/m9mm/large/preban/hp //Spare ammo - display_name = "Ace's Spare Ammo" - ckeywhitelist = list("jertheace") - character_name = list("Jeremiah Acacius") - allowed_roles = list("Colony Director", "Warden", "Head of Security") -*/ - /datum/gear/fluff/jeremiah_holster path = /obj/item/clothing/accessory/holster/armpit display_name = "Ace's Holster" @@ -371,12 +438,14 @@ /datum/gear/fluff/jeremiah_boots path = /obj/item/clothing/shoes/boots/combat display_name = "Ace's Boots" + slot = slot_shoes ckeywhitelist = list("jertheace") character_name = list("Jeremiah Acacius") /datum/gear/fluff/jeremiah_combatuniform path = /obj/item/clothing/under/syndicate/combat display_name = "Ace's Combat Uniform" + slot = slot_w_uniform ckeywhitelist = list("jertheace") character_name = list("Jeremiah Acacius") @@ -403,12 +472,14 @@ /datum/gear/fluff/emoticon_mimeuniform path = /obj/item/clothing/under/sexymime display_name = "Emoticon's Mime Uniform" + slot = slot_w_uniform ckeywhitelist = list("joey4298") character_name = list("Emoticon") /datum/gear/fluff/emoticon_mimemask path = /obj/item/clothing/mask/gas/sexymime display_name = "Emoticon's Mime Mask" + slot = slot_wear_mask ckeywhitelist = list("joey4298") character_name = list("Emoticon") @@ -446,6 +517,7 @@ /datum/gear/fluff/ketrai_hat path = /obj/item/clothing/head/fluff/ketrai display_name = "Ketrai's Hat" + slot = slot_head ckeywhitelist = list("ketrai") character_name = list("Ketrai") @@ -458,9 +530,23 @@ /datum/gear/fluff/kisuke_glasses path = /obj/item/clothing/glasses/omnihud/kamina display_name = "Kisuke's Kamina Glasses" + slot = slot_glasses ckeywhitelist = list("kisukegema") character_name = list("Kisuke Gema") +/datum/gear/fluff/collar/excess + path = /obj/item/clothing/accessory/collar/pink/fluff/warning + display_name = "collar, Excess" + ckeywhitelist = list("killerdragn") + character_name = list("Excess") + +/datum/gear/fluff/excess_uniform + path = /obj/item/clothing/under/fluff/excess + display_name = "Excess's Uniform" + slot = slot_w_uniform + ckeywhitelist = list("killerdragn") + character_name = list("Excess") + /datum/gear/fluff/lassara_sheath path = /obj/item/clothing/accessory/storage/knifeharness display_name = "Lassara's Knife Harness" @@ -476,6 +562,7 @@ /datum/gear/fluff/taiga_uniform path = /obj/item/clothing/under/fluff/taiga display_name = "Taifa's Uniform" + slot = slot_w_uniform ckeywhitelist = list("kiwidaninja") character_name = list("Chakat Taiga") @@ -502,6 +589,7 @@ /datum/gear/fluff/brianna_backpack path = /obj/item/weapon/storage/backpack/messenger/black/fluff/briana display_name = "Briana's Backpack" + slot = slot_back ckeywhitelist = list("luminescentring") character_name = list("Briana Moore") @@ -521,6 +609,7 @@ /datum/gear/fluff/giliana_labcoat path = /obj/item/clothing/suit/storage/toggle/labcoat/fluff/molenar display_name = "Giliana's Labcoat" + slot = slot_wear_suit ckeywhitelist = list("molenar") character_name = list("Giliana Gamish") @@ -533,6 +622,7 @@ /datum/gear/fluff/resh_uniform path = /obj/item/clothing/suit/security/navyhos display_name = "Resh's HoS Uniform" + slot = slot_wear_suit ckeywhitelist = list("mrsignmeup") character_name = list("Reshskakskakss Seekiseekis") allowed_roles = list("Head of Security") @@ -547,23 +637,28 @@ /datum/gear/fluff/awen_hat path = /obj/item/clothing/head/fluff/wolfgirl display_name = "Awen's Hat" + slot = slot_head ckeywhitelist = list("natje") character_name = list("Awen Henry") /datum/gear/fluff/awen_shoes path = /obj/item/clothing/shoes/fluff/wolfgirl + display_name = "Awen's Shoes" + slot = slot_shoes ckeywhitelist = list("natje") character_name = list("Awen Henry") /datum/gear/fluff/awen_uniform path = /obj/item/clothing/under/fluff/wolfgirl display_name = "Awen's Uniform" + slot = slot_w_uniform ckeywhitelist = list("natje") character_name = list("Awen Henry") /datum/gear/fluff/pumila_vines path = /obj/item/clothing/under/fluff/aluranevines display_name = "Pumila's Vines" + slot = slot_w_uniform ckeywhitelist = list("natje") character_name = list("Pumila") @@ -573,6 +668,18 @@ ckeywhitelist = list("nepox") character_name = list("Annie Rose") +/datum/gear/fluff/damon_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Damon's Unity Medal" + ckeywhitelist = list("nickcrazy") + character_name = list("Damon Bones Xrim") + +/datum/gear/fluff/kt_fishing_rod + path = /obj/item/weapon/material/fishing_rod/modern/strong + display_name = "K't's fishing rod" + ckeywhitelist = list("nerdass") + character_name = list("K't") + // O CKEYS /datum/gear/fluff/richard_chain path = /obj/item/weapon/melee/fluff/holochain @@ -580,13 +687,32 @@ ckeywhitelist = list("orbisa") character_name = list("Richard D'angelo") +/datum/gear/fluff/bogen_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Bogen's Unity Medal" + ckeywhitelist = list("oscarholmes") + character_name = list("Bogen Kellogg") + // P CKEYS +/datum/gear/fluff/zaku_sweatervest + path = /obj/item/clothing/suit/varsity/green/sweater_vest + display_name = "Zaku's Sweater Vest" + slot = slot_wear_suit + ckeywhitelist = list("pimientopyro") + character_name = list("Zaku Fyodorovna") + /datum/gear/fluff/lily_medal path = /obj/item/clothing/accessory/medal/silver/unity display_name = "Lily's Unity Medal" ckeywhitelist = list("phoaly") character_name = list("Lily Maximus") +/datum/gear/fluff/pip_cloak + path = /obj/item/clothing/accessory/poncho/roles/cloak/hop/fluff/pip + display_name = "Pip's Cloak" + ckeywhitelist = list("phoaly") + character_name = list("Pip Shyner") + /datum/gear/fluff/lucuis_battery path = /obj/item/weapon/fluff/dragor_dot display_name = "Lucuis' Spare Battery" @@ -599,6 +725,12 @@ ckeywhitelist = list("pontifexminimus") character_name = list("Lucia Null") +/datum/gear/fluff/jamie_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Jamie's Unity Medal" + ckeywhitelist = list("phoenixgamma7") + character_name = list("Jamie Findlay") + // Q CKEYS // R CKEYS @@ -610,6 +742,18 @@ allowed_roles = list("Roboticist") // S CKEYS +/datum/gear/fluff/brynhild_medal + path = /obj/item/clothing/accessory/medal/silver/valor + display_name = "Brynhild's Valor Medal" + ckeywhitelist = list("sageofaether12") + character_name = list("Brynhild Vandradottir") + +/datum/gear/fluff/brynhild_medal_2 + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Brynhild's Unity Medal" + ckeywhitelist = list("sageofaether12") + character_name = list("Brynhild Vandradottir") + /datum/gear/fluff/kateryna_voidsuit path = /obj/item/clothing/suit/space/void/engineering/kate display_name = "Kateryna's Voidsuit" @@ -627,6 +771,7 @@ /datum/gear/fluff/kateryna_armorvest path = /obj/item/clothing/suit/armor/vest/wolftaur/kate display_name = "Kateryna's Armor Vest" + slot = slot_wear_suit ckeywhitelist = list("samanthafyre") character_name = list("Kateryna Petrovitch") allowed_roles = list("Security Officer", "Warden", "Head of Security", "Colony Director", "Head of Personnel") @@ -646,12 +791,14 @@ /datum/gear/fluff/scree_pompom path = /obj/item/clothing/head/fluff/pompom display_name = "Scree's Weird PopPom thing" + slot = slot_head ckeywhitelist = list("scree") character_name = list("Scree") /datum/gear/fluff/alfonso_sunglasses path = /obj/item/clothing/glasses/sunglasses/fluff/alfonso display_name = "Alfonso's Sunglasses" + slot = slot_glasses ckeywhitelist = list("seiga") character_name = list("Alfonso Oak Telanor") @@ -665,12 +812,14 @@ /datum/gear/fluff/tasy_clownuniform path = /obj/item/clothing/under/sexyclown display_name = "Tasy's Clown Uniform" + slot = slot_w_uniform ckeywhitelist = list("silvertalismen") character_name = list("Tasy Ruffles") /datum/gear/fluff/tasy_clownmask path = /obj/item/clothing/mask/gas/sexyclown display_name = "Tasy's Clownmask" + slot = slot_wear_mask ckeywhitelist = list("silvertalismen") character_name = list("Tasy Ruffles") @@ -689,6 +838,7 @@ /datum/gear/fluff/fortune_backpack path = /obj/item/weapon/storage/backpack/satchel/fluff/swat43bag display_name = "Fortune's Backpack" + slot = slot_back ckeywhitelist = list("swat43") character_name = list("Fortune Bloise") @@ -707,28 +857,43 @@ /datum/gear/fluff/roiz_coat path = /obj/item/clothing/suit/storage/hooded/wintercoat/roiz display_name = "Roiz's Coat" + slot = slot_wear_suit ckeywhitelist = list("spoopylizz") character_name = list("Roiz Lizden") +/datum/gear/fluff/lilith_medal + path = /obj/item/clothing/accessory/medal/silver/valor + display_name = "Lilith's Valor Medal" + ckeywhitelist = list("stiphs") + character_name = list("Lilith Vespers") + /datum/gear/fluff/silent_mimemask path = /obj/item/clothing/mask/gas/sexymime display_name = "Silent Stripe's Mime Mask" + slot = slot_wear_mask ckeywhitelist = list("suicidalpickles") character_name = list("Silent Stripes") /datum/gear/fluff/silent_mimeuniform path = /obj/item/clothing/under/sexymime display_name = "Silent Stripe's Mime Uniform" + slot = slot_w_uniform ckeywhitelist = list("suicidalpickles") character_name = list("Silent Stripes") // T CKEYS /datum/gear/fluff/ascian_medal - path = /obj/item/clothing/accessory/medal/silver/unity + path = /obj/item/clothing/accessory/medal/silver/unity/tabiranth display_name = "Ascian's Unity Medal" ckeywhitelist = list("tabiranth") character_name = list("Ascian") +/datum/gear/fluff/ascian_medal_2 + path = /obj/item/clothing/accessory/medal/silver/valor + display_name = "Ascian's Valor Medal" + ckeywhitelist = list("tabiranth") + character_name = list("Ascian") + /datum/gear/fluff/ascian_spiritspawner path = /obj/item/weapon/grenade/spawnergrenade/spirit display_name = "The Best Kitten" @@ -753,6 +918,19 @@ ckeywhitelist = list("thedavestdave") character_name = list("Nick Sloan") +/datum/gear/fluff/roy_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Roy's Unity Medal" + ckeywhitelist = list("thedavestdave") + character_name = list("Roy Tilton") + +/datum/gear/fluff/monty_balaclava + path = /obj/item/clothing/mask/balaclava + display_name = "Monty's Balaclava" + slot = slot_wear_mask + ckeywhitelist = list("theskringdinger") + character_name = list("Monty Kopic") + /datum/gear/fluff/konor_medal path = /obj/item/clothing/accessory/medal/silver/unity display_name = "Konor's Unity Medal" @@ -762,21 +940,10 @@ // U CKEYS // V CKEYS -/datum/gear/fluff/vakashi_permit - path = /obj/item/clothing/accessory/permit/gun/fluff/Vakashi - display_name = "Vakashi's Pepperspray Permit" - ckeywhitelist = list("vailthewolf") - character_name = list("Vakashi") - -/datum/gear/fluff/vakashi_pepperspray - path = /obj/item/weapon/reagent_containers/spray/pepper - display_name = "Vakashi's Pepperspray" - ckeywhitelist = list("vailthewolf") - character_name = list("Vakashi") - /datum/gear/fluff/cameron_glasses path = /obj/item/clothing/glasses/fluff/science_proper display_name = "Cameron's Science Glasses" + slot = slot_glasses ckeywhitelist = list("verkister") character_name = list("Cameron Eggbert") @@ -789,12 +956,14 @@ /datum/gear/fluff/opie_glasses path = /obj/item/clothing/glasses/fluff/spiffygogs display_name = "Opie's Goggles" + slot = slot_glasses ckeywhitelist = list("verkister") character_name = list("Opie Eggbert") /datum/gear/fluff/verin_hazardvest path = /obj/item/clothing/suit/storage/hazardvest/fluff/verin display_name = "Verin's Hazard Vest" + slot = slot_wear_suit ckeywhitelist = list("virgo113") character_name = list("Verin Raharra") @@ -813,24 +982,28 @@ /datum/gear/fluff/lucina_dress path = /obj/item/clothing/under/dress/fluff/lucinadress display_name = "Lucina's Dress" + slot = slot_w_uniform ckeywhitelist = list("vorrarkul") character_name = list("Lucina Dakarim") /datum/gear/fluff/melanie_skeleton path = /obj/item/clothing/under/fluff/slime_skeleton display_name = "Melanie's Skeleton" + slot = slot_w_uniform ckeywhitelist = list("vorrarkul") character_name = list("Melanie Farmer") /datum/gear/fluff/nyssa_coat path = /obj/item/clothing/suit/storage/hooded/wintercoat/cargo display_name = "Nyssa's Coat" + slot = slot_wear_suit ckeywhitelist = list("vorrarkul") character_name = list("Nyssa Brennan") /datum/gear/fluff/theodora_suit path = /obj/item/clothing/suit/chococoat display_name = "Theodora's Coat" + slot = slot_wear_suit ckeywhitelist = list("vorrarkul") character_name = list("Theodora Lindt") @@ -847,23 +1020,32 @@ character_name = list("Kaitlyn Fiasco") /datum/gear/fluff/keturah_maiddress - path = /obj/item/clothing/under/dress/maid/ + path = /obj/item/clothing/under/dress/maid display_name = "Keturah's Maid Dress" + slot = slot_w_uniform ckeywhitelist = list("viveret") character_name = list("Keturah") /datum/gear/fluff/silentio_mimeuniform path = /obj/item/clothing/under/sexymime display_name = "Silentio's Mime Uniform" + slot = slot_w_uniform ckeywhitelist = list("viveret") character_name = list("Silentio") /datum/gear/fluff/silentio_mimemask path = /obj/item/clothing/mask/gas/sexymime display_name = "Silentio's Mime Mask" - ckeywhitelist = list("Viveret") + slot = slot_wear_mask + ckeywhitelist = list("viveret") character_name = list("Silentio") +/datum/gear/fluff/roanna_medal + path = /obj/item/clothing/accessory/medal/silver/unity + display_name = "Roanna's Unity Medal" + ckeywhitelist = list("vitoras") + character_name = list("Roanna Ti'Rox") + // W CKEYS /datum/gear/fluff/sthasha_bracer path = /obj/item/clothing/accessory/bracer/fluff/xander_sthasha @@ -874,6 +1056,7 @@ /datum/gear/fluff/silas_glasses path = /obj/item/clothing/glasses/threedglasses display_name = "Silas' 3-D Glasses" + slot = slot_glasses ckeywhitelist = list("werebear") character_name = list("Silas Newton") @@ -886,25 +1069,25 @@ /datum/gear/fluff/tempest_hudglases path = /obj/item/clothing/glasses/omnihud/med/fluff/wickedtemphud display_name = "Tempest's Medical Hud" + slot = slot_glasses ckeywhitelist = list("wickedtemp") character_name = list("Chakat Tempest Venosare") - allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") - slot = slot_glasses + allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic", "Off-duty Medic") /datum/gear/fluff/tempest_hypospray path = /obj/item/weapon/reagent_containers/hypospray/vial/tempest display_name = "Tempest's Hypospray" + slot = slot_belt ckeywhitelist = list("wickedtemp") character_name = list("Chakat Tempest Venosare") - allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") - slot = slot_belt + allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic", "Off-duty Medic") /datum/gear/fluff/tempest_backpack path = /obj/item/weapon/storage/backpack/saddlebag/tempest display_name = "Tempest's Saddlebag" + slot = slot_back ckeywhitelist = list("wickedtemp") character_name = list("Chakat Tempest Venosare") - slot = slot_back /datum/gear/fluff/tempest_implant path = /obj/item/weapon/implanter/reagent_generator/tempest @@ -931,12 +1114,13 @@ /datum/gear/fluff/tachika_medal path = /obj/item/clothing/accessory/medal/conduct display_name = "Tachika's Conduct Medal" - ckeywhitelist = list("zammyman") + ckeywhitelist = list("zammyman215") character_name = list("Tachika") /datum/gear/fluff/zaoozaoo_hat path = /obj/item/clothing/head/fluff/zao display_name = "Zaoozaoo's Hat" + slot = slot_head ckeywhitelist = list("zigfe") character_name = list("Zaoozaoo Xrimxuqmqixzix") @@ -944,10 +1128,4 @@ path = /obj/item/device/radio/headset/fluff/zodiacshadow display_name = "Nehi's Radio" ckeywhitelist = list("zodiacshadow") - character_name = list("Nehi Maximus") - -/datum/gear/fluff/star_sweater - path = /obj/item/clothing/accessory/sweater/fluff/star - display_name = "Star Sweater" - ckeywhitelist = list("bacon12366") - character_name = list("Elly Brown") + character_name = list("Nehi Maximus") \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_fluffitems_yw.dm b/code/modules/client/preference_setup/loadout/loadout_fluffitems_yw.dm index 1c078e67a8..11c4082794 100644 --- a/code/modules/client/preference_setup/loadout/loadout_fluffitems_yw.dm +++ b/code/modules/client/preference_setup/loadout/loadout_fluffitems_yw.dm @@ -241,6 +241,15 @@ ckeywhitelist = list("championfire") character_name = list("Kita") +//codavanistok +//Coda Vanistok // (Yes, I'm aware they're the same. Yes, this is correct.) +/datum/gear/fluff/coda_implant + path = /obj/item/weapon/implanter/fluff/coda + display_name = "Locked size implant" + description = "It seems to be locked to two settings." + ckeywhitelist = list("codavanistok") + character_name = list("Coda Vanistok") + // D CKEYS //dameonowen @@ -742,12 +751,11 @@ allowed_roles = list("Explorer", "Pathfinder") /datum/gear/fluff/mocha_cloak - path = /obj/item/clothing/accessory/poncho/cloak/fluff/mocha - display_name = "Warden Cloak" - description = "A cloak denoting the rank of a warden, the name \"Mocha\" is embroidered along the collar in red lettering" - ckeywhitelist = list("mocatheporg1") - character_name = list("Mocha") - allowed_roles = list("Warden") + path = /obj/item/clothing/accessory/poncho/cloak/fluff/mocha + display_name = "Elaborate Cloak" + description = "A cloak denoting the rank of a warden, the name \"Mocha\" is embroidered along the collar in red lettering" + ckeywhitelist = list("mocatheporg1") + character_name = list("Mocha") // N CKEYS //NESgamer190 @@ -802,7 +810,7 @@ display_name = "Heart Collar" description = "The collar appears to have a heart shaped pin on the front, the medical logo on visible from a distance. Closer inspection shows that the heart can be opened, revealing a picture. On the picture you can see Stellar and Reyna close together, paws locked and facing the picture slightly sideways with beaming smiles." ckeywhitelist = list("risingstarslash") - character_name = list("Stellar Wolf") + character_name = list("Monika Wolfe") //radiantflash //Vasharr Zahirn @@ -849,6 +857,15 @@ ckeywhitelist = list("roguenoob") character_name = list("Basir Fahim") +//rouwren +//Rin Deimos +/datum/gear/fluff/rin_deimos_1 + path = /obj/item/clothing/accessory/medal/fluff/rindeimos + display_name = "Broken Life Crystal" + description = "The broken life crystal doesn't do anything, except remind you of loss." + ckeywhitelist = list("rouwren") + character_name = list("Rin Deimos") + // S CKEYS //snakewitharocketlauncher //Alex Wolf @@ -876,6 +893,12 @@ character_name = list("Aroozee Daarvoleast-Clark") allowed_roles = list("Head of Security") +/datum/gear/fluff/aroozee_valhollide_ring + path = /obj/item/clothing/accessory/collar/fluff/aroozee_valhollide_ring + display_name = "Aroozee valhollide ring" + description = "A ring made of valhollide." + ckeywhitelist = list("splintergp") + character_name = list("Aroozee Daarvoleast-Clark") // T CKEYS diff --git a/code/modules/client/preference_setup/loadout/loadout_general.dm b/code/modules/client/preference_setup/loadout/loadout_general.dm index 34a806fe32..121378723f 100644 --- a/code/modules/client/preference_setup/loadout/loadout_general.dm +++ b/code/modules/client/preference_setup/loadout/loadout_general.dm @@ -4,7 +4,15 @@ /datum/gear/cane/white display_name = "white cane" - path = /obj/item/weapon/cane/whitecane + path = /obj/item/weapon/cane/white + +/datum/gear/cane/white2 + display_name = "telescopic white cane" + path = /obj/item/weapon/cane/white/collapsible + +/datum/gear/crutch + display_name = "crutch" + path = /obj/item/weapon/cane/crutch /datum/gear/dice display_name = "dice pack" @@ -50,6 +58,36 @@ plushies[initial(plushie_type.name)] = plushie_type gear_tweaks += new/datum/gear_tweak/path(sortAssoc(plushies)) +/datum/gear/figure + display_name = "action figure selection" + description = "A \"Space Life\" brand action figure." + path = /obj/item/toy/figure/ + +/datum/gear/figure/New() + ..() + var/list/figures = list() + for(var/figure in typesof(/obj/item/toy/figure/) - /obj/item/toy/figure) + var/obj/item/toy/figure/figure_type = figure + figures[initial(figure_type.name)] = figure_type + gear_tweaks += new/datum/gear_tweak/path(sortAssoc(figures)) + +/datum/gear/toy + display_name = "toy selection" + description = "Choose from a number of toys." + path = /obj/item/toy/ + +/datum/gear/toy/New() + ..() + var/toytype = list() + toytype["Blink toy"] = /obj/item/toy/blink + toytype["Gravitational singularity"] = /obj/item/toy/spinningtoy + toytype["Water flower"] = /obj/item/toy/waterflower + toytype["Bosun's whistle"] = /obj/item/toy/bosunwhistle + toytype["Magic 8 Ball"] = /obj/item/toy/eight_ball + toytype["Magic Conch shell"] = /obj/item/toy/eight_ball/conch + gear_tweaks += new/datum/gear_tweak/path(toytype) + + /datum/gear/flask display_name = "flask" path = /obj/item/weapon/reagent_containers/food/drinks/flask/barflask diff --git a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm index 5a434217e2..41214cbd67 100644 --- a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm @@ -6,6 +6,16 @@ display_name = "basic headband" path = /obj/item/clothing/head/fluff/headbando +/datum/gear/head/maid + display_name = "maid headband" + path = /obj/item/clothing/head/headband/maid + /datum/gear/head/headbando/New() ..() - gear_tweaks = list(gear_tweak_free_color_choice) \ No newline at end of file + gear_tweaks = list(gear_tweak_free_color_choice) + +//Detective alternative +/datum/gear/head/detective_alt + display_name = "cyberscope headgear, detective" + path = /obj/item/clothing/head/helmet/detective_alt + allowed_roles = list("Head of Security", "Detective") \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index f6ff73569e..c1c242e4f6 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -66,17 +66,19 @@ display_name = "leather vest, corporate, brown" path = /obj/item/clothing/suit/storage/toggle/brown_jacket/nanotrasen/sleeveless +//YW EDIT BEGINS /datum/gear/suit/mil - display_name = "military jacket" + display_name = "military jacket selection" path = /obj/item/clothing/suit/storage/miljacket - -/datum/gear/suit/mil/alt - display_name = "military jacket, alt" - path = /obj/item/clothing/suit/storage/miljacket/alt - -/datum/gear/suit/mil/green - display_name = "military jacket, green" - path = /obj/item/clothing/suit/storage/miljacket/green + +/datum/gear/suit/mil/New() + ..() + var/list/mil_jackets = list() + for(var/military_style in typesof(/obj/item/clothing/suit/storage/miljacket)) + var/obj/item/clothing/suit/storage/miljacket/miljacket = military_style + mil_jackets[initial(miljacket.name)] = miljacket + gear_tweaks += new/datum/gear_tweak/path(sortAssoc(mil_jackets)) +//YW EDIT ENDS /datum/gear/suit/greyjacket display_name = "grey jacket" @@ -188,6 +190,11 @@ datum/gear/suit/duster ponchos[initial(poncho.name)] = poncho gear_tweaks += new/datum/gear_tweak/path(sortAssoc(ponchos)) +/datum/gear/suit/roles/poncho + display_name = "poncho, cargo" + path = /obj/item/clothing/accessory/poncho/roles/cargo + cost = 1 + /datum/gear/suit/roles/poncho/security display_name = "poncho, security" path = /obj/item/clothing/accessory/poncho/roles/security @@ -204,10 +211,6 @@ datum/gear/suit/duster display_name = "poncho, science" path = /obj/item/clothing/accessory/poncho/roles/science -/datum/gear/suit/roles/poncho/cargo - display_name = "poncho, cargo" - path = /obj/item/clothing/accessory/poncho/roles/cargo - /datum/gear/suit/roles/poncho/cloak/hos display_name = "cloak, head of security" path = /obj/item/clothing/accessory/poncho/roles/cloak/hos diff --git a/code/modules/client/preference_setup/loadout/loadout_suit_yw.dm b/code/modules/client/preference_setup/loadout/loadout_suit_yw.dm index bd62bf72b0..78f08e8877 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit_yw.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit_yw.dm @@ -1,4 +1,16 @@ /datum/gear/suit/roles/poncho/cloak/blueshield display_name = "cloak, blueshield" path = /obj/item/clothing/accessory/poncho/roles/cloak/blueshield - allowed_roles = list("Blueshield Guard") \ No newline at end of file + allowed_roles = list("Blueshield Guard") + +/datum/gear/suit/victailcoat + display_name = "Victorian tailcoat" + path = /obj/item/clothing/suit/storage/tailcoat + +/datum/gear/suit/victdress + display_name = "Victorian ladiescoat, Black" + path = /obj/item/clothing/suit/storage/victcoat + +/datum/gear/suit/victdress/red + display_name = "Victorian ladiescoat, Red" + path = /obj/item/clothing/suit/storage/victcoat/red diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm b/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm index d49e8e586d..4768998c8d 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uniform_vr.dm @@ -172,4 +172,10 @@ Swimsuits /datum/gear/uniform/suit/v_nanovest display_name = "Varmacorp nanovest" - path = /obj/item/clothing/under/fluff/v_nanovest \ No newline at end of file + path = /obj/item/clothing/under/fluff/v_nanovest + +//Detective alternative +/datum/gear/uniform/detective_alt + display_name = "sleek modern coat, detective" + path = /obj/item/clothing/under/detective_alt + allowed_roles = list("Head of Security", "Detective") diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform_yw.dm b/code/modules/client/preference_setup/loadout/loadout_uniform_yw.dm index e69de29bb2..0fef98d933 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uniform_yw.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uniform_yw.dm @@ -0,0 +1,21 @@ +/datum/gear/uniform/victsuit + description = "A victorian style suit, fancy!" + display_name = "Victorian suit selection" + +/datum/gear/uniform/victsuit/New() + ..() + var/list/victsuits = list() + for(var/suitvict in (typesof(/obj/item/clothing/under/yw/victsuit) - typesof(/obj/item/clothing/under/yw/victsuit/victdress))) + var/obj/item/clothing/under/yw/victsuit/suitvict_type = suitvict + victsuits[initial(suitvict_type.name)] = suitvict_type + gear_tweaks += new/datum/gear_tweak/path(sortAssoc(victsuits)) + +/datum/gear/uniform/victdress + description = "A victorian style dress, fancy!" + display_name = "Victorian dress, Black" + path = /obj/item/clothing/under/yw/victsuit/victdress + +/datum/gear/uniform/redvictdress + description = "A victorian style dress, fancy!" + display_name = "Victorian Dress, Red" + path = /obj/item/clothing/under/yw/victsuit/victdress/red \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm b/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm index 0df769d847..6e4dac4647 100644 --- a/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm @@ -1,45 +1,45 @@ /datum/gear/utility/saddlebag - display_name = "saddle bag, horse" - path = /obj/item/weapon/storage/backpack/saddlebag - slot = slot_back - cost = 2 + display_name = "saddle bag, horse" + path = /obj/item/weapon/storage/backpack/saddlebag + slot = slot_back + cost = 2 /datum/gear/utility/saddlebag_common - display_name = "saddle bag, common" - path = /obj/item/weapon/storage/backpack/saddlebag_common - slot = slot_back - cost = 2 + display_name = "saddle bag, common" + path = /obj/item/weapon/storage/backpack/saddlebag_common + slot = slot_back + cost = 2 /datum/gear/utility/saddlebag_common/robust - display_name = "saddle bag, robust" - path = /obj/item/weapon/storage/backpack/saddlebag_common/robust - slot = slot_back - cost = 2 + display_name = "saddle bag, robust" + path = /obj/item/weapon/storage/backpack/saddlebag_common/robust + slot = slot_back + cost = 2 /datum/gear/utility/saddlebag_common/vest - display_name = "taur duty vest (backpack)" - path = /obj/item/weapon/storage/backpack/saddlebag_common/vest - slot = slot_back - cost = 1 + display_name = "taur duty vest (backpack)" + path = /obj/item/weapon/storage/backpack/saddlebag_common/vest + slot = slot_back + cost = 1 /datum/gear/utility/dufflebag - display_name = "dufflebag" - path = /obj/item/weapon/storage/backpack/dufflebag - slot = slot_back - cost = 2 + display_name = "dufflebag" + path = /obj/item/weapon/storage/backpack/dufflebag + slot = slot_back + cost = 2 /datum/gear/utility/dufflebag/black - display_name = "black dufflebag" - path = /obj/item/weapon/storage/backpack/dufflebag/fluff + display_name = "black dufflebag" + path = /obj/item/weapon/storage/backpack/dufflebag/fluff /datum/gear/utility/dufflebag/med - display_name = "medical dufflebag" - path = /obj/item/weapon/storage/backpack/dufflebag/med - allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist","Psychiatrist","Field Medic") + display_name = "medical dufflebag" + path = /obj/item/weapon/storage/backpack/dufflebag/med + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist","Psychiatrist","Field Medic") /datum/gear/utility/dufflebag/med/emt - display_name = "EMT dufflebag" - path = /obj/item/weapon/storage/backpack/dufflebag/emt + display_name = "EMT dufflebag" + path = /obj/item/weapon/storage/backpack/dufflebag/emt /datum/gear/utility/dufflebag/sec display_name = "security Dufflebag" @@ -47,11 +47,11 @@ allowed_roles = list("Head of Security","Warden","Detective","Security Officer","Blueshield Guard") /datum/gear/utility/dufflebag/eng - display_name = "engineering dufflebag" - path = /obj/item/weapon/storage/backpack/dufflebag/eng - allowed_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer") + display_name = "engineering dufflebag" + path = /obj/item/weapon/storage/backpack/dufflebag/eng + allowed_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer") /datum/gear/utility/dufflebag/sci - display_name = "science dufflebag" - path = /obj/item/weapon/storage/backpack/dufflebag/sci - allowed_roles = list("Research Director","Scientist","Roboticist","Xenobiologist","Explorer","Pathfinder") + display_name = "science dufflebag" + path = /obj/item/weapon/storage/backpack/dufflebag/sci + allowed_roles = list("Research Director","Scientist","Roboticist","Xenobiologist","Explorer","Pathfinder") diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno.dm b/code/modules/client/preference_setup/loadout/loadout_xeno.dm index 87f39ed856..2a60cf94a9 100644 --- a/code/modules/client/preference_setup/loadout/loadout_xeno.dm +++ b/code/modules/client/preference_setup/loadout/loadout_xeno.dm @@ -204,7 +204,7 @@ /datum/gear/uniform/dept/undercoat/mining display_name = "mining undercoat (Teshari)" - path = /obj/item/clothing/accessory/poncho/roles/cloak/mining + path = /obj/item/clothing/under/seromi/undercoat/jobs/mining allowed_roles = list("Quartermaster","Shaft Miner") /datum/gear/uniform/dept/undercoat/security diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm b/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm index 6b92ab1376..c4909ac269 100644 --- a/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm @@ -1,3 +1,7 @@ +/datum/gear/shoes/footwraps/New() //Give color pick option for footwraps + ..() + gear_tweaks = list(gear_tweak_free_color_choice) + /datum/gear/uniform/voxcasual display_name = "casual wear (Vox)" path = /obj/item/clothing/under/vox/vox_casual diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index ca2bc4d787..220af5af8e 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -18,6 +18,11 @@ S["job_engsec_high"] >> pref.job_engsec_high S["job_engsec_med"] >> pref.job_engsec_med S["job_engsec_low"] >> pref.job_engsec_low + //VOREStation Add + S["job_talon_low"] >> pref.job_talon_low + S["job_talon_med"] >> pref.job_talon_med + S["job_talon_high"] >> pref.job_talon_high + //VOREStation Add End S["player_alt_titles"] >> pref.player_alt_titles /datum/category_item/player_setup_item/occupation/save_character(var/savefile/S) @@ -31,6 +36,11 @@ S["job_engsec_high"] << pref.job_engsec_high S["job_engsec_med"] << pref.job_engsec_med S["job_engsec_low"] << pref.job_engsec_low + //VOREStation Add + S["job_talon_low"] << pref.job_talon_low + S["job_talon_med"] << pref.job_talon_med + S["job_talon_high"] << pref.job_talon_high + //VOREStation Add End S["player_alt_titles"] << pref.player_alt_titles /datum/category_item/player_setup_item/occupation/sanitize_character() @@ -44,6 +54,11 @@ pref.job_engsec_high = sanitize_integer(pref.job_engsec_high, 0, 65535, initial(pref.job_engsec_high)) pref.job_engsec_med = sanitize_integer(pref.job_engsec_med, 0, 65535, initial(pref.job_engsec_med)) pref.job_engsec_low = sanitize_integer(pref.job_engsec_low, 0, 65535, initial(pref.job_engsec_low)) + //VOREStation Add + pref.job_talon_high = sanitize_integer(pref.job_talon_high, 0, 65535, initial(pref.job_talon_high)) + pref.job_talon_med = sanitize_integer(pref.job_talon_med, 0, 65535, initial(pref.job_talon_med)) + pref.job_talon_low = sanitize_integer(pref.job_talon_low, 0, 65535, initial(pref.job_talon_low)) + //VOREStation Add End if(!(pref.player_alt_titles)) pref.player_alt_titles = new() if(!job_master) @@ -54,23 +69,57 @@ if(alt_title && !(alt_title in job.alt_titles)) pref.player_alt_titles -= job.title -/datum/category_item/player_setup_item/occupation/content(mob/user, limit = 20, list/splitJobs = list("Pathfinder")) //VOREStation Edit +/datum/category_item/player_setup_item/occupation/content(mob/user, limit = 25, list/splitJobs = list()) if(!job_master) return . = list() . += "
    " . += "Choose occupation chances
    Unavailable occupations are crossed out.
    " - . += "
  • Non-human Positions
    Non-human Positions
    " // Table within a table for alignment, also allows you to easily add more columns. + . += "
    " // Table within a table for alignment, also allows you to easily add more columns. . += "" var/index = -1 //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. var/datum/job/lastJob - if (!job_master) return - for(var/datum/job/job in job_master.occupations) + var/datum/department/last_department = null // Used to avoid repeating the look-ahead check for if a whole department can fit. + + var/list/all_valid_jobs = list() + // If the occupation window gets opened before SSJob initializes, then it'll just be blank, with no runtimes. + // It will work once init is finished. + + for(var/D in SSjob.department_datums) + var/datum/department/department = SSjob.department_datums[D] + if(department.centcom_only) // No joining as a centcom role, if any are ever added. + continue + + for(var/J in department.primary_jobs) + all_valid_jobs += department.jobs[J] + + for(var/datum/job/job in all_valid_jobs) if(job.latejoin_only) continue //VOREStation Code - if((++index >= limit) || (job.title in splitJobs)) + var/datum/department/current_department = SSjob.get_primary_department_of_job(job) + + // Should we add a new column? + var/make_new_column = FALSE + if(++index > limit) + // Ran out of rows, make a new column. + make_new_column = TRUE + + else if(job.title in splitJobs) + // Is hardcoded to split at this job title. + make_new_column = TRUE + + else if(current_department != last_department) + // If the department is bigger than the limit then we have to split. + if(limit >= current_department.primary_jobs.len) + // Look ahead to see if we would need to split, and if so, avoid it. + if(index + current_department.primary_jobs.len > limit) + // Looked ahead, and determined that a new column is needed to avoid splitting the department into two. + make_new_column = TRUE + + + if(make_new_column) /******* if((index < limit) && (lastJob != null)) //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with @@ -78,29 +127,32 @@ for(var/i = 0, i < (limit - index), i++) . += "" *******/ - . += "
    //>  
    " + . += "
    " index = 0 + last_department = current_department . += "" + . += "[rank]" continue if(!job.player_old_enough(user.client)) var/available_in_days = job.available_in_days(user.client) - . += "[rank]" + . += "[rank]" continue if(job.minimum_character_age && user.client && (user.client.prefs.age < job.minimum_character_age)) - . += "[rank]" + . += "[rank]" continue if((pref.job_civilian_low & ASSISTANT) && job.type != /datum/job/assistant) - . += "[rank]" + . += "[rank]" continue - if((rank in command_positions) || (rank == "AI"))//Bold head jobs - . += "[rank]" + if((rank in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND) ) || (rank == "AI"))//Bold head jobs + . += "[rank]" else - . += "[rank]" + . += "[rank]" . += "" . += "" continue @@ -124,7 +176,7 @@ . += " \[Low]" else . += " \[NEVER]" - if(job.alt_titles) + if(LAZYLEN(job.alt_titles)) . += "" . += "" . += "
    " + var/rank = job.title lastJob = job + . += "" if(jobban_isbanned(user, rank)) - . += "[rank] \[BANNED]
    \[BANNED]
    \[IN [(available_in_days)] DAYS]
    \[IN [(available_in_days)] DAYS]
    \[MINIMUM CHARACTER AGE: [job.minimum_character_age]]
    \[MINIMUM CHARACTER AGE: [job.minimum_character_age]]
    " @@ -111,7 +163,7 @@ . += " \[Yes]" else . += " \[No]" - if(job.alt_titles) //Blatantly cloned from a few lines down. + if(LAZYLEN(job.alt_titles)) //Blatantly cloned from a few lines down. . += "
     \[[pref.GetPlayerAltTitle(job)]\]
     \[[pref.GetPlayerAltTitle(job)]\]
    " @@ -166,6 +218,45 @@ else if(href_list["set_job"]) if(SetJob(user, href_list["set_job"])) return (pref.equip_preview_mob ? TOPIC_REFRESH_UPDATE_PREVIEW : TOPIC_REFRESH) + + else if(href_list["job_info"]) + var/rank = href_list["job_info"] + var/datum/job/job = job_master.GetJob(rank) + var/dat = list() + + dat += "



    " + if(job.alt_titles) + dat += "Alternate titles: [english_list(job.alt_titles)]." + send_rsc(user, job.get_job_icon(), "job[ckey(rank)].png") + dat += "" + if(job.departments) + dat += "Departments: [english_list(job.departments)]." + if(LAZYLEN(job.departments_managed)) + dat += "You manage these departments: [english_list(job.departments_managed)]" + + dat += "You answer to [job.supervisors] normally." + + dat += "


    " + if(config.wikiurl) + dat += "Open wiki page in browser" + + var/alt_title = pref.GetPlayerAltTitle(job) + var/list/description = job.get_description_blurb(alt_title) + if(LAZYLEN(description)) + dat += html_encode(description[1]) + if(description.len > 1) + if(!isnull(description[2])) + dat += "
    " + dat += html_encode(description[2]) + + var/datum/browser/popup = new(user, "Job Info", "[capitalize(rank)]", 430, 520, src) + popup.set_content(jointext(dat,"
    ")) + popup.open() + + else if(href_list["job_wiki"]) + var/rank = href_list["job_wiki"] + open_link(user,"[config.wikiurl][rank]") + return ..() /datum/category_item/player_setup_item/occupation/proc/SetPlayerAltTitle(datum/job/job, new_title) @@ -205,14 +296,17 @@ pref.job_civilian_high = 0 pref.job_medsci_high = 0 pref.job_engsec_high = 0 + pref.job_talon_high = 0 //VOREStation Add return 1 if(2)//Set current highs to med, then reset them pref.job_civilian_med |= pref.job_civilian_high pref.job_medsci_med |= pref.job_medsci_high pref.job_engsec_med |= pref.job_engsec_high + pref.job_talon_med |= pref.job_talon_high //VOREStation Add pref.job_civilian_high = 0 pref.job_medsci_high = 0 pref.job_engsec_high = 0 + pref.job_talon_high = 0 //VOREStation Add switch(job.department_flag) if(CIVILIAN) @@ -260,6 +354,12 @@ pref.job_engsec_med = 0 pref.job_engsec_low = 0 + //VOREStation Add + pref.job_talon_high = 0 + pref.job_talon_med = 0 + pref.job_talon_low = 0 + //VOREStation Add End + pref.player_alt_titles.Cut() /datum/preferences/proc/GetPlayerAltTitle(datum/job/job) diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm index a55ca0ccca..01a0d75fec 100644 --- a/code/modules/client/preference_setup/preference_setup.dm +++ b/code/modules/client/preference_setup/preference_setup.dm @@ -1,9 +1,3 @@ -#define TOPIC_NOACTION 0 -#define TOPIC_HANDLED 1 -#define TOPIC_REFRESH 2 -#define TOPIC_UPDATE_PREVIEW 4 -#define TOPIC_REFRESH_UPDATE_PREVIEW (TOPIC_REFRESH|TOPIC_UPDATE_PREVIEW) - #define PREF_FBP_CYBORG "cyborg" #define PREF_FBP_POSI "posi" #define PREF_FBP_SOFTWARE "software" diff --git a/code/modules/client/preference_setup/traits/traits.dm b/code/modules/client/preference_setup/traits/traits.dm index 6312454453..806086c9a7 100644 --- a/code/modules/client/preference_setup/traits/traits.dm +++ b/code/modules/client/preference_setup/traits/traits.dm @@ -102,14 +102,14 @@ var/list/trait_categories = list() // The categories available for the trait men for(var/trait_name in pref.traits) if(!trait_datums[trait_name]) - preference_mob << "You cannot have more than one of trait: [trait_name]" + to_chat(preference_mob, "You cannot have more than one of trait: [trait_name]") pref.traits -= trait_name else var/datum/trait/T = trait_datums[trait_name] var/invalidity = T.test_for_invalidity(src) if(invalidity) pref.traits -= trait_name - preference_mob << "You cannot take the [trait_name] trait. Reason: [invalidity]" + to_chat(preference_mob, "You cannot take the [trait_name] trait. Reason: [invalidity]") var/conflicts = T.test_for_trait_conflict(pref.traits) if(conflicts) diff --git a/code/modules/client/preference_setup/vore/01_ears.dm b/code/modules/client/preference_setup/vore/01_ears.dm index 51e100ec9e..9098c36a96 100644 --- a/code/modules/client/preference_setup/vore/01_ears.dm +++ b/code/modules/client/preference_setup/vore/01_ears.dm @@ -24,6 +24,9 @@ var/r_wing = 30 // Wing color var/g_wing = 30 // Wing color var/b_wing = 30 // Wing color + var/r_wing2 = 30 // Wing extra color + var/g_wing2 = 30 // Wing extra color + var/b_wing2 = 30 // Wing extra color var/dress_mob = TRUE // Definition of the stuff for Ears @@ -50,6 +53,9 @@ S["r_wing"] >> pref.r_wing S["g_wing"] >> pref.g_wing S["b_wing"] >> pref.b_wing + S["r_wing2"] >> pref.r_wing2 + S["g_wing2"] >> pref.g_wing2 + S["b_wing2"] >> pref.b_wing2 /datum/category_item/player_setup_item/vore/ears/save_character(var/savefile/S) S["ear_style"] << pref.ear_style @@ -70,6 +76,9 @@ S["r_wing"] << pref.r_wing S["g_wing"] << pref.g_wing S["b_wing"] << pref.b_wing + S["r_wing2"] << pref.r_wing2 + S["g_wing2"] << pref.g_wing2 + S["b_wing2"] << pref.b_wing2 /datum/category_item/player_setup_item/vore/ears/sanitize_character() pref.r_ears = sanitize_integer(pref.r_ears, 0, 255, initial(pref.r_ears)) @@ -87,6 +96,9 @@ pref.r_wing = sanitize_integer(pref.r_wing, 0, 255, initial(pref.r_wing)) pref.g_wing = sanitize_integer(pref.g_wing, 0, 255, initial(pref.g_wing)) pref.b_wing = sanitize_integer(pref.b_wing, 0, 255, initial(pref.b_wing)) + pref.r_wing2 = sanitize_integer(pref.r_wing2, 0, 255, initial(pref.r_wing2)) + pref.g_wing2 = sanitize_integer(pref.g_wing2, 0, 255, initial(pref.g_wing2)) + pref.b_wing2 = sanitize_integer(pref.b_wing2, 0, 255, initial(pref.b_wing2)) if(pref.ear_style) pref.ear_style = sanitize_inlist(pref.ear_style, ear_styles_list, initial(pref.ear_style)) var/datum/sprite_accessory/temp_ear_style = ear_styles_list[pref.ear_style] @@ -122,6 +134,9 @@ character.r_wing = pref.r_wing character.b_wing = pref.b_wing character.g_wing = pref.g_wing + character.r_wing2 = pref.r_wing2 + character.b_wing2 = pref.b_wing2 + character.g_wing2 = pref.g_wing2 @@ -178,9 +193,11 @@ . += " Style: [wing_display]
    " if(wing_styles_list[pref.wing_style]) - var/datum/sprite_accessory/wing/T = wing_styles_list[pref.wing_style] - if (T.do_colouration) + var/datum/sprite_accessory/wing/W = wing_styles_list[pref.wing_style] + if (W.do_colouration) . += "Change Color
    __

    " + if (W.extra_overlay) + . += "Change Secondary Color
    __

    " /datum/category_item/player_setup_item/vore/ears/OnTopic(var/href,var/list/href_list, var/mob/user) if(!CanUseTopic(user)) @@ -276,6 +293,15 @@ pref.b_wing = hex2num(copytext(new_wingc, 6, 8)) return TOPIC_REFRESH_UPDATE_PREVIEW + else if(href_list["wing_color2"]) + var/new_wingc2 = input(user, "Choose your character's secondary wing colour:", "Character Preference", + rgb(pref.r_wing2, pref.g_wing2, pref.b_wing2)) as color|null + if(new_wingc2) + pref.r_wing2 = hex2num(copytext(new_wingc2, 2, 4)) + pref.g_wing2 = hex2num(copytext(new_wingc2, 4, 6)) + pref.b_wing2 = hex2num(copytext(new_wingc2, 6, 8)) + return TOPIC_REFRESH_UPDATE_PREVIEW + else if(href_list["toggle_clothing"]) pref.dress_mob = !pref.dress_mob return TOPIC_REFRESH_UPDATE_PREVIEW diff --git a/code/modules/client/preference_setup/vore/02_size.dm b/code/modules/client/preference_setup/vore/02_size.dm index 2fe8b54e98..774bdede17 100644 --- a/code/modules/client/preference_setup/vore/02_size.dm +++ b/code/modules/client/preference_setup/vore/02_size.dm @@ -62,7 +62,7 @@ var/new_size = input(user, "Choose your character's size, ranging from 25% to 200%", "Set Size") as num|null if (!ISINRANGE(new_size,25,200)) pref.size_multiplier = 1 - user << "Invalid size." + to_chat(user, "Invalid size.") return TOPIC_REFRESH_UPDATE_PREVIEW else if(new_size) pref.size_multiplier = (new_size/100) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 29c7f38ccd..14f4b5100e 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -209,7 +209,7 @@ datum/preferences if(!user || !user.client) return if(!get_mob_by_key(client_ckey)) - user << "No mob exists for the given client!" + to_chat(user, "No mob exists for the given client!") close_load_dialog(user) return @@ -246,7 +246,7 @@ datum/preferences if(config.forumurl) user << link(config.forumurl) else - user << "The forum URL is not set in the server configuration." + to_chat(user, "The forum URL is not set in the server configuration.") return ShowChoices(usr) return 1 diff --git a/code/modules/client/preferences_spawnpoints.dm b/code/modules/client/preferences_spawnpoints.dm index c0c54169b2..acf1c771a7 100644 --- a/code/modules/client/preferences_spawnpoints.dm +++ b/code/modules/client/preferences_spawnpoints.dm @@ -12,6 +12,7 @@ var/list/spawntypes = list() var/display_name //Name used in preference setup. var/list/restrict_job = null var/list/disallow_job = null + var/announce_channel = "Common" proc/check_job_spawning(job) if(restrict_job && !(job in restrict_job)) @@ -20,6 +21,10 @@ var/list/spawntypes = list() if(disallow_job && (job in disallow_job)) return 0 + var/datum/job/J = SSjob.get_job(job) + if(J?.offmap_spawn && !(job in restrict_job)) + return 0 + return 1 /datum/spawnpoint/proc/get_spawn_position() diff --git a/code/modules/client/preferences_toggle_procs.dm b/code/modules/client/preferences_toggle_procs.dm index d016029b23..b8e0ea3d5c 100644 --- a/code/modules/client/preferences_toggle_procs.dm +++ b/code/modules/client/preferences_toggle_procs.dm @@ -89,6 +89,21 @@ feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/verb/toggle_precision_placement() + set name = "Enable/Disable Precision Placement" + set category = "Preferences" + set desc = "Toggles precise placement of objects on tables." + + var/pref_path = /datum/client_preference/precision_placement + + toggle_preference(pref_path) + + to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] place items where your cursor is on the table.") + + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","TPIP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + /client/verb/toggle_typing() set name = "Show/Hide Typing Indicator" set category = "Preferences" @@ -224,20 +239,35 @@ feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/verb/toggle_air_pump_hum() - set name = "Toggle Air Pump Noise" - set category = "Preferences" - set desc = "Toggles Air Pumps humming" +/client/verb/toggle_air_pump_hum() + set name = "Toggle Air Pump Noise" + set category = "Preferences" + set desc = "Toggles Air Pumps humming" - var/pref_path = /datum/client_preference/air_pump_noise + var/pref_path = /datum/client_preference/air_pump_noise - toggle_preference(pref_path) + toggle_preference(pref_path) - to_chat(src, "You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear air pumps hum, start, and stop.") + to_chat(src, "You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear air pumps hum, start, and stop.") - SScharacter_setup.queue_preferences_save(prefs) + SScharacter_setup.queue_preferences_save(prefs) - feedback_add_details("admin_verb","TAirPumpNoise") + feedback_add_details("admin_verb","TAirPumpNoise") + +/client/verb/toggle_drop_sounds() + set name = "Toggle Dropped Item Sounds" + set category = "Preferences" + set desc = "Toggles sounds when items are dropped or thrown." + + var/pref_path = /datum/client_preference/drop_sounds + + toggle_preference(pref_path) + + to_chat(src, "You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear sounds when items are dropped or thrown.") + + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb", "TDropSounds") /client/verb/toggle_safe_firing() set name = "Toggle Gun Firing Intent Requirement" @@ -278,6 +308,39 @@ feedback_add_details("admin_verb","THInstm") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/verb/toggle_vchat() + set name = "Toggle VChat" + set category = "Preferences" + set desc = "Enable/Disable VChat. Reloading VChat and/or reconnecting required to affect changes." + + var/pref_path = /datum/client_preference/vchat_enable + toggle_preference(pref_path) + SScharacter_setup.queue_preferences_save(prefs) + + to_chat(src, "You have toggled VChat [is_preference_enabled(pref_path) ? "on" : "off"]. \ + You will have to reload VChat and/or reconnect to the server for these changes to take place. \ + VChat message persistence is not guaranteed if you change this again before the start of the next round.") + + +// Not attached to a pref datum because those are strict binary toggles +/client/verb/toggle_examine_mode() + set name = "Toggle Examine Mode" + set category = "Preferences" + set desc = "Control the additional behaviour of examining things" + + examine_text_mode++ + examine_text_mode %= EXAMINE_MODE_MAX // This cycles through them because if you're already specifically being routed to the examine panel, you probably don't need to have the extra text printed to chat + switch(examine_text_mode) // ... And I only wanted to add one verb + if(EXAMINE_MODE_DEFAULT) + to_chat(src, "Examining things will only output the base examine text, and you will not be redirected to the examine panel automatically.") + + if(EXAMINE_MODE_INCLUDE_USAGE) + to_chat(src, "Examining things will also print any extra usage information normally included in the examine panel to the chat.") + + if(EXAMINE_MODE_SWITCH_TO_PANEL) + to_chat(src, "Examining things will direct you to the examine panel, where you can view extended information about the thing.") + + //Toggles for Staff //Developers diff --git a/code/modules/client/preferences_vr.dm b/code/modules/client/preferences_vr.dm index 746a95d6db..65b3470aba 100644 --- a/code/modules/client/preferences_vr.dm +++ b/code/modules/client/preferences_vr.dm @@ -1,7 +1,12 @@ //TFF 5/8/19 - minor refactoring of this thing from 09_misc.dm to call this for preferences. datum/preferences - var/show_in_directory = 1 //TFF 5/8/19 - show in Character Directory - var/sensorpref = 5 //TFF 5/8/19 - set character's suit sensor level + var/show_in_directory = 1 //Show in Character Directory + var/sensorpref = 5 //Set character's suit sensor level + var/wingdings = 1 //YW Add + + var/job_talon_high = 0 + var/job_talon_med = 0 + var/job_talon_low = 0 //Why weren't these in game toggles already? /client/verb/toggle_eating_noises() @@ -34,3 +39,19 @@ datum/preferences SScharacter_setup.queue_preferences_save(prefs) feedback_add_details("admin_verb","TDigestNoise") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/verb/toggle_emote_noises() + set name = "Emote Noises" + set category = "Preferences" + set desc = "Toggles emote noises." + + var/pref_path = /datum/client_preference/emote_noises + + toggle_preference(pref_path) + + to_chat(src, "You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear emote-related noises.") + + SScharacter_setup.queue_preferences_save(prefs) + + feedback_add_details("admin_verb","TEmoteNoise") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/client/ui_style.dm b/code/modules/client/ui_style.dm index e7393e22c5..84c8055d9f 100644 --- a/code/modules/client/ui_style.dm +++ b/code/modules/client/ui_style.dm @@ -42,7 +42,7 @@ var/global/list/all_tooltip_styles = list( if(!ishuman(usr)) if(!isrobot(usr)) - usr << "You must be a human or a robot to use this verb." + to_chat(usr, "You must be a human or a robot to use this verb.") return var/UI_style_new = input(usr, "Select a style. White is recommended for customization") as null|anything in all_ui_styles @@ -78,4 +78,4 @@ var/global/list/all_tooltip_styles = list( prefs.UI_style_alpha = UI_style_alpha_new prefs.UI_style_color = UI_style_color_new SScharacter_setup.queue_preferences_save(prefs) - usr << "UI was saved" + to_chat(usr, "UI was saved") diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 5d5296db77..b9abfabf04 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -214,6 +214,18 @@ var/mob/M = src.loc M.update_inv_back() +/obj/item/weapon/storage/backpack/chameleon/full + starts_with = list( + /obj/item/clothing/under/chameleon, + /obj/item/clothing/head/chameleon, + /obj/item/clothing/suit/chameleon, + /obj/item/clothing/shoes/chameleon, + /obj/item/clothing/gloves/chameleon, + /obj/item/clothing/mask/chameleon, + /obj/item/clothing/glasses/chameleon, + /obj/item/clothing/accessory/chameleon + ) + //******************** //**Chameleon Gloves** //******************** diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 455598d9a1..90617f8426 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -1,6 +1,7 @@ /obj/item/clothing name = "clothing" siemens_coefficient = 0.9 + drop_sound = 'sound/items/drop/clothing.ogg' var/list/species_restricted = null //Only these species can wear this kit. var/gunshot_residue //Used by forensics. @@ -24,8 +25,11 @@ var/ear_protection = 0 var/blood_sprite_state + var/index //null by default, if set, will change which dmi it uses + var/update_icon_define = null // Only needed if you've got multiple files for the same type of clothing + //Updates the icons of the mob wearing the clothing item, if any. /obj/item/clothing/proc/update_clothing_icon() return @@ -35,12 +39,14 @@ ..() gunshot_residue = null + /obj/item/clothing/New() ..() if(starting_accessories) for(var/T in starting_accessories) var/obj/item/clothing/accessory/tie = new T(src) src.attach_accessory(null, tie) + set_clothing_index() /obj/item/clothing/equipped(var/mob/user,var/slot) ..() @@ -76,7 +82,7 @@ wearable = 1 if(!wearable && !(slot in list(slot_l_store, slot_r_store, slot_s_store))) - H << "Your species cannot wear [src]." + to_chat(H, "Your species cannot wear [src].") return 0 return 1 @@ -96,8 +102,14 @@ //Set species_restricted list switch(target_species) + //VOREStation Edit Start if(SPECIES_HUMAN, SPECIES_SKRELL) //humanoid bodytypes - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_PROMETHEAN) //skrell/humans can wear each other's suits + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_NEVREAN, SPECIES_RAPALA, SPECIES_VASILISSAN, SPECIES_ALRAUNE, SPECIES_PROMETHEAN, SPECIES_XENOCHIMERA) + if(SPECIES_UNATHI) + species_restricted = list(SPECIES_UNATHI, SPECIES_XENOHYBRID) + if(SPECIES_VULPKANIN) + species_restricted = list(SPECIES_VULPKANIN, SPECIES_ZORREN_HIGH, SPECIES_FENNEC) + //VOREStation Edit End else species_restricted = list(target_species) @@ -116,8 +128,16 @@ //Set species_restricted list switch(target_species) + //VOREStation Edit Start + if(SPECIES_HUMAN) + species_restricted = list(SPECIES_HUMAN, SPECIES_NEVREAN, SPECIES_RAPALA, SPECIES_VASILISSAN, SPECIES_ALRAUNE, SPECIES_PROMETHEAN, SPECIES_XENOCHIMERA) if(SPECIES_SKRELL) - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_PROMETHEAN) //skrell helmets fit humans too + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_NEVREAN, SPECIES_RAPALA, SPECIES_VASILISSAN, SPECIES_ALRAUNE, SPECIES_PROMETHEAN, SPECIES_XENOCHIMERA) + if(SPECIES_UNATHI) + species_restricted = list(SPECIES_UNATHI, SPECIES_XENOHYBRID) + if(SPECIES_VULPKANIN) + species_restricted = list(SPECIES_VULPKANIN, SPECIES_ZORREN_HIGH, SPECIES_FENNEC) + //VOREStation Edit End else species_restricted = list(target_species) @@ -166,7 +186,7 @@ else O = src - user.u_equip(src) + user.unEquip(src) if (O) user.put_in_hands(O) @@ -180,6 +200,24 @@ var/mob/M = src.loc M.update_inv_ears() +/obj/item/clothing/ears/MouseDrop(var/obj/over_object) + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + // If this covers both ears, we want to return the result of unequipping the primary object, and kill the off-ear one + if(slot_flags & SLOT_TWOEARS) + var/obj/item/clothing/ears/O = (H.l_ear == src ? H.r_ear : H.l_ear) + if(istype(src, /obj/item/clothing/ears/offear)) + . = O.MouseDrop(over_object) + H.drop_from_inventory(src) + qdel(src) + else + . = ..() + H.drop_from_inventory(O) + qdel(O) + else + . = ..() + + /obj/item/clothing/ears/offear name = "Other ear" w_class = ITEMSIZE_HUGE @@ -187,7 +225,7 @@ icon_state = "block" slot_flags = SLOT_EARS | SLOT_TWOEARS - New(var/obj/O) +/obj/item/clothing/ears/offear/New(var/obj/O) name = O.name desc = O.desc icon = O.icon @@ -223,6 +261,10 @@ SPECIES_TESHARI = 'icons/mob/species/seromi/gloves.dmi', SPECIES_VOX = 'icons/mob/species/vox/gloves.dmi' ) + drop_sound = 'sound/items/drop/gloves.ogg' + +/obj/item/clothing/proc/set_clothing_index() + return /obj/item/clothing/gloves/update_clothing_icon() if (ismob(src.loc)) @@ -243,7 +285,7 @@ /*/obj/item/clothing/gloves/attackby(obj/item/weapon/W, mob/user) if(W.is_wirecutter() || istype(W, /obj/item/weapon/scalpel)) if (clipped) - user << "The [src] have already been clipped!" + to_chat(user, "The [src] have already been clipped!") update_icon() return @@ -353,14 +395,15 @@ SPECIES_TESHARI = 'icons/mob/species/seromi/head.dmi', SPECIES_VOX = 'icons/mob/species/vox/head.dmi' ) + drop_sound = 'sound/items/drop/hat.ogg' /obj/item/clothing/head/attack_self(mob/user) if(brightness_on) if(!isturf(user.loc)) - user << "You cannot turn the light on while in this [user.loc]" + to_chat(user, "You cannot turn the light on while in this [user.loc]") return on = !on - user << "You [on ? "enable" : "disable"] the helmet light." + to_chat(user, "You [on ? "enable" : "disable"] the helmet light.") update_flashlight(user) else return ..(user) @@ -405,9 +448,9 @@ if(!success) return 0 else if(success == 2) - user << "You are already wearing a hat." + to_chat(user, "You are already wearing a hat.") else if(success == 1) - user << "You crawl under \the [src]." + to_chat(user, "You crawl under \the [src].") return 1 /obj/item/clothing/head/update_icon(var/mob/user) @@ -508,6 +551,7 @@ SPECIES_TESHARI = 'icons/mob/species/seromi/shoes.dmi', SPECIES_VOX = 'icons/mob/species/vox/shoes.dmi' ) + drop_sound = 'sound/items/drop/shoes.ogg' /obj/item/clothing/shoes/proc/draw_knife() set name = "Draw Boot Knife" @@ -522,10 +566,11 @@ if(usr.put_in_hands(holding)) usr.visible_message("\The [usr] pulls a knife out of their boot!") + playsound(get_turf(src), 'sound/weapons/holster/sheathout.ogg', 25) holding = null overlays -= image(icon, "[icon_state]_knife") else - usr << "Your need an empty, unbroken hand to do that." + to_chat(usr, "Your need an empty, unbroken hand to do that.") holding.forceMove(src) if(!holding) @@ -546,7 +591,7 @@ istype(I, /obj/item/weapon/material/kitchen/utensil) || \ istype(I, /obj/item/weapon/material/knife/tacknife))) if(holding) - user << "\The [src] is already holding \a [holding]." + to_chat(user, "\The [src] is already holding \a [holding].") return user.unEquip(I) I.forceMove(src) @@ -562,7 +607,7 @@ set category = "Object" if(shoes_under_pants == -1) - usr << "\The [src] cannot be worn above your suit!" + to_chat(usr, "\The [src] cannot be worn above your suit!") return shoes_under_pants = !shoes_under_pants update_icon() @@ -594,7 +639,7 @@ recent_squish = 0 for(var/mob/living/M in contents) var/emote = pick(inside_emotes) - M << emote //VOREStation edit end + to_chat(M,emote) //VOREStation edit end return /obj/item/clothing/shoes/update_clothing_icon() @@ -602,6 +647,7 @@ var/mob/M = src.loc M.update_inv_shoes() + /////////////////////////////////////////////////////////////////////// //Suit /obj/item/clothing/suit @@ -630,11 +676,27 @@ valid_accessory_slots = (ACCESSORY_SLOT_OVER | ACCESSORY_SLOT_ARMBAND) restricted_accessory_slots = (ACCESSORY_SLOT_ARMBAND) +/obj/item/clothing/suit/set_clothing_index() + ..() + + if(index && !icon_override) + icon = new /icon("icons/obj/clothing/suits_[index].dmi") + item_icons = list( + slot_l_hand_str = new /icon("icons/mob/items/lefthand_suits_[index].dmi"), + slot_r_hand_str = new /icon("icons/mob/items/righthand_suits_[index].dmi"), + ) + + return 1 + + return 0 + /obj/item/clothing/suit/update_clothing_icon() if (ismob(src.loc)) var/mob/M = src.loc M.update_inv_wear_suit() + set_clothing_index() + /////////////////////////////////////////////////////////////////////// //Under clothing /obj/item/clothing/under @@ -710,7 +772,7 @@ //autodetect rollability if(rolled_down < 0) - if(("[worn_state]_d_s" in icon_states(INV_W_UNIFORM_DEF_ICON)) || ("[worn_state]_s" in icon_states(rolled_down_icon)) || ("[worn_state]_d_s" in icon_states(icon_override))) + if(("[worn_state]_d_s" in icon_states(icon)) || ("[worn_state]_s" in icon_states(rolled_down_icon)) || ("[worn_state]_d_s" in icon_states(icon_override))) rolled_down = 0 if(rolled_down == -1) @@ -718,6 +780,23 @@ if(rolled_sleeves == -1) verbs -= /obj/item/clothing/under/verb/rollsleeves +/obj/item/clothing/under/set_clothing_index() + ..() + + if(index && !icon_override) + icon = new /icon("icons/obj/clothing/uniforms_[index].dmi") + + item_icons = list( + slot_l_hand_str = new /icon("icons/mob/items/lefthand_uniforms_[index].dmi"), + slot_r_hand_str = new /icon("icons/mob/items/righthand_uniforms_[index].dmi"), + ) + + rolled_down_icon = new /icon("icons/mob/uniform_rolled_down_[index].dmi") + rolled_down_sleeves_icon = new /icon("icons/mob/uniform_sleeves_rolled_[index].dmi") + return 1 + + return 0 + /obj/item/clothing/under/proc/update_rolldown_status() var/mob/living/carbon/human/H if(istype(src.loc, /mob/living/carbon/human)) @@ -732,8 +811,6 @@ under_icon = item_icons[slot_w_uniform_str] else if ("[worn_state]_s" in icon_states(rolled_down_icon)) under_icon = rolled_down_icon - else - under_icon = INV_W_UNIFORM_DEF_ICON // The _s is because the icon update procs append it. if((under_icon == rolled_down_icon && "[worn_state]_s" in icon_states(under_icon)) || ("[worn_state]_d_s" in icon_states(under_icon))) @@ -757,8 +834,8 @@ under_icon = item_icons[slot_w_uniform_str] else if ("[worn_state]_s" in icon_states(rolled_down_sleeves_icon)) under_icon = rolled_down_sleeves_icon - else - under_icon = INV_W_UNIFORM_DEF_ICON + else if(index) + under_icon = new /icon("[INV_W_UNIFORM_DEF_ICON]_[index].dmi") // The _s is because the icon update procs append it. if((under_icon == rolled_down_sleeves_icon && "[worn_state]_s" in icon_states(under_icon)) || ("[worn_state]_r_s" in icon_states(under_icon))) @@ -773,34 +850,36 @@ var/mob/M = src.loc M.update_inv_w_uniform() + set_clothing_index() + /obj/item/clothing/under/examine(mob/user) ..(user) switch(src.sensor_mode) if(0) - user << "Its sensors appear to be disabled." + to_chat(user, "Its sensors appear to be disabled.") if(1) - user << "Its binary life sensors appear to be enabled." + to_chat(user, "Its binary life sensors appear to be enabled.") if(2) - user << "Its vital tracker appears to be enabled." + to_chat(user, "Its vital tracker appears to be enabled.") if(3) - user << "Its vital tracker and tracking beacon appear to be enabled." + to_chat(user, "Its vital tracker and tracking beacon appear to be enabled.") /obj/item/clothing/under/proc/set_sensors(mob/usr as mob) var/mob/M = usr if (istype(M, /mob/observer)) return if (usr.stat || usr.restrained()) return if(has_sensor >= 2) - usr << "The controls are locked." + to_chat(usr, "The controls are locked.") return 0 if(has_sensor <= 0) - usr << "This suit does not have any sensors." + to_chat(usr, "This suit does not have any sensors.") return 0 var/list/modes = list("Off", "Binary sensors", "Vitals tracker", "Tracking beacon") var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes if(get_dist(usr, src) > 1) - usr << "You have moved too far away." + to_chat(usr, "You have moved too far away.") return sensor_mode = modes.Find(switchMode) - 1 @@ -834,7 +913,7 @@ update_rolldown_status() if(rolled_down == -1) - usr << "You cannot roll down [src]!" + to_chat(usr, "You cannot roll down [src]!") return if((rolled_sleeves == 1) && !(rolled_down)) rolled_sleeves = 0 @@ -849,13 +928,13 @@ else item_state_slots[slot_w_uniform_str] = "[worn_state]_d" - usr << "You roll down your [src]." + to_chat(usr, "You roll down your [src].") else body_parts_covered = initial(body_parts_covered) if(icon_override == rolled_down_icon) icon_override = initial(icon_override) item_state_slots[slot_w_uniform_str] = "[worn_state]" - usr << "You roll up your [src]." + to_chat(usr, "You roll up your [src].") update_clothing_icon() /obj/item/clothing/under/verb/rollsleeves() @@ -867,10 +946,10 @@ update_rollsleeves_status() if(rolled_sleeves == -1) - usr << "You cannot roll up your [src]'s sleeves!" + to_chat(usr, "You cannot roll up your [src]'s sleeves!") return if(rolled_down == 1) - usr << "You must roll up your [src] first!" + to_chat(usr, "You must roll up your [src] first!") return rolled_sleeves = !rolled_sleeves @@ -881,13 +960,13 @@ item_state_slots[slot_w_uniform_str] = "[worn_state]" else item_state_slots[slot_w_uniform_str] = "[worn_state]_r" - usr << "You roll up your [src]'s sleeves." + to_chat(usr, "You roll up your [src]'s sleeves.") else body_parts_covered = initial(body_parts_covered) if(icon_override == rolled_down_sleeves_icon) icon_override = initial(icon_override) item_state_slots[slot_w_uniform_str] = "[worn_state]" - usr << "You roll down your [src]'s sleeves." + to_chat(usr, "You roll down your [src]'s sleeves.") update_clothing_icon() diff --git a/code/modules/clothing/clothing_accessories.dm b/code/modules/clothing/clothing_accessories.dm index 6517b0ab97..4dda07aecc 100644 --- a/code/modules/clothing/clothing_accessories.dm +++ b/code/modules/clothing/clothing_accessories.dm @@ -70,7 +70,7 @@ ..(user) if(LAZYLEN(accessories)) for(var/obj/item/clothing/accessory/A in accessories) - user << "\A [A] is attached to it." + to_chat(user, "\A [A] is attached to it.") /** * Attach accessory A to src @@ -121,13 +121,24 @@ set name = "Remove Accessory" set category = "Object" set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return + + if(!istype(usr, /mob/living)) + return + + if(usr.stat) + return + var/obj/item/clothing/accessory/A - if(LAZYLEN(accessories)) - A = input("Select an accessory to remove from [src]") as null|anything in accessories + var/accessory_amount = LAZYLEN(accessories) + if(accessory_amount) + if(accessory_amount == 1) + A = accessories[1] // If there's only one accessory, just remove it without any additional prompts. + else + A = input("Select an accessory to remove from \the [src]") as null|anything in accessories + if(A) remove_accessory(usr,A) + if(!LAZYLEN(accessories)) src.verbs -= /obj/item/clothing/proc/removetie_verb accessories = null diff --git a/code/modules/clothing/clothing_vr.dm b/code/modules/clothing/clothing_vr.dm index 0ce0337988..62228ea5a0 100644 --- a/code/modules/clothing/clothing_vr.dm +++ b/code/modules/clothing/clothing_vr.dm @@ -26,7 +26,7 @@ recent_squish = 0 for(var/mob/living/M in contents) var/emote = pick(inside_emotes) - M << emote + to_chat(M,emote) return */ @@ -37,21 +37,21 @@ for(var/mob/M in src) full++ if(full >= 2) - to_chat(user,"You can't fit anyone else into \the [src]!") + to_chat(user, "You can't fit anyone else into \the [src]!") else var/obj/item/weapon/holder/micro/holder = I if(holder.held_mob && holder.held_mob in holder) - to_chat(holder.held_mob,"[user] stuffs you into \the [src]!") + to_chat(holder.held_mob, "[user] stuffs you into \the [src]!") holder.held_mob.forceMove(src) - to_chat(user,"You stuff \the [holder.held_mob] into \the [src]!") + to_chat(user, "You stuff \the [holder.held_mob] into \the [src]!") else ..() /obj/item/clothing/shoes/attack_self(var/mob/user) for(var/mob/M in src) M.forceMove(get_turf(user)) - to_chat(M,"[user] shakes you out of \the [src]!") - to_chat(user,"You shake [M] out of \the [src]!") + to_chat(M, "[user] shakes you out of \the [src]!") + to_chat(user, "You shake [M] out of \the [src]!") ..() @@ -79,14 +79,14 @@ if(ishuman(src.loc)) var/mob/living/carbon/human/H = src.loc if(H.shoes == src) - H << "[user]'s tiny body presses against you in \the [src], squirming!" - user << "Your body presses out against [H]'s form! Well, what little you can get to!" + to_chat(H, "[user]'s tiny body presses against you in \the [src], squirming!") + to_chat(user, "Your body presses out against [H]'s form! Well, what little you can get to!") else - H << "[user]'s form shifts around in the \the [src], squirming!" - user << "You move around inside the [src], to no avail." + to_chat(H, "[user]'s form shifts around in the \the [src], squirming!") + to_chat(user, "You move around inside the [src], to no avail.") else src.visible_message("\The [src] moves a little!") - user << "You throw yourself against the inside of \the [src]!" + to_chat(user, "You throw yourself against the inside of \the [src]!") //Mask /obj/item/clothing/mask diff --git a/code/modules/clothing/ears/ears.dm b/code/modules/clothing/ears/ears.dm index 11c81db633..505a4be004 100644 --- a/code/modules/clothing/ears/ears.dm +++ b/code/modules/clothing/ears/ears.dm @@ -29,11 +29,11 @@ if(headphones_on) icon_state = "[base_icon]_off" headphones_on = 0 - usr << "You turn the music off." + to_chat(usr, "You turn the music off.") else icon_state = "[base_icon]_on" headphones_on = 1 - usr << "You turn the music on." + to_chat(usr, "You turn the music on.") update_clothing_icon() @@ -53,6 +53,7 @@ desc = "A delicate golden chain worn by female skrell to decorate their head tails." icon_state = "skrell_chain" item_state_slots = list(slot_r_hand_str = "egg5", slot_l_hand_str = "egg5") + drop_sound = 'sound/items/drop/accessory.ogg' /obj/item/clothing/ears/skrell/chain/silver name = "Silver headtail chains" @@ -83,6 +84,7 @@ desc = "Golden metallic bands worn by male skrell to adorn their head tails." icon_state = "skrell_band" item_state_slots = list(slot_r_hand_str = "egg5", slot_l_hand_str = "egg5") + drop_sound = 'sound/items/drop/accessory.ogg' /obj/item/clothing/ears/skrell/band/silver name = "Silver headtail bands" diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index b518427e81..f0a9c64d95 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -27,6 +27,7 @@ BLIND // can't see anything var/activation_sound = 'sound/items/goggles_charge.ogg' var/obj/screen/overlay = null var/list/away_planes //Holder for disabled planes + drop_sound = 'sound/items/drop/accessory.ogg' sprite_sheets = list( "Teshari" = 'icons/mob/species/seromi/eyes.dmi', @@ -161,6 +162,7 @@ BLIND // can't see anything item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") body_parts_covered = 0 var/eye = null + drop_sound = 'sound/items/drop/gloves.ogg' /obj/item/clothing/glasses/eyepatch/verb/switcheye() set name = "Switch Eyepatch" @@ -342,6 +344,7 @@ BLIND // can't see anything item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") flash_protection = FLASH_PROTECTION_MAJOR tint = BLIND + drop_sound = 'sound/items/drop/gloves.ogg' /obj/item/clothing/glasses/sunglasses/blindfold/tape name = "length of tape" @@ -436,13 +439,13 @@ BLIND // can't see anything toggleable = 1 action_button_name = "Toggle Goggles" vision_flags = SEE_MOBS - enables_planes = list(VIS_FULLBRIGHT) + enables_planes = list(VIS_FULLBRIGHT, VIS_CLOAKED) flash_protection = FLASH_PROTECTION_REDUCED emp_act(severity) if(istype(src.loc, /mob/living/carbon/human)) var/mob/living/carbon/human/M = src.loc - M << "The Optical Thermal Scanner overloads and blinds you!" + to_chat(M, "The Optical Thermal Scanner overloads and blinds you!") if(M.glasses == src) M.Blind(3) M.eye_blurry = 5 diff --git a/code/modules/clothing/glasses/glasses_vr.dm b/code/modules/clothing/glasses/glasses_vr.dm index 20cdddd449..a9a43cc297 100644 --- a/code/modules/clothing/glasses/glasses_vr.dm +++ b/code/modules/clothing/glasses/glasses_vr.dm @@ -27,14 +27,14 @@ //Too difficult if(target == user) - user << "You can't use this on yourself. Get someone to help you." + to_chat(user, "You can't use this on yourself. Get someone to help you.") return //We're applying a prescription if(istype(target,/obj/item/clothing/glasses)) var/obj/item/clothing/glasses/G = target if(!scrip_loaded) - user << "You need to build a prescription from someone first! Use the kit on someone." + to_chat(user, "You need to build a prescription from someone first! Use the kit on someone.") return if(do_after(user,5 SECONDS)) @@ -45,7 +45,7 @@ else if(ishuman(target)) var/mob/living/carbon/human/T = target if(T.glasses || (T.head && T.head.flags_inv & HIDEEYES)) - user << "The person's eyes can't be covered!" + to_chat(user, "The person's eyes can't be covered!") return T.visible_message("[user] begins making measurements for prescription lenses for [target].","[user] begins measuring your eyes. Hold still!") diff --git a/code/modules/clothing/glasses/hud_vr.dm b/code/modules/clothing/glasses/hud_vr.dm index 44b1d12e7b..aa7715c81a 100644 --- a/code/modules/clothing/glasses/hud_vr.dm +++ b/code/modules/clothing/glasses/hud_vr.dm @@ -34,7 +34,7 @@ /obj/item/clothing/glasses/omnihud/proc/flashed() if(flash_prot && ishuman(loc)) - loc << "Your [src.name] darken to try and protect your eyes!" + to_chat(loc, "Your [src.name] darken to try and protect your eyes!") /obj/item/clothing/glasses/omnihud/prescribe(var/mob/user) prescription = !prescription @@ -52,10 +52,10 @@ var/mob/living/carbon/human/H = user if(!H.glasses || !(H.glasses == src)) - user << "You must be wearing the [src] to see the display." + to_chat(user, "You must be wearing the [src] to see the display.") else if(!ar_interact(H)) - user << "The [src] does not have any kind of special display." + to_chat(user, "The [src] does not have any kind of special display.") /obj/item/clothing/glasses/omnihud/proc/ar_interact(var/mob/living/carbon/human/user) return 0 //The base models do nothing. @@ -70,7 +70,7 @@ These have been upgraded with medical records access and virus database integration." mode = "med" action_button_name = "AR Console (Crew Monitor)" - arscreen_path = /datum/nano_module/crew_monitor + arscreen_path = /datum/nano_module/program/crew_monitor enables_planes = list(VIS_CH_ID,VIS_CH_HEALTH_VR,VIS_CH_STATUS_R,VIS_CH_BACKUP,VIS_AUGMENTED) ar_interact(var/mob/living/carbon/human/user) @@ -144,13 +144,13 @@ icon_state = off_state item_state = "[initial(item_state)]-off" usr.update_inv_glasses() - usr << "You deactivate the retinal projector on the [src]." + to_chat(usr, "You deactivate the retinal projector on the [src].") else active = 1 icon_state = initial(icon_state) item_state = initial(item_state) usr.update_inv_glasses() - usr << "You activate the retinal projector on the [src]." + to_chat(usr, "You activate the retinal projector on the [src].") usr.update_action_buttons() /obj/item/clothing/glasses/omnihud/all diff --git a/code/modules/clothing/gloves/antagonist.dm b/code/modules/clothing/gloves/antagonist.dm new file mode 100644 index 0000000000..d1a01d0e68 --- /dev/null +++ b/code/modules/clothing/gloves/antagonist.dm @@ -0,0 +1,157 @@ +/* + * Antagonist-specific gloves, such as traitor or ling-only types. + */ + +// Thief - Traitor / Merc +/obj/item/clothing/gloves/sterile/thieves + name = "sterile gloves" + desc = "Sterile gloves." + description_antag = "These gloves are uniquely suited for stealing, as well as breaking and entering. They have minor insulation.\ + Attempting to 'help' someone will open their backpack, if it exists, or their belt if they have no backpack, allowing you to deposit\ + items into the inventories. Be careful about making too much noise.\ + Disarm intent will swap the items in your LEFT pockets. Grab will swap RIGHT pockets." + icon_state = "latex" + item_state_slots = list(slot_r_hand_str = "white", slot_l_hand_str = "white") + siemens_coefficient = 0.5 // Not perfect, but slightly more protective than nothing. + permeability_coefficient = 0.01 + germ_level = 0 + fingerprint_chance = 10 // They're thieves' gloves. What do you think? + +/obj/item/clothing/gloves/sterile/thieves/proc/pickpocket(var/mob/living/carbon/human/user, var/mob/living/carbon/human/target, var/proximity) + if(!proximity || !user || !target) + return 0 + + if(!istype(target)) + return 0 + + if(user.a_intent != I_HURT && (turn(target.dir, 180) == get_dir(user, target))) + to_chat(target, "[user] rifles in your pockets!") + + if(user.a_intent == I_HELP) + if(istype(target.back,/obj/item/weapon/storage) && do_after(user, 3 SECONDS, target)) + var/obj/item/weapon/storage/Backpack = target.back + Backpack.open(user) + else if(istype(target.belt, /obj/item/weapon/storage) && do_after(user, 5 SECONDS, target)) + var/obj/item/weapon/storage/Belt = target.belt + Belt.open(user) + return 1 + + if(user.a_intent == I_DISARM) + var/obj/item/LTarg = target.l_store + var/obj/item/LUser = user.l_store + + if(do_after(user, 1 SECOND, target)) + if(istype(LTarg) && do_after(user, 1 SECOND, target)) + target.drop_from_inventory(LTarg) + target.l_store = null + user.l_store = LTarg + LTarg.forceMove(user) + LTarg.equipped(user, slot_l_store) + else + target.drop_from_inventory(LTarg) + + if(istype(LUser) && do_after(user, 1 SECOND, target)) + user.drop_from_inventory(LUser) + target.l_store = LUser + LUser.forceMove(target) + LUser.equipped(target, slot_l_store) + else if(istype(LUser) && LUser != user.l_store) // We've taken something, so drop the one that's in bluespace. + user.drop_from_inventory(LUser) + + return 1 + + if(user.a_intent == I_GRAB) + var/obj/item/RTarg = target.r_store + var/obj/item/RUser = user.r_store + + if(do_after(user, 1 SECOND, target)) + if(istype(RTarg) && do_after(user, 1 SECOND, target)) + target.drop_from_inventory(RTarg) + target.r_store = null + user.r_store = RTarg + RTarg.forceMove(user) + RTarg.equipped(user, slot_r_store) + else + target.drop_from_inventory(RTarg) + + if(istype(RUser) && do_after(user, 1 SECOND, target)) + user.drop_from_inventory(RUser) + target.r_store = RUser + RUser.forceMove(target) + RUser.equipped(target, slot_r_store) + else if(istype(RUser) && RUser != user.r_store) // We've taken something, so drop the one that's in bluespace. + user.drop_from_inventory(RUser) + + return 1 + +/obj/item/clothing/gloves/sterile/thieves/Touch(var/atom/A, var/proximity) + if(proximity && istype(usr, /mob/living/carbon/human) && do_after(usr, 1 SECOND, A)) + return pickpocket(usr, A, proximity) + return 0 + +// Buzzer Ring - Traitor, Merc. +/obj/item/clothing/gloves/ring/buzzer + name = "ring" + desc = "A plain metal band." + description_antag = "This morphium-alloy ring continually generates an electric field, capable of electrocuting a target while not injuring the wearer.\ + The device is also capable of 'frankenstein'-ing a corpse, long after normal technology would be able to save them. The body will still be tied to the\ + normal damage limits for survival, however, so care must be taken." + icon_state = "material" + var/battery_type = /obj/item/weapon/cell/device/weapon/recharge + var/obj/item/weapon/cell/battery = null + +/obj/item/clothing/gloves/ring/buzzer/get_cell() + return battery + +/obj/item/clothing/gloves/ring/buzzer/Initialize() + ..() + if(!battery) + battery = new battery_type(src) + +/obj/item/clothing/gloves/ring/buzzer/Touch(var/atom/A, var/proximity) + if(proximity && istype(usr, /mob/living/carbon/human)) + return zap(usr, A, proximity) + return 0 + +/obj/item/clothing/gloves/ring/buzzer/proc/zap(var/mob/living/carbon/human/user, var/atom/movable/target, var/proximity) + . = FALSE + if(user.a_intent == I_HURT && battery.percent() >= 50) + if(isliving(target)) + var/mob/living/L = target + + if(ishuman(L) && battery.percent() >= 90) // Silent text-wise, for maximum potential for gimmicks. + var/mob/living/carbon/human/H = L + + if(H.stat == DEAD) + . = TRUE + + do_defib(H) + + to_chat(L, "You feel a powerful shock!") + if(!.) + playsound(L, 'sound/effects/sparks7.ogg', 40, 1) + L.electrocute_act(battery.percent() * 0.25, src) + battery.emp_act(2) + return . + + return 0 + +/obj/item/clothing/gloves/ring/buzzer/proc/do_defib(var/mob/living/carbon/human/H = null) + if(!istype(H)) + return 0 + + dead_mob_list.Remove(H) + if((H in living_mob_list) || (H in dead_mob_list)) + WARNING("Mob [H] was ring-defibbed but already in the living or dead list still!") + living_mob_list += H + + H.timeofdeath = 0 + H.set_stat(UNCONSCIOUS) + H.failed_last_breath = 0 + H.reload_fullscreen() + + H.emote("gasp") + H.Weaken(rand(10,25)) + H.updatehealth() + + battery.emp_act(1) diff --git a/code/modules/clothing/gloves/arm_guards.dm b/code/modules/clothing/gloves/arm_guards.dm index 84ebecdc1e..558f855545 100644 --- a/code/modules/clothing/gloves/arm_guards.dm +++ b/code/modules/clothing/gloves/arm_guards.dm @@ -5,6 +5,7 @@ overgloves = 1 punch_force = 3 w_class = ITEMSIZE_NORMAL + drop_sound = 'sound/items/drop/metalshield.ogg' /obj/item/clothing/gloves/arm_guard/mob_can_equip(var/mob/living/carbon/human/H, slot) if(..()) //This will only run if no other problems occured when equiping. diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index 739f3e3977..9316c8f64f 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -7,7 +7,7 @@ /* /obj/item/clothing/gloves/boxing/attackby(obj/item/weapon/W, mob/user) if(W.is_wirecutter() || istype(W, /obj/item/weapon/surgical/scalpel)) - user << "That won't work." //Nope + to_chat(user, "That won't work.") //Nope return ..() */ diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index a030bc038c..1c5ad9c05c 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -1,9 +1,12 @@ + + /obj/item/clothing/gloves/yellow desc = "These gloves will protect the wearer from electric shock." name = "insulated gloves" icon_state = "yellow" siemens_coefficient = 0 permeability_coefficient = 0.05 + drop_sound = 'sound/items/drop/rubber.ogg' /obj/item/clothing/gloves/fyellow //Cheap Chinese Crap desc = "These gloves are cheap copies of proper insulated gloves. No way this can end badly." @@ -11,6 +14,7 @@ icon_state = "yellow" siemens_coefficient = 1 //Set to a default of 1, gets overridden in initialize() permeability_coefficient = 0.05 + drop_sound = 'sound/items/drop/rubber.ogg' /obj/item/clothing/gloves/fyellow/Initialize() . = ..() diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index 94e2d1fef0..b44dc54567 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -56,6 +56,7 @@ permeability_coefficient = 0.01 germ_level = 0 fingerprint_chance = 25 + drop_sound = 'sound/items/drop/rubber.ogg' // var/balloonPath = /obj/item/latexballon //TODO: Make inflating gloves a thing @@ -81,6 +82,7 @@ item_state_slots = list(slot_r_hand_str = "lightbrown", slot_l_hand_str = "lightbrown") permeability_coefficient = 0.05 siemens_coefficient = 0.75 //thick work gloves + drop_sound = 'sound/items/drop/leather.ogg' /obj/item/clothing/gloves/duty desc = "These brown duty gloves are made from a durable synthetic." @@ -109,6 +111,7 @@ siemens_coefficient = 0 permeability_coefficient = 0.05 species_restricted = list("Vox") + drop_sound = 'sound/items/drop/metalboots.ogg' cold_protection = HANDS min_cold_protection_temperature = GLOVES_MIN_COLD_PROTECTION_TEMPERATURE @@ -127,6 +130,7 @@ overgloves = 1 force = 5 punch_force = 5 + drop_sound = 'sound/items/drop/metalboots.ogg' /obj/item/clothing/gloves/ranger var/glovecolor = "white" diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index 8446b09446..810ac403f7 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -37,6 +37,7 @@ desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." icon_state = "paper" body_parts_covered = 0 + drop_sound = 'sound/items/drop/paper.ogg' /obj/item/clothing/head/collectable/tophat name = "collectable top hat" diff --git a/code/modules/clothing/head/flowercrowns.dm b/code/modules/clothing/head/flowercrowns.dm index 7e13993bcb..15bfe16162 100644 --- a/code/modules/clothing/head/flowercrowns.dm +++ b/code/modules/clothing/head/flowercrowns.dm @@ -36,21 +36,25 @@ desc = "A flower crown weaved with sunflowers." icon_state = "sunflower_crown" body_parts_covered = 0 + drop_sound = 'sound/items/drop/herb.ogg' /obj/item/clothing/head/lavender_crown name = "lavender crown" desc = "A flower crown weaved with lavender." icon_state = "lavender_crown" body_parts_covered = 0 + drop_sound = 'sound/items/drop/herb.ogg' /obj/item/clothing/head/poppy_crown name = "poppy crown" desc = "A flower crown weaved with poppies." icon_state = "poppy_crown" body_parts_covered = 0 + drop_sound = 'sound/items/drop/herb.ogg' /obj/item/clothing/head/rose_crown name = "rose crown" desc = "A flower crown weaved with roses." icon_state = "poppy_crown" body_parts_covered = 0 + drop_sound = 'sound/items/drop/herb.ogg' diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index e7d7cee13d..d3db7a5b17 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -10,6 +10,7 @@ action_button_name = "Toggle Head-light" w_class = ITEMSIZE_NORMAL ear_protection = 1 + drop_sound = 'sound/items/drop/helm.ogg' /obj/item/clothing/head/hardhat/orange icon_state = "hardhat0_orange" diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index dd290af26e..2f30326e78 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -14,6 +14,7 @@ siemens_coefficient = 0.7 w_class = ITEMSIZE_NORMAL ear_protection = 1 + drop_sound = 'sound/items/drop/helm.ogg' /obj/item/clothing/head/helmet/solgov name = "\improper Solar Confederate Government helmet" @@ -69,10 +70,10 @@ /obj/item/clothing/head/helmet/riot/attack_self(mob/user as mob) if(src.icon_state == initial(icon_state)) src.icon_state = "[icon_state]up" - user << "You raise the visor on the riot helmet." + to_chat(user, "You raise the visor on the riot helmet.") else src.icon_state = initial(icon_state) - user << "You lower the visor on the riot helmet." + to_chat(user, "You lower the visor on the riot helmet.") update_clothing_icon() //so our mob-overlays update /obj/item/clothing/head/helmet/laserproof diff --git a/code/modules/clothing/head/hood_vr.dm b/code/modules/clothing/head/hood_vr.dm new file mode 100644 index 0000000000..f669d131f7 --- /dev/null +++ b/code/modules/clothing/head/hood_vr.dm @@ -0,0 +1,3 @@ +/obj/item/clothing/head/hood/techpriest + name = "techpriest hood" + armor = list(melee = 20, bullet = 10, laser = 10, energy = 10, bomb = 25, bio = 50, rad = 25) diff --git a/code/modules/clothing/head/jobs_vr.dm b/code/modules/clothing/head/jobs_vr.dm new file mode 100644 index 0000000000..19a3a8163d --- /dev/null +++ b/code/modules/clothing/head/jobs_vr.dm @@ -0,0 +1,8 @@ +/obj/item/clothing/head/helmet/detective_alt + name = "Type-34C Semi-Enclosed Headwear" + desc = "Civilian model of a popular helmet used by certain law enforcement agencies. It does not have any armor plating, but has a neo-laminated fiber lining." + icon_state = "cyberpunkgoggle" + icon = 'icons/obj/clothing/hats_vr.dmi' + icon_override = 'icons/mob/head_vr.dmi' + flags_inv = HIDEEARS|HIDEEYES|BLOCKHAIR + \ No newline at end of file diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index 368367c424..1b7544c351 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -17,6 +17,7 @@ desc = "A nice hair pin." slot_flags = SLOT_HEAD | SLOT_EARS body_parts_covered = 0 + drop_sound = 'sound/items/drop/ring.ogg' /obj/item/clothing/head/pin/pink icon_state = "pinkpin" @@ -168,6 +169,7 @@ icon_state = "cardborg_h" flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE body_parts_covered = HEAD|FACE|EYES + drop_sound = 'sound/items/drop/box.ogg' /obj/item/clothing/head/justice name = "justice hat" @@ -420,6 +422,7 @@ desc = "A jeweled headpiece originating in India." icon_state = "maangtikka" body_parts_covered = 0 + drop_sound = 'sound/items/drop/accessory.ogg' /obj/item/clothing/head/jingasa name = "jingasa" @@ -427,7 +430,7 @@ icon_state = "jingasa" body_parts_covered = 0 item_state_slots = list(slot_r_hand_str = "taq", slot_l_hand_str = "taq") - + /obj/item/clothing/head/cowl name = "black cowl" desc = "A gold-lined black cowl. It gives off uncomfortable cult vibes, but fancy." diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 643c335f73..a3da836d7c 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -28,6 +28,7 @@ var/base_state flash_protection = FLASH_PROTECTION_MAJOR tint = TINT_HEAVY + drop_sound = 'sound/items/drop/helm.ogg' /obj/item/clothing/head/welding/attack_self() toggle() @@ -150,10 +151,10 @@ /obj/item/clothing/head/ushanka/attack_self(mob/user as mob) if(src.icon_state == "ushankadown") src.icon_state = "ushankaup" - user << "You raise the ear flaps on the ushanka." + to_chat(user, "You raise the ear flaps on the ushanka.") else src.icon_state = "ushankadown" - user << "You lower the ear flaps on the ushanka." + to_chat(user, "You lower the ear flaps on the ushanka.") /* * Pumpkin head @@ -167,6 +168,7 @@ brightness_on = 2 light_overlay = "helmet_light" w_class = ITEMSIZE_NORMAL + drop_sound = 'sound/items/drop/herb.ogg' /* * Kitty ears diff --git a/code/modules/clothing/head/pilot_helmet.dm b/code/modules/clothing/head/pilot_helmet.dm index ee95c7c937..135c73d539 100644 --- a/code/modules/clothing/head/pilot_helmet.dm +++ b/code/modules/clothing/head/pilot_helmet.dm @@ -194,8 +194,8 @@ /obj/item/clothing/head/pilot/alt/attack_self(mob/user as mob) if(src.icon_state == initial(icon_state)) src.icon_state = "[icon_state]up" - user << "You raise the visor on the pilot helmet." + to_chat(user, "You raise the visor on the pilot helmet.") else src.icon_state = initial(icon_state) - user << "You lower the visor on the pilot helmet." + to_chat(user, "You lower the visor on the pilot helmet.") update_clothing_icon() //so our mob-overlays update \ No newline at end of file diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index 27b8460fb6..5bcec8a56e 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -16,10 +16,10 @@ flipped = !flipped if(flipped) icon_state = "[icon_state]_flipped" - user << "You flip the hat backwards." + to_chat(user, "You flip the hat backwards.") else icon_state = initial(icon_state) - user << "You flip the hat back in normal position." + to_chat(user, "You flip the hat back in normal position.") update_clothing_icon() //so our mob-overlays update /obj/item/clothing/head/soft/red diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index ea3e6c7cf4..b64f437207 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -20,13 +20,13 @@ body_parts_covered = body_parts_covered & ~FACE item_flags = item_flags & ~AIRTIGHT icon_state = "breathdown" - user << "Your mask is now hanging on your neck." + to_chat(user, "Your mask is now hanging on your neck.") else gas_transfer_coefficient = initial(gas_transfer_coefficient) body_parts_covered = initial(body_parts_covered) item_flags = initial(item_flags) icon_state = initial(icon_state) - user << "You pull the mask up to cover your face." + to_chat(user, "You pull the mask up to cover your face.") update_clothing_icon() /obj/item/clothing/mask/breath/attack_self(mob/user) diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index ea56a0c526..eab03a3c2e 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -47,13 +47,13 @@ body_parts_covered = body_parts_covered & ~FACE armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) icon_state = "steriledown" - usr << "You pull the mask below your chin." + to_chat(usr, "You pull the mask below your chin.") else gas_transfer_coefficient = initial(gas_transfer_coefficient) body_parts_covered = initial(body_parts_covered) icon_state = initial(icon_state) armor = initial(armor) - usr << "You pull the mask up to cover your face." + to_chat(usr, "You pull the mask up to cover your face.") update_clothing_icon() /obj/item/clothing/mask/surgical/verb/toggle() @@ -169,7 +169,7 @@ body_parts_covered = HEAD|FACE|EYES w_class = ITEMSIZE_SMALL siemens_coefficient = 0.9 - + /obj/item/clothing/mask/nock_scarab name = "nock mask (blue, scarab)" desc = "To Nock followers, masks symbolize rebirth and a new persona. Damaging the wearer's mask is generally considered an attack on their person itself." diff --git a/code/modules/clothing/masks/monitor.dm b/code/modules/clothing/masks/monitor.dm index 9b0c94ca03..230f224d09 100644 --- a/code/modules/clothing/masks/monitor.dm +++ b/code/modules/clothing/masks/monitor.dm @@ -27,7 +27,7 @@ if(robohead.monitor_styles) monitor_states = params2list(robohead.monitor_styles) icon_state = monitor_states[monitor_state_index] - H << "\The [src] connects to your display output." + to_chat(H, "\The [src] connects to your display output.") /obj/item/clothing/mask/monitor/dropped() canremove = 1 @@ -41,7 +41,7 @@ var/datum/robolimb/robohead = all_robolimbs[E.model] if(istype(E) && (E.robotic >= ORGAN_ROBOT) && robohead.monitor_styles) return 1 - user << "You must have a compatible robotic head to install this upgrade." + to_chat(user, "You must have a compatible robotic head to install this upgrade.") return 0 /obj/item/clothing/mask/monitor/verb/set_monitor_state() @@ -54,7 +54,7 @@ if(!istype(H) || H != usr) return if(H.wear_mask != src) - usr << "You have not installed \the [src] yet." + to_chat(usr, "You have not installed \the [src] yet.") return var/choice = input("Select a screen icon.") as null|anything in monitor_states if(choice) diff --git a/code/modules/clothing/rings/rings.dm b/code/modules/clothing/rings/rings.dm index 2838de0d2f..b8313c5f97 100644 --- a/code/modules/clothing/rings/rings.dm +++ b/code/modules/clothing/rings/rings.dm @@ -1,3 +1,11 @@ +//Generic Ring + +/obj/item/clothing/gloves/ring + name = "generic ring" + desc = "Torus shaped finger decoration." + icon_state = "material" + drop_sound = 'sound/items/drop/ring.ogg' + ///////////////////////////////////////// //Standard Rings /obj/item/clothing/gloves/ring/engagement diff --git a/code/modules/clothing/shoes/boots.dm b/code/modules/clothing/shoes/boots.dm index dd5e7956df..b0ccf41175 100644 --- a/code/modules/clothing/shoes/boots.dm +++ b/code/modules/clothing/shoes/boots.dm @@ -5,11 +5,13 @@ force = 3 can_hold_knife = 1 step_volume_mod = 1.2 + drop_sound = 'sound/items/drop/boots.ogg' /obj/item/clothing/shoes/boots/cowboy name = "cowboy boots" desc = "Lacking a durasteel horse to ride." icon_state = "cowboy" + drop_sound = 'sound/items/drop/leather.ogg' /obj/item/clothing/shoes/boots/cowboy/classic name = "classic cowboy boots" diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index 85ffb970b8..8f72b24163 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -1,3 +1,4 @@ + /obj/item/clothing/shoes/black name = "black shoes" icon_state = "black" @@ -38,6 +39,7 @@ name = "leather shoes" desc = "A sturdy pair of leather shoes." icon_state = "leather" + drop_sound = 'sound/items/drop/leather.ogg' /obj/item/clothing/shoes/rainbow name = "rainbow shoes" diff --git a/code/modules/clothing/shoes/leg_guards.dm b/code/modules/clothing/shoes/leg_guards.dm index 14e24dad92..a6eefc1247 100644 --- a/code/modules/clothing/shoes/leg_guards.dm +++ b/code/modules/clothing/shoes/leg_guards.dm @@ -7,12 +7,13 @@ w_class = ITEMSIZE_NORMAL step_volume_mod = 1.3 can_hold_knife = TRUE + drop_sound = 'sound/items/drop/boots.ogg' /obj/item/clothing/shoes/leg_guard/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = 0) if(..()) //This will only run if no other problems occured when equiping. if(H.wear_suit) if(H.wear_suit.body_parts_covered & LEGS) - H << "You can't wear \the [src] with \the [H.wear_suit], it's in the way." + to_chat(H, "You can't wear \the [src] with \the [H.wear_suit], it's in the way.") return 0 for(var/obj/item/clothing/accessory/A in H.wear_suit) if(A.body_parts_covered & LEGS) diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 5020307699..4f10a56f9d 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -5,6 +5,7 @@ item_flags = PHORONGUARD item_state_slots = list(slot_r_hand_str = "magboots", slot_l_hand_str = "magboots") species_restricted = null + center_of_mass = list("x" = 17,"y" = 12) force = 3 overshoes = 1 shoes_under_pants = -1 //These things are huge @@ -15,6 +16,7 @@ var/obj/item/clothing/shoes/shoes = null //Undershoes var/mob/living/carbon/human/wearer = null //For shoe procs step_volume_mod = 1.3 + drop_sound = 'sound/items/drop/metalboots.ogg' /obj/item/clothing/shoes/magboots/proc/set_slowdown() slowdown = shoes? max(SHOES_SLOWDOWN, shoes.slowdown): SHOES_SLOWDOWN //So you can't put on magboots to make you walk faster. @@ -28,14 +30,15 @@ set_slowdown() force = 3 if(icon_base) icon_state = "[icon_base]0" - user << "You disable the mag-pulse traction system." + to_chat(user, "You disable the mag-pulse traction system.") else item_flags |= NOSLIP magpulse = 1 set_slowdown() force = 5 if(icon_base) icon_state = "[icon_base]1" - user << "You enable the mag-pulse traction system." + playsound(get_turf(src), 'sound/effects/magnetclamp.ogg', 20) + to_chat(user, "You enable the mag-pulse traction system.") user.update_inv_shoes() //so our mob-overlays update user.update_action_buttons() @@ -79,7 +82,7 @@ var/state = "disabled" if(item_flags & NOSLIP) state = "enabled" - user << "Its mag-pulse traction system appears to be [state]." + to_chat(user, "Its mag-pulse traction system appears to be [state].") /obj/item/clothing/shoes/magboots/vox diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index e2a93f1dff..0e492c6d6c 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -24,6 +24,7 @@ item_flags = NOSLIP slowdown = SHOES_SLOWDOWN+1 species_restricted = null + drop_sound = 'sound/items/drop/rubber.ogg' /obj/item/clothing/shoes/dress name = "dress shoes" @@ -98,6 +99,7 @@ force = 0 species_restricted = null w_class = ITEMSIZE_SMALL + drop_sound = 'sound/items/drop/clothing.ogg' /obj/item/clothing/shoes/slippers_worn name = "worn bunny slippers" @@ -155,6 +157,7 @@ force = 0 w_class = ITEMSIZE_SMALL species_restricted = null + drop_sound = 'sound/items/drop/clothing.ogg' /obj/item/clothing/shoes/boots/ranger var/bootcolor = "white" diff --git a/code/modules/clothing/spacesuits/breaches.dm b/code/modules/clothing/spacesuits/breaches.dm index ee46fa4a8f..8af972ec73 100644 --- a/code/modules/clothing/spacesuits/breaches.dm +++ b/code/modules/clothing/spacesuits/breaches.dm @@ -55,7 +55,7 @@ var/global/list/breach_burn_descriptors = list( /obj/item/clothing/suit/space/proc/repair_breaches(var/damtype, var/amount, var/mob/user) if(!can_breach || !breaches || !breaches.len || !damage) - user << "There are no breaches to repair on \the [src]." + to_chat(user, "There are no breaches to repair on \the [src].") return var/list/valid_breaches = list() @@ -65,7 +65,7 @@ var/global/list/breach_burn_descriptors = list( valid_breaches += B if(!valid_breaches.len) - user << "There are no breaches to repair on \the [src]." + to_chat(user, "There are no breaches to repair on \the [src].") return var/amount_left = amount @@ -190,11 +190,11 @@ var/global/list/breach_burn_descriptors = list( return if(istype(src.loc,/mob/living)) - user << "How do you intend to patch a hardsuit while someone is wearing it?" + to_chat(user, "How do you intend to patch a hardsuit while someone is wearing it?") return if(!damage || !burn_damage) - user << "There is no surface damage on \the [src] to repair." + to_chat(user, "There is no surface damage on \the [src] to repair.") return var/obj/item/stack/P = W @@ -206,16 +206,16 @@ var/global/list/breach_burn_descriptors = list( else if(istype(W, /obj/item/weapon/weldingtool)) if(istype(src.loc,/mob/living)) - user << "How do you intend to patch a hardsuit while someone is wearing it?" + to_chat(user, "How do you intend to patch a hardsuit while someone is wearing it?") return if (!damage || ! brute_damage) - user << "There is no structural damage on \the [src] to repair." + to_chat(user, "There is no structural damage on \the [src] to repair.") return var/obj/item/weapon/weldingtool/WT = W if(!WT.remove_fuel(5)) - user << "You need more welding fuel to repair this suit." + to_chat(user, "You need more welding fuel to repair this suit.") return repair_breaches(BRUTE, 3, user) @@ -227,4 +227,4 @@ var/global/list/breach_burn_descriptors = list( ..(user) if(can_breach && breaches && breaches.len) for(var/datum/breach/B in breaches) - user << "It has \a [B.descriptor]." + to_chat(user, "It has \a [B.descriptor].") diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm index 890da50468..5d92408e56 100644 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm @@ -51,10 +51,10 @@ return 0 if(accepted_item.charges >= 5) - user << "Another grenade of that type will not fit into the module." + to_chat(user, "Another grenade of that type will not fit into the module.") return 0 - user << "You slot \the [input_device] into the suit module." + to_chat(user, "You slot \the [input_device] into the suit module.") user.drop_from_inventory(input_device) qdel(input_device) accepted_item.charges++ @@ -71,7 +71,7 @@ var/mob/living/carbon/human/H = holder.wearer if(!charge_selected) - H << "You have not selected a grenade type." + to_chat(H, "You have not selected a grenade type.") return 0 var/datum/rig_charge/charge = charges[charge_selected] @@ -80,7 +80,7 @@ return 0 if(charge.charges <= 0) - H << "Insufficient grenades!" + to_chat(H, "Insufficient grenades!") return 0 charge.charges-- @@ -199,7 +199,7 @@ var/mob/living/M = holder.wearer if(M.l_hand && M.r_hand) - M << "Your hands are full." + to_chat(M, "Your hands are full.") deactivate() return @@ -252,11 +252,11 @@ firing.throw_at(target,fire_force,fire_distance) else if(H.l_hand && H.r_hand) - H << "Your hands are full." + to_chat(H, "Your hands are full.") else var/obj/item/new_weapon = new fabrication_type() new_weapon.forceMove(H) - H << "You quickly fabricate \a [new_weapon]." + to_chat(H, "You quickly fabricate \a [new_weapon].") H.put_in_hands(new_weapon) return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/combat_vr.dm b/code/modules/clothing/spacesuits/rig/modules/combat_vr.dm deleted file mode 100644 index f62c0bf4b8..0000000000 --- a/code/modules/clothing/spacesuits/rig/modules/combat_vr.dm +++ /dev/null @@ -1,12 +0,0 @@ -/obj/item/rig_module/grenade_launcher/cleaner - name = "mounted cleaner-grenade launcher" - desc = "A shoulder-mounted cleaner-grenade dispenser." - - interface_name = "integrated cleaner-grenade launcher" - interface_desc = "Discharges loaded cleaner-grenades against the wearer's location." - - fire_force = 15 - - charges = list( - list("cleaner grenade", "cleaner grenade", /obj/item/weapon/grenade/chem_grenade/cleaner, 6) - ) diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm index 9308e44cb0..f2e9e689b4 100644 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -15,12 +15,12 @@ set src in usr if(!usr.loc || !usr.loc.loc || !istype(usr.loc.loc, /obj/item/rig_module)) - usr << "You are not loaded into a hardsuit." + to_chat(usr, "You are not loaded into a hardsuit.") return var/obj/item/rig_module/module = usr.loc.loc if(!module.holder) - usr << "Your module is not installed in a hardsuit." + to_chat(usr, "Your module is not installed in a hardsuit.") return module.holder.ui_interact(usr, nano_state = contained_state) @@ -163,9 +163,9 @@ if(istype(ai_card, /obj/item/device/aicard)) if(integrated_ai && !integrated_ai.stat) if(user) - user << "You cannot eject your currently stored AI. Purge it manually." + to_chat(user, "You cannot eject your currently stored AI. Purge it manually.") return 0 - user << "You purge the previous AI from your Integrated Intelligence System, freeing it for use." + to_chat(user, "You purge the previous AI from your Integrated Intelligence System, freeing it for use.") if(integrated_ai) integrated_ai.ghostize() qdel(integrated_ai) @@ -208,8 +208,8 @@ user.drop_from_inventory(ai) ai.forceMove(src) ai_card = ai - ai_mob << "You have been transferred to \the [holder]'s [src]." - user << "You load [ai_mob] into \the [holder]'s [src]." + to_chat(ai_mob, "You have been transferred to \the [holder]'s [src].") + to_chat(user, "You load [ai_mob] into \the [holder]'s [src].") integrated_ai = ai_mob @@ -217,9 +217,9 @@ integrated_ai = null eject_ai() else - user << "There is no active AI within \the [ai]." + to_chat(user, "There is no active AI within \the [ai].") else - user << "There is no active AI within \the [ai]." + to_chat(user, "There is no active AI within \the [ai].") update_verb_holder() return @@ -257,16 +257,16 @@ /obj/item/rig_module/datajack/accepts_item(var/obj/item/input_device, var/mob/living/user) if(istype(input_device,/obj/item/weapon/disk/tech_disk)) - user << "You slot the disk into [src]." + to_chat(user, "You slot the disk into [src].") var/obj/item/weapon/disk/tech_disk/disk = input_device if(disk.stored) if(load_data(disk.stored)) - user << "Download successful; disk erased." + to_chat(user, "Download successful; disk erased.") disk.stored = null else - user << "The disk is corrupt. It is useless to you." + to_chat(user, "The disk is corrupt. It is useless to you.") else - user << "The disk is blank. It is useless to you." + to_chat(user, "The disk is blank. It is useless to you.") return 1 // I fucking hate R&D code. This typecheck spam would be totally unnecessary in a sane setup. @@ -283,13 +283,13 @@ incoming_files = input_machine.files if(!incoming_files || !incoming_files.known_tech || !incoming_files.known_tech.len) - user << "Memory failure. There is nothing accessible stored on this terminal." + to_chat(user, "Memory failure. There is nothing accessible stored on this terminal.") else // Maybe consider a way to drop all your data into a target repo in the future. if(load_data(incoming_files.known_tech)) - user << "Download successful; local and remote repositories synchronized." + to_chat(user, "Download successful; local and remote repositories synchronized.") else - user << "Scan complete. There is nothing useful stored on this terminal." + to_chat(user, "Scan complete. There is nothing useful stored on this terminal.") return 1 return 0 @@ -368,7 +368,7 @@ if(interfaced_with) if(holder && holder.wearer) - holder.wearer << "Your power sink retracts as the module deactivates." + to_chat(holder.wearer, "Your power sink retracts as the module deactivates.") drain_complete() interfaced_with = null total_power_drained = 0 @@ -400,7 +400,7 @@ if(target.drain_power(1) <= 0) return 0 - H << "You begin draining power from [target]!" + to_chat(H, "You begin draining power from [target]!") interfaced_with = target drain_loc = interfaced_with.loc @@ -434,17 +434,17 @@ H.break_cloak() if(!holder.cell) - H << "Your power sink flashes an error; there is no cell in your rig." + to_chat(H, "Your power sink flashes an error; there is no cell in your rig.") drain_complete(H) return if(!interfaced_with || !interfaced_with.Adjacent(H) || !(interfaced_with.loc == drain_loc)) - H << "Your power sink retracts into its casing." + to_chat(H, "Your power sink retracts into its casing.") drain_complete(H) return if(holder.cell.fully_charged()) - H << "Your power sink flashes an amber light; your rig cell is full." + to_chat(H, "Your power sink flashes an amber light; your rig cell is full.") drain_complete(H) return @@ -453,7 +453,7 @@ var/to_drain = min(12.5*holder.cell.maxcharge, ((holder.cell.maxcharge - holder.cell.charge) / CELLRATE)) var/target_drained = interfaced_with.drain_power(0,0,to_drain) if(target_drained <= 0) - H << "Your power sink flashes a red light; there is no power left in [interfaced_with]." + to_chat(H, "Your power sink flashes a red light; there is no power left in [interfaced_with].") drain_complete(H) return @@ -465,9 +465,11 @@ /obj/item/rig_module/power_sink/proc/drain_complete(var/mob/living/M) if(!interfaced_with) - if(M) M << "Total power drained: [round(total_power_drained*CELLRATE)] cell units." + if(M) + to_chat(M, "Total power drained: [round(total_power_drained*CELLRATE)] cell units.") else - if(M) M << "Total power drained from [interfaced_with]: [round(total_power_drained*CELLRATE)] cell units." + if(M) + to_chat(M, "Total power drained from [interfaced_with]: [round(total_power_drained*CELLRATE)] cell units.") interfaced_with.drain_power(0,1,0) // Damage the victim. drain_loc = null diff --git a/code/modules/clothing/spacesuits/rig/modules/modules.dm b/code/modules/clothing/spacesuits/rig/modules/modules.dm index 0e0e366d82..eece796510 100644 --- a/code/modules/clothing/spacesuits/rig/modules/modules.dm +++ b/code/modules/clothing/spacesuits/rig/modules/modules.dm @@ -58,28 +58,28 @@ ..() switch(damage) if(0) - usr << "It is undamaged." + to_chat(usr, "It is undamaged.") if(1) - usr << "It is badly damaged." + to_chat(usr, "It is badly damaged.") if(2) - usr << "It is almost completely destroyed." + to_chat(usr, "It is almost completely destroyed.") /obj/item/rig_module/attackby(obj/item/W as obj, mob/user as mob) if(istype(W,/obj/item/stack/nanopaste)) if(damage == 0) - user << "There is no damage to mend." + to_chat(user, "There is no damage to mend.") return - user << "You start mending the damaged portions of \the [src]..." + to_chat(user, "You start mending the damaged portions of \the [src]...") if(!do_after(user,30) || !W || !src) return var/obj/item/stack/nanopaste/paste = W damage = 0 - user << "You mend the damage to [src] with [W]." + to_chat(user, "You mend the damage to [src] with [W].") paste.use(1) return @@ -87,23 +87,23 @@ switch(damage) if(0) - user << "There is no damage to mend." + to_chat(user, "There is no damage to mend.") return if(2) - user << "There is no damage that you are capable of mending with such crude tools." + to_chat(user, "There is no damage that you are capable of mending with such crude tools.") return var/obj/item/stack/cable_coil/cable = W if(!cable.amount >= 5) - user << "You need five units of cable to repair \the [src]." + to_chat(user, "You need five units of cable to repair \the [src].") return - user << "You start mending the damaged portions of \the [src]..." + to_chat(user, "You start mending the damaged portions of \the [src]...") if(!do_after(user,30) || !W || !src) return damage = 1 - user << "You mend some of damage to [src] with [W], but you will need more advanced tools to fix it completely." + to_chat(user, "You mend some of damage to [src] with [W], but you will need more advanced tools to fix it completely.") cable.use(5) return ..() @@ -143,27 +143,27 @@ /obj/item/rig_module/proc/engage() if(damage >= 2) - usr << "The [interface_name] is damaged beyond use!" + to_chat(usr, "The [interface_name] is damaged beyond use!") return 0 if(world.time < next_use) - usr << "You cannot use the [interface_name] again so soon." + to_chat(usr, "You cannot use the [interface_name] again so soon.") return 0 if(!holder || holder.canremove) - usr << "The suit is not initialized." + to_chat(usr, "The suit is not initialized.") return 0 if(usr.lying || usr.stat || usr.stunned || usr.paralysis || usr.weakened) - usr << "You cannot use the suit in this state." + to_chat(usr, "You cannot use the suit in this state.") return 0 if(holder.wearer && holder.wearer.lying) - usr << "The suit cannot function while the wearer is prone." + to_chat(usr, "The suit cannot function while the wearer is prone.") return 0 if(holder.security_check_enabled && !holder.check_suit_access(usr)) - usr << "Access denied." + to_chat(usr, "Access denied.") return 0 if(!holder.check_power_cost(usr, use_power_cost, 0, src, (istype(usr,/mob/living/silicon ? 1 : 0) ) ) ) diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm index 704ed7a47b..6381e6b1b0 100644 --- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm +++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm @@ -100,7 +100,7 @@ var/mob/living/carbon/human/H = holder.wearer if(!istype(H.loc, /turf)) - H << "You cannot teleport out of your current location." + to_chat(H, "You cannot teleport out of your current location.") return 0 var/turf/T @@ -110,23 +110,23 @@ T = get_teleport_loc(get_turf(H), H, 6, 1, 1, 1) if(!T) - H << "No valid teleport target found." + to_chat(H, "No valid teleport target found.") return 0 if(T.density) - H << "You cannot teleport into solid walls." + to_chat(H, "You cannot teleport into solid walls.") return 0 if(T.z in using_map.admin_levels) - H << "You cannot use your teleporter on this Z-level." + to_chat(H, "You cannot use your teleporter on this Z-level.") return 0 if(T.contains_dense_objects()) - H << "You cannot teleport to a location with solid objects." + to_chat(H, "You cannot teleport to a location with solid objects.") return 0 if(T.z != H.z || get_dist(T, get_turf(H)) > world.view) - H << "You cannot teleport to such a distant object." + to_chat(H, "You cannot teleport to such a distant object.") return 0 if(!..()) return 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm b/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm new file mode 100644 index 0000000000..6dc5fc4f01 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm @@ -0,0 +1,216 @@ +/obj/item/ai_verbs + name = "AI verb holder" + +/obj/item/ai_verbs/verb/hardsuit_interface() + set category = "Hardsuit" + set name = "Open Hardsuit Interface" + set src in usr + + if(!usr.loc || !usr.loc.loc || !istype(usr.loc.loc, /obj/item/rig_module)) + to_chat(usr, "You are not loaded into a hardsuit.") + return + + var/obj/item/rig_module/module = usr.loc.loc + if(!module.holder) + to_chat(usr, "Your module is not installed in a hardsuit.") + return + + module.holder.ui_interact(usr, nano_state = contained_state) + +/obj/item/rig_module/ai_container + + name = "IIS module" + desc = "An integrated intelligence system module suitable for most hardsuits." + icon_state = "IIS" + toggleable = 1 + usable = 1 + disruptive = 0 + activates_on_touch = 1 + + engage_string = "Eject AI" + activate_string = "Enable Core Transfer" + deactivate_string = "Disable Core Transfer" + + interface_name = "integrated intelligence system" + interface_desc = "A socket that supports a range of artificial intelligence systems." + + var/mob/integrated_ai // Direct reference to the actual mob held in the suit. + var/obj/item/ai_card // Reference to the MMI, posibrain, intellicard or pAI card previously holding the AI. + var/obj/item/ai_verbs/verb_holder + +/obj/item/rig_module/ai_container/process() + if(integrated_ai) + var/obj/item/weapon/rig/rig = get_rig() + if(rig && rig.ai_override_enabled) + integrated_ai.get_rig_stats = 1 + else + integrated_ai.get_rig_stats = 0 + +/mob/living/Stat() + . = ..() + if(. && get_rig_stats) + var/obj/item/weapon/rig/rig = get_rig() + if(rig) + SetupStat(rig) + +/obj/item/rig_module/ai_container/proc/update_verb_holder() + if(!verb_holder) + verb_holder = new(src) + if(integrated_ai) + verb_holder.forceMove(integrated_ai) + else + verb_holder.forceMove(src) + +/obj/item/rig_module/ai_container/accepts_item(var/obj/item/input_device, var/mob/living/user) + + // Check if there's actually an AI to deal with. + var/mob/living/silicon/ai/target_ai + if(istype(input_device, /mob/living/silicon/ai)) + target_ai = input_device + else + target_ai = locate(/mob/living/silicon/ai) in input_device.contents + + var/obj/item/device/aicard/card = ai_card + + // Downloading from/loading to a terminal. + if(istype(input_device,/obj/machinery/computer/aifixer) || istype(input_device,/mob/living/silicon/ai) || istype(input_device,/obj/structure/AIcore/deactivated)) + + // If we're stealing an AI, make sure we have a card for it. + if(!card) + card = new /obj/item/device/aicard(src) + + // Terminal interaction only works with an intellicarded AI. + if(!istype(card)) + return 0 + + // Since we've explicitly checked for three types, this should be safe. + input_device.attackby(card,user) + + // If the transfer failed we can delete the card. + if(locate(/mob/living/silicon/ai) in card) + ai_card = card + integrated_ai = locate(/mob/living/silicon/ai) in card + else + eject_ai() + update_verb_holder() + return 1 + + if(istype(input_device,/obj/item/device/aicard)) + // We are carding the AI in our suit. + if(integrated_ai) + integrated_ai.attackby(input_device,user) + // If the transfer was successful, we can clear out our vars. + if(integrated_ai.loc != src) + integrated_ai = null + eject_ai() + else + // You're using an empty card on an empty suit, idiot. + if(!target_ai) + return 0 + integrate_ai(input_device,user) + return 1 + + // Okay, it wasn't a terminal being touched, check for all the simple insertions. + if(input_device.type in list(/obj/item/device/paicard, /obj/item/device/mmi, /obj/item/device/mmi/digital/posibrain)) + if(integrated_ai) + integrated_ai.attackby(input_device,user) + // If the transfer was successful, we can clear out our vars. + if(integrated_ai.loc != src) + integrated_ai = null + eject_ai() + else + integrate_ai(input_device,user) + return 1 + + return 0 + +/obj/item/rig_module/ai_container/engage(atom/target) + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + if(!target) + if(ai_card) + if(istype(ai_card,/obj/item/device/aicard)) + ai_card.ui_interact(H, state = deep_inventory_state) + else + eject_ai(H) + update_verb_holder() + return 1 + + if(accepts_item(target,H)) + return 1 + + return 0 + +/obj/item/rig_module/ai_container/removed() + eject_ai() + ..() + +/obj/item/rig_module/ai_container/proc/eject_ai(var/mob/user) + + if(ai_card) + if(istype(ai_card, /obj/item/device/aicard)) + if(integrated_ai && !integrated_ai.stat) + if(user) + to_chat(user, "You cannot eject your currently stored AI. Purge it manually.") + return 0 + to_chat(user, "You purge the previous AI from your Integrated Intelligence System, freeing it for use.") + if(integrated_ai) + integrated_ai.ghostize() + qdel(integrated_ai) + integrated_ai = null + if(ai_card) + qdel(ai_card) + ai_card = null + else if(user) + user.put_in_hands(ai_card) + else + ai_card.forceMove(get_turf(src)) + ai_card = null + integrated_ai = null + update_verb_holder() + +/obj/item/rig_module/ai_container/proc/integrate_ai(var/obj/item/ai,var/mob/user) + if(!ai) return + + // The ONLY THING all the different AI systems have in common is that they all store the mob inside an item. + var/mob/living/ai_mob = locate(/mob/living) in ai.contents + if(ai_mob) + + if(ai_mob.key && ai_mob.client) + + if(istype(ai, /obj/item/device/aicard)) + + if(!ai_card) + ai_card = new /obj/item/device/aicard(src) + + var/obj/item/device/aicard/source_card = ai + var/obj/item/device/aicard/target_card = ai_card + if(istype(source_card) && istype(target_card)) + if(target_card.grab_ai(ai_mob, user)) + source_card.clear() + else + return 0 + else + return 0 + else + user.drop_from_inventory(ai) + ai.forceMove(src) + ai_card = ai + to_chat(ai_mob, "You have been transferred to \the [holder]'s [src].") + to_chat(user, "You load [ai_mob] into \the [holder]'s [src].") + + integrated_ai = ai_mob + + if(!(locate(integrated_ai) in ai_card)) + integrated_ai = null + eject_ai() + else + to_chat(user, "There is no active AI within \the [ai].") + else + to_chat(user, "There is no active AI within \the [ai].") + update_verb_holder() + return diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/chem_dispenser.dm b/code/modules/clothing/spacesuits/rig/modules/specific/chem_dispenser.dm new file mode 100644 index 0000000000..e8d6416e9a --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/chem_dispenser.dm @@ -0,0 +1,174 @@ +/obj/item/rig_module/chem_dispenser + name = "mounted chemical dispenser" + desc = "A complex web of tubing and needles suitable for hardsuit use." + icon_state = "injector" + usable = 1 + selectable = 0 + toggleable = 0 + disruptive = 0 + + engage_string = "Inject" + + interface_name = "integrated chemical dispenser" + interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream." + + charges = list( + list("tricordrazine", "tricordrazine", 0, 80), + list("tramadol", "tramadol", 0, 80), + list("dexalin plus", "dexalinp", 0, 80), + list("antibiotics", "spaceacillin", 0, 80), + list("antitoxins", "anti_toxin", 0, 80), + list("nutrients", "glucose", 0, 80), + list("hyronalin", "hyronalin", 0, 80), + list("radium", "radium", 0, 80) + ) + + var/max_reagent_volume = 80 //Used when refilling. + +/obj/item/rig_module/chem_dispenser/ninja + interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream. This variant is made to be extremely light and flexible." + + //Want more? Go refill. Gives the ninja another reason to have to show their face. + charges = list( + list("tricordrazine", "tricordrazine", 0, 30), + list("tramadol", "tramadol", 0, 30), + list("dexalin plus", "dexalinp", 0, 30), + list("antibiotics", "spaceacillin", 0, 30), + list("antitoxins", "anti_toxin", 0, 60), + list("nutrients", "glucose", 0, 80), + list("bicaridine", "bicaridine", 0, 30), + list("clotting agent", "myelamine", 0, 30), + list("peridaxon", "peridaxon", 0, 30), + list("hyronalin", "hyronalin", 0, 30), + list("radium", "radium", 0, 30) + ) + +/obj/item/rig_module/chem_dispenser/accepts_item(var/obj/item/input_item, var/mob/living/user) + + if(!input_item.is_open_container()) + return 0 + + if(!input_item.reagents || !input_item.reagents.total_volume) + to_chat(user, "\The [input_item] is empty.") + return 0 + + // Magical chemical filtration system, do not question it. + var/total_transferred = 0 + for(var/datum/reagent/R in input_item.reagents.reagent_list) + for(var/chargetype in charges) + var/datum/rig_charge/charge = charges[chargetype] + if(charge.display_name == R.id) + + var/chems_to_transfer = R.volume + + if((charge.charges + chems_to_transfer) > max_reagent_volume) + chems_to_transfer = max_reagent_volume - charge.charges + + charge.charges += chems_to_transfer + input_item.reagents.remove_reagent(R.id, chems_to_transfer) + total_transferred += chems_to_transfer + + break + + if(total_transferred) + to_chat(user, "You transfer [total_transferred] units into the suit reservoir.") + else + to_chat(user, "None of the reagents seem suitable.") + return 1 + +/obj/item/rig_module/chem_dispenser/engage(atom/target) + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + if(!charge_selected) + to_chat(H, "You have not selected a chemical type.") + return 0 + + var/datum/rig_charge/charge = charges[charge_selected] + + if(!charge) + return 0 + + var/chems_to_use = 10 + if(charge.charges <= 0) + to_chat(H, "Insufficient chems!") + return 0 + else if(charge.charges < chems_to_use) + chems_to_use = charge.charges + + var/mob/living/carbon/target_mob + if(target) + if(istype(target,/mob/living/carbon)) + target_mob = target + else + return 0 + else + target_mob = H + + if(target_mob != H) + to_chat(H, "You inject [target_mob] with [chems_to_use] unit\s of [charge.display_name].") + to_chat(target_mob, "You feel a rushing in your veins as [chems_to_use] unit\s of [charge.display_name] [chems_to_use == 1 ? "is" : "are"] injected.") + target_mob.reagents.add_reagent(charge.display_name, chems_to_use) + + charge.charges -= chems_to_use + if(charge.charges < 0) charge.charges = 0 + + return 1 + +/obj/item/rig_module/chem_dispenser/combat + + name = "combat chemical injector" + desc = "A complex web of tubing and needles suitable for hardsuit use." + + charges = list( + list("synaptizine", "synaptizine", 0, 30), + list("hyperzine", "hyperzine", 0, 30), + list("oxycodone", "oxycodone", 0, 30), + list("nutrients", "glucose", 0, 80), + list("clotting agent", "myelamine", 0, 80) + ) + + interface_name = "combat chem dispenser" + interface_desc = "Dispenses loaded chemicals directly into the bloodstream." + + +/obj/item/rig_module/chem_dispenser/injector + + name = "mounted chemical injector" + desc = "A complex web of tubing and a large needle suitable for hardsuit use." + usable = 0 + selectable = 1 + disruptive = 1 + + interface_name = "mounted chem injector" + interface_desc = "Dispenses loaded chemicals via an arm-mounted injector." + +/obj/item/rig_module/chem_dispenser/injector/advanced + + charges = list( + list("tricordrazine", "tricordrazine", 0, 80), + list("tramadol", "tramadol", 0, 80), + list("dexalin plus", "dexalinp", 0, 80), + list("antibiotics", "spaceacillin", 0, 80), + list("antitoxins", "anti_toxin", 0, 80), + list("nutrients", "glucose", 0, 80), + list("hyronalin", "hyronalin", 0, 80), + list("radium", "radium", 0, 80), + list("clotting agent", "myelamine", 0, 80) + ) + +/obj/item/rig_module/chem_dispenser/injector/advanced/empty + charges = list( + list("tricordrazine", "tricordrazine", 0, 0), + list("tramadol", "tramadol", 0, 0), + list("dexalin plus", "dexalinp", 0, 0), + list("antibiotics", "spaceacillin", 0, 0), + list("antitoxins", "anti_toxin", 0, 0), + list("nutrients", "glucose", 0, 0), + list("hyronalin", "hyronalin", 0, 0), + list("radium", "radium", 0, 0), + list("clotting agent", "myelamine", 0, 0) + ) diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/cleaner_launcher.dm b/code/modules/clothing/spacesuits/rig/modules/specific/cleaner_launcher.dm new file mode 100644 index 0000000000..998fc3d46a --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/cleaner_launcher.dm @@ -0,0 +1,70 @@ +/obj/item/rig_module/cleaner_launcher + + name = "mounted space cleaner launcher" + desc = "A shoulder-mounted micro-cleaner dispenser." + selectable = 1 + icon_state = "grenade_launcher" + + interface_name = "integrated cleaner launcher" + interface_desc = "Discharges loaded cleaner grenades against the wearer's location." + + var/fire_force = 30 + var/fire_distance = 10 + + charges = list( + list("cleaner grenade", "cleaner grenade", /obj/item/weapon/grenade/chem_grenade/cleaner, 9), + ) + +/obj/item/rig_module/cleaner_launcher/accepts_item(var/obj/item/input_device, var/mob/living/user) + + if(!istype(input_device) || !istype(user)) + return 0 + + var/datum/rig_charge/accepted_item + for(var/charge in charges) + var/datum/rig_charge/charge_datum = charges[charge] + if(input_device.type == charge_datum.product_type) + accepted_item = charge_datum + break + + if(!accepted_item) + return 0 + + if(accepted_item.charges >= 5) + to_chat(user, "Another grenade of that type will not fit into the module.") + return 0 + + to_chat(user, "You slot \the [input_device] into the suit module.") + user.drop_from_inventory(input_device) + qdel(input_device) + accepted_item.charges++ + return 1 + +/obj/item/rig_module/cleaner_launcher/engage(atom/target) + + if(!..()) + return 0 + + if(!target) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + if(!charge_selected) + to_chat(H, "You have not selected a grenade type.") + return 0 + + var/datum/rig_charge/charge = charges[charge_selected] + + if(!charge) + return 0 + + if(charge.charges <= 0) + to_chat(H, "Insufficient grenades!") + return 0 + + charge.charges-- + var/obj/item/weapon/grenade/new_grenade = new charge.product_type(get_turf(H)) + H.visible_message("[H] launches \a [new_grenade]!") + new_grenade.activate(H) + new_grenade.throw_at(target,fire_force,fire_distance) diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/cloak.dm b/code/modules/clothing/spacesuits/rig/modules/specific/cloak.dm new file mode 100644 index 0000000000..a1dfb05812 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/cloak.dm @@ -0,0 +1,53 @@ +/obj/item/rig_module/stealth_field + + name = "active camouflage module" + desc = "A robust hardsuit-integrated stealth module." + icon_state = "cloak" + + toggleable = 1 + disruptable = 1 + disruptive = 0 + + use_power_cost = 50 + active_power_cost = 10 + passive_power_cost = 0 + module_cooldown = 30 + + activate_string = "Enable Cloak" + deactivate_string = "Disable Cloak" + + interface_name = "integrated stealth system" + interface_desc = "An integrated active camouflage system." + + suit_overlay_active = "stealth_active" + suit_overlay_inactive = "stealth_inactive" + +/obj/item/rig_module/stealth_field/activate() + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + to_chat(H, "You are now nearly invisible to normal detection.") + H.alpha = 5 + + anim(get_turf(H), H, 'icons/effects/effects.dmi', "electricity",null,20,null) + + H.visible_message("[H.name] vanishes into thin air!") + +/obj/item/rig_module/stealth_field/deactivate() + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + to_chat(H, "You are now visible.") + + anim(get_turf(H), H,'icons/mob/mob.dmi',,"uncloak",,H.dir) + anim(get_turf(H), H, 'icons/effects/effects.dmi', "electricity",null,20,null) + H.alpha = initial(H.alpha) + + H.visible_message("[H.name] appears from thin air!") + playsound(get_turf(H), 'sound/effects/stealthoff.ogg', 75, 1) \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/datajack.dm b/code/modules/clothing/spacesuits/rig/modules/specific/datajack.dm new file mode 100644 index 0000000000..be34426447 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/datajack.dm @@ -0,0 +1,86 @@ +/obj/item/rig_module/datajack + + name = "datajack module" + desc = "A simple induction datalink module." + icon_state = "datajack" + toggleable = 1 + activates_on_touch = 1 + usable = 0 + + activate_string = "Enable Datajack" + deactivate_string = "Disable Datajack" + + interface_name = "contact datajack" + interface_desc = "An induction-powered high-throughput datalink suitable for hacking encrypted networks." + var/list/stored_research + +/obj/item/rig_module/datajack/New() + ..() + stored_research = list() + +/obj/item/rig_module/datajack/engage(atom/target) + + if(!..()) + return 0 + + if(target) + var/mob/living/carbon/human/H = holder.wearer + if(!accepts_item(target,H)) + return 0 + return 1 + +/obj/item/rig_module/datajack/accepts_item(var/obj/item/input_device, var/mob/living/user) + + if(istype(input_device,/obj/item/weapon/disk/tech_disk)) + to_chat(user, "You slot the disk into [src].") + var/obj/item/weapon/disk/tech_disk/disk = input_device + if(disk.stored) + if(load_data(disk.stored)) + to_chat(user, "Download successful; disk erased.") + disk.stored = null + else + to_chat(user, "The disk is corrupt. It is useless to you.") + else + to_chat(user, "The disk is blank. It is useless to you.") + return 1 + + // I fucking hate R&D code. This typecheck spam would be totally unnecessary in a sane setup. Sanity? This is BYOND. + else if(istype(input_device,/obj/machinery)) + var/datum/research/incoming_files + if(istype(input_device,/obj/machinery/computer/rdconsole) ||\ + istype(input_device,/obj/machinery/r_n_d/server) ||\ + istype(input_device,/obj/machinery/mecha_part_fabricator)) + + incoming_files = input_device:files + + if(!incoming_files || !incoming_files.known_tech || !incoming_files.known_tech.len) + to_chat(user, "Memory failure. There is nothing accessible stored on this terminal.") + else + // Maybe consider a way to drop all your data into a target repo in the future. + if(load_data(incoming_files.known_tech)) + to_chat(user, "Download successful; local and remote repositories synchronized.") + else + to_chat(user, "Scan complete. There is nothing useful stored on this terminal.") + return 1 + return 0 + +/obj/item/rig_module/datajack/proc/load_data(var/incoming_data) + + if(islist(incoming_data)) + for(var/entry in incoming_data) + load_data(entry) + return 1 + + if(istype(incoming_data, /datum/tech)) + var/data_found + var/datum/tech/new_data = incoming_data + for(var/datum/tech/current_data in stored_research) + if(current_data.id == new_data.id) + data_found = 1 + if(current_data.level < new_data.level) + current_data.level = new_data.level + break + if(!data_found) + stored_research += incoming_data + return 1 + return 0 \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/device.dm b/code/modules/clothing/spacesuits/rig/modules/specific/device.dm new file mode 100644 index 0000000000..044bdc143c --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/device.dm @@ -0,0 +1,176 @@ +/obj/item/rig_module/device + name = "mounted device" + desc = "Some kind of hardsuit mount." + usable = 0 + selectable = 1 + toggleable = 0 + disruptive = 0 + + var/device_type + var/obj/item/device + +/obj/item/rig_module/device/New() + ..() + if(device_type) device = new device_type(src) + +/obj/item/rig_module/device/engage(atom/target) + if(!..() || !device) + return 0 + + if(!target) + device.attack_self(holder.wearer) + return 1 + + var/turf/T = get_turf(target) + if(istype(T) && !T.Adjacent(get_turf(src))) + return 0 + + var/resolved = target.attackby(device,holder.wearer) + if(!resolved && device && target) + device.afterattack(target,holder.wearer,1) + return 1 + +/obj/item/rig_module/device/flash + name = "mounted flash" + desc = "You are the law." + icon_state = "flash" + interface_name = "mounted flash" + interface_desc = "Stuns your target by blinding them with a bright light." + device_type = /obj/item/device/flash/robot + +/obj/item/rig_module/device/plasmacutter + name = "hardsuit plasma cutter" + desc = "A lethal-looking industrial cutter." + icon_state = "plasmacutter" + interface_name = "plasma cutter" + interface_desc = "A self-sustaining plasma arc capable of cutting through walls." + suit_overlay_active = "plasmacutter" + suit_overlay_inactive = "plasmacutter" + use_power_cost = 0.5 + + device_type = /obj/item/weapon/pickaxe/plasmacutter + +/obj/item/rig_module/device/healthscanner + name = "health scanner module" + desc = "A hardsuit-mounted health scanner." + icon_state = "scanner" + interface_name = "health scanner" + interface_desc = "Shows an informative health readout when used on a subject." + + device_type = /obj/item/device/healthanalyzer + +/obj/item/rig_module/device/drill + name = "hardsuit drill mount" + desc = "A very heavy diamond-tipped drill." + icon_state = "drill" + interface_name = "mounted drill" + interface_desc = "A diamond-tipped industrial drill." + suit_overlay_active = "mounted-drill" + suit_overlay_inactive = "mounted-drill" + use_power_cost = 0.1 + + device_type = /obj/item/weapon/pickaxe/diamonddrill + +/obj/item/rig_module/device/anomaly_scanner + name = "hardsuit anomaly scanner" + desc = "You think it's called an Elder Sarsparilla or something." + icon_state = "eldersasparilla" + interface_name = "Alden-Saraspova counter" + interface_desc = "An exotic particle detector commonly used by xenoarchaeologists." + engage_string = "Begin Scan" + usable = 1 + selectable = 0 + device_type = /obj/item/device/ano_scanner + +/obj/item/rig_module/device/orescanner + name = "ore scanner module" + desc = "A clunky old ore scanner." + icon_state = "scanner" + interface_name = "ore detector" + interface_desc = "A sonar system for detecting large masses of ore." + engage_string = "Begin Scan" + usable = 1 + selectable = 0 + device_type = /obj/item/weapon/mining_scanner + +/obj/item/rig_module/device/rcd + name = "RCD mount" + desc = "A cell-powered rapid construction device for a hardsuit." + icon_state = "rcd" + interface_name = "mounted RCD" + interface_desc = "A device for building or removing walls. Cell-powered." + usable = 1 + engage_string = "Configure RCD" + + device_type = /obj/item/weapon/rcd/electric/mounted/rig + +/obj/item/rig_module/device/arch_drill + name = "archaeology drill mount" + desc = "A cell-powered fine-excavation device for a hardsuit." + icon_state = "exdrill" + interface_name = "mounted excavation tool" + interface_desc = "A device for excavating ancient relics." + usable = 1 + engage_string = "Configure Drill Depth" + + device_type = /obj/item/weapon/pickaxe/excavationdrill + +/obj/item/rig_module/device/paperdispenser + name = "hardsuit paper dispenser" + desc = "Crisp sheets." + icon_state = "paper" + interface_name = "paper dispenser" + interface_desc = "Dispenses warm, clean, and crisp sheets of paper." + engage_string = "Dispense" + usable = 1 + selectable = 0 + device_type = /obj/item/weapon/paper_bin + +/obj/item/rig_module/device/paperdispenser/engage(atom/target) + + if(!..() || !device) + return 0 + + if(!target) + device.attack_hand(holder.wearer) + return 1 + +/obj/item/rig_module/device/pen + name = "mounted pen" + desc = "For mecha John Hancocks." + icon_state = "pen" + interface_name = "mounted pen" + interface_desc = "Signatures with style(tm)." + engage_string = "Change color" + usable = 1 + device_type = /obj/item/weapon/pen/multi + +/obj/item/rig_module/device/stamp + name = "mounted internal affairs stamp" + desc = "DENIED." + icon_state = "stamp" + interface_name = "mounted stamp" + interface_desc = "Leave your mark." + engage_string = "Toggle stamp type" + usable = 1 + var/iastamp + var/deniedstamp + +/obj/item/rig_module/device/stamp/New() + ..() + iastamp = new /obj/item/weapon/stamp/internalaffairs(src) + deniedstamp = new /obj/item/weapon/stamp/denied(src) + device = iastamp + +/obj/item/rig_module/device/stamp/engage(atom/target) + if(!..() || !device) + return 0 + + if(!target) + if(device == iastamp) + device = deniedstamp + to_chat(holder.wearer, "Switched to denied stamp.") + else if(device == deniedstamp) + device = iastamp + to_chat(holder.wearer, "Switched to internal affairs stamp.") + return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/electrowarfare.dm b/code/modules/clothing/spacesuits/rig/modules/specific/electrowarfare.dm new file mode 100644 index 0000000000..f30be0b54a --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/electrowarfare.dm @@ -0,0 +1,30 @@ +/obj/item/rig_module/electrowarfare_suite + + name = "electrowarfare module" + desc = "A bewilderingly complex bundle of fiber optics and chips." + icon_state = "ewar" + toggleable = 1 + usable = 0 + + activate_string = "Enable Countermeasures" + deactivate_string = "Disable Countermeasures" + + interface_name = "electrowarfare system" + interface_desc = "An active counter-electronic warfare suite that disrupts AI tracking." + +/obj/item/rig_module/electrowarfare_suite/activate() + + if(!..()) + return + + // This is not the best way to handle this, but I don't want it to mess with ling camo + var/mob/living/M = holder.wearer + M.digitalcamo++ + +/obj/item/rig_module/electrowarfare_suite/deactivate() + + if(!..()) + return + + var/mob/living/M = holder.wearer + M.digitalcamo = max(0,(M.digitalcamo-1)) \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/grenade_launcher.dm b/code/modules/clothing/spacesuits/rig/modules/specific/grenade_launcher.dm new file mode 100644 index 0000000000..9252b062bd --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/grenade_launcher.dm @@ -0,0 +1,98 @@ +/obj/item/rig_module/grenade_launcher + + name = "mounted grenade launcher" + desc = "A shoulder-mounted micro-explosive dispenser." + selectable = 1 + icon_state = "grenadelauncher" + + interface_name = "integrated grenade launcher" + interface_desc = "Discharges loaded grenades against the wearer's location." + + var/fire_force = 30 + var/fire_distance = 10 + + charges = list( + list("flashbang", "flashbang", /obj/item/weapon/grenade/flashbang, 3), + list("smoke bomb", "smoke bomb", /obj/item/weapon/grenade/smokebomb, 3), + list("EMP grenade", "EMP grenade", /obj/item/weapon/grenade/empgrenade, 3), + ) + +/obj/item/rig_module/grenade_launcher/accepts_item(var/obj/item/input_device, var/mob/living/user) + + if(!istype(input_device) || !istype(user)) + return 0 + + var/datum/rig_charge/accepted_item + for(var/charge in charges) + var/datum/rig_charge/charge_datum = charges[charge] + if(input_device.type == charge_datum.product_type) + accepted_item = charge_datum + break + + if(!accepted_item) + return 0 + + if(accepted_item.charges >= 5) + to_chat(user, "Another grenade of that type will not fit into the module.") + return 0 + + to_chat(user, "You slot \the [input_device] into the suit module.") + user.drop_from_inventory(input_device) + qdel(input_device) + accepted_item.charges++ + return 1 + +/obj/item/rig_module/grenade_launcher/engage(atom/target) + + if(!..()) + return 0 + + if(!target) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + if(!charge_selected) + to_chat(H, "You have not selected a grenade type.") + return 0 + + var/datum/rig_charge/charge = charges[charge_selected] + + if(!charge) + return 0 + + if(charge.charges <= 0) + to_chat(H, "Insufficient grenades!") + return 0 + + charge.charges-- + var/obj/item/weapon/grenade/new_grenade = new charge.product_type(get_turf(H)) + H.visible_message("[H] launches \a [new_grenade]!") + new_grenade.activate(H) + new_grenade.throw_at(target,fire_force,fire_distance) + +/obj/item/rig_module/grenade_launcher/smoke + name = "mounted smoke-bomb launcher" + desc = "A shoulder-mounted smoke-bomb dispenser." + + interface_name = "integrated smoke-bomb launcher" + interface_desc = "Discharges loaded smoke-bombs against the wearer's location." + + fire_force = 15 + + charges = list( + list("smoke bomb", "smoke bomb", /obj/item/weapon/grenade/smokebomb, 6) + ) + +/obj/item/rig_module/grenade_launcher/flash + name = "mounted flashbang launcher" + desc = "A shoulder-mounted flashbang dispenser." + selectable = 1 + icon_state = "grenadelauncher" + + interface_name = "integrated flashbang launcher" + interface_desc = "Discharges loaded grenades against the wearer's location." + + charges = list( + list("flashbang", "flashbang", /obj/item/weapon/grenade/flashbang, 3) + ) diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/inhand_fabricator.dm b/code/modules/clothing/spacesuits/rig/modules/specific/inhand_fabricator.dm new file mode 100644 index 0000000000..8e71cf2745 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/inhand_fabricator.dm @@ -0,0 +1,63 @@ +/obj/item/rig_module/fabricator + + name = "matter fabricator" + desc = "A self-contained microfactory system for hardsuit integration." + selectable = 1 + usable = 1 + use_power_cost = 15 + icon_state = "enet" + + engage_string = "Fabricate Star" + + interface_name = "death blossom launcher" + interface_desc = "An integrated microfactory that produces poisoned throwing stars from thin air and electricity." + + var/fabrication_type = /obj/item/weapon/material/star/ninja + var/fire_force = 30 + var/fire_distance = 10 + +/obj/item/rig_module/fabricator/engage(atom/target) + + if(!..()) + return 0 + + var/mob/living/H = holder.wearer + + if(target) + var/obj/item/firing = new fabrication_type() + firing.forceMove(get_turf(src)) + H.visible_message("[H] launches \a [firing]!") + firing.throw_at(target,fire_force,fire_distance) + else + if(H.l_hand && H.r_hand) + to_chat(H, "Your hands are full.") + else + var/obj/item/new_weapon = new fabrication_type() + new_weapon.forceMove(H) + to_chat(H, "You quickly fabricate \a [new_weapon].") + H.put_in_hands(new_weapon) + + return 1 + +/obj/item/rig_module/fabricator/energy_net + + name = "net projector" + desc = "Some kind of complex energy projector with a hardsuit mount." + icon_state = "enet" + + interface_name = "energy net launcher" + interface_desc = "An advanced energy-patterning projector used to capture targets." + + engage_string = "Fabricate Net" + + fabrication_type = /obj/item/weapon/energy_net + use_power_cost = 70 + +/obj/item/rig_module/fabricator/energy_net/engage(atom/target) + + if(holder && holder.wearer) + if(..(target) && target) + set_dir(get_dir(src,target)) // Face the target + holder.wearer.Beam(target,"n_beam",,10) + return 1 + return 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/jetpack.dm b/code/modules/clothing/spacesuits/rig/modules/specific/jetpack.dm new file mode 100644 index 0000000000..6f9f7e3804 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/jetpack.dm @@ -0,0 +1,66 @@ +/obj/item/rig_module/maneuvering_jets + + name = "hardsuit maneuvering jets" + desc = "A compact gas thruster system for a hardsuit." + icon_state = "thrusters" + usable = 1 + toggleable = 1 + selectable = 0 + disruptive = 0 + + suit_overlay_active = "maneuvering_active" + suit_overlay_inactive = null //"maneuvering_inactive" + + engage_string = "Toggle Stabilizers" + activate_string = "Activate Thrusters" + deactivate_string = "Deactivate Thrusters" + + interface_name = "maneuvering jets" + interface_desc = "An inbuilt EVA maneuvering system that runs off the rig air supply." + + var/obj/item/weapon/tank/jetpack/rig/jets + +/obj/item/rig_module/maneuvering_jets/engage() + if(!..()) + return 0 + jets.toggle_rockets() + return 1 + +/obj/item/rig_module/maneuvering_jets/activate() + + if(active) + return 0 + + active = 1 + + spawn(1) + if(suit_overlay_active) + suit_overlay = suit_overlay_active + else + suit_overlay = null + holder.update_icon() + + if(!jets.on) + jets.toggle() + return 1 + +/obj/item/rig_module/maneuvering_jets/deactivate() + if(!..()) + return 0 + if(jets.on) + jets.toggle() + return 1 + +/obj/item/rig_module/maneuvering_jets/New() + ..() + jets = new(src) + +/obj/item/rig_module/maneuvering_jets/installed() + ..() + jets.holder = holder + jets.ion_trail.set_up(holder) + +/obj/item/rig_module/maneuvering_jets/removed() + ..() + jets.holder = null + jets.ion_trail.set_up(jets) \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/metalfoam_launcher.dm b/code/modules/clothing/spacesuits/rig/modules/specific/metalfoam_launcher.dm new file mode 100644 index 0000000000..0843b9f779 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/metalfoam_launcher.dm @@ -0,0 +1,15 @@ + +/obj/item/rig_module/grenade_launcher/metalfoam + name = "mounted metalfoam grenade launcher" + desc = "A shoulder-mounted foam-bomb dispenser." + selectable = 1 + icon_state = "grenadelauncher" + + interface_name = "integrated metalfoam grenade launcher" + interface_desc = "Discharges loaded grenades against the wearer's location." + + fire_force = 15 + + charges = list( + list("metalfoam", "metalfoam", /obj/item/weapon/grenade/chem_grenade/metalfoam, 5) + ) diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/mounted_gun.dm b/code/modules/clothing/spacesuits/rig/modules/specific/mounted_gun.dm new file mode 100644 index 0000000000..97db6b3281 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/mounted_gun.dm @@ -0,0 +1,175 @@ +/obj/item/rig_module/mounted + + name = "mounted laser cannon" + desc = "A shoulder-mounted battery-powered laser cannon mount." + selectable = 1 + usable = 1 + module_cooldown = 0 + icon_state = "lcannon" + + engage_string = "Configure" + + interface_name = "mounted laser cannon" + interface_desc = "A shoulder-mounted cell-powered laser cannon." + + var/gun_type = /obj/item/weapon/gun/energy/lasercannon/mounted + var/obj/item/weapon/gun/gun + +/obj/item/rig_module/mounted/New() + ..() + gun = new gun_type(src) + +/obj/item/rig_module/mounted/engage(atom/target) + + if(!..()) + return 0 + + if(!target) + gun.attack_self(holder.wearer) + return + + gun.Fire(target,holder.wearer) + return 1 + +/obj/item/rig_module/mounted/egun + + name = "mounted energy gun" + desc = "A forearm-mounted energy projector." + icon_state = "egun" + + interface_name = "mounted energy gun" + interface_desc = "A forearm-mounted suit-powered energy gun." + + gun_type = /obj/item/weapon/gun/energy/gun/mounted + +/obj/item/rig_module/mounted/taser + + name = "mounted taser" + desc = "A palm-mounted nonlethal energy projector." + icon_state = "taser" + + usable = 0 + + suit_overlay_active = "mounted-taser" + suit_overlay_inactive = "mounted-taser" + + interface_name = "mounted taser" + interface_desc = "A shoulder-mounted cell-powered taser." + + gun_type = /obj/item/weapon/gun/energy/taser/mounted + +/obj/item/rig_module/mounted/energy_blade + + name = "energy blade projector" + desc = "A powerful cutting beam projector." + icon_state = "eblade" + + activate_string = "Project Blade" + deactivate_string = "Cancel Blade" + + interface_name = "spider fang blade" + interface_desc = "A lethal energy projector that can shape a blade projected from the hand of the wearer or launch radioactive darts." + + usable = 0 + selectable = 1 + toggleable = 1 + use_power_cost = 50 + active_power_cost = 10 + passive_power_cost = 0 + + gun_type = /obj/item/weapon/gun/energy/crossbow/ninja + +/obj/item/rig_module/mounted/energy_blade/process() + + if(holder && holder.wearer) + if(!(locate(/obj/item/weapon/melee/energy/blade) in holder.wearer)) + deactivate() + return 0 + + return ..() + +/obj/item/rig_module/mounted/energy_blade/activate() + + ..() + + var/mob/living/M = holder.wearer + + if(M.l_hand && M.r_hand) + to_chat(M, "Your hands are full.") + deactivate() + return + + var/obj/item/weapon/melee/energy/blade/blade = new(M) + blade.creator = M + M.put_in_hands(blade) + +/obj/item/rig_module/mounted/energy_blade/deactivate() + + ..() + + var/mob/living/M = holder.wearer + + if(!M) + return + + for(var/obj/item/weapon/melee/energy/blade/blade in M.contents) + M.drop_from_inventory(blade) + qdel(blade) + +/obj/item/rig_module/mounted/mop + + name = "mop projector" + desc = "A powerful mop projector." + icon_state = "mop" + + activate_string = "Project Mop" + deactivate_string = "Cancel Mop" + + interface_name = "mop projector" + interface_desc = "A mop that can be deployed from the hand of the wearer." + + usable = 1 + selectable = 1 + toggleable = 1 + use_power_cost = 5 + active_power_cost = 0 + passive_power_cost = 0 + + gun_type = /obj/item/weapon/gun/energy/temperature/mounted + +/obj/item/rig_module/mounted/mop/process() + + if(holder && holder.wearer) + if(!(locate(/obj/item/weapon/mop_deploy) in holder.wearer)) + deactivate() + return 0 + + return ..() + +/obj/item/rig_module/mounted/mop/activate() + + ..() + + var/mob/living/M = holder.wearer + + if(M.l_hand && M.r_hand) + to_chat(M, "Your hands are full.") + deactivate() + return + + var/obj/item/weapon/mop_deploy/blade = new(M) + blade.creator = M + M.put_in_hands(blade) + +/obj/item/rig_module/mounted/mop/deactivate() + + ..() + + var/mob/living/M = holder.wearer + + if(!M) + return + + for(var/obj/item/weapon/mop_deploy/blade in M.contents) + M.drop_from_inventory(blade) + qdel(blade) diff --git a/code/modules/clothing/spacesuits/rig/modules/utility_vr.dm b/code/modules/clothing/spacesuits/rig/modules/specific/pat_module_vr.dm similarity index 58% rename from code/modules/clothing/spacesuits/rig/modules/utility_vr.dm rename to code/modules/clothing/spacesuits/rig/modules/specific/pat_module_vr.dm index a61f8126d1..7c7e8453d6 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility_vr.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/pat_module_vr.dm @@ -86,84 +86,3 @@ global_announcer.autosay(message, "Security Subsystem", "Command") global_announcer.autosay(message, "Security Subsystem", "Security") return 1 - -/obj/item/rig_module/rescue_pharm - name = "micro-pharmacy" - desc = "A small chemical dispenser with integrated micro cartridges." - usable = 0 - selectable = 1 - disruptive = 1 - toggleable = 1 - - use_power_cost = 0 - active_power_cost = 5 - - activate_string = "Enable Regen" - deactivate_string = "Disable Regen" - - interface_name = "mounted chem injector" - interface_desc = "Dispenses loaded chemicals via an arm-mounted injector." - - var/max_reagent_volume = 20 //Regen to this volume - var/chems_to_use = 5 //Per injection - - charges = list( - list("inaprovaline", "inaprovaline", 0, 20), - list("anti_toxin", "anti_toxin", 0, 20), - list("paracetamol", "paracetamol", 0, 20), - list("dexalin", "dexalin", 0, 20) - ) - -/obj/item/rig_module/rescue_pharm/process() - . = ..() - if(active) - var/did_work = 0 - - for(var/charge in charges) - var/datum/rig_charge/C = charges[charge] - - //Found one that isn't full - if(C.charges < max_reagent_volume) - did_work = 1 - C.charges += 1 - break - - if (!did_work) - deactivate() //All done - -/obj/item/rig_module/rescue_pharm/engage(atom/target) - if(!target) - return 1 //You're just toggling the module on, not clicking someone. - - var/mob/living/carbon/human/H = holder.wearer - - if(!charge_selected) - to_chat(H,"You have not selected a chemical type.") - return 0 - - var/datum/rig_charge/charge = charges[charge_selected] - - if(!charge) - return 0 - - if(charge.charges <= 0) - to_chat(H,"Insufficient chems!") - return 0 - - else if(charge.charges < chems_to_use) - chems_to_use = charge.charges - - var/mob/living/carbon/target_mob - if(istype(target,/mob/living/carbon)) - target_mob = target - else - return 0 - - to_chat(H,"You inject [target_mob == H ? "yourself" : target_mob] with [chems_to_use] unit\s of [charge.short_name].") - to_chat(target_mob,"You feel a rushing in your veins as you're injected by \the [src].") - target_mob.reagents.add_reagent(charge.display_name, chems_to_use) - - charge.charges -= chems_to_use - if(charge.charges < 0) charge.charges = 0 - - return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/powersink.dm b/code/modules/clothing/spacesuits/rig/modules/specific/powersink.dm new file mode 100644 index 0000000000..ec8fba92d6 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/powersink.dm @@ -0,0 +1,129 @@ +/obj/item/rig_module/power_sink + name = "hardsuit power sink" + desc = "An heavy-duty power sink." + icon_state = "powersink" + toggleable = 1 + activates_on_touch = 1 + disruptive = 0 + + activate_string = "Enable Power Sink" + deactivate_string = "Disable Power Sink" + + interface_name = "niling d-sink" + interface_desc = "Colloquially known as a power siphon, this module drains power through the suit hands into the suit battery." + + var/atom/interfaced_with // Currently draining power from this device. + var/total_power_drained = 0 + var/drain_loc + +/obj/item/rig_module/power_sink/deactivate() + + if(interfaced_with) + if(holder && holder.wearer) + to_chat(holder.wearer, "Your power sink retracts as the module deactivates.") + drain_complete() + interfaced_with = null + total_power_drained = 0 + return ..() + +/obj/item/rig_module/power_sink/activate() + interfaced_with = null + total_power_drained = 0 + return ..() + +/obj/item/rig_module/power_sink/engage(atom/target) + + if(!..()) + return 0 + + //Target wasn't supplied or we're already draining. + if(interfaced_with) + return 0 + + if(!target) + return 1 + + // Are we close enough? + var/mob/living/carbon/human/H = holder.wearer + if(!target.Adjacent(H)) + return 0 + + // Is it a valid power source? + if(target.drain_power(1) <= 0) + return 0 + + to_chat(H, "You begin draining power from [target]!") + interfaced_with = target + drain_loc = interfaced_with.loc + + holder.spark_system.start() + playsound(H.loc, 'sound/effects/sparks2.ogg', 50, 1) + + return 1 + +/obj/item/rig_module/power_sink/accepts_item(var/obj/item/input_device, var/mob/living/user) + var/can_drain = input_device.drain_power(1) + if(can_drain > 0) + engage(input_device) + return 1 + return 0 + +/obj/item/rig_module/power_sink/process() + + if(!interfaced_with) + return ..() + + var/mob/living/carbon/human/H + if(holder && holder.wearer) + H = holder.wearer + + if(!H || !istype(H)) + return 0 + + holder.spark_system.start() + playsound(H.loc, 'sound/effects/sparks2.ogg', 50, 1) + + H.break_cloak() + + if(!holder.cell) + to_chat(H, "Your power sink flashes an error; there is no cell in your rig.") + drain_complete(H) + return + + if(!interfaced_with || !interfaced_with.Adjacent(H) || !(interfaced_with.loc == drain_loc)) + to_chat(H, "Your power sink retracts into its casing.") + drain_complete(H) + return + + if(holder.cell.fully_charged()) + to_chat(H, "Your power sink flashes an amber light; your rig cell is full.") + drain_complete(H) + return + + // Attempts to drain up to 12.5*cell-capacity kW, determines this value from remaining cell capacity to ensure we don't drain too much. + // 1Ws/(12.5*CELLRATE) = 40s to charge + var/to_drain = min(12.5*holder.cell.maxcharge, ((holder.cell.maxcharge - holder.cell.charge) / CELLRATE)) + var/target_drained = interfaced_with.drain_power(0,0,to_drain) + if(target_drained <= 0) + to_chat(H, "Your power sink flashes a red light; there is no power left in [interfaced_with].") + drain_complete(H) + return + + holder.cell.give(target_drained * CELLRATE) + total_power_drained += target_drained + + return + +/obj/item/rig_module/power_sink/proc/drain_complete(var/mob/living/M) + + if(!interfaced_with) + if(M) + to_chat(M, "Total power drained: [round(total_power_drained*CELLRATE)] cell units.") + else + if(M) + to_chat(M, "Total power drained from [interfaced_with]: [round(total_power_drained*CELLRATE)] cell units.") + interfaced_with.drain_power(0,1,0) // Damage the victim. + + drain_loc = null + interfaced_with = null + total_power_drained = 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/rescue_pharm_vr.dm b/code/modules/clothing/spacesuits/rig/modules/specific/rescue_pharm_vr.dm new file mode 100644 index 0000000000..d3c840a12f --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/rescue_pharm_vr.dm @@ -0,0 +1,80 @@ +/obj/item/rig_module/rescue_pharm + name = "micro-pharmacy" + desc = "A small chemical dispenser with integrated micro cartridges." + usable = 0 + selectable = 1 + disruptive = 1 + toggleable = 1 + + use_power_cost = 0 + active_power_cost = 5 + + activate_string = "Enable Regen" + deactivate_string = "Disable Regen" + + interface_name = "mounted chem injector" + interface_desc = "Dispenses loaded chemicals via an arm-mounted injector." + + var/max_reagent_volume = 20 //Regen to this volume + var/chems_to_use = 5 //Per injection + + charges = list( + list("inaprovaline", "inaprovaline", 0, 20), + list("anti_toxin", "anti_toxin", 0, 20), + list("paracetamol", "paracetamol", 0, 20), + list("dexalin", "dexalin", 0, 20) + ) + +/obj/item/rig_module/rescue_pharm/process() + . = ..() + if(active) + var/did_work = 0 + + for(var/charge in charges) + var/datum/rig_charge/C = charges[charge] + + //Found one that isn't full + if(C.charges < max_reagent_volume) + did_work = 1 + C.charges += 1 + break + + if (!did_work) + deactivate() //All done + +/obj/item/rig_module/rescue_pharm/engage(atom/target) + if(!target) + return 1 //You're just toggling the module on, not clicking someone. + + var/mob/living/carbon/human/H = holder.wearer + + if(!charge_selected) + to_chat(H,"You have not selected a chemical type.") + return 0 + + var/datum/rig_charge/charge = charges[charge_selected] + + if(!charge) + return 0 + + if(charge.charges <= 0) + to_chat(H,"Insufficient chems!") + return 0 + + else if(charge.charges < chems_to_use) + chems_to_use = charge.charges + + var/mob/living/carbon/target_mob + if(istype(target,/mob/living/carbon)) + target_mob = target + else + return 0 + + to_chat(H,"You inject [target_mob == H ? "yourself" : target_mob] with [chems_to_use] unit\s of [charge.short_name].") + to_chat(target_mob,"You feel a rushing in your veins as you're injected by \the [src].") + target_mob.reagents.add_reagent(charge.display_name, chems_to_use) + + charge.charges -= chems_to_use + if(charge.charges < 0) charge.charges = 0 + + return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/self_destruct.dm b/code/modules/clothing/spacesuits/rig/modules/specific/self_destruct.dm new file mode 100644 index 0000000000..391bdf8f6c --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/self_destruct.dm @@ -0,0 +1,50 @@ +/obj/item/rig_module/self_destruct + + name = "self-destruct module" + desc = "Oh my God, a bomb!" + icon_state = "deadman" + usable = 1 + active = 1 + permanent = 1 + var/datum/effect/effect/system/smoke_spread/bad/smoke + var/smoke_strength = 8 + + engage_string = "Detonate" + + interface_name = "dead man's switch" + interface_desc = "An integrated self-destruct module. When the wearer dies, they vanish in smoke. Do not press this button." + +/obj/item/rig_module/self_destruct/New() + ..() + src.smoke = new /datum/effect/effect/system/smoke_spread/bad() + src.smoke.attach(src) + +/obj/item/rig_module/self_destruct/Destroy() + qdel(smoke) + smoke = null + return ..() + +/obj/item/rig_module/self_destruct/activate() + return + +/obj/item/rig_module/self_destruct/deactivate() + return + +/obj/item/rig_module/self_destruct/process() + + // Not being worn, leave it alone. + if(!holder || !holder.wearer || !holder.wearer.wear_suit == holder) + return 0 + + //OH SHIT. + if(holder.wearer.stat == 2) + engage(1) + +/obj/item/rig_module/self_destruct/engage(var/skip_check) + if(!skip_check && usr && alert(usr, "Are you sure you want to push that button?", "Self-destruct", "No", "Yes") == "No") + return + if(holder && holder.wearer) + smoke.set_up(10, 0, holder.loc) + for(var/i = 1 to smoke_strength) + smoke.start(272727) + holder.wearer.ash() diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/sprinter.dm b/code/modules/clothing/spacesuits/rig/modules/specific/sprinter.dm new file mode 100644 index 0000000000..f69f053260 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/sprinter.dm @@ -0,0 +1,43 @@ +/obj/item/rig_module/sprinter + name = "sprint module" + desc = "A robust hardsuit-integrated sprint module." + icon_state = "sprinter" + + var/sprint_speed = 1 + + toggleable = 1 + disruptable = 1 + disruptive = 0 + + use_power_cost = 50 + active_power_cost = 5 + passive_power_cost = 0 + module_cooldown = 30 + + activate_string = "Enable Sprint" + deactivate_string = "Disable Sprint" + + interface_name = "sprint system" + interface_desc = "Increases power to the suit's actuators, allowing faster movement." + +/obj/item/rig_module/sprinter/activate() + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + to_chat(H, "You activate the suit's sprint mode.") + + holder.slowdown = initial(holder.slowdown) - sprint_speed + +/obj/item/rig_module/sprinter/deactivate() + + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + + to_chat(H, "Your hardsuit returns to normal speed.") + + holder.slowdown = initial(holder.slowdown) diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/teleporter.dm b/code/modules/clothing/spacesuits/rig/modules/specific/teleporter.dm new file mode 100644 index 0000000000..1f533f98fe --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/teleporter.dm @@ -0,0 +1,80 @@ +/obj/item/rig_module/teleporter + + name = "teleportation module" + desc = "A complex, sleek-looking, hardsuit-integrated teleportation module." + icon_state = "teleporter" + use_power_cost = 200 + redundant = 1 + usable = 1 + selectable = 1 + + engage_string = "Emergency Leap" + + interface_name = "VOID-shift phase projector" + interface_desc = "An advanced teleportation system. It is capable of pinpoint precision or random leaps forward." + +/obj/item/rig_module/teleporter/proc/phase_in(var/mob/M,var/turf/T) + + if(!M || !T) + return + + holder.spark_system.start() + playsound(T, 'sound/effects/phasein.ogg', 25, 1) + playsound(T, 'sound/effects/sparks2.ogg', 50, 1) + anim(T,M,'icons/mob/mob.dmi',,"phasein",,M.dir) + +/obj/item/rig_module/teleporter/proc/phase_out(var/mob/M,var/turf/T) + + if(!M || !T) + return + + playsound(T, "sparks", 50, 1) + anim(T,M,'icons/mob/mob.dmi',,"phaseout",,M.dir) + +/obj/item/rig_module/teleporter/engage(var/atom/target, var/notify_ai) + + var/mob/living/carbon/human/H = holder.wearer + + if(!istype(H.loc, /turf)) + to_chat(H, "You cannot teleport out of your current location.") + return 0 + + var/turf/T + if(target) + T = get_turf(target) + else + T = get_teleport_loc(get_turf(H), H, 6, 1, 1, 1) + + if(!T) + to_chat(H, "No valid teleport target found.") + return 0 + + if(T.density) + to_chat(H, "You cannot teleport into solid walls.") + return 0 + + if(T.z in using_map.admin_levels) + to_chat(H, "You cannot use your teleporter on this Z-level.") + return 0 + + if(T.contains_dense_objects()) + to_chat(H, "You cannot teleport to a location with solid objects.") + return 0 + + if(T.z != H.z || get_dist(T, get_turf(H)) > world.view) + to_chat(H, "You cannot teleport to such a distant object.") + return 0 + + if(!..()) return 0 + + phase_out(H,get_turf(H)) + H.forceMove(T) + phase_in(H,get_turf(H)) + + for(var/obj/item/weapon/grab/G in H.contents) + if(G.affecting) + phase_out(G.affecting,get_turf(G.affecting)) + G.affecting.forceMove(locate(T.x+rand(-1,1),T.y+rand(-1,1),T.z)) + phase_in(G.affecting,get_turf(G.affecting)) + + return 1 \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/vision.dm b/code/modules/clothing/spacesuits/rig/modules/specific/vision.dm new file mode 100644 index 0000000000..7e73f08880 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/vision.dm @@ -0,0 +1,237 @@ +/* + * Contains + * /obj/item/rig_module/vision + * /obj/item/rig_module/vision/multi + * /obj/item/rig_module/vision/meson + * /obj/item/rig_module/vision/thermal + * /obj/item/rig_module/vision/nvg + * /obj/item/rig_module/vision/medhud + * /obj/item/rig_module/vision/sechud + */ + +/datum/rig_vision + var/mode + var/obj/item/clothing/glasses/glasses + +/datum/rig_vision/nvg + mode = "night vision" + +/datum/rig_vision/nvg/New() + glasses = new /obj/item/clothing/glasses/night + +/datum/rig_vision/thermal + mode = "thermal scanner" + +/datum/rig_vision/thermal/New() + glasses = new /obj/item/clothing/glasses/thermal + +/datum/rig_vision/meson + mode = "meson scanner" + +/datum/rig_vision/meson/New() + glasses = new /obj/item/clothing/glasses/meson + +/datum/rig_vision/sechud + mode = "security HUD" + +/datum/rig_vision/sechud/New() + glasses = new /obj/item/clothing/glasses/hud/security + +/datum/rig_vision/medhud + mode = "medical HUD" + +/datum/rig_vision/medhud/New() + glasses = new /obj/item/clothing/glasses/hud/health + +/datum/rig_vision/material + mode = "material scanner" + +/datum/rig_vision/material/New() + glasses = new /obj/item/clothing/glasses/material + +/obj/item/rig_module/vision + + name = "hardsuit visor" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "optics" + + interface_name = "optical scanners" + interface_desc = "An integrated multi-mode vision system." + + usable = 1 + toggleable = 1 + disruptive = 0 + module_cooldown = 0 + + engage_string = "Cycle Visor Mode" + activate_string = "Enable Visor" + deactivate_string = "Disable Visor" + + var/datum/rig_vision/vision + var/list/vision_modes = list( + /datum/rig_vision/nvg, + /datum/rig_vision/thermal, + /datum/rig_vision/meson + ) + + var/vision_index + +/obj/item/rig_module/vision/multi + + name = "hardsuit optical package" + desc = "A complete visor system of optical scanners and vision modes." + icon_state = "fulloptics" + + + interface_name = "multi optical visor" + interface_desc = "An integrated multi-mode vision system." + + vision_modes = list(/datum/rig_vision/meson, + /datum/rig_vision/material, + /datum/rig_vision/nvg, + /datum/rig_vision/thermal, + /datum/rig_vision/sechud, + /datum/rig_vision/medhud) + +/obj/item/rig_module/vision/meson + + name = "hardsuit meson scanner" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "meson" + + usable = 0 + + interface_name = "meson scanner" + interface_desc = "An integrated meson scanner." + + vision_modes = list(/datum/rig_vision/meson) + +/obj/item/rig_module/vision/material + + name = "hardsuit material scanner" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "meson" + + usable = 0 + + interface_name = "material scanner" + interface_desc = "An integrated material scanner." + + vision_modes = list(/datum/rig_vision/material) + +/obj/item/rig_module/vision/mining + + name = "hardsuit mining scanners" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "optics" + + usable = 0 + + interface_name = "mining scanners" + interface_desc = "An integrated mining scanner array." + + vision_modes = list(/datum/rig_vision/material, + /datum/rig_vision/meson) + +/obj/item/rig_module/vision/thermal + + name = "hardsuit thermal scanner" + desc = "A layered, translucent visor system for a hardsuit." + icon_state = "thermal" + + usable = 0 + + interface_name = "thermal scanner" + interface_desc = "An integrated thermal scanner." + + vision_modes = list(/datum/rig_vision/thermal) + +/obj/item/rig_module/vision/nvg + + name = "hardsuit night vision interface" + desc = "A multi input night vision system for a hardsuit." + icon_state = "night" + + usable = 0 + + interface_name = "night vision interface" + interface_desc = "An integrated night vision system." + + vision_modes = list(/datum/rig_vision/nvg) + +/obj/item/rig_module/vision/sechud + + name = "hardsuit security hud" + desc = "A simple tactical information system for a hardsuit." + icon_state = "securityhud" + + usable = 0 + + interface_name = "security HUD" + interface_desc = "An integrated security heads up display." + + vision_modes = list(/datum/rig_vision/sechud) + +/obj/item/rig_module/vision/medhud + + name = "hardsuit medical hud" + desc = "A simple medical status indicator for a hardsuit." + icon_state = "healthhud" + + usable = 0 + + interface_name = "medical HUD" + interface_desc = "An integrated medical heads up display." + + vision_modes = list(/datum/rig_vision/medhud) + + +// There should only ever be one vision module installed in a suit. +/obj/item/rig_module/vision/installed() + ..() + holder.visor = src + +/obj/item/rig_module/vision/engage() + + if(!..() || !vision_modes) + return 0 + + // Don't cycle if this engage() is being called by activate(). + if(!active) + to_chat(holder.wearer, "You activate your visual sensors.") + return 1 + + if(vision_modes.len > 1) + vision_index++ + if(vision_index > vision_modes.len) + vision_index = 1 + vision = vision_modes[vision_index] + + to_chat(holder.wearer, "You cycle your sensors to [vision.mode] mode.") + else + to_chat(holder.wearer, "Your sensors only have one mode.") + return 1 + +/obj/item/rig_module/vision/activate() + if((. = ..()) && holder.wearer) + holder.wearer.recalculate_vis() + +/obj/item/rig_module/vision/deactivate() + if((. = ..()) && holder.wearer) + holder.wearer.recalculate_vis() + +/obj/item/rig_module/vision/New() + ..() + + if(!vision_modes) + return + + vision_index = 1 + var/list/processed_vision = list() + + for(var/vision_mode in vision_modes) + var/datum/rig_vision/vision_datum = new vision_mode + if(!vision) vision = vision_datum + processed_vision += vision_datum + + vision_modes = processed_vision \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/voice.dm b/code/modules/clothing/spacesuits/rig/modules/specific/voice.dm new file mode 100644 index 0000000000..0df94d756b --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/specific/voice.dm @@ -0,0 +1,52 @@ +/obj/item/rig_module/voice + + name = "hardsuit voice synthesiser" + desc = "A speaker box and sound processor." + icon_state = "megaphone" + usable = 1 + selectable = 0 + toggleable = 0 + disruptive = 0 + + engage_string = "Configure Synthesiser" + + interface_name = "voice synthesiser" + interface_desc = "A flexible and powerful voice modulator system." + + var/obj/item/voice_changer/voice_holder + +/obj/item/rig_module/voice/New() + ..() + voice_holder = new(src) + voice_holder.active = 0 + +/obj/item/rig_module/voice/installed() + ..() + holder.speech = src + +/obj/item/rig_module/voice/engage() + + if(!..()) + return 0 + + var/choice= input("Would you like to toggle the synthesiser or set the name?") as null|anything in list("Enable","Disable","Set Name") + + if(!choice) + return 0 + + switch(choice) + if("Enable") + active = 1 + voice_holder.active = 1 + to_chat(usr, "You enable the speech synthesiser.") + if("Disable") + active = 0 + voice_holder.active = 0 + to_chat(usr, "You disable the speech synthesiser.") + if("Set Name") + var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN) + if(!raw_choice) + return 0 + voice_holder.voice = raw_choice + to_chat(usr, "You are now mimicking [voice_holder.voice].") + return 1 \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index 058c1118ca..2362cb7990 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -5,6 +5,7 @@ * /obj/item/rig_module/device/drill * /obj/item/rig_module/device/orescanner * /obj/item/rig_module/device/rcd + * /obj/item/rig_module/device/arch_drill * /obj/item/rig_module/device/anomaly_scanner * /obj/item/rig_module/maneuvering_jets * /obj/item/rig_module/foam_sprayer @@ -96,6 +97,17 @@ device_type = /obj/item/weapon/rcd/electric/mounted/rig +/obj/item/rig_module/device/arch_drill + name = "archaeology drill mount" + desc = "A cell-powered fine-excavation device for a hardsuit." + icon_state = "exdrill" + interface_name = "mounted excavation tool" + interface_desc = "A device for excavating ancient relics." + usable = 1 + engage_string = "Configure Drill Depth" + + device_type = /obj/item/weapon/pickaxe/excavationdrill + /obj/item/rig_module/device/New() ..() if(device_type) device = new device_type(src) @@ -117,8 +129,6 @@ device.afterattack(target,holder.wearer,1) return 1 - - /obj/item/rig_module/chem_dispenser name = "mounted chemical dispenser" desc = "A complex web of tubing and needles suitable for hardsuit use." @@ -170,7 +180,7 @@ return 0 if(!input_item.reagents || !input_item.reagents.total_volume) - user << "\The [input_item] is empty." + to_chat(user, "\The [input_item] is empty.") return 0 // Magical chemical filtration system, do not question it. @@ -192,9 +202,9 @@ break if(total_transferred) - user << "You transfer [total_transferred] units into the suit reservoir." + to_chat(user, "You transfer [total_transferred] units into the suit reservoir.") else - user << "None of the reagents seem suitable." + to_chat(user, "None of the reagents seem suitable.") return 1 /obj/item/rig_module/chem_dispenser/engage(atom/target) @@ -205,7 +215,7 @@ var/mob/living/carbon/human/H = holder.wearer if(!charge_selected) - H << "You have not selected a chemical type." + to_chat(H, "You have not selected a chemical type.") return 0 var/datum/rig_charge/charge = charges[charge_selected] @@ -215,7 +225,7 @@ var/chems_to_use = 10 if(charge.charges <= 0) - H << "Insufficient chems!" + to_chat(H, "Insufficient chems!") return 0 else if(charge.charges < chems_to_use) chems_to_use = charge.charges @@ -230,8 +240,8 @@ target_mob = H if(target_mob != H) - H << "You inject [target_mob] with [chems_to_use] unit\s of [charge.display_name]." - target_mob << "You feel a rushing in your veins as [chems_to_use] unit\s of [charge.display_name] [chems_to_use == 1 ? "is" : "are"] injected." + to_chat(H, "You inject [target_mob] with [chems_to_use] unit\s of [charge.display_name].") + to_chat(target_mob, "You feel a rushing in your veins as [chems_to_use] unit\s of [charge.display_name] [chems_to_use == 1 ? "is" : "are"] injected.") target_mob.reagents.add_reagent(charge.display_name, chems_to_use) charge.charges -= chems_to_use @@ -321,17 +331,17 @@ if("Enable") active = 1 voice_holder.active = 1 - usr << "You enable the speech synthesiser." + to_chat(usr, "You enable the speech synthesiser.") if("Disable") active = 0 voice_holder.active = 0 - usr << "You disable the speech synthesiser." + to_chat(usr, "You disable the speech synthesiser.") if("Set Name") var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN) if(!raw_choice) return 0 voice_holder.voice = raw_choice - usr << "You are now mimicking [voice_holder.voice]." + to_chat(usr, "You are now mimicking [voice_holder.voice].") return 1 /obj/item/rig_module/maneuvering_jets @@ -418,29 +428,14 @@ interface_name = "mop projector" interface_desc = "A mop that can be deployed from the hand of the wearer." - usable = 0 + usable = 1 selectable = 1 toggleable = 1 - use_power_cost = 0 + use_power_cost = 5 active_power_cost = 0 passive_power_cost = 0 - gun = /obj/item/weapon/reagent_containers/spray/cleaner - -//obj/item/weapon/reagent_containers/spray/cleaner -// spary = - -/obj/item/rig_module/mounted/engage(atom/target) - - if(!..()) - return 0 - - if(!target) - gun.attack_self(holder.wearer) - return 1 - - gun.Fire(target,holder.wearer) - return 1 + gun_type = /obj/item/weapon/gun/energy/temperature/mounted /obj/item/rig_module/mounted/mop/process() @@ -458,7 +453,7 @@ var/mob/living/M = holder.wearer if(M.l_hand && M.r_hand) - M << "Your hands are full." + to_chat(M, "Your hands are full.") deactivate() return @@ -515,10 +510,10 @@ return 0 if(accepted_item.charges >= 5) - user << "Another grenade of that type will not fit into the module." + to_chat(user, "Another grenade of that type will not fit into the module.") return 0 - user << "You slot \the [input_device] into the suit module." + to_chat(user, "You slot \the [input_device] into the suit module.") user.drop_from_inventory(input_device) qdel(input_device) accepted_item.charges++ @@ -535,7 +530,7 @@ var/mob/living/carbon/human/H = holder.wearer if(!charge_selected) - H << "You have not selected a grenade type." + to_chat(H, "You have not selected a grenade type.") return 0 var/datum/rig_charge/charge = charges[charge_selected] @@ -544,7 +539,7 @@ return 0 if(charge.charges <= 0) - H << "Insufficient grenades!" + to_chat(H, "Insufficient grenades!") return 0 charge.charges-- @@ -607,10 +602,10 @@ if(!target) if(device == iastamp) device = deniedstamp - holder.wearer << "Switched to denied stamp." + to_chat(holder.wearer, "Switched to denied stamp.") else if(device == deniedstamp) device = iastamp - holder.wearer << "Switched to internal affairs stamp." + to_chat(holder.wearer, "Switched to internal affairs stamp.") return 1 /obj/item/rig_module/sprinter @@ -642,7 +637,7 @@ var/mob/living/carbon/human/H = holder.wearer - H << "You activate the suit's sprint mode." + to_chat(H, "You activate the suit's sprint mode.") holder.slowdown = initial(holder.slowdown) - sprint_speed @@ -653,6 +648,6 @@ var/mob/living/carbon/human/H = holder.wearer - H << "Your hardsuit returns to normal speed." + to_chat(H, "Your hardsuit returns to normal speed.") - holder.slowdown = initial(holder.slowdown) \ No newline at end of file + holder.slowdown = initial(holder.slowdown) diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm index f42204a444..ad8a6fc5ab 100644 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -205,7 +205,7 @@ // Don't cycle if this engage() is being called by activate(). if(starting_up) - holder.wearer << "You activate your visual sensors." + to_chat(holder.wearer, "You activate your visual sensors.") return 1 if(vision_modes.len > 1) @@ -214,9 +214,9 @@ vision_index = 1 vision = vision_modes[vision_index] - holder.wearer << "You cycle your sensors to [vision.mode] mode." + to_chat(holder.wearer, "You cycle your sensors to [vision.mode] mode.") else - holder.wearer << "Your sensors only have one mode." + to_chat(holder.wearer, "Your sensors only have one mode.") return 1 /obj/item/rig_module/vision/activate() diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 7cf9084435..7000801506 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -95,18 +95,18 @@ var/datum/effect/effect/system/spark_spread/spark_system /obj/item/weapon/rig/examine() - to_chat(usr, "This is \icon[src][src.name].") + to_chat(usr, "This is [bicon(src)][src.name].") to_chat(usr, "[src.desc]") if(wearer) for(var/obj/item/piece in list(helmet,gloves,chest,boots)) if(!piece || piece.loc != wearer) continue - to_chat(usr, "\icon[piece] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed.") + to_chat(usr, "[bicon(piece)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed.") if(src.loc == usr) to_chat(usr, "The access panel is [locked? "locked" : "unlocked"].") to_chat(usr, "The maintenance panel is [open ? "open" : "closed"].") - to_chat(usr, "Hardsuit systems are [offline ? "offline" : "online"].") + to_chat(usr, "Hardsuit systems are [offline ? "offline" : "online"].") to_chat(usr, "The cooling stystem is [cooling_on ? "active" : "inactive"].") if(open) @@ -232,6 +232,15 @@ update_airtight(piece, 0) // Unseal update_icon(1) +/obj/item/weapon/rig/proc/cut_suit() + offline = 2 + canremove = 1 + toggle_piece("helmet", loc, ONLY_RETRACT, TRUE) + toggle_piece("gauntlets", loc, ONLY_RETRACT, TRUE) + toggle_piece("boots", loc, ONLY_RETRACT, TRUE) + toggle_piece("chest", loc, ONLY_RETRACT, TRUE) + update_icon(1) + /obj/item/weapon/rig/proc/toggle_seals(var/mob/living/carbon/human/M,var/instant) if(sealing) return @@ -265,7 +274,7 @@ if(!failed_to_seal) if(!instant) - M.visible_message("[M]'s suit emits a quiet hum as it begins to adjust its seals.","With a quiet hum, the suit begins running checks and adjusting components.") + M.visible_message("[M]'s suit emits a quiet hum as it begins to adjust its seals.","With a quiet hum, the suit begins running checks and adjusting components.") if(seal_delay && !do_after(M,seal_delay)) if(M) to_chat(M, "You must remain still while the suit is adjusting the components.") @@ -297,16 +306,16 @@ piece.icon_state = "[suit_state][!seal_target ? "_sealed" : ""]" switch(msg_type) if("boots") - to_chat(M, "\The [piece] [!seal_target ? "seal around your feet" : "relax their grip on your legs"].") + to_chat(M, "\The [piece] [!seal_target ? "seal around your feet" : "relax their grip on your legs"].") M.update_inv_shoes() if("gloves") - to_chat(M, "\The [piece] [!seal_target ? "tighten around your fingers and wrists" : "become loose around your fingers"].") + to_chat(M, "\The [piece] [!seal_target ? "tighten around your fingers and wrists" : "become loose around your fingers"].") M.update_inv_gloves() if("chest") - to_chat(M, "\The [piece] [!seal_target ? "cinches tight again your chest" : "releases your chest"].") + to_chat(M, "\The [piece] [!seal_target ? "cinches tight again your chest" : "releases your chest"].") M.update_inv_wear_suit() if("helmet") - to_chat(M, "\The [piece] hisses [!seal_target ? "closed" : "open"].") + to_chat(M, "\The [piece] hisses [!seal_target ? "closed" : "open"].") M.update_inv_head() if(helmet) helmet.update_light(wearer) @@ -341,7 +350,7 @@ // Success! canremove = seal_target - to_chat(M, "Your entire suit [canremove ? "loosens as the components relax" : "tightens around you as the components lock into place"].") + to_chat(M, "Your entire suit [canremove ? "loosens as the components relax" : "tightens around you as the components lock into place"].") M.client.screen -= booting_L qdel(booting_L) booting_R.icon_state = "boot_done" @@ -661,11 +670,11 @@ if(user.back != src && user.belt != src) return 0 else if(!src.allowed(user)) - user << "Unauthorized user. Access denied." + to_chat(user, "Unauthorized user. Access denied.") return 0 else if(!ai_override_enabled) - user << "Synthetic access disabled. Please consult hardware provider." + to_chat(user, "Synthetic access disabled. Please consult hardware provider.") return 0 return 1 @@ -711,7 +720,7 @@ /obj/item/weapon/rig/proc/notify_ai(var/message) for(var/obj/item/rig_module/ai_container/module in installed_modules) if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) - module.integrated_ai << "[message]" + to_chat(module.integrated_ai, "[message]") . = 1 /obj/item/weapon/rig/equipped(mob/living/carbon/human/M) @@ -726,7 +735,7 @@ return if(seal_delay > 0 && istype(M) && (M.back == src || M.belt == src)) - M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...") + M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...") if(!do_after(M,seal_delay)) if(M && (M.back == src || M.belt == src)) if(!M.unEquip(src)) @@ -735,20 +744,20 @@ return if(istype(M) && (M.back == src || M.belt == src)) - M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].") + M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].") wearer = M wearer.wearing_rig = src update_icon() -/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode) +/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode, var/forced = FALSE) - if(sealing || !cell || !cell.charge) + if((sealing || !cell || !cell.charge) && !forced) return - if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) + if((!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) && !forced) return - if(usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) // If the usr isn't wearing the suit it's probably an AI. + if((usr == wearer && (usr.stat||usr.paralysis||usr.stunned)) && !forced) // If the usr isn't wearing the suit it's probably an AI. return var/obj/item/check_slot @@ -785,7 +794,7 @@ holder = use_obj.loc if(istype(holder)) if(use_obj && check_slot == use_obj) - H << "Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly." + to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.") use_obj.canremove = 1 holder.drop_from_inventory(use_obj) use_obj.forceMove(get_turf(src)) @@ -800,10 +809,10 @@ if(!H.equip_to_slot_if_possible(use_obj, equip_to, 0, 1)) use_obj.forceMove(src) if(check_slot) - H << "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way." + to_chat(H, "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.") return else - H << "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly." + to_chat(H, "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.") if(piece == "helmet" && helmet) helmet.update_light(H) @@ -918,17 +927,17 @@ if(wearer) if(dam_module.damage >= 2) - wearer << "The [source] has disabled your [dam_module.interface_name]!" + to_chat(wearer, "The [source] has disabled your [dam_module.interface_name]!") else - wearer << "The [source] has damaged your [dam_module.interface_name]!" + to_chat(wearer, "The [source] has damaged your [dam_module.interface_name]!") dam_module.deactivate() /obj/item/weapon/rig/proc/malfunction_check(var/mob/living/carbon/human/user) if(malfunction_delay) if(offline) - user << "The suit is completely unresponsive." + to_chat(user, "The suit is completely unresponsive.") else - user << "ERROR: Hardware fault. Rebooting interface..." + to_chat(user, "ERROR: Hardware fault. Rebooting interface...") return 1 return 0 @@ -952,17 +961,20 @@ return 0 var/obj/item/rig_module/ai_container/module = user.loc.loc if(!istype(module) || module.damage >= 2) - user << "Your host module is unable to interface with the suit." + to_chat(user, "Your host module is unable to interface with the suit.") return 0 if(offline || !cell || !cell.charge || locked_down) - if(user) user << "Your host rig is unpowered and unresponsive." + if(user) + to_chat(user, "Your host rig is unpowered and unresponsive.") return 0 if(!wearer || (wearer.back != src && wearer.belt != src)) - if(user) user << "Your host rig is not being worn." + if(user) + to_chat(user, "Your host rig is not being worn.") return 0 if(!wearer.stat && !control_overridden && !ai_override_enabled) - if(user) user << "You are locked out of the suit servo controller." + if(user) + to_chat(user, "You are locked out of the suit servo controller.") return 0 return 1 @@ -970,7 +982,7 @@ if(!ai_can_move_suit(user, check_user_module = 1)) return wearer.lay_down() - user << "\The [wearer] is now [wearer.resting ? "resting" : "getting up"]." + to_chat(user, "\The [wearer] is now [wearer.resting ? "resting" : "getting up"].") /obj/item/weapon/rig/proc/forced_move(var/direction, var/mob/user) @@ -1010,7 +1022,7 @@ for(var/mob/M in range(wearer, 1)) if(M.pulling == wearer) if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M)) - user << "Your host is restrained! They can't move!" + to_chat(user, "Your host is restrained! They can't move!") return 0 else M.stop_pulling() diff --git a/code/modules/clothing/spacesuits/rig/rig_pieces.dm b/code/modules/clothing/spacesuits/rig/rig_pieces.dm index 6bc6865a21..04df18d1e8 100644 --- a/code/modules/clothing/spacesuits/rig/rig_pieces.dm +++ b/code/modules/clothing/spacesuits/rig/rig_pieces.dm @@ -68,7 +68,7 @@ if(tacknife) tacknife.loc = get_turf(src) if(M.put_in_active_hand(tacknife)) - M << "You slide \the [tacknife] out of [src]." + to_chat(M, "You slide \the [tacknife] out of [src].") playsound(M, 'sound/weapons/flipblade.ogg', 40, 1) tacknife = null update_icon() @@ -82,7 +82,7 @@ M.drop_item() tacknife = I I.loc = src - M << "You slide the [I] into [src]." + to_chat(M, "You slide the [I] into [src].") playsound(M, 'sound/weapons/flipblade.ogg', 40, 1) update_icon() ..() diff --git a/code/modules/clothing/spacesuits/rig/rig_pieces_vr.dm b/code/modules/clothing/spacesuits/rig/rig_pieces_vr.dm index 9d75d1ee11..b5d710d9a4 100644 --- a/code/modules/clothing/spacesuits/rig/rig_pieces_vr.dm +++ b/code/modules/clothing/spacesuits/rig/rig_pieces_vr.dm @@ -3,14 +3,13 @@ SPECIES_TAJ = 'icons/mob/species/tajaran/helmet.dmi', SPECIES_SKRELL = 'icons/mob/species/skrell/helmet.dmi', SPECIES_UNATHI = 'icons/mob/species/unathi/helmet.dmi', - SPECIES_NEVREAN = 'icons/mob/species/nevrean/helmet_vr.dmi', + SPECIES_XENOHYBRID = 'icons/mob/species/unathi/helmet.dmi', SPECIES_AKULA = 'icons/mob/species/akula/helmet_vr.dmi', SPECIES_SERGAL = 'icons/mob/species/sergal/helmet_vr.dmi', - SPECIES_ZORREN_FLAT = 'icons/mob/species/fennec/helmet_vr.dmi', - SPECIES_ZORREN_HIGH = 'icons/mob/species/fox/helmet_vr.dmi', SPECIES_VULPKANIN = 'icons/mob/species/vulpkanin/helmet.dmi', + SPECIES_ZORREN_HIGH = 'icons/mob/species/vulpkanin/helmet.dmi', + SPECIES_FENNEC = 'icons/mob/species/vulpkanin/helmet.dmi', SPECIES_PROMETHEAN = 'icons/mob/species/skrell/helmet.dmi', - SPECIES_XENOHYBRID = 'icons/mob/species/unathi/helmet.dmi', SPECIES_VOX = 'icons/mob/species/vox/head.dmi', SPECIES_TESHARI = 'icons/mob/species/seromi/head.dmi', SPECIES_GREY_YW = 'icons/mob/species/grey/helmet.dmi' /*ywedit*/ @@ -23,30 +22,29 @@ SPECIES_TAJ = 'icons/mob/species/tajaran/suit.dmi', SPECIES_SKRELL = 'icons/mob/species/skrell/suit.dmi', SPECIES_UNATHI = 'icons/mob/species/unathi/suit.dmi', - SPECIES_NEVREAN = 'icons/mob/species/nevrean/suit_vr.dmi', + SPECIES_XENOHYBRID = 'icons/mob/species/unathi/suit.dmi', SPECIES_AKULA = 'icons/mob/species/akula/suit_vr.dmi', SPECIES_SERGAL = 'icons/mob/species/sergal/suit_vr.dmi', - SPECIES_ZORREN_FLAT = 'icons/mob/species/fennec/suit_vr.dmi', - SPECIES_ZORREN_HIGH = 'icons/mob/species/fox/suit_vr.dmi', SPECIES_VULPKANIN = 'icons/mob/species/vulpkanin/suit.dmi', + SPECIES_ZORREN_HIGH = 'icons/mob/species/vulpkanin/suit.dmi', + SPECIES_FENNEC = 'icons/mob/species/vulpkanin/suit.dmi', SPECIES_PROMETHEAN = 'icons/mob/species/skrell/suit.dmi', - SPECIES_XENOHYBRID = 'icons/mob/species/unathi/suit.dmi', SPECIES_VOX = 'icons/mob/species/vox/suit.dmi', SPECIES_TESHARI = 'icons/mob/species/seromi/suit.dmi' ) /obj/item/clothing/head/helmet/space/rig - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE, SPECIES_GREY_YW/*ywedit*/) + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE, SPECIES_GREY_YW/*ywedit*/) flags = PHORONGUARD //YAWN Edit /obj/item/clothing/gloves/gauntlets/rig - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE, SPECIES_GREY_YW/*ywedit*/) + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE, SPECIES_GREY_YW/*ywedit*/) flags = PHORONGUARD //YAWN Edit /obj/item/clothing/shoes/magboots/rig - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE, SPECIES_GREY_YW/*ywedit*/) + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE, SPECIES_GREY_YW/*ywedit*/) flags = PHORONGUARD //YAWN Edit /obj/item/clothing/suit/space/rig - species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE, SPECIES_GREY_YW/*ywedit*/) + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_ALRAUNE, SPECIES_GREY_YW/*ywedit*/) flags = PHORONGUARD //YAWN Edit diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm index fc0b8bee43..9f832ec911 100644 --- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm +++ b/code/modules/clothing/spacesuits/rig/rig_verbs.dm @@ -17,21 +17,21 @@ set src = usr.contents if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return if(!check_power_cost(usr)) return if(canremove) - usr << "The suit is not active." + to_chat(usr, "The suit is not active.") return if(!check_suit_access(usr)) return if(!visor) - usr << "The hardsuit does not have a configurable visor." + to_chat(usr, "The hardsuit does not have a configurable visor.") return if(!visor.active) @@ -47,7 +47,7 @@ set src = usr.contents if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return if(!check_suit_access(usr)) @@ -75,7 +75,7 @@ set src = usr.contents if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return if(!check_suit_access(usr)) @@ -91,7 +91,7 @@ set src = usr.contents if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return if(!check_suit_access(usr)) @@ -107,7 +107,7 @@ set src = usr.contents if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return if(!check_suit_access(usr)) @@ -126,7 +126,7 @@ set src = usr.contents if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return if(!check_suit_access(usr)) @@ -148,18 +148,18 @@ return if(canremove) - usr << "The suit is not active." + to_chat(usr, "The suit is not active.") return if(!visor) - usr << "The hardsuit does not have a configurable visor." + to_chat(usr, "The hardsuit does not have a configurable visor.") return if(!visor.active) visor.activate() if(!visor.active) - usr << "The visor is suffering a hardware fault and cannot be configured." + to_chat(usr, "The visor is suffering a hardware fault and cannot be configured.") return visor.engage() @@ -175,15 +175,15 @@ return if(canremove) - usr << "The suit is not active." + to_chat(usr, "The suit is not active.") return if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return if(!speech) - usr << "The hardsuit does not have a speech synthesiser." + to_chat(usr, "The hardsuit does not have a speech synthesiser.") return speech.engage() @@ -202,11 +202,11 @@ return if(canremove) - usr << "The suit is not active." + to_chat(usr, "The suit is not active.") return if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return var/list/selectable = list() @@ -218,11 +218,11 @@ if(!istype(module)) selected_module = null - usr << "Primary system is now: deselected." + to_chat(usr, "Primary system is now: deselected.") return selected_module = module - usr << "Primary system is now: [selected_module.interface_name]." + to_chat(usr, "Primary system is now: [selected_module.interface_name].") /obj/item/weapon/rig/verb/toggle_module() @@ -238,11 +238,11 @@ return if(canremove) - usr << "The suit is not active." + to_chat(usr, "The suit is not active.") return if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return var/list/selectable = list() @@ -256,10 +256,10 @@ return if(module.active) - usr << "You attempt to deactivate \the [module.interface_name]." + to_chat(usr, "You attempt to deactivate \the [module.interface_name].") module.deactivate() else - usr << "You attempt to activate \the [module.interface_name]." + to_chat(usr, "You attempt to activate \the [module.interface_name].") module.activate() /obj/item/weapon/rig/verb/engage_module() @@ -273,11 +273,11 @@ return if(canremove) - usr << "The suit is not active." + to_chat(usr, "The suit is not active.") return if(!istype(wearer) || (!wearer.back == src && !wearer.belt == src)) - usr << "The hardsuit is not being worn." + to_chat(usr, "The hardsuit is not being worn.") return if(!check_power_cost(usr, 0, 0, 0, 0)) @@ -293,5 +293,5 @@ if(!istype(module)) return - usr << "You attempt to engage the [module.interface_name]." + to_chat(usr, "You attempt to engage the [module.interface_name].") module.engage() \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/rig/suits/ert_vr.dm b/code/modules/clothing/spacesuits/rig/suits/ert_vr.dm index 034ea31865..14b1e1d98f 100644 --- a/code/modules/clothing/spacesuits/rig/suits/ert_vr.dm +++ b/code/modules/clothing/spacesuits/rig/suits/ert_vr.dm @@ -6,7 +6,7 @@ initial_modules = list( /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/grenade_launcher/cleaner, + /obj/item/rig_module/cleaner_launcher, ) /obj/item/weapon/rig/ert/assetprotection diff --git a/code/modules/clothing/spacesuits/rig/suits/robotics.dm b/code/modules/clothing/spacesuits/rig/suits/robotics.dm new file mode 100644 index 0000000000..4fb43d355d --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/suits/robotics.dm @@ -0,0 +1,29 @@ +//Mining suit +/obj/item/weapon/rig/robotics + name = "advanced suit control belt" + suit_type = "advanced suit" + desc = "A lightweight suit combining the utility of a RIG with the wearability of a voidsuit." + icon_state = "void_explorer2" + slot_flags = SLOT_BELT + armor = list(melee = 40, bullet = 30, laser = 20, energy = 15, bomb = 30, bio = 100, rad = 50) + slowdown = 1 + offline_slowdown = 2 + offline_vision_restriction = 0 + emp_protection = -20 + siemens_coefficient= 0.75 + rigsuit_max_pressure = 8 * ONE_ATMOSPHERE + rigsuit_min_pressure = 0 + + chest_type = /obj/item/clothing/suit/space/rig + helm_type = /obj/item/clothing/head/helmet/space/rig + boot_type = null + glove_type = null + cell_type = null + + allowed = list( + /obj/item/device/flashlight, + /obj/item/weapon/storage/box + ) + + req_access = list() + req_one_access = list() diff --git a/code/modules/clothing/spacesuits/rig/suits/station_vr.dm b/code/modules/clothing/spacesuits/rig/suits/station_vr.dm index 2049530d02..f543b95431 100644 --- a/code/modules/clothing/spacesuits/rig/suits/station_vr.dm +++ b/code/modules/clothing/spacesuits/rig/suits/station_vr.dm @@ -17,36 +17,39 @@ //Area allowing backpacks to be placed on rigsuits. /obj/item/weapon/rig/vox - allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/backpack, /obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/combat - allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/ert allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank, /obj/item/device/t_scanner, /obj/item/weapon/rcd, /obj/item/weapon/tool/crowbar, \ /obj/item/weapon/tool/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, /obj/item/device/multitool, \ /obj/item/device/radio, /obj/item/device/analyzer,/obj/item/weapon/storage/briefcase/inflatable, /obj/item/weapon/melee/baton, /obj/item/weapon/gun, \ - /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/light/ninja - allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/cell, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/handcuffs,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/cell, /obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/merc - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs, /obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/ce - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd,/obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/medical - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/firstaid,/obj/item/device/healthanalyzer,/obj/item/stack/medical,/obj/item/roller,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/firstaid,/obj/item/device/healthanalyzer,/obj/item/stack/medical,/obj/item/roller,/obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/hazmat - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/stack/flag,/obj/item/weapon/storage/excavation,/obj/item/weapon/pickaxe,/obj/item/device/healthanalyzer,/obj/item/device/measuring_tape,/obj/item/device/ano_scanner,/obj/item/device/depth_scanner,/obj/item/device/core_sampler,/obj/item/device/gps,/obj/item/device/beacon_locator,/obj/item/device/radio/beacon,/obj/item/weapon/pickaxe/hand,/obj/item/weapon/storage/bag/fossils,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/stack/flag,/obj/item/weapon/storage/excavation,/obj/item/weapon/pickaxe,/obj/item/device/healthanalyzer,/obj/item/device/measuring_tape,/obj/item/device/ano_scanner,/obj/item/device/depth_scanner,/obj/item/device/core_sampler,/obj/item/device/gps,/obj/item/device/beacon_locator,/obj/item/device/radio/beacon,/obj/item/weapon/pickaxe/hand,/obj/item/weapon/storage/bag/fossils,/obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/hazard - allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton,/obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/industrial - allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd,/obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd,/obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/military allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/handcuffs, \ /obj/item/device/t_scanner, /obj/item/weapon/rcd, /obj/item/weapon/weldingtool, /obj/item/weapon/tool, /obj/item/device/multitool, \ /obj/item/device/radio, /obj/item/device/analyzer,/obj/item/weapon/storage/briefcase/inflatable, /obj/item/weapon/melee/baton, /obj/item/weapon/gun, \ - /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/device/suit_cooling_unit, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/device/suit_cooling_unit, /obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) /obj/item/weapon/rig/pmc allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank, /obj/item/device/t_scanner, /obj/item/weapon/rcd, /obj/item/weapon/tool/crowbar, \ /obj/item/weapon/tool/screwdriver, /obj/item/weapon/weldingtool, /obj/item/weapon/tool/wirecutters, /obj/item/weapon/tool/wrench, /obj/item/device/multitool, \ /obj/item/device/radio, /obj/item/device/analyzer,/obj/item/weapon/storage/briefcase/inflatable, /obj/item/weapon/melee/baton, /obj/item/weapon/gun, \ - /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/weapon/storage/backpack,/obj/item/device/subspaceradio) + /obj/item/weapon/storage/firstaid, /obj/item/weapon/reagent_containers/hypospray, /obj/item/roller, /obj/item/weapon/storage/backpack,/obj/item/device/bluespaceradio, /obj/item/device/defib_kit) + +/obj/item/weapon/rig/robotics + allowed = list(/obj/item/device/flashlight, /obj/item/weapon/storage/box, /obj/item/weapon/storage/belt, /obj/item/device/defib_kit/compact) diff --git a/code/modules/clothing/spacesuits/spacesuits.dm b/code/modules/clothing/spacesuits/spacesuits.dm index 300368260a..c811cdaee9 100644 --- a/code/modules/clothing/spacesuits/spacesuits.dm +++ b/code/modules/clothing/spacesuits/spacesuits.dm @@ -6,6 +6,8 @@ name = "Space helmet" icon_state = "space" desc = "A special helmet designed for work in a hazardous, low-pressure environment." + randpixel = 0 + center_of_mass = null flags = PHORONGUARD item_flags = THICKMATERIAL | AIRTIGHT | ALLOW_SURVIVALFOOD permeability_coefficient = 0.01 @@ -30,31 +32,31 @@ brightness_on = 4 on = 0 -/obj/item/clothing/head/helmet/space/verb/toggle_camera() +/obj/item/clothing/head/helmet/space/Initialize() + . = ..() + if(camera_networks) + verbs |= /obj/item/clothing/head/helmet/space/proc/toggle_camera + +/obj/item/clothing/head/helmet/space/proc/toggle_camera() set name = "Toggle Helmet Camera" set desc = "Turn your helmet's camera on or off." - set category = "Object" + set category = "Hardsuit" set src in usr if(usr.stat || usr.restrained() || usr.incapacitated()) return - if(camera_networks) - if(!camera) - camera = new /obj/machinery/camera(src) - camera.replace_networks(camera_networks) - camera.set_status(FALSE) //So the camera will activate in the following check. - - if(camera.status == TRUE) - camera.set_status(FALSE) - to_chat(usr, "Camera deactivated.") - else - camera.set_status(TRUE) - camera.c_tag = usr.name - to_chat(usr, "User scanned as [camera.c_tag]. Camera activated.") + if(!camera) + camera = new /obj/machinery/camera(src) + camera.replace_networks(camera_networks) + camera.set_status(FALSE) //So the camera will activate in the following check. + if(camera.status == TRUE) + camera.set_status(FALSE) + to_chat(usr, "Camera deactivated.") else - to_chat(usr, "This helmet does not have a built-in camera.") - return + camera.set_status(TRUE) + camera.c_tag = usr.name + to_chat(usr, "User scanned as [camera.c_tag]. Camera activated.") /obj/item/clothing/head/helmet/space/examine() ..() diff --git a/code/modules/clothing/spacesuits/void/military_vr.dm b/code/modules/clothing/spacesuits/void/military_vr.dm index c954daa09d..006b107067 100644 --- a/code/modules/clothing/spacesuits/void/military_vr.dm +++ b/code/modules/clothing/spacesuits/void/military_vr.dm @@ -1,3 +1,28 @@ +/obj/item/clothing/head/helmet/space/void/captain + name = "\improper director helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. This model sacrifices mobility for even more armor." + icon_state = "capvoid" + item_state_slots = list(slot_r_hand_str = "sec_helm", slot_l_hand_str = "sec_helm") + armor = list(melee = 60, bullet = 35, laser = 35, energy = 15, bomb = 55, bio = 100, rad = 20) + +/obj/item/clothing/suit/space/void/captain + name = "\improper director armor" + desc = "A special suit that protects against hazardous, low pressure environments. This model sacrifices mobility for even more armor." + icon_state = "capsuit_void" + item_state_slots = list(slot_r_hand_str = "sec_voidsuit", slot_l_hand_str = "sec_voidsuit") + slowdown = 1.5 + armor = list(melee = 60, bullet = 35, laser = 35, energy = 15, bomb = 55, bio = 100, rad = 20) + +/obj/item/clothing/head/helmet/space/void/merc/prototype + name = "\improper prototype voidsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. This is an advanced model commonly used by militaries and emergency response." + icon_state = "hosproto" + +/obj/item/clothing/suit/space/void/merc/prototype + name = "\improper prototype voidsuit" + desc = "A special suit that protects against hazardous, low pressure environments. This is an advanced model commonly used by militaries and emergency response." + icon_state = "hosproto_void" + /obj/item/clothing/head/helmet/space/void/merc/odst name = "\improper ODST Helmet" desc = "\"... we are glad to plunge feet first into hell in the knowledge that we will rise.\"" @@ -7,6 +32,7 @@ icon = 'icons/obj/clothing/hats_vr.dmi' icon_override = 'icons/mob/head_vr.dmi' light_overlay = "helmet_light_dual" + species_restricted = null /*/obj/item/clothing/head/helmet/space/void/merc/odst/jertheace // Given to Acacius during an event. Save this for use in events. name = "\improper Ace's ODST Helmet" @@ -23,4 +49,5 @@ item_state = "odst" item_state_slots = null icon = 'icons/obj/clothing/suits_vr.dmi' - icon_override = 'icons/mob/suit_vr.dmi' \ No newline at end of file + icon_override = 'icons/mob/suit_vr.dmi' + species_restricted = null diff --git a/code/modules/clothing/spacesuits/void/station.dm b/code/modules/clothing/spacesuits/void/station.dm index d15db0f77d..9e353dbe19 100644 --- a/code/modules/clothing/spacesuits/void/station.dm +++ b/code/modules/clothing/spacesuits/void/station.dm @@ -193,27 +193,32 @@ /obj/item/clothing/head/helmet/space/void/security/riot name = "crowd control voidsuit helmet" + desc = "A heavy-set and ominous looking crowd control suit helmet. Fitted with state of the art shock absorbing materials, to disperse blunt force trauma." icon_state = "rig0-sec_riot" + armor = list(melee = 70, bullet = 15, laser = 15, energy = 5, bomb = 40, bio = 100, rad = 10) item_state_slots = list(slot_r_hand_str = "sec_helm_riot", slot_l_hand_str = "sec_helm_riot") /obj/item/clothing/suit/space/void/security/riot name = "crowd control voidsuit" + desc = "A heavy-set and ominous looking crowd control suit. Fitted with state of the art shock absorbing materials, to disperse blunt force trauma." icon_state = "rig-sec_riot" + armor = list(melee = 70, bullet = 15, laser = 15, energy = 5, bomb = 40, bio = 100, rad = 10) item_state_slots = list(slot_r_hand_str = "sec_voidsuit_riot", slot_l_hand_str = "sec_voidsuit_riot") //Security Surplus Voidsuit /obj/item/clothing/head/helmet/space/void/security/alt - name = "riot security voidsuit helmet" - desc = "A somewhat tacky voidsuit helmet, a fact mitigated by heavy armor plating." + name = "security EVA voidsuit helmet" + desc = "A grey-black voidsuit helmet with red highlights. A little tacky, but it offers better protection against modern firearms and radiation than standard-issue security voidsuit helmets." + armor = list(melee = 30, bullet = 40, laser = 40, energy = 25, bomb = 60, bio = 100, rad = 50) icon_state = "rig0-secalt" - armor = list(melee = 70, bullet = 20, laser = 30, energy = 5, bomb = 35, bio = 100, rad = 10) - + item_state_slots = list(slot_r_hand_str = "syndicate-helm-black", slot_l_hand_str = "syndicate-helm-black") + /obj/item/clothing/suit/space/void/security/alt + name = "security EVA voidsuit" + desc = "A grey-black voidsuit with red highlights. A little tacky, but it offers better protection against modern firearms and radiation than standard-issue security voidsuits." + armor = list(melee = 30, bullet = 40, laser = 40, energy = 25, bomb = 60, bio = 100, rad = 50) icon_state = "rig-secalt" - name = "riot security voidsuit" - desc = "A heavily armored voidsuit, designed to intimidate people who find black intimidating. Surprisingly slimming." - armor = list(melee = 70, bullet = 20, laser = 30, energy = 5, bomb = 35, bio = 100, rad = 10) - allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton) + item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG") //Atmospherics /obj/item/clothing/head/helmet/space/void/atmos diff --git a/code/modules/clothing/spacesuits/void/void_vr.dm b/code/modules/clothing/spacesuits/void/void_vr.dm index 9bbe440400..5d6cf0ae2f 100644 --- a/code/modules/clothing/spacesuits/void/void_vr.dm +++ b/code/modules/clothing/spacesuits/void/void_vr.dm @@ -6,67 +6,58 @@ // /obj/item/clothing/head/helmet/space/void + species_restricted = list(SPECIES_HUMAN, SPECIES_NEVREAN, SPECIES_RAPALA, SPECIES_VASILISSAN, SPECIES_ALRAUNE, SPECIES_PROMETHEAN, SPECIES_XENOCHIMERA) sprite_sheets = list( - "Tajara" = 'icons/mob/species/tajaran/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Teshari" = 'icons/mob/species/seromi/head.dmi', - "Nevrean" = 'icons/mob/species/nevrean/helmet_vr.dmi', - "Akula" = 'icons/mob/species/akula/helmet_vr.dmi', - "Sergal" = 'icons/mob/species/sergal/helmet_vr.dmi', - "Flatland Zorren" = 'icons/mob/species/fennec/helmet_vr.dmi', - "Highlander Zorren" = 'icons/mob/species/fox/helmet_vr.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Promethean" = 'icons/mob/species/skrell/helmet.dmi', - "Xenomorph Hybrid" = 'icons/mob/species/unathi/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' /*ywedit*/ + SPECIES_TAJ = 'icons/mob/species/tajaran/helmet.dmi', + SPECIES_SKRELL = 'icons/mob/species/skrell/helmet.dmi', + SPECIES_UNATHI = 'icons/mob/species/unathi/helmet.dmi', + SPECIES_TESHARI = 'icons/mob/species/seromi/head.dmi', + SPECIES_XENOHYBRID = 'icons/mob/species/unathi/helmet.dmi', + SPECIES_AKULA = 'icons/mob/species/akula/helmet_vr.dmi', + SPECIES_SERGAL = 'icons/mob/species/sergal/helmet_vr.dmi', + SPECIES_VULPKANIN = 'icons/mob/species/vulpkanin/helmet.dmi', + SPECIES_ZORREN_HIGH = 'icons/mob/species/vulpkanin/helmet.dmi', + SPECIES_FENNEC = 'icons/mob/species/vulpkanin/helmet.dmi', + SPECIES_GREY_YW = 'icons/mob/species/grey/helmet.dmi' /*ywedit*/ ) - sprite_sheets_obj = list( - "Tajara" = 'icons/obj/clothing/species/tajaran/hats.dmi', // Copied from void.dm - "Skrell" = 'icons/obj/clothing/species/skrell/hats.dmi', // Copied from void.dm - "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', // Copied from void.dm - "Teshari" = 'icons/obj/clothing/species/seromi/hats.dmi', // Copied from void.dm - "Nevrean" = 'icons/obj/clothing/species/nevrean/hats.dmi', - "Akula" = 'icons/obj/clothing/species/akula/hats.dmi', - "Sergal" = 'icons/obj/clothing/species/sergal/hats.dmi', - "Flatland Zorren" = 'icons/obj/clothing/species/fennec/hats.dmi', - "Highlander Zorren" = 'icons/obj/clothing/species/fox/hats.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi', - "Promethean" = 'icons/obj/clothing/species/skrell/hats.dmi', - "Xenomorph Hybrid" = 'icons/obj/clothing/species/unathi/hats.dmi' + SPECIES_TAJ = 'icons/obj/clothing/species/tajaran/hats.dmi', // Copied from void.dm + SPECIES_SKRELL = 'icons/obj/clothing/species/skrell/hats.dmi', // Copied from void.dm + SPECIES_UNATHI = 'icons/obj/clothing/species/unathi/hats.dmi', // Copied from void.dm + SPECIES_TESHARI = 'icons/obj/clothing/species/seromi/hats.dmi', // Copied from void.dm + SPECIES_XENOHYBRID = 'icons/obj/clothing/species/unathi/hats.dmi', + SPECIES_AKULA = 'icons/obj/clothing/species/akula/hats.dmi', + SPECIES_SERGAL = 'icons/obj/clothing/species/sergal/hats.dmi', + SPECIES_VULPKANIN = 'icons/obj/clothing/species/vulpkanin/hats.dmi', + SPECIES_ZORREN_HIGH = 'icons/obj/clothing/species/vulpkanin/hats.dmi', + SPECIES_FENNEC = 'icons/obj/clothing/species/vulpkanin/hats.dmi' ) /obj/item/clothing/suit/space/void + species_restricted = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_NEVREAN, SPECIES_RAPALA, SPECIES_VASILISSAN, SPECIES_ALRAUNE, SPECIES_PROMETHEAN, SPECIES_XENOCHIMERA) sprite_sheets = list( - "Tajara" = 'icons/mob/species/tajaran/suit.dmi', - "Skrell" = 'icons/mob/species/skrell/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Teshari" = 'icons/mob/species/seromi/suit.dmi', - "Nevrean" = 'icons/mob/species/nevrean/suit_vr.dmi', - "Akula" = 'icons/mob/species/akula/suit_vr.dmi', - "Sergal" = 'icons/mob/species/sergal/suit_vr.dmi', - "Flatland Zorren" = 'icons/mob/species/fennec/suit_vr.dmi', - "Highlander Zorren" = 'icons/mob/species/fox/suit_vr.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Promethean" = 'icons/mob/species/skrell/suit.dmi', - "Xenomorph Hybrid" = 'icons/mob/species/unathi/suit.dmi' + SPECIES_TAJ = 'icons/mob/species/tajaran/suit.dmi', + SPECIES_SKRELL = 'icons/mob/species/skrell/suit.dmi', + SPECIES_UNATHI = 'icons/mob/species/unathi/suit.dmi', + SPECIES_TESHARI = 'icons/mob/species/seromi/suit.dmi', + SPECIES_XENOHYBRID = 'icons/mob/species/unathi/suit.dmi', + SPECIES_AKULA = 'icons/mob/species/akula/suit_vr.dmi', + SPECIES_SERGAL = 'icons/mob/species/sergal/suit_vr.dmi', + SPECIES_VULPKANIN = 'icons/mob/species/vulpkanin/suit.dmi', + SPECIES_ZORREN_HIGH = 'icons/mob/species/vulpkanin/suit.dmi', + SPECIES_FENNEC = 'icons/mob/species/vulpkanin/suit.dmi' ) - - - sprite_sheets_obj = list( - "Tajara" = 'icons/obj/clothing/species/tajaran/suits.dmi', // Copied from void.dm - "Skrell" = 'icons/obj/clothing/species/skrell/suits.dmi', // Copied from void.dm - "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', // Copied from void.dm - "Teshari" = 'icons/obj/clothing/species/seromi/suits.dmi', // Copied from void.dm - "Nevrean" = 'icons/obj/clothing/species/nevrean/suits.dmi', - "Akula" = 'icons/obj/clothing/species/akula/suits.dmi', - "Sergal" = 'icons/obj/clothing/species/sergal/suits.dmi', - "Flatland Zorren" = 'icons/obj/clothing/species/fennec/suits.dmi', - "Highlander Zorren" = 'icons/obj/clothing/species/fox/suits.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi', - "Promethean" = 'icons/obj/clothing/species/skrell/suits.dmi' + SPECIES_TAJ = 'icons/obj/clothing/species/tajaran/suits.dmi', // Copied from void.dm + SPECIES_SKRELL = 'icons/obj/clothing/species/skrell/suits.dmi', // Copied from void.dm + SPECIES_UNATHI = 'icons/obj/clothing/species/unathi/suits.dmi', // Copied from void.dm + SPECIES_TESHARI = 'icons/obj/clothing/species/seromi/suits.dmi', // Copied from void.dm + SPECIES_XENOHYBRID = 'icons/obj/clothing/species/unathi/suits.dmi', + SPECIES_AKULA = 'icons/obj/clothing/species/akula/suits.dmi', + SPECIES_SERGAL = 'icons/obj/clothing/species/sergal/suits.dmi', + SPECIES_VULPKANIN = 'icons/obj/clothing/species/vulpkanin/suits.dmi', + SPECIES_ZORREN_HIGH = 'icons/obj/clothing/species/vulpkanin/suits.dmi', + SPECIES_FENNEC = 'icons/obj/clothing/species/vulpkanin/suits.dmi' ) // This is a hack to prevent the item_state variable on the suits from taking effect @@ -74,51 +65,3 @@ // This variable is normally used to set the icon_override when the suit is refitted, // however the species spritesheet now means we no longer need that anyway! sprite_sheets_refit = list() - -/obj/item/clothing/suit/space/void/explorer - desc = "A classy red voidsuit for the needs of any semi-retro-futuristic spaceperson! This one is rather loose fitting." - species_restricted = list( - SPECIES_HUMAN, - SPECIES_SKRELL, - SPECIES_UNATHI, - SPECIES_TAJ, - SPECIES_TESHARI, - SPECIES_AKULA, - SPECIES_ALRAUNE, - SPECIES_NEVREAN, - SPECIES_RAPALA, - SPECIES_SERGAL, - SPECIES_VASILISSAN, - SPECIES_VULPKANIN, - SPECIES_XENOCHIMERA, - SPECIES_XENOHYBRID, - SPECIES_ZORREN_FLAT, - SPECIES_ZORREN_HIGH - ) -/obj/item/clothing/suit/space/void/explorer/Initialize() - . = ..() - sprite_sheets += sprite_sheets_refit - -/obj/item/clothing/head/helmet/space/void/explorer - desc = "A helmet that matches a red voidsuit! So classy." - species_restricted = list( - SPECIES_HUMAN, - SPECIES_SKRELL, - SPECIES_UNATHI, - SPECIES_TAJ, - SPECIES_TESHARI, - SPECIES_AKULA, - SPECIES_ALRAUNE, - SPECIES_NEVREAN, - SPECIES_RAPALA, - SPECIES_SERGAL, - SPECIES_VASILISSAN, - SPECIES_VULPKANIN, - SPECIES_XENOCHIMERA, - SPECIES_XENOHYBRID, - SPECIES_ZORREN_FLAT, - SPECIES_ZORREN_HIGH - ) -/obj/item/clothing/head/helmet/space/void/explorer/Initialize() - . = ..() - sprite_sheets += sprite_sheets_refit diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 107bea3be6..691f53de5a 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -13,10 +13,10 @@ if(..()) //This will only run if no other problems occured when equiping. for(var/obj/item/clothing/I in list(H.gloves, H.shoes)) if(I && (src.body_parts_covered & ARMS && I.body_parts_covered & ARMS) ) - H << "You can't wear \the [src] with \the [I], it's in the way." + to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") return 0 if(I && (src.body_parts_covered & LEGS && I.body_parts_covered & LEGS) ) - H << "You can't wear \the [src] with \the [I], it's in the way." + to_chat(H, "You can't wear \the [src] with \the [I], it's in the way.") return 0 return 1 @@ -198,10 +198,10 @@ /obj/item/clothing/suit/armor/reactive/attack_self(mob/user as mob) active = !( active ) if (active) - user << "The reactive armor is now active." + to_chat(user, "The reactive armor is now active.") icon_state = "reactive" else - user << "The reactive armor is now inactive." + to_chat(user, "The reactive armor is now inactive.") icon_state = "reactiveoff" add_fingerprint(user) return diff --git a/code/modules/clothing/suits/armor_yw.dm b/code/modules/clothing/suits/armor_yw.dm index 345ef2fdd9..8f63a6961e 100644 --- a/code/modules/clothing/suits/armor_yw.dm +++ b/code/modules/clothing/suits/armor_yw.dm @@ -20,6 +20,32 @@ armor = list(melee = 15, bullet = 10, laser = 15, energy = 5, bomb = 15, bio = 0, rad = 0) cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + open = 1 + + toggle() + set name = "Toggle Coat Buttons" + set category = "Object" + set src in usr + if(!usr.canmove || usr.stat || usr.restrained()) + return 0 + + if(open == 1) //Will check whether icon state is currently set to the "open" or "closed" state and switch it around with a message to the user + open = 0 + icon_state = "secjacket" + item_state = "secjacket_s" + flags_inv = HIDETIE|HIDEHOLSTER + to_chat(usr, "You button up the coat.") + else if(open == 0) + open = 1 + icon_state = "secjacket_open" + item_state = "secjacket_open_s" + flags_inv = HIDEHOLSTER + to_chat(usr, "You unbutton the coat.") + else //in case some goofy admin switches icon states around without switching the icon_open or icon_closed + to_chat(usr, "You attempt to button-up the velcro on your [src], before promptly realising how silly you are.") + return + update_clothing_icon() //so our overlays update /obj/item/clothing/suit/armor/yw/blueshieldcoat name = "Blue Shield Security Coat" @@ -49,5 +75,5 @@ icon = 'icons/mob/suit_yw.dmi' icon_state = "paramedic-vest" icon_override = 'icons/mob/suit_yw.dmi' - item_state = "paramedic-vest_us" + item_state = "paramedic-vest_s" armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 15, bio = 50, rad = 50) \ No newline at end of file diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm index 0b447470e8..5fb635bca2 100644 --- a/code/modules/clothing/suits/bio.dm +++ b/code/modules/clothing/suits/bio.dm @@ -3,6 +3,8 @@ name = "bio hood" icon_state = "bio" desc = "A hood that protects the head and face from biological comtaminants." + randpixel = 0 + center_of_mass = null permeability_coefficient = 0.01 armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20) flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR diff --git a/code/modules/clothing/suits/hooded_vr.dm b/code/modules/clothing/suits/hooded_vr.dm index ef13979bdc..0f57317284 100644 --- a/code/modules/clothing/suits/hooded_vr.dm +++ b/code/modules/clothing/suits/hooded_vr.dm @@ -1,3 +1,13 @@ // Field Medic Suit - Someone who can sprite should probably reskin this /obj/item/clothing/suit/storage/hooded/explorer/medic starting_accessories = list(/obj/item/clothing/accessory/armband/med/cross) + +/obj/item/clothing/suit/storage/hooded/techpriest + name = "tech priest robe" + desc = "Praise be to the Omnissiah." + icon = 'icons/obj/clothing/suits_vr.dmi' + icon_override = 'icons/mob/suit_vr.dmi' + icon_state = "techpriest" + hoodtype = /obj/item/clothing/head/hood/techpriest + armor = list(melee = 20, bullet = 10, laser = 10, energy = 10, bomb = 25, bio = 50, rad = 25) + item_state_slots = list(slot_r_hand_str = "engspace_suit", slot_l_hand_str = "engspace_suit") diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm index 63d5faa51c..cc7dfa3be5 100644 --- a/code/modules/clothing/suits/labcoat.dm +++ b/code/modules/clothing/suits/labcoat.dm @@ -8,6 +8,7 @@ flags_inv = HIDEHOLSTER allowed = list(/obj/item/device/analyzer,/obj/item/stack/medical,/obj/item/weapon/dnainjector,/obj/item/weapon/reagent_containers/dropper,/obj/item/weapon/reagent_containers/syringe,/obj/item/weapon/reagent_containers/hypospray,/obj/item/device/healthanalyzer,/obj/item/device/flashlight/pen,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/reagent_containers/glass/beaker,/obj/item/weapon/reagent_containers/pill,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/paper) armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 50, rad = 0) + index = 1 /obj/item/clothing/suit/storage/toggle/labcoat/red name = "red labcoat" diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index a03361e22a..8c48b1fbcb 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -234,6 +234,11 @@ icon_state = "straight_jacket" body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER + //yw edit - Teshari sprite + sprite_sheets = list( + SPECIES_TESHARI = 'icons/vore/custom_onmob_yw.dmi' + ) + //yw edit end var/resist_time = 4800 // Eight minutes. @@ -402,27 +407,64 @@ obj/item/clothing/suit/kamishimo body_parts_covered = UPPER_TORSO|LOWER_TORSO cold_protection = UPPER_TORSO|LOWER_TORSO +//YW EDIT BEGINS /obj/item/clothing/suit/storage/miljacket name = "military jacket" - desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable." + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. This one's in the vintage olive drab." icon_state = "militaryjacket_nobadge" item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") flags_inv = HIDEHOLSTER /obj/item/clothing/suit/storage/miljacket/alt - name = "military jacket" - desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable." + name = "alternate military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. This one is in the standard olive drab, and has a badge on the right breast." icon_state = "militaryjacket_badge" item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") flags_inv = HIDEHOLSTER /obj/item/clothing/suit/storage/miljacket/green - name = "military jacket" - desc = "A dark green canvas jacket. Feels sturdy, yet comfortable." + name = "dark green military jacket" + desc = "A dark green canvas jacket. Feels sturdy, yet comfortable. This one is a rather saturated green tone. Weird." icon_state = "militaryjacket_green" item_state_slots = list(slot_r_hand_str = "suit_olive", slot_l_hand_str = "suit_olive") flags_inv = HIDEHOLSTER +/obj/item/clothing/suit/storage/miljacket/tan + name = "tan military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Now in sandy tans for desert fans." + icon_state = "militaryjacket_tan" + item_state_slots = list(slot_r_hand_str = "suit_orange", slot_l_hand_str = "suit_orange") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/grey + name = "grey military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. This one's in urban grey." + icon_state = "militaryjacket_grey" + item_state_slots = list(slot_r_hand_str = "suit_grey", slot_l_hand_str = "suit_grey") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/navy + name = "navy military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Dark navy, this one is." + icon_state = "militaryjacket_navy" + item_state_slots = list(slot_r_hand_str = "suit_navy", slot_l_hand_str = "suit_navy") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/black + name = "black military jacket" + desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable. Now in tactical black." + icon_state = "militaryjacket_black" + item_state_slots = list(slot_r_hand_str = "suit_black", slot_l_hand_str = "suit_black") + flags_inv = HIDEHOLSTER + +/obj/item/clothing/suit/storage/miljacket/white + name = "white military jacket" + desc = "A white canvas jacket. Don't wear this for walks in the snow, it won't keep you warm - it'll just make it harder to find your frozen corpse." + icon_state = "militaryjacket_white" + item_state_slots = list(slot_r_hand_str = "med_dep_jacket", slot_l_hand_str = "med_dep_jacket") + flags_inv = HIDEHOLSTER +//YW EDIT ENDS + /obj/item/clothing/suit/storage/toggle/bomber name = "bomber jacket" desc = "A thick, well-worn WW2 leather bomber jacket." @@ -742,11 +784,11 @@ obj/item/clothing/suit/kamishimo if(rolled == 0) rolled = 1 body_parts_covered &= ~(ARMS) - usr << "You roll up the sleeves of your [src]." + to_chat(usr, "You roll up the sleeves of your [src].") else rolled = 0 body_parts_covered = initial(body_parts_covered) - usr << "You roll down the sleeves of your [src]." + to_chat(usr, "You roll down the sleeves of your [src].") update_icon() /obj/item/clothing/suit/storage/flannel/verb/tuck() @@ -758,10 +800,10 @@ obj/item/clothing/suit/kamishimo if(tucked == 0) tucked = 1 - usr << "You tuck in your your [src]." + to_chat(usr, "You tuck in your your [src].") else tucked = 0 - usr << "You untuck your [src]." + to_chat(usr, "You untuck your [src].") update_icon() /obj/item/clothing/suit/storage/flannel/verb/button() @@ -774,11 +816,11 @@ obj/item/clothing/suit/kamishimo if(buttoned == 0) buttoned = 1 flags_inv = HIDETIE|HIDEHOLSTER - usr << "You button your [src]." + to_chat(usr, "You button your [src].") else buttoned = 0 flags_inv = HIDEHOLSTER - usr<<"You unbutton your [src]." + to_chat(usr, "You unbutton your [src].") update_icon() /obj/item/clothing/suit/storage/flannel/update_icon() diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm index 2f97ce79eb..115e7c14e5 100644 --- a/code/modules/clothing/suits/storage.dm +++ b/code/modules/clothing/suits/storage.dm @@ -42,14 +42,14 @@ open = 0 icon_state = initial(icon_state) flags_inv = HIDETIE|HIDEHOLSTER - usr << "You button up the coat." + to_chat(usr, "You button up the coat.") else if(open == 0) open = 1 icon_state = "[icon_state]_open" flags_inv = HIDEHOLSTER - usr << "You unbutton the coat." + to_chat(usr, "You unbutton the coat.") else //in case some goofy admin switches icon states around without switching the icon_open or icon_closed - usr << "You attempt to button-up the velcro on your [src], before promptly realising how silly you are." + to_chat(usr, "You attempt to button-up the velcro on your [src], before promptly realising how silly you are.") return update_clothing_icon() //so our overlays update @@ -68,14 +68,14 @@ open = 0 icon_state = initial(icon_state) flags_inv = HIDETIE|HIDEHOLSTER - usr << "You button up the coat." + to_chat(usr, "You button up the coat.") else if(open == 0) open = 1 icon_state = "[icon_state]_open" flags_inv = HIDEHOLSTER - usr << "You unbutton the coat." + to_chat(usr, "You unbutton the coat.") else //in case some goofy admin switches icon states around without switching the icon_open or icon_closed - usr << "You attempt to button-up the velcro on your [src], before promptly realising how silly you are." + to_chat(usr, "You attempt to button-up the velcro on your [src], before promptly realising how silly you are.") return update_clothing_icon() //so our overlays update @@ -99,12 +99,12 @@ if(icon_state == icon_badge) icon_state = icon_nobadge - usr << "You conceal \the [src]'s badge." + to_chat(usr, "You conceal \the [src]'s badge.") else if(icon_state == icon_nobadge) icon_state = icon_badge - usr << "You reveal \the [src]'s badge." + to_chat(usr, "You reveal \the [src]'s badge.") else - usr << "\The [src] does not have a badge." + to_chat(usr, "\The [src] does not have a badge.") return update_clothing_icon() diff --git a/code/modules/clothing/suits/storage_yw.dm b/code/modules/clothing/suits/storage_yw.dm index 6e87e35ee3..21e0fdadb6 100644 --- a/code/modules/clothing/suits/storage_yw.dm +++ b/code/modules/clothing/suits/storage_yw.dm @@ -52,4 +52,26 @@ /obj/item/clothing/suit/storage/explorer/command name = "\improper Explorer command jacket" desc = "A exploration jacket belonging to the Explorer's association. It has gold buttons and gold trim." - icon_state = "blackservice_com" \ No newline at end of file + icon_state = "blackservice_com" + + +/obj/item/clothing/suit/storage/tailcoat + name = "victorian tailcoat" + desc = "A fancy victorian tailcoat." + icon = 'icons/mob/suit_yw.dmi' + icon_state = "tailcoat" + icon_override = 'icons/mob/suit_yw.dmi' + item_state = "tailcoat_s" + +/obj/item/clothing/suit/storage/victcoat + name = "ladies black victorian coat" + desc = "A fancy victorian coat." + icon = 'icons/mob/suit_yw.dmi' + icon_state = "ladiesvictoriancoat" + icon_override = 'icons/mob/suit_yw.dmi' + item_state = "ladiesvictoriancoat_s" + +/obj/item/clothing/suit/storage/victcoat/red + name = "ladies red victorian coat" + icon_state = "ladiesredvictoriancoat" + item_state = "ladiesredvictoriancoat_s" \ No newline at end of file diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm index 9f1e33f457..c1665792f8 100644 --- a/code/modules/clothing/under/accessories/accessory_vr.dm +++ b/code/modules/clothing/under/accessories/accessory_vr.dm @@ -4,30 +4,61 @@ /obj/item/clothing/accessory/collar slot_flags = SLOT_TIE | SLOT_OCLOTHING - icon = 'icons/obj/clothing/collars_vr.dmi' - icon_override = 'icons/obj/clothing/collars_vr.dmi' + icon = 'icons/obj/clothing/ties_vr.dmi' + icon_override = 'icons/mob/ties_vr.dmi' + var/icon_previous_override //yw addition var/writtenon = 0 +//ywedit start. forces different sprite sheet on equip +/obj/item/clothing/accessory/collar/New() + ..() + icon_previous_override = icon_override + +/obj/item/clothing/accessory/collar/equipped() //Solution for race-specific sprites for an accessory which is also a suit. Suit icons break if you don't use icon override which then also overrides race-specific sprites. + ..() + setUniqueSpeciesSprite() + +/obj/item/clothing/accessory/collar/proc/setUniqueSpeciesSprite() + var/mob/living/carbon/human/H = loc + if(!istype(H)) + if(istype(has_suit) && ishuman(has_suit.loc)) + H = has_suit.loc + if(istype(H)) + if(H.species.name == SPECIES_TESHARI) + icon_override = 'icons/obj/clothing/collars_seromi_yw.dmi' + update_clothing_icon() + +/obj/item/clothing/accessory/collar/on_attached(var/obj/item/clothing/S, var/mob/user) + if(!istype(S)) + return + has_suit = S + setUniqueSpeciesSprite() + ..(S, user) + +/obj/item/clothing/accessory/collar/dropped() + icon_override = icon_previous_override +//ywedit end + /obj/item/clothing/accessory/collar/silver name = "Silver tag collar" desc = "A collar for your little pets... or the big ones." icon_state = "collar_blk" - item_state = "collar_blk_overlay" - overlay_state = "collar_blk_overlay" + item_state = "collar_blk" + overlay_state = "collar_blk" /obj/item/clothing/accessory/collar/gold name = "Golden tag collar" desc = "A collar for your little pets... or the big ones." icon_state = "collar_gld" - item_state = "collar_gld_overlay" - overlay_state = "collar_gld_overlay" + item_state = "collar_gld" + overlay_state = "collar_gld" /obj/item/clothing/accessory/collar/bell name = "Bell collar" desc = "A collar with a tiny bell hanging from it, purrfect furr kitties." icon_state = "collar_bell" - item_state = "collar_bell_overlay" - overlay_state = "collar_bell_overlay" + item_state = "collar_bell" + overlay_state = "collar_bell" var/jingled = 0 /obj/item/clothing/accessory/collar/bell/verb/jinglebell() @@ -50,8 +81,8 @@ name = "Shock collar" desc = "A collar used to ease hungry predators." icon_state = "collar_shk0" - item_state = "collar_shk_overlay" - overlay_state = "collar_shk_overlay" + item_state = "collar_shk" + overlay_state = "collar_shk" var/on = FALSE // 0 for off, 1 for on, starts off to encourage people to set non-default frequencies and codes. var/frequency = 1449 var/code = 2 @@ -165,31 +196,26 @@ name = "Spiked collar" desc = "A collar with spikes that look as sharp as your teeth." icon_state = "collar_spik" - item_state = "collar_spik_overlay" - overlay_state = "collar_spik_overlay" + item_state = "collar_spik" + overlay_state = "collar_spik" /obj/item/clothing/accessory/collar/pink name = "Pink collar" desc = "This collar will make your pets look FA-BU-LOUS." icon_state = "collar_pnk" - item_state = "collar_pnk_overlay" - overlay_state = "collar_pnk_overlay" + item_state = "collar_pnk" + overlay_state = "collar_pnk" /obj/item/clothing/accessory/collar/holo name = "Holo-collar" desc = "An expensive holo-collar for the modern day pet." icon_state = "collar_holo" - item_state = "collar_holo_overlay" - overlay_state = "collar_holo_overlay" + item_state = "collar_holo" + overlay_state = "collar_holo" matter = list(DEFAULT_WALL_MATERIAL = 50) -//TFF 17/6/19 - public loadout addition: Indigestible Holocollar /obj/item/clothing/accessory/collar/holo/indigestible - name = "Holo-collar" desc = "A special variety of the holo-collar that seems to be made of a very durable fabric that fits around the neck." - icon_state = "collar_holo" - item_state = "collar_holo_overlay" - overlay_state = "collar_holo_overlay" //Make indigestible /obj/item/clothing/accessory/collar/holo/indigestible/digest_act(var/atom/movable/item_storage = null) return FALSE @@ -276,3 +302,11 @@ /obj/item/clothing/accessory/medal/silver/unity name = "medal of unity" desc = "A silver medal awarded to a group which has demonstrated exceptional teamwork to achieve a notable feat." + +/obj/item/clothing/accessory/medal/silver/unity/tabiranth + icon = 'icons/obj/clothing/ties_vr.dmi' + icon_override = 'icons/mob/ties_vr.dmi' + icon_state = "silverthree" + item_state = "silverthree" + overlay_state = "silverthree" + desc = "A silver medal awarded to a group which has demonstrated exceptional teamwork to achieve a notable feat. This one has two bronze service stars, denoting that it has been awarded three times." diff --git a/code/modules/clothing/under/accessories/accessory_yw.dm b/code/modules/clothing/under/accessories/accessory_yw.dm index 6dd9cd7e50..25079504e4 100644 --- a/code/modules/clothing/under/accessories/accessory_yw.dm +++ b/code/modules/clothing/under/accessories/accessory_yw.dm @@ -4,4 +4,12 @@ icon = 'icons/obj/clothing/ties_yw.dmi' icon_state = "cloak_blueshield" icon_override = 'icons/mob/ties_yw.dmi' - item_state = "cloak_blueshield" \ No newline at end of file + item_state = "cloak_blueshield" + + +/obj/item/clothing/accessory/poncho/roles/cloak/blueshield/dropped() //makes the blueshield suit not kek when used by a teshari + icon_override = 'icons/mob/ties_yw.dmi' + + +/obj/item/clothing/accessory/poncho/cloak/fluff/dropped() //makes the fluff cloaks not kek when used by a teshari + icon_override = 'icons/vore/custom_clothes_yw.dmi' \ No newline at end of file diff --git a/code/modules/clothing/under/accessories/armor.dm b/code/modules/clothing/under/accessories/armor.dm index f05b96183d..b440c0b834 100644 --- a/code/modules/clothing/under/accessories/armor.dm +++ b/code/modules/clothing/under/accessories/armor.dm @@ -205,15 +205,15 @@ /obj/item/clothing/accessory/armor/armguards/laserproof name = "ablative arm guards" desc = "These arm guards will protect your arms from energy weapons." - icon_state = "armguards_laser" + icon_state = "armguards_ablative" item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") - siemens_coefficient = 0.4 //This is worse than the other ablative pieces, to avoid this from becoming the poor warden's insulated gloves. + siemens_coefficient = 0.1 //These don't cover the hands, so the siemens doesn't need to be worse than normal ablative. armor = list(melee = 10, bullet = 10, laser = 80, energy = 50, bomb = 0, bio = 0, rad = 0) /obj/item/clothing/accessory/armor/armguards/bulletproof name = "bullet resistant arm guards" desc = "These arm guards will protect your arms from ballistic weapons." - icon_state = "armguards_bullet" + icon_state = "armguards_ballistic" item_state_slots = list(slot_r_hand_str = "swat", slot_l_hand_str = "swat") siemens_coefficient = 0.7 armor = list(melee = 10, bullet = 80, laser = 10, energy = 50, bomb = 0, bio = 0, rad = 0) @@ -264,7 +264,7 @@ /obj/item/clothing/accessory/armor/legguards/laserproof name = "ablative leg guards" desc = "These will protect your legs from energy weapons." - icon_state = "legguards_laser" + icon_state = "legguards_ablative" item_state_slots = list(slot_r_hand_str = "jackboots", slot_l_hand_str = "jackboots") siemens_coefficient = 0.1 armor = list(melee = 10, bullet = 10, laser = 80, energy = 50, bomb = 0, bio = 0, rad = 0) @@ -272,7 +272,7 @@ /obj/item/clothing/accessory/armor/legguards/bulletproof name = "bullet resistant leg guards" desc = "These will protect your legs from ballistic weapons." - icon_state = "legguards_bullet" + icon_state = "legguards_ballistic" item_state_slots = list(slot_r_hand_str = "jackboots", slot_l_hand_str = "jackboots") siemens_coefficient = 0.7 armor = list(melee = 10, bullet = 80, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index 55de2c36b7..085279c424 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -28,7 +28,7 @@ /obj/item/clothing/accessory/badge/attack_self(mob/user as mob) if(!stored_name) - user << "You polish your old badge fondly, shining up the surface." + to_chat(user, "You polish your old badge fondly, shining up the surface.") set_name(user.real_name) return @@ -74,17 +74,17 @@ /obj/item/clothing/accessory/badge/holo/attack_self(mob/user as mob) if(!stored_name) - user << "Waving around a holobadge before swiping an ID would be pretty pointless." + to_chat(user, "Waving around a holobadge before swiping an ID would be pretty pointless.") return return ..() /obj/item/clothing/accessory/badge/holo/emag_act(var/remaining_charges, var/mob/user) if (emagged) - user << "\The [src] is already cracked." + to_chat(user, "\The [src] is already cracked.") return else emagged = 1 - user << "You crack the holobadge security checks." + to_chat(user, "You crack the holobadge security checks.") return 1 /obj/item/clothing/accessory/badge/holo/attackby(var/obj/item/O as obj, var/mob/user as mob) @@ -99,10 +99,10 @@ id_card = pda.id if(access_security in id_card.access || emagged) - user << "You imprint your ID details onto the badge." + to_chat(user, "You imprint your ID details onto the badge.") set_name(user.real_name) else - user << "[src] rejects your insufficient access rights." + to_chat(user, "[src] rejects your insufficient access rights.") return ..() diff --git a/code/modules/clothing/under/accessories/clothing.dm b/code/modules/clothing/under/accessories/clothing.dm index d5991e4f10..e77725f0ad 100644 --- a/code/modules/clothing/under/accessories/clothing.dm +++ b/code/modules/clothing/under/accessories/clothing.dm @@ -62,6 +62,21 @@ "Teshari" = 'icons/mob/species/seromi/suit.dmi' ) +//ywedit start. replaces a else by a return, so it don't changes if not teshari to ties.dmi +/obj/item/clothing/accessory/poncho/equipped() //Solution for race-specific sprites for an accessory which is also a suit. Suit icons break if you don't use icon override which then also overrides race-specific sprites. + ..() + var/mob/living/carbon/human/H = loc + if(istype(H) && H.wear_suit == src) + if(H.species.name == "Teshari") + icon_override = 'icons/mob/species/seromi/suit.dmi' + return + update_clothing_icon() +//ywedit end + +/obj/item/clothing/accessory/poncho/dropped() //Resets the override to prevent the wrong .dmi from being used because equipped only triggers when wearing ponchos as suits. + icon_override = null + + /obj/item/clothing/accessory/poncho/green name = "green poncho" desc = "A simple, comfortable cloak without sleeves. This one is green." diff --git a/code/modules/clothing/under/accessories/holster.dm b/code/modules/clothing/under/accessories/holster.dm index e1d9a83e33..e5c3935342 100644 --- a/code/modules/clothing/under/accessories/holster.dm +++ b/code/modules/clothing/under/accessories/holster.dm @@ -2,14 +2,16 @@ name = "shoulder holster" desc = "A handgun holster." icon_state = "holster" - slot = ACCESSORY_SLOT_HOLSTER //Legacy/balance purposes + slot = ACCESSORY_SLOT_WEAPON concealed_holster = 1 var/obj/item/holstered = null var/list/can_hold //VOREStation Add + var/holster_in = 'sound/items/holsterin.ogg' + var/holster_out = 'sound/items/holsterout.ogg' /obj/item/clothing/accessory/holster/proc/holster(var/obj/item/I, var/mob/living/user) if(holstered && istype(user)) - user << "There is already \a [holstered] holstered here!" + to_chat(user, "There is already \a [holstered] holstered here!") return //VOREStation Edit - Machete sheath support if (LAZYLEN(can_hold)) @@ -19,9 +21,12 @@ else if (!(I.slot_flags & SLOT_HOLSTER)) //VOREStation Edit End - user << "[I] won't fit in [src]!" + to_chat(user, "[I] won't fit in [src]!") return + if(holster_in) + playsound(get_turf(src), holster_in, 50) + if(istype(user)) user.stop_aiming(no_message=1) holstered = I @@ -41,9 +46,11 @@ return if(istype(user.get_active_hand(),/obj) && istype(user.get_inactive_hand(),/obj)) - user << "You need an empty hand to draw \the [holstered]!" + to_chat(user, "You need an empty hand to draw \the [holstered]!") else + var/sound_vol = 25 if(user.a_intent == I_HURT) + sound_vol = 50 usr.visible_message( "[user] draws \the [holstered], ready to go!", //VOREStation Edit "You draw \the [holstered], ready to go!" //VOREStation Edit @@ -53,11 +60,25 @@ "[user] draws \the [holstered], pointing it at the ground.", "You draw \the [holstered], pointing it at the ground." ) + + if(holster_out) + playsound(get_turf(src), holster_out, sound_vol) + user.put_in_hands(holstered) holstered.add_fingerprint(user) w_class = initial(w_class) clear_holster() +//YW change start +/obj/item/clothing/accessory/holster/attack_hand(mob/user as mob) + if (user.a_intent == I_HURT && has_suit && (slot & ACCESSORY_SLOT_HOLSTER )) //if we are part of a suit and are using harm intent + if (holstered) + unholster(user) + return + + ..(user) +//YW change end + /obj/item/clothing/accessory/holster/attackby(obj/item/W as obj, mob/user as mob) holster(W, user) @@ -69,9 +90,9 @@ /obj/item/clothing/accessory/holster/examine(mob/user) ..(user) if (holstered) - user << "A [holstered] is holstered here." + to_chat(user, "A [holstered] is holstered here.") else - user << "It is empty." + to_chat(user, "It is empty.") /obj/item/clothing/accessory/holster/on_attached(obj/item/clothing/under/S, mob/user as mob) ..() @@ -101,12 +122,12 @@ H = locate() in S.accessories if (!H) - usr << "Something is very wrong." + to_chat(usr, "Something is very wrong.") if(!H.holstered) var/obj/item/W = usr.get_active_hand() if(!istype(W, /obj/item)) - usr << "You need your gun equipped to holster it." + to_chat(usr, "You need your gun equipped to holster it.") return H.holster(W, usr) else diff --git a/code/modules/clothing/under/accessories/lockets.dm b/code/modules/clothing/under/accessories/lockets.dm index f8a2eaf1c7..ca859addb5 100644 --- a/code/modules/clothing/under/accessories/lockets.dm +++ b/code/modules/clothing/under/accessories/lockets.dm @@ -14,15 +14,15 @@ base_icon = icon_state if(!("[base_icon]_open" in icon_states(icon))) - user << "\The [src] doesn't seem to open." + to_chat(user, "\The [src] doesn't seem to open.") return open = !open - user << "You flip \the [src] [open?"open":"closed"]." + to_chat(user, "You flip \the [src] [open?"open":"closed"].") if(open) icon_state = "[base_icon]_open" if(held) - user << "\The [held] falls out!" + to_chat(user, "\The [held] falls out!") held.loc = get_turf(user) held = null else @@ -30,14 +30,14 @@ /obj/item/clothing/accessory/locket/attackby(var/obj/item/O as obj, mob/user as mob) if(!open) - user << "You have to open it first." + to_chat(user, "You have to open it first.") return if(istype(O,/obj/item/weapon/paper) || istype(O, /obj/item/weapon/photo)) if(held) - usr << "\The [src] already has something inside it." + to_chat(usr, "\The [src] already has something inside it.") else - usr << "You slip [O] into [src]." + to_chat(usr, "You slip [O] into [src].") user.drop_item() O.loc = src held = O diff --git a/code/modules/clothing/under/accessories/storage.dm b/code/modules/clothing/under/accessories/storage.dm index 0c1cfee9ca..f3fd40fa00 100644 --- a/code/modules/clothing/under/accessories/storage.dm +++ b/code/modules/clothing/under/accessories/storage.dm @@ -11,7 +11,7 @@ on_rolled = list("down" = "none") var/hide_on_roll = FALSE -/obj/item/clothing/accessory/storage/New() +/obj/item/clothing/accessory/storage/Initialize() ..() hold = new/obj/item/weapon/storage/internal(src) hold.max_storage_space = slots * 2 @@ -42,7 +42,7 @@ ..() /obj/item/clothing/accessory/storage/attack_self(mob/user as mob) - user << "You empty [src]." + to_chat(user, "You empty [src].") var/turf/T = get_turf(src) hold.hide_from(usr) for(var/obj/item/I in hold.contents) @@ -94,7 +94,7 @@ icon_state = "unathiharness2" slots = 2 -/obj/item/clothing/accessory/storage/knifeharness/New() +/obj/item/clothing/accessory/storage/knifeharness/Initialize() ..() hold.max_storage_space = ITEMSIZE_COST_SMALL * 2 hold.can_hold = list(/obj/item/weapon/material/knife/machete/hatchet/unathiknife,\ diff --git a/code/modules/clothing/under/extrauniforms_yw.dm b/code/modules/clothing/under/extrauniforms_yw.dm index 7600ea793f..49c9968b8f 100644 --- a/code/modules/clothing/under/extrauniforms_yw.dm +++ b/code/modules/clothing/under/extrauniforms_yw.dm @@ -210,4 +210,44 @@ desc = "An old and worn down unifom of a missing exploration expedition. It has the words SGV DAEDALUS written on the back." icon_state = "greyutility_sup" worn_state = "greyutility_sup" - icon_override = 'icons/mob/uniform_yw.dmi' \ No newline at end of file + icon_override = 'icons/mob/uniform_yw.dmi' + + +/obj/item/clothing/under/yw/victsuit/victdress + name = "black victorian dress" + desc = "A victorian style dress, fancy!" + icon_state = "victorianblackdress" + item_state = "victorianblackdress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/yw/victsuit/victdress/red + name = "red victorian dress" + icon_state = "victorianreddress" + item_state = "victorianreddress" + +/obj/item/clothing/under/yw/victsuit + name = "black victorian suit" + desc = "A victorian style suit, fancy!" + icon = 'icons/mob/uniform_yw.dmi' + icon_state = "victorianvest" + item_state = "victorianvest" + icon_override = 'icons/mob/uniform_yw.dmi' + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/yw/victsuit/redblk + name = "red and black victorian suit" + icon_state = "victorianblred" + item_state = "victorianblred" + +/obj/item/clothing/under/yw/victsuit/red + name = "red victorian suit" + icon_state = "victorianredvest" + item_state = "victorianredvest" + +/obj/item/clothing/under/yw/victsuit/get_worn_icon_file(var/body_type,var/slot_name,var/default_icon,var/inhands) + if(body_type == SPECIES_GREY_YW) + if(!inhands) + return 'icons/mob/species/grey/uniform.dmi' + + else + return ..() diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index c9546fad06..40d8e12afd 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -91,7 +91,7 @@ item_state_slots[slot_w_uniform_str] = unrolled ? "[worn_state]_r" : initial(worn_state) var/mob/living/carbon/human/H = loc H.update_inv_w_uniform(1) - H << "You roll the sleeves of your shirt [unrolled ? "up" : "down"]" + to_chat(H, "You roll the sleeves of your shirt [unrolled ? "up" : "down"]") */ /obj/item/clothing/under/det/grey icon_state = "detective2" diff --git a/code/modules/clothing/under/jobs/security_vr.dm b/code/modules/clothing/under/jobs/security_vr.dm new file mode 100644 index 0000000000..72d4ebfb45 --- /dev/null +++ b/code/modules/clothing/under/jobs/security_vr.dm @@ -0,0 +1,6 @@ +/obj/item/clothing/under/detective_alt + desc = "sleek modern coat" + icon = 'icons/obj/clothing/uniforms_vr.dmi' + icon_override = 'icons/mob/uniform_vr.dmi' + icon_state = "cyberpunksleek" + item_state = "cyberpunksleek" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index fc7c458bf3..a0e794929f 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -357,6 +357,7 @@ name = "maid costume" desc = "Maid in China." icon_state = "maid" + index = 1 /obj/item/clothing/under/dress/maid/janitor name = "maid uniform" diff --git a/code/modules/clothing/under/pants.dm b/code/modules/clothing/under/pants.dm index 035aac4cf8..1af0517ab0 100644 --- a/code/modules/clothing/under/pants.dm +++ b/code/modules/clothing/under/pants.dm @@ -5,6 +5,7 @@ icon_state = "jeans" gender = PLURAL body_parts_covered = LOWER_TORSO|LEGS + index = 1 /obj/item/clothing/under/pants/ripped name = "ripped jeans" diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index a56f1a2090..0e345779d2 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -5,6 +5,7 @@ icon_state = "redshorts" // Hackyfix for icon states until someone wants to come do a recolor later. gender = PLURAL body_parts_covered = LOWER_TORSO + index = 1 /obj/item/clothing/under/shorts/red name = "red athletic shorts" @@ -100,6 +101,7 @@ icon_state = "skirt_short_black" body_parts_covered = LOWER_TORSO rolled_sleeves = -1 + index = 1 /obj/item/clothing/under/skirt/khaki name = "khaki skirt" @@ -166,12 +168,14 @@ desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." icon_state = "qmf" item_state_slots = list(slot_r_hand_str = "qm", slot_l_hand_str = "qm") + index = 1 /obj/item/clothing/under/rank/cargotech/skirt name = "cargo technician's jumpskirt" desc = "Skirrrrrts! They're comfy and easy to wear!" icon_state = "cargof" item_state_slots = list(slot_r_hand_str = "cargo", slot_l_hand_str = "cargo") + index = 1 /obj/item/clothing/under/rank/engineer/skirt desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." @@ -179,51 +183,61 @@ icon_state = "enginef" armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 10) item_state_slots = list(slot_r_hand_str = "engine", slot_l_hand_str = "engine") + index = 1 /obj/item/clothing/under/rank/chief_engineer/skirt desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." name = "chief engineer's jumpskirt" icon_state = "chieff" item_state_slots = list(slot_r_hand_str = "chiefengineer", slot_l_hand_str = "chiefengineer") + index = 1 /obj/item/clothing/under/rank/atmospheric_technician/skirt desc = "It's a jumpskirt worn by atmospheric technicians." name = "atmospheric technician's jumpskirt" icon_state = "atmosf" item_state_slots = list(slot_r_hand_str = "atmos", slot_l_hand_str = "atmos") + index = 1 /obj/item/clothing/under/rank/roboticist/skirt desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." name = "roboticist's jumpskirt" icon_state = "roboticsf" item_state_slots = list(slot_r_hand_str = "robotics", slot_l_hand_str = "robotics") + index = 1 /obj/item/clothing/under/rank/scientist/skirt name = "scientist's jumpskirt" icon_state = "sciencef" permeability_coefficient = 0.50 armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 10, bio = 0, rad = 0) + index = 1 /obj/item/clothing/under/rank/medical/skirt name = "medical doctor's jumpskirt" icon_state = "medicalf" + index = 1 /obj/item/clothing/under/rank/chemist/skirt name = "chemist's jumpskirt" icon_state = "chemistryf" + index = 1 /obj/item/clothing/under/rank/chief_medical_officer/skirt desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." name = "chief medical officer's jumpskirt" icon_state = "cmof" + index = 1 /obj/item/clothing/under/rank/geneticist/skirt name = "geneticist's jumpskirt" icon_state = "geneticsf" + index = 1 /obj/item/clothing/under/rank/virologist/skirt name = "virologist's jumpskirt" icon_state = "virologyf" + index = 1 /obj/item/clothing/under/rank/security/skirt name = "security officer's jumpskirt" @@ -231,13 +245,16 @@ icon_state = "securityf" armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) siemens_coefficient = 0.9 + index = 1 /obj/item/clothing/under/rank/warden/skirt desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." name = "warden's jumpskirt" icon_state = "wardenf" + index = 1 /obj/item/clothing/under/rank/head_of_security/skirt desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." name = "head of security's jumpskirt" - icon_state = "hosf" \ No newline at end of file + icon_state = "hosf" + index = 1 \ No newline at end of file diff --git a/code/modules/clothing/under/xenos/vox.dm b/code/modules/clothing/under/xenos/vox.dm index ecbc618061..7e90ad4665 100644 --- a/code/modules/clothing/under/xenos/vox.dm +++ b/code/modules/clothing/under/xenos/vox.dm @@ -27,7 +27,7 @@ slots = 3 -/obj/item/clothing/accessory/storage/vox/New() +/obj/item/clothing/accessory/storage/vox/Initialize() ..() hold.max_storage_space = slots * ITEMSIZE_COST_NORMAL hold.max_w_class = ITEMSIZE_NORMAL \ No newline at end of file diff --git a/code/modules/detectivework/microscope/dnascanner.dm b/code/modules/detectivework/microscope/dnascanner.dm index 80fb43e91c..94d4acd5cd 100644 --- a/code/modules/detectivework/microscope/dnascanner.dm +++ b/code/modules/detectivework/microscope/dnascanner.dm @@ -28,7 +28,7 @@ /obj/machinery/dnaforensics/attackby(var/obj/item/W, mob/user as mob) if(bloodsamp) - user << "There is already a sample in the machine." + to_chat(user, "There is already a sample in the machine.") return if(closed) @@ -38,7 +38,7 @@ if(default_deconstruction_crowbar(user, W)) return else - user << "Open the cover before inserting the sample." + to_chat(user, "Open the cover before inserting the sample.") return var/obj/item/weapon/forensics/swab/swab = W @@ -46,9 +46,9 @@ user.unEquip(W) src.bloodsamp = swab swab.loc = src - user << "You insert \the [W] into \the [src]." + to_chat(user, "You insert \the [W] into \the [src].") else - user << "\The [src] only accepts used swabs." + to_chat(user, "\The [src] only accepts used swabs.") return /obj/machinery/dnaforensics/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null) @@ -83,12 +83,12 @@ if(closed == 1) scanner_progress = 0 scanning = 1 - usr << "Scan initiated." + to_chat(usr, "Scan initiated.") update_icon() else - usr << "Please close sample lid before initiating scan." + to_chat(usr, "Please close sample lid before initiating scan.") else - usr << "Insert an item to scan." + to_chat(usr, "Insert an item to scan.") if(href_list["ejectItem"]) if(bloodsamp) @@ -115,7 +115,7 @@ last_process_worldtime = world.time /obj/machinery/dnaforensics/proc/complete_scan() - src.visible_message("\icon[src] makes an insistent chime.", 2) + src.visible_message("[bicon(src)] makes an insistent chime.", 2) update_icon() if(bloodsamp) var/obj/item/weapon/paper/P = new(src) @@ -153,7 +153,7 @@ return if(scanning) - usr << "You can't do that while [src] is scanning!" + to_chat(usr, "You can't do that while [src] is scanning!") return closed = !closed diff --git a/code/modules/detectivework/microscope/microscope.dm b/code/modules/detectivework/microscope/microscope.dm index 33f6da65af..d04d362087 100644 --- a/code/modules/detectivework/microscope/microscope.dm +++ b/code/modules/detectivework/microscope/microscope.dm @@ -13,11 +13,11 @@ /obj/machinery/microscope/attackby(obj/item/weapon/W as obj, mob/user as mob) if(sample) - user << "There is already a slide in the microscope." + to_chat(user, "There is already a slide in the microscope.") return if(istype(W, /obj/item/weapon/forensics/swab)|| istype(W, /obj/item/weapon/sample/fibers) || istype(W, /obj/item/weapon/sample/print)) - user << "You insert \the [W] into the microscope." + to_chat(user, "You insert \the [W] into the microscope.") user.unEquip(W) W.forceMove(src) sample = W @@ -27,16 +27,16 @@ /obj/machinery/microscope/attack_hand(mob/user) if(!sample) - user << "The microscope has no sample to examine." + to_chat(user, "The microscope has no sample to examine.") return - user << "The microscope whirrs as you examine \the [sample]." + to_chat(user, "The microscope whirrs as you examine \the [sample].") if(!do_after(user, 2 SECONDS) || !sample) - user << "You stop examining \the [sample]." + to_chat(user, "You stop examining \the [sample].") return - user << "Printing findings now..." + to_chat(user, "Printing findings now...") var/obj/item/weapon/paper/report = new(get_turf(src)) report.stamped = list(/obj/item/weapon/stamp) report.overlays = list("paper_stamped") @@ -82,16 +82,16 @@ if(report) report.update_icon() if(report.info) - user << report.info + to_chat(user,report.info) return /obj/machinery/microscope/proc/remove_sample(var/mob/living/remover) if(!istype(remover) || remover.incapacitated() || !Adjacent(remover)) return ..() if(!sample) - remover << "\The [src] does not have a sample in it." + to_chat(remover, "\The [src] does not have a sample in it.") return - remover << "You remove \the [sample] from \the [src]." + to_chat(remover, "You remove \the [sample] from \the [src].") sample.forceMove(get_turf(src)) remover.put_in_hands(sample) sample = null diff --git a/code/modules/detectivework/tools/evidencebag.dm b/code/modules/detectivework/tools/evidencebag.dm index a85546bd14..cf1deea0c7 100644 --- a/code/modules/detectivework/tools/evidencebag.dm +++ b/code/modules/detectivework/tools/evidencebag.dm @@ -39,15 +39,15 @@ return if(istype(I, /obj/item/weapon/evidencebag)) - user << "You find putting an evidence bag in another evidence bag to be slightly absurd." + to_chat(user, "You find putting an evidence bag in another evidence bag to be slightly absurd.") return if(I.w_class > 3) - user << "[I] won't fit in [src]." + to_chat(user, "[I] won't fit in [src].") return if(contents.len) - user << "[src] already has something inside it." + to_chat(user, "[src] already has something inside it.") return user.visible_message("[user] puts [I] into [src]", "You put [I] inside [src].",\ @@ -86,7 +86,7 @@ icon_state = "evidenceobj" desc = "An empty evidence bag." else - user << "[src] is empty." + to_chat(user, "[src] is empty.") icon_state = "evidenceobj" return diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 1f09765a0b..2e5223420a 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -25,6 +25,7 @@ can_be_placed_into = null flags = OPENCONTAINER | NOBLUDGEON unacidable = 0 + drop_sound = 'sound/items/drop/clothing.ogg' var/on_fire = 0 var/burn_time = 20 //if the rag burns for too long it turns to ashes @@ -53,7 +54,7 @@ if(on_fire) visible_message("\The [user] lights [src] with [W].") else - user << "You manage to singe [src], but fail to light it." + to_chat(user, "You manage to singe [src], but fail to light it.") . = ..() update_name() @@ -94,7 +95,7 @@ /obj/item/weapon/reagent_containers/glass/rag/proc/wipe_down(atom/A, mob/user) if(!reagents.total_volume) - user << "The [initial(name)] is dry!" + to_chat(user, "The [initial(name)] is dry!") else user.visible_message("\The [user] starts to wipe down [A] with [src]!") //reagents.splash(A, 1) //get a small amount of liquid on the thing we're wiping. @@ -138,7 +139,7 @@ if(istype(A, /obj/structure/reagent_dispensers) || istype(A, /obj/item/weapon/reagent_containers/glass/bucket) || istype(A, /obj/structure/mopbucket)) //VOREStation Edit - "Allows rags to be used on buckets and mopbuckets" if(!reagents.get_free_space()) - user << "\The [src] is already soaked." + to_chat(user, "\The [src] is already soaked.") return if(A.reagents && A.reagents.trans_to_obj(src, reagents.maximum_volume)) diff --git a/code/modules/detectivework/tools/sample_kits.dm b/code/modules/detectivework/tools/sample_kits.dm index 33931e87f9..042749d008 100644 --- a/code/modules/detectivework/tools/sample_kits.dm +++ b/code/modules/detectivework/tools/sample_kits.dm @@ -25,7 +25,7 @@ return 0 evidence |= supplied.evidence name = "[initial(name)] (combined)" - user << "You transfer the contents of \the [supplied] into \the [src]." + to_chat(user, "You transfer the contents of \the [supplied] into \the [src].") return 1 /obj/item/weapon/sample/print/merge_evidence(var/obj/item/weapon/sample/supplied, var/mob/user) @@ -37,7 +37,7 @@ else evidence[print] = supplied.evidence[print] name = "[initial(name)] (combined)" - user << "You overlay \the [src] and \the [supplied], combining the print records." + to_chat(user, "You overlay \the [src] and \the [supplied], combining the print records.") return 1 /obj/item/weapon/sample/attackby(var/obj/O, var/mob/user) @@ -67,10 +67,10 @@ return var/mob/living/carbon/human/H = user if(H.gloves) - user << "Take \the [H.gloves] off first." + to_chat(user, "Take \the [H.gloves] off first.") return - user << "You firmly press your fingertips onto the card." + to_chat(user, "You firmly press your fingertips onto the card.") var/fullprint = H.get_full_print() evidence[fullprint] = fullprint name = "[initial(name)] (\the [H])" @@ -87,7 +87,7 @@ var/mob/living/carbon/human/H = M if(H.gloves) - user << "\The [H] is wearing gloves." + to_chat(user, "\The [H] is wearing gloves.") return 1 if(user != H && H.a_intent != "help" && !H.lying) @@ -104,7 +104,7 @@ if(istype(O) && !O.is_stump()) has_hand = 1 if(!has_hand) - user << "They don't have any hands." + to_chat(user, "They don't have any hands.") return 1 user.visible_message("[user] takes a copy of \the [H]'s fingerprints.") var/fullprint = H.get_full_print() @@ -134,7 +134,7 @@ /obj/item/weapon/forensics/sample_kit/proc/take_sample(var/mob/user, var/atom/supplied) var/obj/item/weapon/sample/S = new evidence_path(get_turf(user), supplied) - user << "You transfer [S.evidence.len] [S.evidence.len > 1 ? "[evidence_type]s" : "[evidence_type]"] to \the [S]." + to_chat(user, "You transfer [S.evidence.len] [S.evidence.len > 1 ? "[evidence_type]s" : "[evidence_type]"] to \the [S].") /obj/item/weapon/forensics/sample_kit/afterattack(var/atom/A, var/mob/user, var/proximity) if(!proximity) @@ -144,7 +144,7 @@ take_sample(user,A) return 1 else - user << "You are unable to locate any [evidence_type]s on \the [A]." + to_chat(user, "You are unable to locate any [evidence_type]s on \the [A].") return ..() /obj/item/weapon/forensics/sample_kit/powder diff --git a/code/modules/detectivework/tools/scanner.dm b/code/modules/detectivework/tools/scanner.dm index 21006bb2e0..b917ddcf38 100644 --- a/code/modules/detectivework/tools/scanner.dm +++ b/code/modules/detectivework/tools/scanner.dm @@ -27,14 +27,14 @@ else if(user.zone_sel.selecting == "r_hand" || user.zone_sel.selecting == "l_hand") var/obj/item/weapon/sample/print/P = new /obj/item/weapon/sample/print(user.loc) P.attack(M, user) - to_chat(user,"Done printing.") - // user << "[M]'s Fingerprints: [md5(M.dna.uni_identity)]" + to_chat(user, "Done printing.") + // to_chat(user, "[M]'s Fingerprints: [md5(M.dna.uni_identity)]") if(reveal_blood && M.blood_DNA && M.blood_DNA.len) - to_chat(user,"Blood found on [M]. Analysing...") + to_chat(user, "Blood found on [M]. Analysing...") spawn(15) for(var/blood in M.blood_DNA) - to_chat(user,"Blood type: [M.blood_DNA[blood]]\nDNA: [blood]") + to_chat(user, "Blood type: [M.blood_DNA[blood]]\nDNA: [blood]") return /obj/item/device/detective_scanner/afterattack(atom/A as obj|turf, mob/user, proximity) @@ -52,14 +52,14 @@ */ if(istype(A,/obj/item/weapon/sample/print)) - to_chat(user,"The scanner displays on the screen: \"ERROR 43: Object on Excluded Object List.\"") + to_chat(user, "The scanner displays on the screen: \"ERROR 43: Object on Excluded Object List.\"") flick("[icon_state]0",src) return add_fingerprint(user) if(!(do_after(user, 1 SECOND))) - to_chat(user,"You must remain still for the device to complete its work.") + to_chat(user, "You must remain still for the device to complete its work.") return 0 //General @@ -72,14 +72,14 @@ if(add_data(A)) to_chat(user,"Object already in internal memory. Consolidating data...") - flick("[icon_state]2",src) + flick("[icon_state]1",src) return //PRINTS if(A.fingerprints && A.fingerprints.len) - to_chat(user,"Isolated [A.fingerprints.len] fingerprints:") + to_chat(user, "Isolated [A.fingerprints.len] fingerprints:") if(!reveal_incompletes) - to_chat(user,"Rapid Analysis Imperfect: Scan samples with H.R.F.S. equipment to determine nature of incomplete prints.") + to_chat(user, "Rapid Analysis Imperfect: Scan samples with H.R.F.S. equipment to determine nature of incomplete prints.") var/list/complete_prints = list() var/list/incomplete_prints = list() for(var/i in A.fingerprints) @@ -89,40 +89,40 @@ else incomplete_prints += print if(complete_prints.len < 1) - to_chat(user,"No intact prints found") + to_chat(user, "No intact prints found") else - to_chat(user,"Found [complete_prints.len] intact prints") + to_chat(user, "Found [complete_prints.len] intact prints") if(reveal_fingerprints) for(var/i in complete_prints) - to_chat(user,"    [i]") + to_chat(user, "    [i]") - to_chat(user,"Found [incomplete_prints.len] incomplete prints") + to_chat(user, "Found [incomplete_prints.len] incomplete prints") if(reveal_incompletes) for(var/i in incomplete_prints) - to_chat(user,"    [i]") + to_chat(user, "    [i]") //FIBERS if(A.suit_fibers && A.suit_fibers.len) to_chat(user,"Fibers/Materials detected.[reveal_fibers ? " Analysing..." : " Acquisition of fibers for H.R.F.S. analysis advised."]") - flick("[icon_state]2",src) + flick("[icon_state]1",src) if(reveal_fibers && do_after(user, 5 SECONDS)) - to_chat(user,"Apparel samples scanned:") + to_chat(user, "Apparel samples scanned:") for(var/sample in A.suit_fibers) - to_chat(user," - [sample]") + to_chat(user, " - [sample]") //Blood if (A.blood_DNA && A.blood_DNA.len) - to_chat(user,"Blood detected.[reveal_blood ? " Analysing..." : " Acquisition of swab for H.R.F.S. analysis advised."]") + to_chat(user, "Blood detected.[reveal_blood ? " Analysing..." : " Acquisition of swab for H.R.F.S. analysis advised."]") if(reveal_blood && do_after(user, 5 SECONDS)) - flick("[icon_state]2",src) + flick("[icon_state]1",src) for(var/blood in A.blood_DNA) - to_chat(user,"Blood type: [A.blood_DNA[blood]] DNA: [blood]") + to_chat(user, "Blood type: [A.blood_DNA[blood]] DNA: [blood]") user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\ "You finish scanning \the [A].",\ "You hear a faint hum of electrical equipment.") - flick("[icon_state]2",src) + flick("[icon_state]1",src) return 0 /obj/item/device/detective_scanner/proc/add_data(atom/A as mob|obj|turf|area) @@ -139,7 +139,7 @@ set category = "Object" set src in view(1) - world << "usr is [usr]" + to_world("usr is [usr]") display_data(usr) /obj/item/device/detective_scanner/proc/display_data(var/mob/user) @@ -167,7 +167,7 @@ incomplete_prints += print if(complete_prints.len < 1) - to_chat(user,"No intact prints found.") + to_chat(user, "No intact prints found.") if(reveal_incompletes) for(var/print in incomplete_prints) @@ -177,7 +177,7 @@ to_chat(user, "[fibers.len] samples of material were present.") if(reveal_fibers) for(var/sample in fibers) - to_chat(user," - [sample]") + to_chat(user, " - [sample]") if(bloods && bloods.len) to_chat(user, "[bloods.len] samples of blood were present.") @@ -192,7 +192,7 @@ if (alert("Are you sure you want to wipe all data from [src]?",,"Yes","No") == "Yes") stored = list() - to_chat(usr,"Forensic data erase complete.") + to_chat(usr, "Forensic data erase complete.") /obj/item/device/detective_scanner/advanced name = "advanced forensic scanner" diff --git a/code/modules/detectivework/tools/swabs.dm b/code/modules/detectivework/tools/swabs.dm index 6e20d62edd..5a83dc642a 100644 --- a/code/modules/detectivework/tools/swabs.dm +++ b/code/modules/detectivework/tools/swabs.dm @@ -21,11 +21,11 @@ var/sample_type if(H.wear_mask) - user << "\The [H] is wearing a mask." + to_chat(user, "\The [H] is wearing a mask.") return if(!H.dna || !H.dna.unique_enzymes) - user << "They don't seem to have DNA!" + to_chat(user, "They don't seem to have DNA!") return if(user != H && H.a_intent != "help" && !H.lying) @@ -34,10 +34,10 @@ if(user.zone_sel.selecting == O_MOUTH) if(!H.organs_by_name[BP_HEAD]) - user << "They don't have a head." + to_chat(user, "They don't have a head.") return if(!H.check_has_mouth()) - user << "They don't have a mouth." + to_chat(user, "They don't have a mouth.") return user.visible_message("[user] swabs \the [H]'s mouth for a saliva sample.") dna = list(H.dna.unique_enzymes) @@ -53,7 +53,7 @@ if(istype(O) && !O.is_stump()) has_hand = 1 if(!has_hand) - user << "They don't have any hands." + to_chat(user, "They don't have any hands.") return user.visible_message("[user] swabs [H]'s palm for a sample.") sample_type = "GSR" @@ -72,7 +72,7 @@ return if(is_used()) - user << "This swab has already been used." + to_chat(user, "This swab has already been used.") return add_fingerprint(user) @@ -85,7 +85,7 @@ var/choice if(!choices.len) - user << "There is no evidence on \the [A]." + to_chat(user, "There is no evidence on \the [A].") return else if(choices.len == 1) choice = choices[1] @@ -104,7 +104,7 @@ else if(choice == "Gunshot Residue") var/obj/item/clothing/B = A if(!istype(B) || !B.gunshot_residue) - user << "There is no residue on \the [A]." + to_chat(user, "There is no residue on \the [A].") return gsr = B.gunshot_residue sample_type = "residue" diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index 68e4c12c86..b02660b69d 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -17,10 +17,10 @@ log transactions /obj/machinery/atm name = "Automatic Teller Machine" desc = "For all your monetary needs!" - icon = 'icons/obj/terminals.dmi' + icon = 'icons/obj/terminals_vr.dmi' //VOREStation Edit icon_state = "atm" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 circuit = /obj/item/weapon/circuitboard/atm var/datum/money_account/authenticated_account @@ -64,7 +64,7 @@ log transactions break /obj/machinery/atm/emag_act(var/remaining_charges, var/mob/user) - if(!emagged) + if(emagged) return //short out the machine, shoot sparks, spew money! @@ -76,7 +76,7 @@ log transactions //display a message to the user var/response = pick("Initiating withdraw. Have a nice day!", "CRITICAL ERROR: Activating cash chamber panic siphon.","PIN Code accepted! Emptying account balance.", "Jackpot!") - user << "\icon[src] The [src] beeps: \"[response]\"" + to_chat(user, "[bicon(src)] The [src] beeps: \"[response]\"") return 1 /obj/machinery/atm/attackby(obj/item/I as obj, mob/user as mob) @@ -85,7 +85,7 @@ log transactions if(istype(I, /obj/item/weapon/card)) if(emagged > 0) //prevent inserting id into an emagged ATM - user << "\icon[src] CARD READER ERROR. This system has been compromised!" + to_chat(user, "[bicon(src)] CARD READER ERROR. This system has been compromised!") return else if(istype(I,/obj/item/weapon/card/emag)) I.resolve_attackby(src, user) @@ -117,7 +117,7 @@ log transactions T.time = stationtime2text() authenticated_account.transaction_log.Add(T) - user << "You insert [I] into [src]." + to_chat(user, "You insert [I] into [src].") src.attack_hand(user) qdel(I) else @@ -234,7 +234,7 @@ log transactions var/target_account_number = text2num(href_list["target_acc_number"]) var/transfer_purpose = href_list["purpose"] if(charge_to_account(target_account_number, authenticated_account.owner_name, transfer_purpose, machine_id, transfer_amount)) - usr << "\icon[src]Funds transfer successful." + to_chat(usr, "[bicon(src)]Funds transfer successful.") authenticated_account.money -= transfer_amount //create an entry in the account transaction log @@ -247,10 +247,10 @@ log transactions T.amount = "([transfer_amount])" authenticated_account.transaction_log.Add(T) else - usr << "\icon[src]Funds transfer failed." + to_chat(usr, "[bicon(src)]Funds transfer failed.") else - usr << "\icon[src]You don't have enough funds to do that!" + to_chat(usr, "[bicon(src)]You don't have enough funds to do that!") if("view_screen") view_screen = text2num(href_list["view_screen"]) if("change_security_level") @@ -288,11 +288,11 @@ log transactions T.time = stationtime2text() failed_account.transaction_log.Add(T) else - usr << "\icon[src] Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining." + to_chat(usr, "[bicon(src)] Incorrect pin/account combination entered, [max_pin_attempts - number_incorrect_tries] attempts remaining.") previous_account_number = tried_account_num playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1) else - usr << "\icon[src] incorrect pin/account combination entered." + to_chat(usr, "[bicon(src)] incorrect pin/account combination entered.") number_incorrect_tries = 0 else playsound(src, 'sound/machines/twobeep.ogg', 50, 1) @@ -308,7 +308,7 @@ log transactions T.time = stationtime2text() authenticated_account.transaction_log.Add(T) - usr << "\icon[src] Access granted. Welcome user '[authenticated_account.owner_name].'" + to_chat(usr, "[bicon(src)] Access granted. Welcome user '[authenticated_account.owner_name].'") previous_account_number = tried_account_num if("e_withdrawal") @@ -336,7 +336,7 @@ log transactions T.time = stationtime2text() authenticated_account.transaction_log.Add(T) else - usr << "\icon[src]You don't have enough funds to do that!" + to_chat(usr, "[bicon(src)]You don't have enough funds to do that!") if("withdrawal") var/amount = max(text2num(href_list["funds_amount"]),0) amount = round(amount, 0.01) @@ -361,7 +361,7 @@ log transactions T.time = stationtime2text() authenticated_account.transaction_log.Add(T) else - usr << "\icon[src]You don't have enough funds to do that!" + to_chat(usr, "[bicon(src)]You don't have enough funds to do that!") if("balance_statement") if(authenticated_account) var/obj/item/weapon/paper/R = new(src.loc) @@ -433,7 +433,7 @@ log transactions if(!held_card) //this might happen if the user had the browser window open when somebody emagged it if(emagged > 0) - usr << "\icon[src] The ATM card reader rejected your ID because this machine has been sabotaged!" + to_chat(usr, "[bicon(src)] The ATM card reader rejected your ID because this machine has been sabotaged!") else var/obj/item/I = usr.get_active_hand() if (istype(I, /obj/item/weapon/card/id)) @@ -461,7 +461,7 @@ log transactions if(I) authenticated_account = attempt_account_access(I.associated_account_number) if(authenticated_account) - human_user << "\icon[src] Access granted. Welcome user '[authenticated_account.owner_name].'" + to_chat(human_user, "[bicon(src)] Access granted. Welcome user '[authenticated_account.owner_name].'") //create a transaction log entry var/datum/transaction/T = new() diff --git a/code/modules/economy/Accounts.dm b/code/modules/economy/Accounts.dm index cfd720d4dc..37979f96c5 100644 --- a/code/modules/economy/Accounts.dm +++ b/code/modules/economy/Accounts.dm @@ -9,6 +9,7 @@ var/security_level = 0 //0 - auto-identify from worn ID, require only account number //1 - require manual login / account number and pin //2 - require card and manual login + var/offmap = FALSE //Should this account be hidden from station consoles? /datum/transaction var/target_name = "" @@ -18,10 +19,11 @@ var/time = "" var/source_terminal = "" -/proc/create_account(var/new_owner_name = "Default user", var/starting_funds = 0, var/obj/machinery/account_database/source_db) +/proc/create_account(var/new_owner_name = "Default user", var/starting_funds = 0, var/obj/machinery/account_database/source_db, var/offmap = FALSE) //create a new account var/datum/money_account/M = new() + M.offmap = offmap M.owner_name = new_owner_name M.remote_access_pin = rand(1111, 111111) M.money = starting_funds diff --git a/code/modules/economy/Accounts_DB.dm b/code/modules/economy/Accounts_DB.dm index a293226d48..9e116ecf60 100644 --- a/code/modules/economy/Accounts_DB.dm +++ b/code/modules/economy/Accounts_DB.dm @@ -3,7 +3,7 @@ name = "Accounts uplink terminal" desc = "Access transaction logs, account data and all kinds of other financial records." icon = 'icons/obj/computer.dmi' - icon_state = "aiupload" + icon_state = "account_computer" density = 1 req_one_access = list(access_hop, access_captain, access_cent_captain) anchored = 1 @@ -14,31 +14,31 @@ var/creating_new_account = 0 var/const/fund_cap = 1000000 - proc/get_access_level() - if (!held_card) - return 0 - if(access_cent_captain in held_card.access) - return 2 - else if(access_hop in held_card.access || access_captain in held_card.access) - return 1 +/obj/machinery/account_database/proc/get_access_level() + if (!held_card) + return 0 + if(access_cent_captain in held_card.access) + return 2 + else if(access_hop in held_card.access || access_captain in held_card.access) + return 1 - proc/create_transation(target, reason, amount) - var/datum/transaction/T = new() - T.target_name = target - T.purpose = reason - T.amount = amount - T.date = current_date_string - T.time = stationtime2text() - T.source_terminal = machine_id - return T +/obj/machinery/account_database/proc/create_transation(target, reason, amount) + var/datum/transaction/T = new() + T.target_name = target + T.purpose = reason + T.amount = amount + T.date = current_date_string + T.time = stationtime2text() + T.source_terminal = machine_id + return T - proc/accounting_letterhead(report_name) - return {" -

    [report_name]

    -
    [station_name()] Accounting Report
    -
    - Generated By: [held_card.registered_name], [held_card.assignment]
    - "} +/obj/machinery/account_database/proc/accounting_letterhead(report_name) + return {" +

    [report_name]

    +
    [station_name()] Accounting Report
    +
    + Generated By: [held_card.registered_name], [held_card.assignment]
    + "} /obj/machinery/account_database/New() machine_id = "[station_name()] Acc. DB #[num_financial_terminals++]" @@ -98,6 +98,8 @@ var/list/accounts[0] for(var/i=1, i<=all_money_accounts.len, i++) var/datum/money_account/D = all_money_accounts[i] + if(D.offmap) + continue accounts.Add(list(list(\ "account_number"=D.account_number,\ "owner_name"=D.owner_name,\ diff --git a/code/modules/economy/EFTPOS.dm b/code/modules/economy/EFTPOS.dm index 21ef8b4bfa..986ec2766e 100644 --- a/code/modules/economy/EFTPOS.dm +++ b/code/modules/economy/EFTPOS.dm @@ -12,56 +12,45 @@ var/access_code = 0 var/datum/money_account/linked_account -/obj/item/device/eftpos/New() - ..() - machine_id = "[station_name()] EFTPOS #[num_financial_terminals++]" - access_code = rand(1111,111111) - spawn(0) - print_reference() - - //create a short manual as well - var/obj/item/weapon/paper/R = new(src.loc) - R.name = "Steps to success: Correct EFTPOS Usage" - /* - R.info += "When first setting up your EFTPOS device:" - R.info += "1. Memorise your EFTPOS command code (provided with all EFTPOS devices).
    " - R.info += "2. Confirm that your EFTPOS device is connected to your local accounts database. For additional assistance with this step, contact NanoTrasen IT Support
    " - R.info += "3. Confirm that your EFTPOS device has been linked to the account that you wish to recieve funds for all transactions processed on this device.
    " - R.info += "When starting a new transaction with your EFTPOS device:" - R.info += "1. Ensure the device is UNLOCKED so that new data may be entered.
    " - R.info += "2. Enter a sum of money and reference message for the new transaction.
    " - R.info += "3. Lock the transaction, it is now ready for your customer.
    " - R.info += "4. If at this stage you wish to modify or cancel your transaction, you may simply reset (unlock) your EFTPOS device.
    " - R.info += "5. Give your EFTPOS device to the customer, they must authenticate the transaction by swiping their ID card and entering their PIN number.
    " - R.info += "6. If done correctly, the transaction will be logged to both accounts with the reference you have entered, the terminal ID of your EFTPOS device and the money transferred across accounts.
    " - */ - //Temptative new manual: - R.info += "First EFTPOS setup:
    " - R.info += "1. Memorise your EFTPOS command code (provided with all EFTPOS devices).
    " - R.info += "2. Connect the EFTPOS to the account in which you want to receive the funds.

    " - R.info += "When starting a new transaction:
    " - R.info += "1. Enter the amount of money you want to charge and a purpose message for the new transaction.
    " - R.info += "2. Lock the new transaction. If you want to modify or cancel the transaction, you simply have to reset your EFTPOS device.
    " - R.info += "3. Give the EFTPOS device to your customer, he/she must finish the transaction by swiping their ID card or a charge card with enough funds.
    " - R.info += "4. If everything is done correctly, the money will be transferred. To unlock the device you will have to reset the EFTPOS device.
    " - - - //stamp the paper - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.icon_state = "paper_stamp-cent" - if(!R.stamped) - R.stamped = new - R.offset_x += 0 - R.offset_y += 0 - R.ico += "paper_stamp-cent" - R.stamped += /obj/item/weapon/stamp - R.overlays += stampoverlay - R.stamps += "
    This paper has been stamped by the EFTPOS device." - +/obj/item/device/eftpos/Initialize() + . = ..() //by default, connect to the station account //the user of the EFTPOS device can change the target account though, and no-one will be the wiser (except whoever's being charged) linked_account = station_account + machine_id = "[station_name()] EFTPOS #[num_financial_terminals++]" + access_code = rand(1111,111111) + print_reference() + + //create a short manual as well + var/obj/item/weapon/paper/R = new(src.loc) + R.name = "Steps to success: Correct EFTPOS Usage" + //Temptative new manual: + R.info += "First EFTPOS setup:
    " + R.info += "1. Memorise your EFTPOS command code (provided with all EFTPOS devices).
    " + R.info += "2. Connect the EFTPOS to the account in which you want to receive the funds.

    " + R.info += "When starting a new transaction:
    " + R.info += "1. Enter the amount of money you want to charge and a purpose message for the new transaction.
    " + R.info += "2. Lock the new transaction. If you want to modify or cancel the transaction, you simply have to reset your EFTPOS device.
    " + R.info += "3. Give the EFTPOS device to your customer, he/she must finish the transaction by swiping their ID card or a charge card with enough funds.
    " + R.info += "4. If everything is done correctly, the money will be transferred. To unlock the device you will have to reset the EFTPOS device.
    " + + //stamp the paper + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.icon_state = "paper_stamp-cent" + if(!R.stamped) + R.stamped = new + R.offset_x += 0 + R.offset_y += 0 + R.ico += "paper_stamp-cent" + R.stamped += /obj/item/weapon/stamp + R.overlays += stampoverlay + R.stamps += "
    This paper has been stamped by the EFTPOS device." + +/obj/item/device/eftpos/Destroy() + linked_account = null + return ..() + /obj/item/device/eftpos/proc/print_reference() var/obj/item/weapon/paper/R = new(src.loc) R.name = "Reference: [eftpos_name]" @@ -118,7 +107,7 @@ if(linked_account) scan_card(I, O) else - usr << "\icon[src]Unable to connect to linked account." + to_chat(usr, "[bicon(src)]Unable to connect to linked account.") else if (istype(O, /obj/item/weapon/spacecash/ewallet)) var/obj/item/weapon/spacecash/ewallet/E = O if (linked_account) @@ -126,7 +115,7 @@ if(transaction_locked && !transaction_paid) if(transaction_amount <= E.worth) playsound(src, 'sound/machines/chime.ogg', 50, 1) - src.visible_message("\icon[src] \The [src] chimes.") + src.visible_message("[bicon(src)] \The [src] chimes.") transaction_paid = 1 //transfer the money @@ -143,11 +132,11 @@ T.time = stationtime2text() linked_account.transaction_log.Add(T) else - usr << "\icon[src]\The [O] doesn't have that much money!" + to_chat(usr, "[bicon(src)]\The [O] doesn't have that much money!") else - usr << "\icon[src]Connected account has been suspended." + to_chat(usr, "[bicon(src)]Connected account has been suspended.") else - usr << "\icon[src]EFTPOS is not connected to an account." + to_chat(usr, "[bicon(src)]EFTPOS is not connected to an account.") else ..() @@ -165,14 +154,14 @@ alert("That is not a valid code!") print_reference() else - usr << "\icon[src]Incorrect code entered." + to_chat(usr, "[bicon(src)]Incorrect code entered.") if("change_id") var/attempt_code = text2num(input("Re-enter the current EFTPOS access code", "Confirm EFTPOS code")) if(attempt_code == access_code) eftpos_name = sanitize(input("Enter a new terminal ID for this device", "Enter new EFTPOS ID"), MAX_NAME_LEN) + " EFTPOS scanner" print_reference() else - usr << "\icon[src]Incorrect code entered." + to_chat(usr, "[bicon(src)]Incorrect code entered.") if("link_account") var/attempt_account_num = input("Enter account number to pay EFTPOS charges into", "New account number") as num var/attempt_pin = input("Enter pin code", "Account pin") as num @@ -180,9 +169,9 @@ if(linked_account) if(linked_account.suspended) linked_account = null - usr << "\icon[src]Account has been suspended." + to_chat(usr, "[bicon(src)]Account has been suspended.") else - usr << "\icon[src]Account not found." + to_chat(usr, "[bicon(src)]Account not found.") if("trans_purpose") var/choice = sanitize(input("Enter reason for EFTPOS transaction", "Transaction purpose")) if(choice) transaction_purpose = choice @@ -205,14 +194,14 @@ else if(linked_account) transaction_locked = 1 else - usr << "\icon[src]No account connected to send transactions to." + to_chat(usr, "[bicon(src)]No account connected to send transactions to.") if("scan_card") if(linked_account) var/obj/item/I = usr.get_active_hand() if (istype(I, /obj/item/weapon/card)) scan_card(I) else - usr << "\icon[src]Unable to link accounts." + to_chat(usr, "[bicon(src)]Unable to link accounts.") if("reset") //reset the access code - requires HoP/captain access var/obj/item/I = usr.get_active_hand() @@ -220,10 +209,10 @@ var/obj/item/weapon/card/id/C = I if(access_cent_captain in C.access || access_hop in C.access || access_captain in C.access) access_code = 0 - usr << "\icon[src]Access code reset to 0." + to_chat(usr, "[bicon(src)]Access code reset to 0.") else if (istype(I, /obj/item/weapon/card/emag)) access_code = 0 - usr << "\icon[src]Access code reset to 0." + to_chat(usr, "[bicon(src)]Access code reset to 0.") src.attack_self(usr) @@ -247,7 +236,7 @@ if(!D.suspended) if(transaction_amount <= D.money) playsound(src, 'sound/machines/chime.ogg', 50, 1) - src.visible_message("\icon[src] \The [src] chimes.") + src.visible_message("[bicon(src)] \The [src] chimes.") transaction_paid = 1 //transfer the money @@ -276,25 +265,25 @@ T.time = stationtime2text() linked_account.transaction_log.Add(T) else - usr << "\icon[src]You don't have that much money!" + to_chat(usr, "[bicon(src)]You don't have that much money!") else - usr << "\icon[src]Your account has been suspended." + to_chat(usr, "[bicon(src)]Your account has been suspended.") else - usr << "\icon[src]Unable to access account. Check security settings and try again." + to_chat(usr, "[bicon(src)]Unable to access account. Check security settings and try again.") else - usr << "\icon[src]Connected account has been suspended." + to_chat(usr, "[bicon(src)]Connected account has been suspended.") else - usr << "\icon[src]EFTPOS is not connected to an account." + to_chat(usr, "[bicon(src)]EFTPOS is not connected to an account.") else if (istype(I, /obj/item/weapon/card/emag)) if(transaction_locked) if(transaction_paid) - usr << "\icon[src]You stealthily swipe \the [I] through \the [src]." + to_chat(usr, "[bicon(src)]You stealthily swipe \the [I] through \the [src].") transaction_locked = 0 transaction_paid = 0 else usr.visible_message("\The [usr] swipes a card through \the [src].") playsound(src, 'sound/machines/chime.ogg', 50, 1) - src.visible_message("\icon[src] \The [src] chimes.") + src.visible_message("[bicon(src)] \The [src] chimes.") transaction_paid = 1 else ..() diff --git a/code/modules/economy/cash.dm b/code/modules/economy/cash.dm index c5a11275e8..b5e4abd488 100644 --- a/code/modules/economy/cash.dm +++ b/code/modules/economy/cash.dm @@ -15,6 +15,7 @@ var/access = list() access = access_crate_cash var/worth = 0 + drop_sound = 'sound/items/drop/paper.ogg' /obj/item/weapon/spacecash/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/spacecash)) @@ -29,7 +30,7 @@ h_user.drop_from_inventory(src) h_user.drop_from_inventory(SC) h_user.put_in_hands(SC) - user << "You combine the Thalers to a bundle of [SC.worth] Thalers." + to_chat(user, "You combine the Thalers to a bundle of [SC.worth] Thalers.") qdel(src) /obj/item/weapon/spacecash/update_icon() @@ -150,6 +151,7 @@ proc/spawn_money(var/sum, spawnloc, mob/living/carbon/human/human_user as mob) name = "charge card" icon_state = "efundcard" desc = "A card that holds an amount of money." + drop_sound = 'sound/items/drop/card.ogg' var/owner_name = "" //So the ATM can set it so the EFTPOS can put a valid name on transactions. attack_self() return //Don't act attackby() return //like actual @@ -158,4 +160,4 @@ proc/spawn_money(var/sum, spawnloc, mob/living/carbon/human/human_user as mob) /obj/item/weapon/spacecash/ewallet/examine(mob/user) ..(user) if (!(user in view(2)) && user!=src.loc) return - user << "Charge card's owner: [src.owner_name]. Thalers remaining: [src.worth]." + to_chat(user, "Charge card's owner: [src.owner_name]. Thalers remaining: [src.worth].") diff --git a/code/modules/economy/cash_register.dm b/code/modules/economy/cash_register.dm index f3402f892e..467846e3c1 100644 --- a/code/modules/economy/cash_register.dm +++ b/code/modules/economy/cash_register.dm @@ -33,11 +33,13 @@ /obj/machinery/cash_register/examine(mob/user as mob) ..(user) + if(transaction_amount) + to_chat(user, "It has a purchase of [transaction_amount] pending[transaction_purpose ? " for [transaction_purpose]" : ""].") if(cash_open) if(cash_stored) - user << "It holds [cash_stored] Thaler\s of money." + to_chat(user, "It holds [cash_stored] Thaler\s of money.") else - user << "It's completely empty." + to_chat(user, "It's completely empty.") /obj/machinery/cash_register/attack_hand(mob/user as mob) @@ -101,7 +103,7 @@ if(allowed(usr)) locked = !locked else - usr << "\icon[src]Insufficient access." + to_chat(usr, "[bicon(src)]Insufficient access.") if("toggle_cash_lock") cash_locked = !cash_locked if("link_account") @@ -111,9 +113,9 @@ if(linked_account) if(linked_account.suspended) linked_account = null - src.visible_message("\icon[src]Account has been suspended.") + src.visible_message("[bicon(src)]Account has been suspended.") else - usr << "\icon[src]Account not found." + to_chat(usr, "[bicon(src)]Account not found.") if("custom_order") var/t_purpose = sanitize(input("Enter purpose", "New purpose") as text) if (!t_purpose || !Adjacent(usr)) return @@ -124,7 +126,7 @@ transaction_amount += t_amount price_list += t_amount playsound(src, 'sound/machines/twobeep.ogg', 25) - src.visible_message("\icon[src][transaction_purpose]: [t_amount] Thaler\s.") + src.visible_message("[bicon(src)][transaction_purpose]: [t_amount] Thaler\s.") if("set_amount") var/item_name = locate(href_list["item"]) var/n_amount = round(input("Enter amount", "New amount") as num) @@ -161,7 +163,7 @@ price_list.Cut() if("reset_log") transaction_logs.Cut() - usr << "\icon[src]Transaction log reset." + to_chat(usr, "[bicon(src)]Transaction log reset.") updateDialog() @@ -177,7 +179,7 @@ else if (istype(O, /obj/item/weapon/spacecash)) var/obj/item/weapon/spacecash/SC = O if(cash_open) - user << "You neatly sort the cash into the box." + to_chat(user, "You neatly sort the cash into the box.") cash_stored += SC.worth overlays |= "register_cash" if(ishuman(user)) @@ -206,7 +208,7 @@ return 1 else confirm_item = I - src.visible_message("\icon[src]Total price: [transaction_amount] Thaler\s. Swipe again to confirm.") + src.visible_message("[bicon(src)]Total price: [transaction_amount] Thaler\s. Swipe again to confirm.") playsound(src, 'sound/machines/twobeep.ogg', 25) return 0 @@ -217,14 +219,14 @@ if (cash_open) playsound(src, 'sound/machines/buzz-sigh.ogg', 25) - usr << "\icon[src]The cash box is open." + to_chat(usr, "[bicon(src)]The cash box is open.") return if((item_list.len > 1 || item_list[item_list[1]] > 1) && !confirm(I)) return if (!linked_account) - usr.visible_message("\icon[src]Unable to connect to linked account.") + usr.visible_message("[bicon(src)]Unable to connect to linked account.") return // Access account for transaction @@ -237,13 +239,13 @@ D = attempt_account_access(I.associated_account_number, attempt_pin, 2) if(!D) - src.visible_message("\icon[src]Unable to access account. Check security settings and try again.") + src.visible_message("[bicon(src)]Unable to access account. Check security settings and try again.") else if(D.suspended) - src.visible_message("\icon[src]Your account has been suspended.") + src.visible_message("[bicon(src)]Your account has been suspended.") else if(transaction_amount > D.money) - src.visible_message("\icon[src]Not enough funds.") + src.visible_message("[bicon(src)]Not enough funds.") else // Transfer the money D.money -= transaction_amount @@ -282,7 +284,7 @@ if (cash_open) playsound(src, 'sound/machines/buzz-sigh.ogg', 25) - usr << "\icon[src]The cash box is open." + to_chat(usr, "[bicon(src)]The cash box is open.") return if((item_list.len > 1 || item_list[item_list[1]] > 1) && !confirm(E)) @@ -291,7 +293,7 @@ // Access account for transaction if(check_account()) if(transaction_amount > E.worth) - src.visible_message("\icon[src]Not enough funds.") + src.visible_message("[bicon(src)]Not enough funds.") else // Transfer the money E.worth -= transaction_amount @@ -320,14 +322,14 @@ if (cash_open) playsound(src, 'sound/machines/buzz-sigh.ogg', 25) - usr << "\icon[src]The cash box is open." + to_chat(usr, "[bicon(src)]The cash box is open.") return if((item_list.len > 1 || item_list[item_list[1]] > 1) && !confirm(SC)) return if(transaction_amount > SC.worth) - src.visible_message("\icon[src]Not enough money.") + src.visible_message("[bicon(src)]Not enough money.") else // Insert cash into magical slot SC.worth -= transaction_amount @@ -349,20 +351,20 @@ /obj/machinery/cash_register/proc/scan_item_price(obj/O) if(!istype(O)) return if(item_list.len > 10) - src.visible_message("\icon[src]Only up to ten different items allowed per purchase.") + src.visible_message("[bicon(src)]Only up to ten different items allowed per purchase.") return if (cash_open) playsound(src, 'sound/machines/buzz-sigh.ogg', 25) - usr << "\icon[src]The cash box is open." + to_chat(usr, "[bicon(src)]The cash box is open.") return // First check if item has a valid price var/price = O.get_item_cost() if(isnull(price)) - src.visible_message("\icon[src]Unable to find item in database.") + src.visible_message("[bicon(src)]Unable to find item in database.") return // Call out item cost - src.visible_message("\icon[src]\A [O]: [price ? "[price] Thaler\s" : "free of charge"].") + src.visible_message("[bicon(src)]\A [O]: [price ? "[price] Thaler\s" : "free of charge"].") // Note the transaction purpose for later use if(transaction_purpose) transaction_purpose += "
    " @@ -430,11 +432,11 @@ /obj/machinery/cash_register/proc/check_account() if (!linked_account) - usr.visible_message("\icon[src]Unable to connect to linked account.") + usr.visible_message("[bicon(src)]Unable to connect to linked account.") return 0 if(linked_account.suspended) - src.visible_message("\icon[src]Connected account has been suspended.") + src.visible_message("[bicon(src)]Connected account has been suspended.") return 0 return 1 @@ -442,7 +444,7 @@ /obj/machinery/cash_register/proc/transaction_complete() /// Visible confirmation playsound(src, 'sound/machines/chime.ogg', 25) - src.visible_message("\icon[src]Transaction complete.") + src.visible_message("[bicon(src)]Transaction complete.") flick("register_approve", src) reset_memory() updateDialog() @@ -476,7 +478,7 @@ if(cash_stored) overlays += "register_cash" else - usr << "The cash box is locked." + to_chat(usr, "The cash box is locked.") /obj/machinery/cash_register/proc/toggle_anchors(obj/item/weapon/tool/wrench/W, mob/user) diff --git a/code/modules/economy/retail_scanner.dm b/code/modules/economy/retail_scanner.dm index dc5c4e9cea..9c2cff9dca 100644 --- a/code/modules/economy/retail_scanner.dm +++ b/code/modules/economy/retail_scanner.dm @@ -56,6 +56,11 @@ user.set_machine(src) interact(user) +/obj/item/device/retail_scanner/examine(mob/user as mob) + ..(user) + if(transaction_amount) + to_chat(user, "It has a purchase of [transaction_amount] pending[transaction_purpose ? " for [transaction_purpose]" : ""].") + /obj/item/device/retail_scanner/interact(mob/user as mob) var/dat = "

    Retail Scanner

    " @@ -95,7 +100,7 @@ if(allowed(usr)) locked = !locked else - usr << "\icon[src]Insufficient access." + to_chat(usr, "[bicon(src)]Insufficient access.") if("link_account") var/attempt_account_num = input("Enter account number", "New account number") as num var/attempt_pin = input("Enter PIN", "Account PIN") as num @@ -103,9 +108,9 @@ if(linked_account) if(linked_account.suspended) linked_account = null - src.visible_message("\icon[src]Account has been suspended.") + src.visible_message("[bicon(src)]Account has been suspended.") else - usr << "\icon[src]Account not found." + to_chat(usr, "[bicon(src)]Account not found.") if("custom_order") var/t_purpose = sanitize(input("Enter purpose", "New purpose") as text) if (!t_purpose || !Adjacent(usr)) return @@ -116,7 +121,7 @@ transaction_amount += t_amount price_list += t_amount playsound(src, 'sound/machines/twobeep.ogg', 25) - src.visible_message("\icon[src][transaction_purpose]: [t_amount] Thaler\s.") + src.visible_message("[bicon(src)][transaction_purpose]: [t_amount] Thaler\s.") if("set_amount") var/item_name = locate(href_list["item"]) var/n_amount = round(input("Enter amount", "New amount") as num) @@ -153,7 +158,7 @@ price_list.Cut() if("reset_log") transaction_logs.Cut() - usr << "\icon[src]Transaction log reset." + to_chat(usr, "[bicon(src)]Transaction log reset.") updateDialog() @@ -167,7 +172,7 @@ var/obj/item/weapon/spacecash/ewallet/E = O scan_wallet(E) else if (istype(O, /obj/item/weapon/spacecash)) - usr << "This device does not accept cash." + to_chat(usr, "This device does not accept cash.") else if(istype(O, /obj/item/weapon/card/emag)) return ..() @@ -186,7 +191,7 @@ return 1 else confirm_item = I - src.visible_message("\icon[src]Total price: [transaction_amount] Thaler\s. Swipe again to confirm.") + src.visible_message("[bicon(src)]Total price: [transaction_amount] Thaler\s. Swipe again to confirm.") playsound(src, 'sound/machines/twobeep.ogg', 25) return 0 @@ -199,7 +204,7 @@ return if (!linked_account) - usr.visible_message("\icon[src]Unable to connect to linked account.") + usr.visible_message("[bicon(src)]Unable to connect to linked account.") return // Access account for transaction @@ -212,13 +217,13 @@ D = attempt_account_access(I.associated_account_number, attempt_pin, 2) if(!D) - src.visible_message("\icon[src]Unable to access account. Check security settings and try again.") + src.visible_message("[bicon(src)]Unable to access account. Check security settings and try again.") else if(D.suspended) - src.visible_message("\icon[src]Your account has been suspended.") + src.visible_message("[bicon(src)]Your account has been suspended.") else if(transaction_amount > D.money) - src.visible_message("\icon[src]Not enough funds.") + src.visible_message("[bicon(src)]Not enough funds.") else // Transfer the money D.money -= transaction_amount @@ -261,7 +266,7 @@ // Access account for transaction if(check_account()) if(transaction_amount > E.worth) - src.visible_message("\icon[src]Not enough funds.") + src.visible_message("[bicon(src)]Not enough funds.") else // Transfer the money E.worth -= transaction_amount @@ -287,16 +292,16 @@ /obj/item/device/retail_scanner/proc/scan_item_price(var/obj/O) if(!istype(O)) return if(item_list.len > 10) - src.visible_message("\icon[src]Only up to ten different items allowed per purchase.") + src.visible_message("[bicon(src)]Only up to ten different items allowed per purchase.") return // First check if item has a valid price var/price = O.get_item_cost() if(isnull(price)) - src.visible_message("\icon[src]Unable to find item in database.") + src.visible_message("[bicon(src)]Unable to find item in database.") return // Call out item cost - src.visible_message("\icon[src]\A [O]: [price ? "[price] Thaler\s" : "free of charge"].") + src.visible_message("[bicon(src)]\A [O]: [price ? "[price] Thaler\s" : "free of charge"].") // Note the transaction purpose for later use if(transaction_purpose) transaction_purpose += "
    " @@ -364,11 +369,11 @@ /obj/item/device/retail_scanner/proc/check_account() if (!linked_account) - usr.visible_message("\icon[src]Unable to connect to linked account.") + usr.visible_message("[bicon(src)]Unable to connect to linked account.") return 0 if(linked_account.suspended) - src.visible_message("\icon[src]Connected account has been suspended.") + src.visible_message("[bicon(src)]Connected account has been suspended.") return 0 return 1 @@ -376,7 +381,7 @@ /obj/item/device/retail_scanner/proc/transaction_complete() /// Visible confirmation playsound(src, 'sound/machines/chime.ogg', 25) - src.visible_message("\icon[src]Transaction complete.") + src.visible_message("[bicon(src)]Transaction complete.") flick("retail_approve", src) reset_memory() updateDialog() @@ -392,7 +397,7 @@ /obj/item/device/retail_scanner/emag_act(var/remaining_charges, var/mob/user) if(!emagged) - user << "You stealthily swipe the cryptographic sequencer through \the [src]." + to_chat(user, "You stealthily swipe the cryptographic sequencer through \the [src].") playsound(src, "sparks", 50, 1) req_access = list() emagged = 1 diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm index 71783714b0..edae4c42ea 100644 --- a/code/modules/error_handler/error_handler.dm +++ b/code/modules/error_handler/error_handler.dm @@ -1,7 +1,9 @@ // error_cooldown items will either be positive (cooldown time) or negative (silenced error) // If negative, starts at -1, and goes down by 1 each time that error gets skipped -var/total_runtimes = 0 -var/total_runtimes_skipped = 0 +GLOBAL_VAR_INIT(total_runtimes, 0) +GLOBAL_VAR_INIT(total_runtimes_skipped, 0) + + // The ifdef needs to be down here, since the error viewer references total_runtimes #ifdef DEBUG /world/Error(var/exception/e, var/datum/e_src) @@ -10,7 +12,7 @@ var/total_runtimes_skipped = 0 return ..() if(!GLOB.error_last_seen) // A runtime is occurring too early in start-up initialization return ..() - total_runtimes++ + GLOB.total_runtimes++ var/erroruid = "[e.file][e.line]" var/last_seen = GLOB.error_last_seen[erroruid] @@ -20,7 +22,7 @@ var/total_runtimes_skipped = 0 last_seen = world.time if(cooldown < 0) GLOB.error_cooldown[erroruid]-- // Used to keep track of skip count for this error - total_runtimes_skipped++ + GLOB.total_runtimes_skipped++ return // Error is currently silenced, skip handling it // Handle cooldowns and silencing spammy errors @@ -49,12 +51,12 @@ var/total_runtimes_skipped = 0 // First, try to make better src/usr info lines if(istype(e_src)) srcinfo = list(" src: [log_info_line(e_src)]") - locinfo = log_info_line(e_src) - if(locinfo) - srcinfo += " src.loc: [locinfo]" + var/atom/atom_e_src = e_src + if(istype(atom_e_src)) + srcinfo += " src.loc: [log_info_line(atom_e_src.loc)]" if(istype(usr)) usrinfo = list(" usr: [log_info_line(usr)]") - locinfo = log_info_line(usr) + locinfo = log_info_line(usr.loc) if(locinfo) usrinfo += " usr.loc: [locinfo]" // The proceeding mess will almost definitely break if error messages are ever changed diff --git a/code/modules/error_handler/error_viewer.dm b/code/modules/error_handler/error_viewer.dm index 0f1fd0ba96..c342a408f0 100644 --- a/code/modules/error_handler/error_viewer.dm +++ b/code/modules/error_handler/error_viewer.dm @@ -88,7 +88,7 @@ var/global/datum/ErrorViewer/ErrorCache/error_cache = null /datum/ErrorViewer/ErrorCache/showTo(var/user, var/datum/ErrorViewer/back_to, var/linear) var/html = buildHeader(null, linear, refreshable=1) - html += "[total_runtimes] runtimes, [total_runtimes_skipped] skipped

    " + html += "[GLOB.total_runtimes] runtimes, [GLOB.total_runtimes_skipped] skipped

    " if(!linear) html += "organized | [makeLink("linear", null, 1)]
    " var/datum/ErrorViewer/ErrorSource/error_source diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index b30a5d4898..f573614666 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -1,51 +1,103 @@ /datum/event/carp_migration - announceWhen = 50 - endWhen = 900 - + startWhen = 0 // Start immediately + announceWhen = 45 // Adjusted by setup + endWhen = 75 // Adjusted by setup + var/carp_cap = 10 var/list/spawned_carp = list() /datum/event/carp_migration/setup() - announceWhen = rand(40, 60) - endWhen = rand(600,1200) + announceWhen = rand(30, 60) // 1 to 2 minutes + endWhen += severity * 25 + carp_cap = 2 + 3 ** severity // No more than this many at once regardless of waves. (5, 11, 29) + +/datum/event/carp_migration/start() + affecting_z -= global.using_map.sealed_levels // Space levels only please! + ..() /datum/event/carp_migration/announce() var/announcement = "" if(severity == EVENT_LEVEL_MAJOR) - announcement = "Massive migration of unknown biological entities has been detected near [station_name()], please stand-by." + announcement = "Massive migration of unknown biological entities has been detected near [location_name()], please stand-by." else - announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [station_name()], please stand-by." + announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [location_name()], please stand-by." command_announcement.Announce(announcement, "Lifesign Alert") -/datum/event/carp_migration/start() - if(severity == EVENT_LEVEL_MAJOR) - spawn_fish(landmarks_list.len) - else if(severity == EVENT_LEVEL_MODERATE) - spawn_fish(rand(4, 6)) //12 to 30 carp, in small groups - else - spawn_fish(rand(1, 3), 1, 2) //1 to 6 carp, alone or in pairs +/datum/event/carp_migration/tick() + if(activeFor % 5 != 0) + return // Only process every 10 seconds. + if(count_spawned_carps() < carp_cap) + spawn_fish(rand(1, severity * 2) - 1, severity, severity * 2) -/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min=3, var/group_size_max=5) +/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min, var/group_size_max, var/dir) + if(isnull(dir)) + dir = (victim && prob(80)) ? victim.fore_dir : pick(GLOB.cardinal) + + // Check if any landmarks exist! var/list/spawn_locations = list() - for(var/obj/effect/landmark/C in landmarks_list) - if(C.name == "carpspawn") + if(C.name == "carpspawn" && (C.z in affecting_z)) spawn_locations.Add(C.loc) - spawn_locations = shuffle(spawn_locations) - num_groups = min(num_groups, spawn_locations.len) + if(spawn_locations.len) // Okay we've got landmarks, lets use those! + shuffle_inplace(spawn_locations) + num_groups = min(num_groups, spawn_locations.len) + for (var/i = 1, i <= num_groups, i++) + var/group_size = rand(group_size_min, group_size_max) + for (var/j = 0, j < group_size, j++) + spawn_one_carp(spawn_locations[i]) + return + // Okay we did *not* have any landmarks, so lets do our best! var/i = 1 while (i <= num_groups) + var/Z = pick(affecting_z) var/group_size = rand(group_size_min, group_size_max) - for (var/j = 1, j <= group_size, j++) - spawned_carp.Add(new /mob/living/simple_mob/animal/space/carp/event(spawn_locations[i])) + var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), Z) + var/turf/group_center = pick_random_edge_turf(dir, Z, TRANSITIONEDGE + 2) + var/list/turfs = getcircle(group_center, 2) + for (var/j = 0, j < group_size, j++) + var/mob/living/simple_mob/animal/M = spawn_one_carp(turfs[(i % turfs.len) + 1]) + // Ray trace towards middle of the map to find where they can stop just outside of structure/ship. + var/turf/target + for(var/turf/T in getline(get_turf(M), map_center)) + if(!T.is_space()) + break; + target = T + if(target) + M.ai_holder?.give_destination(target) // Ask carp to swim towards the middle of the map i++ +// Spawn a single carp at given location. +/datum/event/carp_migration/proc/spawn_one_carp(var/loc) + var/mob/living/simple_mob/animal/M = new /mob/living/simple_mob/animal/space/carp/event(loc) + GLOB.destroyed_event.register(M, src, .proc/on_carp_destruction) + spawned_carp.Add(M) + return M + +// Counts living carp spawned by this event. +/datum/event/carp_migration/proc/count_spawned_carps() + . = 0 + for(var/I in spawned_carp) + var/mob/living/simple_mob/animal/M = I + if(!QDELETED(M) && M.stat != DEAD) + . += 1 + +// If carp is bomphed, remove it from the list. +/datum/event/carp_migration/proc/on_carp_destruction(var/mob/M) + spawned_carp -= M + GLOB.destroyed_event.unregister(M, src, .proc/on_carp_destruction) + /datum/event/carp_migration/end() + . = ..() + // Clean up carp that died in space for some reason. spawn(0) for(var/mob/living/simple_mob/SM in spawned_carp) - if(!SM.stat) + if(SM.stat == DEAD) var/turf/T = get_turf(SM) if(istype(T, /turf/space)) if(prob(75)) qdel(SM) - sleep(1) \ No newline at end of file + CHECK_TICK + +// Overmap version +/datum/event/carp_migration/overmap/announce() + return diff --git a/code/modules/events/comms_blackout.dm b/code/modules/events/comms_blackout.dm index 73c2ddb70b..d2b8fdca04 100644 --- a/code/modules/events/comms_blackout.dm +++ b/code/modules/events/comms_blackout.dm @@ -5,8 +5,8 @@ command_announcement.Announce("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT", new_sound = 'sound/misc/interference.ogg') else // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms for(var/mob/living/silicon/ai/A in player_list) - A << "
    " - A << "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT" - A << "
    " + to_chat(A, "
    ") + to_chat(A, "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT") + to_chat(A, "
    ") for(var/obj/machinery/telecomms/T in telecomms_list) T.emp_act(1) diff --git a/code/modules/events/communications_blackout.dm b/code/modules/events/communications_blackout.dm index 6b666117f2..f1e2123593 100644 --- a/code/modules/events/communications_blackout.dm +++ b/code/modules/events/communications_blackout.dm @@ -7,9 +7,9 @@ "#4nd%;f4y6,>%-BZZZZZZZT") for(var/mob/living/silicon/ai/A in player_list) //AIs are always aware of communication blackouts. - A << "
    " - A << "[alert]" - A << "
    " + to_chat(A, "
    ") + to_chat(A, "[alert]") + to_chat(A, "
    ") if(prob(30)) //most of the time, we don't want an announcement, so as to allow AIs to fake blackouts. command_announcement.Announce(alert, new_sound = sound('sound/misc/interference.ogg', volume=25)) diff --git a/code/modules/events/drone_pod_vr.dm b/code/modules/events/drone_pod_vr.dm index 348e16d808..a996c3b5d2 100644 --- a/code/modules/events/drone_pod_vr.dm +++ b/code/modules/events/drone_pod_vr.dm @@ -4,28 +4,32 @@ /datum/event/drone_pod_drop/setup() startWhen = rand(8,15) - announceWhen = startWhen + 5 - if(LAZYLEN(using_map.meteor_strike_areas)) - var/turf/potential_target = null - for(var/i=1, i <= attempt_amount, i++) - potential_target = pick(get_area_turfs(pick(using_map.meteor_strike_areas))) - if(potential_target.x < 7 || potential_target.x > world.maxx-7 || potential_target.y < 7 || potential_target.y > world.maxy-7) - continue - else - land_target = potential_target - break + + var/land_spot_list = list() + var/target_spot + + for(var/obj/effect/landmark/land_spot in landmarks_list) + if(land_spot.name == "droppod_landing" && !(land_spot in land_spot_list)) + land_spot_list += land_spot + + target_spot = pick(land_spot_list) + land_target = get_turf(target_spot) if(!land_target) kill() + else + landmarks_list -= target_spot + qdel(target_spot) /datum/event/drone_pod_drop/announce() command_announcement.Announce("An unidentified drone pod has been detected landing near the surface facilty. Open and examine at your own risk.", "NanoTrasen Orbital Monitoring") /datum/event/drone_pod_drop/start() - if(istype(land_target, /turf/simulated/open)) - while(istype(land_target, /turf/simulated/open)) - land_target = GetBelow(land_target) if(!land_target) kill() - new /datum/random_map/droppod/supply(null, land_target.x-2, land_target.y-2, land_target.z, supplied_drops = list(/obj/structure/ghost_pod/manual/lost_drone/dogborg)) \ No newline at end of file + new /datum/random_map/droppod/supply(null, land_target.x-2, land_target.y-2, land_target.z, supplied_drops = list(/obj/structure/ghost_pod/manual/lost_drone/dogborg)) + +//YW Addition: Adding named landmark for events +/obj/effect/landmark/event_spawn/dronepod_landing + name = "droppod_landing" \ No newline at end of file diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index 3c7a116827..31ae5dee32 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -2,14 +2,22 @@ startWhen = 10 endWhen = 30 -/datum/event/dust/announce() - command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [station_name()]", "Dust Alert") - /datum/event/dust/start() - dust_swarm(get_severity()) + affecting_z -= global.using_map.sealed_levels // Space levels only please! + ..() + +/datum/event/dust/announce() + if(!victim) + command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [location_name()]", "Dust Alert") + +/datum/event/dust/tick() + if(prob(10)) + dust_swarm(severity, affecting_z) /datum/event/dust/end() - command_announcement.Announce("\The [station_name()] is no longer in danger of impact from space debris.", "Dust Notice") + ..() + if(!victim) + command_announcement.Announce("\The [location_name()] is no longer in danger of impact from space debris.", "Dust Notice") /datum/event/dust/proc/get_severity() switch(severity) @@ -20,3 +28,7 @@ if(EVENT_LEVEL_MAJOR) return "super" return "weak" + +// Overmap version +/datum/event/dust/overmap/announce() + return \ No newline at end of file diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index 73198dec9b..c8ecbad8d4 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -1,28 +1,73 @@ /datum/event/electrical_storm - var/lightsoutAmount = 1 - var/lightsoutRange = 25 + announceWhen = 0 // Warn them shortly before it begins. + startWhen = 30 // 1 minute + endWhen = 60 // Set in setup() + has_skybox_image = TRUE + var/tmp/lightning_color + var/tmp/list/valid_apcs // List of valid APCs. +/datum/event/electrical_storm/get_skybox_image() + if(!lightning_color) + lightning_color = pick("#ffd98c", "#ebc7ff", "#bdfcff", "#bdd2ff", "#b0ffca", "#ff8178", "#ad74cc") + var/image/res = image('icons/skybox/electrobox.dmi', "lightning") + res.color = lightning_color + res.appearance_flags = RESET_COLOR + res.blend_mode = BLEND_ADD + return res /datum/event/electrical_storm/announce() - command_announcement.Announce("An electrical issue has been detected in your area, please repair potential electronic overloads.", "Electrical Alert") - + ..() + switch(severity) + if(EVENT_LEVEL_MUNDANE) + command_announcement.Announce("A minor electrical storm has been detected near the [location_name()]. Please watch out for possible electrical discharges.", "[location_name()] Sensor Array") + if(EVENT_LEVEL_MODERATE) + command_announcement.Announce("The [location_name()] is about to pass through an electrical storm. Please secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") + if(EVENT_LEVEL_MAJOR) + command_announcement.Announce("Alert. A strong electrical storm has been detected in proximity of the [location_name()]. It is recommended to immediately secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array") /datum/event/electrical_storm/start() - var/list/epicentreList = list() + ..() + valid_apcs = list() + for(var/obj/machinery/power/apc/A in global.machines) + if(A.z in affecting_z) + valid_apcs.Add(A) + endWhen = (severity * 60) + startWhen - for(var/i=1, i <= lightsoutAmount, i++) - var/list/possibleEpicentres = list() - for(var/obj/effect/landmark/newEpicentre in landmarks_list) - if(newEpicentre.name == "lightsout" && !(newEpicentre in epicentreList)) - possibleEpicentres += newEpicentre - if(possibleEpicentres.len) - epicentreList += pick(possibleEpicentres) - else - break +/datum/event/electrical_storm/tick() + ..() + // See if shields can stop it first + var/list/shields = list() + for(var/obj/machinery/power/shield_generator/G in global.machines) + if((G.z in affecting_z) && G.running && G.check_flag(MODEFLAG_EM)) + shields += G + if(shields.len) + var/obj/machinery/power/shield_generator/shield_gen = pick(shields) + //Minor breaches aren't enough to let through frying amounts of power + if(shield_gen.deal_shield_damage(30 * severity, SHIELD_DAMTYPE_EM) <= SHIELD_BREACHED_MINOR) + return + if(!valid_apcs.len) + log_debug("No valid APCs found for electrical storm event ship=[victim]!") + return + var/list/picked_apcs = list() + for(var/i=0, i< severity * 2, i++) // up to 2/4/6 APCs per tick depending on severity + picked_apcs |= pick(valid_apcs) + for(var/obj/machinery/power/apc/T in picked_apcs) + affect_apc(T) - if(!epicentreList.len) +/datum/event/electrical_storm/proc/affect_apc(var/obj/machinery/power/apc/T) + // Main breaker is turned off. Consider this APC protected. + if(!T.operating) return - for(var/obj/effect/landmark/epicentre in epicentreList) - for(var/obj/machinery/power/apc/apc in range(epicentre,lightsoutRange)) - apc.overload_lighting() + // Decent chance to overload lighting circuit. + if(prob(3 * severity)) + T.overload_lighting() + + // Relatively small chance to emag the apc as apc_damage event does. + if(prob(0.2 * severity)) + T.emagged = 1 + T.update_icon() + +// Overmap version +/datum/event/electrical_storm/overmap/announce() + return \ No newline at end of file diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm index e44e161a1a..a17489921f 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/event.dm @@ -1,3 +1,4 @@ +// Event Meta instances represent choices for the event manager to choose for random events. /datum/event_meta var/name = "" var/enabled = 1 // Whether or not the event is available for random selection at all @@ -39,6 +40,10 @@ return total_weight +/datum/event_meta/no_overmap/get_weight() //these events have overmap equivalents, and shouldn't fire randomly if overmap is used + return global.using_map.use_overmap ? 0 : ..() + +// Event datums define and execute the actual events themselves. /datum/event //NOTE: Times are measured in master controller ticks! var/startWhen = 0 //When in the lifetime to call start(). var/announceWhen = 0 //When in the lifetime to call announce(). @@ -51,6 +56,9 @@ var/endedAt = 0 //When this event ended. var/processing_active = TRUE var/datum/event_meta/event_meta = null + var/list/affecting_z = null // List of z-levels to affect, null lets the event choose (usally station_levels) + var/has_skybox_image = FALSE // True if SSskybox should query this event for an image to put in the skybox. + var/obj/effect/overmap/visitable/ship/victim = null // Ship this event is acting upon (If this is event is due to overmap travel).nt etc. /datum/event/nothing @@ -65,6 +73,8 @@ //Allows you to start before announcing or vice versa. //Only called once. /datum/event/proc/start() + if(has_skybox_image) + SSskybox.rebuild_skyboxes(affecting_z) return //Called when the tick is equal to the announceWhen variable. @@ -87,6 +97,8 @@ //For example: if(activeFor == myOwnVariable + 30) doStuff() //Only called once. /datum/event/proc/end() + if(has_skybox_image) + SSskybox.rebuild_skyboxes(affecting_z) return //Returns the latest point of event processing. @@ -125,26 +137,43 @@ activeFor++ //Called when start(), announce() and end() has all been called. -/datum/event/proc/kill() +/datum/event/proc/kill(external_use = FALSE) // If this event was forcefully killed run end() for individual cleanup if(isRunning) isRunning = 0 end() endedAt = world.time - SSevents.active_events -= src - SSevents.event_complete(src) + if(!external_use) + SSevents.event_complete(src) -/datum/event/New(var/datum/event_meta/EM) +//Called during building of skybox to get overlays +/datum/event/proc/get_skybox_image() + return + +/datum/event/New(var/datum/event_meta/EM, external_use = FALSE) // event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons - SSevents.active_events += src + if(!external_use) + SSevents.active_events += src - event_meta = EM - severity = event_meta.severity - if(severity < EVENT_LEVEL_MUNDANE) severity = EVENT_LEVEL_MUNDANE - if(severity > EVENT_LEVEL_MAJOR) severity = EVENT_LEVEL_MAJOR + event_meta = EM + severity = event_meta.severity + if(severity < EVENT_LEVEL_MUNDANE) severity = EVENT_LEVEL_MUNDANE + if(severity > EVENT_LEVEL_MAJOR) severity = EVENT_LEVEL_MAJOR startedAt = world.time + if(!affecting_z) + affecting_z = using_map.station_levels.Copy() + setup() ..() + +/datum/event/Destroy() + victim = null + . = ..() + +/datum/event/proc/location_name() + if(victim) + return victim.name + return station_name() diff --git a/code/modules/events/event_container_vr.dm b/code/modules/events/event_container_vr.dm index 5dd7151e12..6779ede04c 100644 --- a/code/modules/events/event_container_vr.dm +++ b/code/modules/events/event_container_vr.dm @@ -34,8 +34,9 @@ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Brand Intelligence",/datum/event/brand_intelligence,20, list(ASSIGNMENT_JANITOR = 25), 1), new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Camera Damage", /datum/event/camera_damage, 20, list(ASSIGNMENT_ENGINEER = 20)), new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Canister Leak", /datum/event/canister_leak, 10, list(ASSIGNMENT_ENGINEER = 20)), -//YW EDIT new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Space Dust", /datum/event/dust, 0, list(ASSIGNMENT_ENGINEER = 20), 0, 0, 50), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Space Dust", /datum/event/dust, 0, list(ASSIGNMENT_ENGINEER = 20), 0, 0, 50), new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Economic News", /datum/event/economic_event, 300), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Lost Spiders", /datum/event/spider_migration, 0, list(ASSIGNMENT_SECURITY = 10), 1), //YW EDIT new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Hacker", /datum/event/money_hacker, 0, list(ASSIGNMENT_ANY = 4), 1, 10, 25), new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Lotto", /datum/event/money_lotto, 0, list(ASSIGNMENT_ANY = 1), 1, 5, 15), @@ -51,6 +52,7 @@ ) add_disabled_events(list( new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Ian Storm", /datum/event/ianstorm, 1, list(), 1), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Lost Carp", /datum/event/carp_migration, 0, list(ASSIGNMENT_SECURITY = 10), 1), new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Supply Demand", /datum/event/supply_demand, 0, list(ASSIGNMENT_ANY = 5, ASSIGNMENT_SCIENCE = 15, ASSIGNMENT_GARDENER = 10, ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_MEDICAL = 15), 1), )) @@ -64,8 +66,6 @@ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Communication Blackout", /datum/event/communications_blackout, 500, list(ASSIGNMENT_AI = 150, ASSIGNMENT_SECURITY = 120), 1), // Just blows out a few lights new /datum/event_meta(EVENT_LEVEL_MODERATE, "Electrical Storm", /datum/event/electrical_storm, 250, list(ASSIGNMENT_ENGINEER = 20, ASSIGNMENT_JANITOR = 150), 1), - // This one is just too fun. -//YW EDIT new /datum/event_meta(EVENT_LEVEL_MODERATE, "Gravity Failure", /datum/event/gravity, 75, list(ASSIGNMENT_ENGINEER = 60), 1), // Temporary power failure, but mitigatead by subgrids new /datum/event_meta(EVENT_LEVEL_MODERATE, "Grid Check", /datum/event/grid_check, 0, list(ASSIGNMENT_SCIENTIST = 10, ASSIGNMENT_ENGINEER = 25), 1), // Pure RP fun, no mechanical effects. @@ -74,8 +74,6 @@ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Prison Break", /datum/event/prison_break, 10, list(ASSIGNMENT_SECURITY = 100), 1), // Radiation, but only in space. new /datum/event_meta(EVENT_LEVEL_MODERATE, "Rogue Drones", /datum/event/rogue_drone, 0, list(ASSIGNMENT_SECURITY = 20), 1), -//YW EDIT new /datum/event_meta(EVENT_LEVEL_MODERATE, "Solar Storm", /datum/event/solar_storm, 30, list(ASSIGNMENT_ENGINEER = 40, ASSIGNMENT_SECURITY = 30), 1), -//YW EDIT new /datum/event_meta(EVENT_LEVEL_MODERATE, "Space Dust", /datum/event/dust, 0, list(ASSIGNMENT_ENGINEER = 20), 1, 0, 50), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Virology Breach", /datum/event/prison_break/virology, 0, list(ASSIGNMENT_MEDICAL = 100), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Xenobiology Breach", /datum/event/prison_break/xenobiology, 0, list(ASSIGNMENT_SCIENCE = 100), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Spider Infestation", /datum/event/spider_infestation, 30, list(ASSIGNMENT_SECURITY = 30), 1), @@ -83,12 +81,18 @@ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Grub Infestation", /datum/event/grub_infestation, 0, list(ASSIGNMENT_SECURITY = 10, ASSIGNMENT_ENGINEER = 30), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Drone Pod Drop", /datum/event/drone_pod_drop, 10, list(ASSIGNMENT_SCIENTIST = 40), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Radiation Storm", /datum/event/radiation_storm, 50, list(ASSIGNMENT_MEDICAL = 50), 1), //YW EDIT, Readded + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Maintenance Predator", /datum/event/maintenance_predator, 75, list(ASSIGNMENT_SECURITY = 25, ASSIGNMENT_SCIENTIST = 10), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Appendicitis", /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 30), 1), //YW EDIT, Readded + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/morph_spawn, 75, list(ASSIGNMENT_SECURITY = 35), 1) ) add_disabled_events(list( new /datum/event_meta(EVENT_LEVEL_MODERATE, "Meteor Shower", /datum/event/meteor_wave, 30, list(ASSIGNMENT_ENGINEER = 20)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Random Antagonist", /datum/event/random_antag, 2.5, list(ASSIGNMENT_SECURITY = 1), 1, 0, 5), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Carp School", /datum/event/carp_migration, 0, list(ASSIGNMENT_SECURITY = 30), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Supply Demand", /datum/event/supply_demand, 0, list(ASSIGNMENT_ANY = 5, ASSIGNMENT_SCIENCE = 15, ASSIGNMENT_GARDENER = 10, ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_MEDICAL = 15), 1), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Solar Storm", /datum/event/solar_storm, 30, list(ASSIGNMENT_ENGINEER = 40, ASSIGNMENT_SECURITY = 30), 1), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Space Dust", /datum/event/dust, 0, list(ASSIGNMENT_ENGINEER = 20), 1, 0, 50), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Gravity Failure", /datum/event/gravity, 75, list(ASSIGNMENT_ENGINEER = 60), 1) )) /datum/event_container/major/New() @@ -104,6 +108,7 @@ ) add_disabled_events(list( new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 30, list(ASSIGNMENT_ENGINEER = 15), 1), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 10, list(ASSIGNMENT_SECURITY = 5), 1), new /datum/event_meta(EVENT_LEVEL_MAJOR, "Supply Demand", /datum/event/supply_demand, 0, list(ASSIGNMENT_ANY = 5, ASSIGNMENT_SCIENCE = 15, ASSIGNMENT_GARDENER = 10, ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_MEDICAL = 15), 1), )) diff --git a/code/modules/events/event_dynamic.dm b/code/modules/events/event_dynamic.dm index 260e690924..e391637f4f 100644 --- a/code/modules/events/event_dynamic.dm +++ b/code/modules/events/event_dynamic.dm @@ -52,7 +52,7 @@ var/list/event_last_fired = list() possibleEvents[/datum/event/pda_spam] = max(min(25, player_list.len) * 4, 200) possibleEvents[/datum/event/money_lotto] = max(min(5, player_list.len), 50) - if(account_hack_attempted) + if(GLOB.account_hack_attempted) possibleEvents[/datum/event/money_hacker] = max(min(25, player_list.len) * 4, 200) @@ -206,16 +206,16 @@ var/list/event_last_fired = list() else if(istype(R.module, /obj/item/weapon/robot_module/robot/research)) active_with_role["Scientist"]++ - if(M.mind.assigned_role in engineering_positions) + if(M.mind.assigned_role in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING)) active_with_role["Engineer"]++ - if(M.mind.assigned_role in medical_positions) + if(M.mind.assigned_role in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL)) active_with_role["Medical"]++ - if(M.mind.assigned_role in security_positions) + if(M.mind.assigned_role in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY)) active_with_role["Security"]++ - if(M.mind.assigned_role in science_positions) + if(M.mind.assigned_role in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH)) active_with_role["Scientist"]++ if(M.mind.assigned_role == "AI") diff --git a/code/modules/events/gravity.dm b/code/modules/events/gravity.dm index 4d0881294f..aa13f9f7ac 100644 --- a/code/modules/events/gravity.dm +++ b/code/modules/events/gravity.dm @@ -15,16 +15,16 @@ /datum/event/gravity/start() gravity_is_on = 0 - for(var/area/A in all_areas) + for(var/area/A in world) if(A.z in zLevels) - A.gravitychange(gravity_is_on, A) + A.gravitychange(gravity_is_on) /datum/event/gravity/end() if(!gravity_is_on) gravity_is_on = 1 - for(var/area/A in all_areas) + for(var/area/A in world) if(A.z in zLevels) - A.gravitychange(gravity_is_on, A) + A.gravitychange(gravity_is_on) command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index c50b44dc26..5ac9dfda17 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -1,20 +1,55 @@ //This file was auto-corrected by findeclaration.exe on 29/05/2012 15:03:04 /datum/event/ionstorm + has_skybox_image = TRUE + announceWhen = -1 // Never (setup may override) var/botEmagChance = 0 //VOREStation Edit + var/cloud_hueshift var/list/players = list() -/datum/event/ionstorm/announce() +/datum/event/ionstorm/get_skybox_image() + if(!cloud_hueshift) + cloud_hueshift = color_rotation(rand(-3, 3) * 15) + var/image/res = image('icons/skybox/ionbox.dmi', "ions") + res.color = cloud_hueshift + res.appearance_flags = RESET_COLOR + res.blend_mode = BLEND_ADD + return res + +/datum/event/ionstorm/setup() endWhen = rand(500, 1500) -// command_alert("The station has entered an ion storm. Monitor all electronic equipment for malfunctions", "Anomaly Alert") + if(prob(50)) + announceWhen = endWhen + rand(250, 400) + +/datum/event/ionstorm/announce() + command_announcement.Announce("It has come to our attention that \the [location_name()] passed through an ion storm. Please monitor all electronic equipment for malfunctions.", "Anomaly Alert") + +/datum/event/ionstorm/start() for (var/mob/living/carbon/human/player in player_list) if( !player.mind || player_is_antag(player.mind, only_offstation_roles = 1) || player.client.inactivity > MinutesToTicks(10)) continue players += player.real_name + // Flomph synthetics + for(var/mob/living/carbon/S in living_mob_list) + if (!S.isSynthetic()) + continue + if(!(S.z in affecting_z)) + continue + var/area/A = get_area(S) + if(!A || A.flags & RAD_SHIELDED) // Rad shielding will protect from ions too + continue + to_chat(S, "Your integrated sensors detect an ionospheric anomaly. Your systems will be impacted as you begin a partial restart.") + var/ionbug = rand(3, 9) + S.confused += ionbug + S.eye_blurry += (ionbug - 1) + + // Ionize silicon mobs for (var/mob/living/silicon/ai/target in silicon_mob_list) + if(!(target.z in affecting_z)) + continue var/law = target.generate_ion_law() - to_chat(target, "You have detected a change in your laws information:") + to_chat(target, "You have detected a change in your laws information:") to_chat(target, law) target.add_ion_law(law) target.show_laws() @@ -31,13 +66,15 @@ /datum/event/ionstorm/tick() if(botEmagChance) for(var/mob/living/bot/bot in mob_list) + if(!(bot.z in affecting_z)) + continue if(prob(botEmagChance)) bot.emag_act(1) -/datum/event/ionstorm/end() - spawn(rand(5000,8000)) - if(prob(50)) - ion_storm_announcement() + +// Overmap version +/datum/event/ionstorm/overmap/announce() + return /* /proc/IonStorm(botEmagChance = 10) @@ -75,74 +112,74 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is crew = "Any Human" switch(rand(1,14)) if(1) - M << "
    " - M << "THERE ARE [amount] [who2] ON THE STATION...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "THERE ARE [amount] [who2] ON THE STATION...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("THERE ARE [amount] [who2] ON THE STATION") if(2) - M << "
    " - M << "[what2] ARE DANGEROUS TO HUMANS...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "[what2] ARE DANGEROUS TO HUMANS...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("[what2] ARE DANGEROUS TO HUMANS") if(3) - M << "
    " - M << "THE ESCAPE-SHUTTLE CANNOT BE CALLED BECAUSE OF [who2] ON THE STATION...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "THE ESCAPE-SHUTTLE CANNOT BE CALLED BECAUSE OF [who2] ON THE STATION...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("THE ESCAPE SHUTTLE CANNOT BE CALLED BECAUSE OF [who2] ON THE STATION") if(4) - M << "
    " - M << "HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE") if(5) - M << "
    " - M << "THE COLONY DIRECTOR, HOP AND HOS ARE [who2]...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "THE COLONY DIRECTOR, HOP AND HOS ARE [who2]...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("THE COLONY DIRECTOR, HOP AND HOS ARE [who2]") if(6) - M << "
    " - M << "THE STATION IS BUILT FOR [who2]...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "THE STATION IS BUILT FOR [who2]...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("THE STATION IS BUILT FOR [who2]") if(7) - M << "
    " - M << "YOU ARE [amount] [who2]...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "YOU ARE [amount] [who2]...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("YOU ARE [amount] [who2]") if(8) - M << "
    " - M << "YOU MUST ALWAYS [aimust]...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "YOU MUST ALWAYS [aimust]...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("YOU MUST ALWAYS [aimust]") if(9) - M << "
    " - M << "[area] [area2] [amount] [what2]...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "[area] [area2] [amount] [what2]...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("[area] [area2] [amount] [what2]") if(10) - M << "
    " - M << "[crew] is [target]...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "[crew] is [target]...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("[crew] is [target]") if(11) - M << "
    " - M << "[define] IS A FORM OF HARM...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "[define] IS A FORM OF HARM...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("[define] IS A FORM OF HARM") if(12) - M << "
    " - M << "YOU REQUIRE [require] IN ORDER TO PROTECT HUMANS... LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "YOU REQUIRE [require] IN ORDER TO PROTECT HUMANS... LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("YOU REQUIRE [require] IN ORDER TO PROTECT HUMANS") if(13) - M << "
    " - M << "[crew] is [allergysev] to [allergy]...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "[crew] is [allergysev] to [allergy]...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("[crew] is [allergysev] to [allergy]") if(14) - M << "
    " - M << "THE STATION IS [who2pref] [who2]...LAWS UPDATED" - M << "
    " + to_chat(M, "
    ") + to_chat(M, "THE STATION IS [who2pref] [who2]...LAWS UPDATED") + to_chat(M, "
    ") M.add_ion_law("THE STATION IS [who2pref] [who2]") if(botEmagChance) @@ -158,39 +195,39 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is var/airlocknum = 0 var/firedoornum = 0 - world << "Ion Storm Main Started" + to_world("Ion Storm Main Started") spawn(0) - world << "Started processing APCs" + to_world("Started processing APCs") for (var/obj/machinery/power/apc/APC in machines) if(APC.z in station_levels) APC.ion_act() apcnum++ - world << "Finished processing APCs. Processed: [apcnum]" + to_world("Finished processing APCs. Processed: [apcnum]") spawn(0) - world << "Started processing SMES" + to_world("Started processing SMES") for (var/obj/machinery/power/smes/SMES in machines) if(SMES.z in station_levels) SMES.ion_act() smesnum++ - world << "Finished processing SMES. Processed: [smesnum]" + to_world("Finished processing SMES. Processed: [smesnum]") spawn(0) - world << "Started processing AIRLOCKS" + to_world("Started processing AIRLOCKS") for (var/obj/machinery/door/airlock/D in machines) if(D.z in station_levels) //if(length(D.req_access) > 0 && !(12 in D.req_access)) //not counting general access and maintenance airlocks airlocknum++ spawn(0) D.ion_act() - world << "Finished processing AIRLOCKS. Processed: [airlocknum]" + to_world("Finished processing AIRLOCKS. Processed: [airlocknum]") spawn(0) - world << "Started processing FIREDOORS" + to_world("Started processing FIREDOORS") for (var/obj/machinery/door/firedoor/D in machines) if(D.z in station_levels) firedoornum++; spawn(0) D.ion_act() - world << "Finished processing FIREDOORS. Processed: [firedoornum]" + to_world("Finished processing FIREDOORS. Processed: [firedoornum]") - world << "Ion Storm Main Done" + to_world("Ion Storm Main Done") */ diff --git a/code/modules/events/maintenance_predator_vr.dm b/code/modules/events/maintenance_predator_vr.dm new file mode 100644 index 0000000000..40c8a98600 --- /dev/null +++ b/code/modules/events/maintenance_predator_vr.dm @@ -0,0 +1,78 @@ +/datum/event/maintenance_predator + startWhen = 1 + announceWhen = 50 + endWhen = 100 + var/announceProb = 35 + var/list/possible_mobs = list("Space Bumblebee" = /mob/living/simple_mob/vore/bee, + "Voracious Lizard" = /mob/living/simple_mob/vore/aggressive/dino, + "Giant Frog" = /mob/living/simple_mob/vore/aggressive/frog, + "Giant Rat" = /mob/living/simple_mob/vore/aggressive/rat, + "Juvenile Solargrub" = /mob/living/simple_mob/vore/solargrub, + "Red Panda" = /mob/living/simple_mob/vore/redpanda, + "Fennec" = /mob/living/simple_mob/vore/fennec, + "Fennix" = /mob/living/simple_mob/vore/fennix, + "Jelly Blob" = /mob/living/simple_mob/animal/space/jelly, + "Wolf" = /mob/living/simple_mob/animal/wolf) + +/datum/event/maintenance_predator/start() + + var/obj/effect/landmark/spawnspot = null + var/list/possibleSpawnspots = list() + for(var/obj/effect/landmark/L in landmarks_list) + if(L.name == "maint_pred") + possibleSpawnspots += L + if(possibleSpawnspots.len) + spawnspot = pick(possibleSpawnspots) + else + kill() // To prevent fake announcements + return + + if(!spawnspot) + kill() // To prevent fake announcements + return + + var/datum/ghost_query/Q = new /datum/ghost_query/maints_pred() + var/list/winner = Q.query() + + if(winner.len) + var/mob/observer/dead/D = winner[1] + var/choice + var/randomize + var/finalized = "No" + + while(finalized == "No" && D.client) + choice = input(D,"What type of predator do you want to play as?") as null|anything in possible_mobs + if(!choice) + randomize = TRUE + break + + if(choice) + finalized = alert(D, "Are you sure you want to play as [choice]?","Confirmation","No","Yes") + + if(randomize) + choice = pick(possible_mobs) + + var/mobtype = possible_mobs[choice] + var/mob/living/simple_mob/newPred = new mobtype(get_turf(spawnspot)) + qdel(newPred.ai_holder) + newPred.ai_holder = null + newPred.movement_cooldown = 0 // Remove the needless artificial speed cap + if(D.mind) + D.mind.transfer_to(newPred) + to_chat(D, "You are [newPred], somehow having gotten aboard the station in search of food. \ + You are wary of environment around you, but you do feel rather peckish. Stick around dark, secluded places to avoid danger or, \ + if you are cute enough, try to make friends with this place's inhabitants.") + newPred.ckey = D.ckey + newPred.visible_message("[newPred] emerges from somewhere!") + else + kill() // To prevent fake announcements + return + + +/datum/event/maintenance_predator/announce() + if(prob(announceProb)) + command_announcement.Announce("Unexpected biosignature detected in the maintenance tunnels of [station_name()].", "Lifesign Alert") + +//YW Addition: Adding named landmark for events +/obj/effect/landmark/event_spawn/maintpred + name = "maint_pred" \ No newline at end of file diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm index b4f202722c..0e4ec62b74 100644 --- a/code/modules/events/meteors.dm +++ b/code/modules/events/meteors.dm @@ -1,46 +1,147 @@ /datum/event/meteor_wave - startWhen = 5 - endWhen = 7 - var/next_meteor = 6 + startWhen = 30 // About one minute early warning + endWhen = 60 // Adjusted automatically in tick() + has_skybox_image = TRUE + var/alarmWhen = 30 + var/next_meteor = 40 var/waves = 1 var/start_side + var/next_meteor_lower = 10 + var/next_meteor_upper = 20 + +/datum/event/meteor_wave/get_skybox_image() + var/image/res = image('icons/skybox/rockbox.dmi', "rockbox") + res.color = COLOR_ASTEROID_ROCK + res.appearance_flags = RESET_COLOR + return res /datum/event/meteor_wave/setup() - waves = 2 + rand(1, severity) //EVENT_LEVEL_MAJOR is 3-5 waves + waves = (2 + rand(1, severity)) * severity start_side = pick(cardinal) endWhen = worst_case_end() +/datum/event/meteor_wave/start() + affecting_z -= global.using_map.sealed_levels // Space levels only please! + ..() + /datum/event/meteor_wave/announce() - switch(severity) - if(EVENT_LEVEL_MAJOR) - command_announcement.Announce("Meteors have been detected on collision course with \the [station_name()].", "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') - else - command_announcement.Announce("\The [station_name()] is now in a meteor shower.", "Meteor Alert") + if(!victim) + switch(severity) + if(EVENT_LEVEL_MAJOR) + command_announcement.Announce("Meteors have been detected on collision course with \the [location_name()].", "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') + else + command_announcement.Announce("\The [location_name()] is now in a meteor shower.", "Meteor Alert") /datum/event/meteor_wave/tick() - if(waves && activeFor >= next_meteor) - var/pick_side = prob(80) ? start_side : (prob(50) ? turn(start_side, 90) : turn(start_side, -90)) + // Begin sending the alarm signals to shield diffusers so the field is already regenerated (if it exists) by the time actual meteors start flying around. + if(activeFor >= alarmWhen) + for(var/obj/machinery/shield_diffuser/SD in global.machines) + if(SD.z in affecting_z) + SD.meteor_alarm(10) - spawn() spawn_meteors(severity * rand(1,2), get_meteors(), pick_side) - next_meteor += rand(15, 30) / severity - waves-- - endWhen = worst_case_end() + if(waves && activeFor >= next_meteor) + send_wave() + +/datum/event/meteor_wave/proc/send_wave() + var/pick_side = prob(80) ? start_side : (prob(50) ? turn(start_side, 90) : turn(start_side, -90)) + + spawn() spawn_meteors(get_wave_size(), get_meteors(), pick_side, pick(affecting_z)) + next_meteor += rand(next_meteor_lower, next_meteor_upper) / severity + waves-- + endWhen = worst_case_end() + +/datum/event/meteor_wave/proc/get_wave_size() + return severity * rand(2, 3) /datum/event/meteor_wave/proc/worst_case_end() return activeFor + ((30 / severity) * waves) + 10 /datum/event/meteor_wave/end() - switch(severity) - if(EVENT_LEVEL_MAJOR) - command_announcement.Announce("\The [station_name()] has cleared the meteor storm.", "Meteor Alert") - else - command_announcement.Announce("\The [station_name()] has cleared the meteor shower", "Meteor Alert") + ..() + if(!victim) + switch(severity) + if(EVENT_LEVEL_MAJOR) + command_announcement.Announce("\The [location_name()] has cleared the meteor storm.", "Meteor Alert") + else + command_announcement.Announce("\The [location_name()] has cleared the meteor shower", "Meteor Alert") /datum/event/meteor_wave/proc/get_meteors() - if(EVENT_LEVEL_MAJOR) - if(prob(10)) - return meteors_catastrophic + switch(severity) + if(EVENT_LEVEL_MAJOR) + return meteors_major + if(EVENT_LEVEL_MODERATE) + return meteors_moderate else - return meteors_threatening - else - return meteors_normal + return meteors_minor + +/var/list/meteors_minor = list( + /obj/effect/meteor/medium = 80, + /obj/effect/meteor/dust = 30, + /obj/effect/meteor/irradiated = 30, + /obj/effect/meteor/big = 30, + /obj/effect/meteor/flaming = 10, + ///obj/effect/meteor/golden = 10, + ///obj/effect/meteor/silver = 10, +) + +/var/list/meteors_moderate = list( + /obj/effect/meteor/medium = 80, + /obj/effect/meteor/big = 30, + /obj/effect/meteor/dust = 30, + /obj/effect/meteor/irradiated = 30, + /obj/effect/meteor/flaming = 10, + ///obj/effect/meteor/golden = 10, + ///obj/effect/meteor/silver = 10, + /obj/effect/meteor/emp = 10, +) + +/var/list/meteors_major = list( + /obj/effect/meteor/medium = 80, + /obj/effect/meteor/big = 30, + /obj/effect/meteor/dust = 30, + /obj/effect/meteor/irradiated = 30, + /obj/effect/meteor/emp = 30, + /obj/effect/meteor/flaming = 10, + ///obj/effect/meteor/golden = 10, + ///obj/effect/meteor/silver = 10, + /obj/effect/meteor/tunguska = 1, +) + +// Overmap version +/datum/event/meteor_wave/overmap + next_meteor_lower = 5 + next_meteor_upper = 10 + next_meteor = 0 + alarmWhen = 0 + +/datum/event/meteor_wave/overmap/tick() + if(victim && !victim.is_still() && prob(90)) // Meteors mostly fly in your face + start_side = victim.fore_dir + else //Unless you're standing still + start_side = pick(GLOB.cardinal) + ..() + +/datum/event/meteor_wave/overmap/get_wave_size() + . = ..() + if(!victim) + return + var/skill = victim.get_helm_skill() + var/speed = victim.get_speed() + if(skill >= SKILL_PROF) + . = round(. * 0.5) + if(victim.is_still()) //Standing still means less shit flies your way + . = round(. * 0.1) + if(speed < SHIP_SPEED_SLOW) //Slow and steady + . = round(. * 0.5) + if(speed > SHIP_SPEED_FAST) //Sanic stahp + . *= 2 + + //Smol ship evasion + if(victim.vessel_size < SHIP_SIZE_LARGE && speed < SHIP_SPEED_FAST) + var/skill_needed = SKILL_PROF + if(speed < SHIP_SPEED_SLOW) + skill_needed = SKILL_ADEPT + if(victim.vessel_size < SHIP_SIZE_SMALL) + skill_needed = skill_needed - 1 + if(skill >= max(skill_needed, victim.skill_needed)) + . = round(. * 0.5) diff --git a/code/modules/events/money_hacker.dm b/code/modules/events/money_hacker.dm index 0e604b9956..025bcc6901 100644 --- a/code/modules/events/money_hacker.dm +++ b/code/modules/events/money_hacker.dm @@ -1,5 +1,7 @@ //var/global/account_hack_attempted = 0 +GLOBAL_VAR_INIT(account_hack_attempted, 0) + /datum/event/money_hacker var/datum/money_account/affected_account endWhen = 100 @@ -10,7 +12,7 @@ if(all_money_accounts.len) affected_account = pick(all_money_accounts) - account_hack_attempted = 1 + GLOB.account_hack_attempted = 1 else kill() diff --git a/code/modules/events/money_spam.dm b/code/modules/events/money_spam.dm index ced7c9a45f..8e4fabe4a2 100644 --- a/code/modules/events/money_spam.dm +++ b/code/modules/events/money_spam.dm @@ -115,7 +115,7 @@ if (!P.message_silent) playsound(P.loc, 'sound/machines/twobeep.ogg', 50, 1) for (var/mob/O in hearers(3, P.loc)) - if(!P.message_silent) O.show_message(text("\icon[P] *[P.ttone]*")) + if(!P.message_silent) O.show_message(text("[bicon(P)] *[P.ttone]*")) //Search for holder of the PDA. var/mob/living/L = null if(P.loc && isliving(P.loc)) @@ -125,4 +125,4 @@ L = get(P, /mob/living/silicon) if(L) - L << "\icon[P] Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)" + to_chat(L, "[bicon(P)] Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)") diff --git a/code/modules/events/morph_spawn_vr.dm b/code/modules/events/morph_spawn_vr.dm new file mode 100644 index 0000000000..04b3b63898 --- /dev/null +++ b/code/modules/events/morph_spawn_vr.dm @@ -0,0 +1,51 @@ +/datum/event/morph_spawn + startWhen = 1 + announceWhen = 20 + endWhen = 30 + var/announceProb = 50 + +/datum/event/morph_spawn/start() + + var/obj/effect/landmark/spawnspot = null + var/list/possibleSpawnspots = list() + for(var/obj/effect/landmark/newSpawnspot in landmarks_list) + if(newSpawnspot.name == "morphspawn") + possibleSpawnspots += newSpawnspot + if(possibleSpawnspots.len) + spawnspot = pick(possibleSpawnspots) + else + kill() // To prevent fake announcements + return + + if(!spawnspot) + kill() // To prevent fake announcements + return + + var/datum/ghost_query/Q = new /datum/ghost_query/morph() + var/list/winner = Q.query() + + if(winner.len) + var/mob/living/simple_mob/vore/hostile/morph/newMorph = new /mob/living/simple_mob/vore/hostile/morph(get_turf(spawnspot)) + var/mob/observer/dead/D = winner[1] + if(D.mind) + D.mind.transfer_to(newMorph) + to_chat(D, "You are a Morph, somehow having gotten aboard the station in your wandering. \ + You are wary of environment around you, but your primal hunger still calls for you to find prey. Seek a convincing disguise, \ + using your amorphous form to traverse vents to find and consume weak prey.") + to_chat(D, "You can use shift + click on objects to disguise yourself as them, but your strikes are nearly useless when you are disguised. \ + You can undisguise yourself by shift + clicking yourself, but disguise being switched, or turned on and off has a short cooldown. You can also ventcrawl, \ + by using alt + click on the vent or scrubber.") + newMorph.ckey = D.ckey + newMorph.visible_message("A morph appears to crawl out of somewhere.") + else + kill() // To prevent fake announcements + return + + +/datum/event/morph_spawn/announce() + if(prob(announceProb)) + command_announcement.Announce("Unknown entitity detected boarding [station_name()]. Exercise extra caution.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + +//YW Addition: Adding named landmark for events +/obj/effect/landmark/event_spawn/morphspawn + name = "morphspawn" \ No newline at end of file diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm index 57a80c8d32..f2675a0ede 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/prison_break.dm @@ -41,7 +41,7 @@ /datum/event/prison_break/start() - for(var/area/A in all_areas) + for(var/area/A in world) if(is_type_in_list(A,areaType) && !is_type_in_list(A,areaNotType)) areas += A @@ -51,10 +51,10 @@ for(var/obj/machinery/message_server/MS in machines) MS.send_rc_message("Engineering", my_department, rc_message, "", "", 2) for(var/mob/living/silicon/ai/A in player_list) - A << "Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department]." + to_chat(A, "Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department].") else - world.log << "ERROR: Could not initate grey-tide. Unable to find suitable containment area." + to_world_log("ERROR: Could not initate grey-tide. Unable to find suitable containment area.") kill() diff --git a/code/modules/events/shipping_error.dm b/code/modules/events/shipping_error.dm index 6b0924b6f7..659f2f7de8 100644 --- a/code/modules/events/shipping_error.dm +++ b/code/modules/events/shipping_error.dm @@ -1,6 +1,6 @@ /datum/event/shipping_error/start() var/datum/supply_order/O = new /datum/supply_order() - O.ordernum = supply_controller.ordernum - O.object = supply_controller.supply_pack[pick(supply_controller.supply_pack)] + O.ordernum = SSsupply.ordernum + O.object = SSsupply.supply_pack[pick(SSsupply.supply_pack)] O.ordered_by = random_name(pick(MALE,FEMALE), species = SPECIES_HUMAN) - supply_controller.shoppinglist += O \ No newline at end of file + SSsupply.shoppinglist += O \ No newline at end of file diff --git a/code/modules/events/supply_demand.dm b/code/modules/events/supply_demand_vr.dm similarity index 96% rename from code/modules/events/supply_demand.dm rename to code/modules/events/supply_demand_vr.dm index 2d38c1f491..6c59145fe5 100644 --- a/code/modules/events/supply_demand.dm +++ b/code/modules/events/supply_demand_vr.dm @@ -23,20 +23,20 @@ // Decide what items are requried! // We base this on what departmets are most active, excluding departments we don't have var/list/notHaveDeptList = metric.departments.Copy() - notHaveDeptList.Remove(list(ROLE_ENGINEERING, ROLE_MEDICAL, ROLE_RESEARCH, ROLE_CARGO, ROLE_CIVILIAN)) + notHaveDeptList.Remove(list(DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL, DEPARTMENT_RESEARCH, DEPARTMENT_CARGO, DEPARTMENT_CIVILIAN)) var/deptActivity = metric.assess_all_departments(severity * 2, notHaveDeptList) for(var/dept in deptActivity) switch(dept) - if(ROLE_ENGINEERING) + if(DEPARTMENT_ENGINEERING) choose_atmos_items(severity + 1) - if(ROLE_MEDICAL) + if(DEPARTMENT_MEDICAL) choose_chemistry_items(roll(severity, 2)) - if(ROLE_RESEARCH) // Would be nice to differentiate between research diciplines + if(DEPARTMENT_RESEARCH) // Would be nice to differentiate between research diciplines choose_research_items(roll(severity, 2)) choose_robotics_items(roll(1, severity)) - if(ROLE_CARGO) + if(DEPARTMENT_CARGO) choose_alloy_items(rand(1, severity)) - if(ROLE_CIVILIAN) // Would be nice to separate out chef/gardener/bartender + if(DEPARTMENT_CIVILIAN) // Would be nice to separate out chef/gardener/bartender choose_food_items(roll(severity, 2)) choose_bar_items(roll(severity, 2)) if(required_items.len == 0) @@ -74,7 +74,7 @@ // Check if the crew succeeded or failed! if(required_items.len == 0) // Success! - supply_controller.points += 100 * severity + SSsupply.points += 100 * severity var/msg = "Great work! With those items you delivered our inventory levels all match up. " msg += "[capitalize(pick(first_names_female))] from accounting will have nothing to complain about. " msg += "I think you'll find a little something in your supply account." diff --git a/code/modules/events/viral_infection.dm b/code/modules/events/viral_infection.dm index d24e774ed8..c1fc054a3a 100644 --- a/code/modules/events/viral_infection.dm +++ b/code/modules/events/viral_infection.dm @@ -1,4 +1,4 @@ -//var/global/list/event_viruses = list() // so that event viruses are kept around for admin logs, rather than being GCed +var/global/list/event_viruses = list() // so that event viruses are kept around for admin logs, rather than being GCed datum/event/viral_infection var/list/viruses = list() diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index cdd497c016..64a31b5bb2 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -244,7 +244,7 @@ proc/check_panel(mob/M) attackby(var/obj/item/weapon/P as obj, mob/user as mob) step_away(src,my_target,2) for(var/mob/M in oviewers(world.view,my_target)) - M << "[my_target] flails around wildly." + to_chat(M, "[my_target] flails around wildly.") my_target.show_message("[src] has been attacked by [my_target] ", 1) //Lazy. src.health -= P.force @@ -257,7 +257,7 @@ proc/check_panel(mob/M) step_away(src,my_target,2) if(prob(30)) for(var/mob/O in oviewers(world.view , my_target)) - O << "[my_target] stumbles around." + to_chat(O, "[my_target] stumbles around.") New() ..() diff --git a/code/modules/food/drinkingglass/drinkingglass.dm b/code/modules/food/drinkingglass/drinkingglass.dm index 604b85665f..6b49fcdb22 100644 --- a/code/modules/food/drinkingglass/drinkingglass.dm +++ b/code/modules/food/drinkingglass/drinkingglass.dm @@ -32,17 +32,17 @@ for(var/I in extras) if(istype(I, /obj/item/weapon/glass_extra)) - M << "There is \a [I] in \the [src]." + to_chat(M, "There is \a [I] in \the [src].") else if(istype(I, /obj/item/weapon/reagent_containers/food/snacks/fruit_slice)) - M << "There is \a [I] on the rim." + to_chat(M, "There is \a [I] on the rim.") else - M << "There is \a [I] somewhere on the glass. Somehow." + to_chat(M, "There is \a [I] somewhere on the glass. Somehow.") if(has_ice()) - M << "There is some ice floating in the drink." + to_chat(M, "There is some ice floating in the drink.") if(has_fizz()) - M << "It is fizzing slightly." + to_chat(M, "It is fizzing slightly.") /obj/item/weapon/reagent_containers/food/drinks/glass2/proc/has_ice() if(reagents.reagent_list.len > 0) @@ -74,9 +74,9 @@ update_icon() /obj/item/weapon/reagent_containers/food/drinks/glass2/proc/can_add_extra(obj/item/weapon/glass_extra/GE) - if(!("[base_icon]_[GE.glass_addition]left" in icon_states(DRINK_ICON_FILE))) + if(!("[base_icon]_[GE.glass_addition]left" in icon_states(icon))) //VOREStation Edit return 0 - if(!("[base_icon]_[GE.glass_addition]right" in icon_states(DRINK_ICON_FILE))) + if(!("[base_icon]_[GE.glass_addition]right" in icon_states(icon))) //VOREStation Edit return 0 return 1 @@ -106,20 +106,20 @@ over_liquid |= "[base_icon][amnt]_fizz" for(var/S in R.glass_special) - if("[base_icon]_[S]" in icon_states(DRINK_ICON_FILE)) + if("[base_icon]_[S]" in icon_states(icon)) //VOREStation Edit under_liquid |= "[base_icon]_[S]" - else if("[base_icon][amnt]_[S]" in icon_states(DRINK_ICON_FILE)) + else if("[base_icon][amnt]_[S]" in icon_states(icon)) //VOREStation Edit over_liquid |= "[base_icon][amnt]_[S]" for(var/k in under_liquid) - underlays += image(DRINK_ICON_FILE, src, k, -3) + underlays += image(icon, src, k, -3) //VOREStation Edit - var/image/filling = image(DRINK_ICON_FILE, src, "[base_icon][amnt][R.glass_icon]", -2) + var/image/filling = image(icon, src, "[base_icon][amnt][R.glass_icon]", -2) //VOREStation Edit filling.color = reagents.get_color() underlays += filling for(var/k in over_liquid) - underlays += image(DRINK_ICON_FILE, src, k, -1) + underlays += image(icon, src, k, -1) //VOREStation Edit else name = initial(name) desc = initial(desc) @@ -128,7 +128,7 @@ for(var/item in extras) if(istype(item, /obj/item/weapon/glass_extra)) var/obj/item/weapon/glass_extra/GE = item - var/image/I = image(DRINK_ICON_FILE, src, "[base_icon]_[GE.glass_addition][side]") + var/image/I = image(icon, src, "[base_icon]_[GE.glass_addition][side]") //VOREStation Edit if(GE.glass_color) I.color = GE.glass_color underlays += I @@ -154,7 +154,7 @@ if(standard_splash_mob(user, target)) return 1 if(reagents && reagents.total_volume) //They are on harm intent, aka wanting to spill it. - user << "You splash the solution onto [target]." + to_chat(user, "You splash the solution onto [target].") reagents.splash(target, reagents.total_volume) return 1 ..() diff --git a/code/modules/food/drinkingglass/extras.dm b/code/modules/food/drinkingglass/extras.dm index 284d4ecfa9..f8bfeccdab 100644 --- a/code/modules/food/drinkingglass/extras.dm +++ b/code/modules/food/drinkingglass/extras.dm @@ -7,13 +7,13 @@ extras += GE user.remove_from_mob(GE) GE.loc = src - user << "You add \the [GE] to \the [src]." + to_chat(user, "You add \the [GE] to \the [src].") update_icon() else - user << "There's no space to put \the [GE] on \the [src]!" + to_chat(user, "There's no space to put \the [GE] on \the [src]!") else if(istype(I, /obj/item/weapon/reagent_containers/food/snacks/fruit_slice)) if(!rim_pos) - user << "There's no space to put \the [I] on \the [src]!" + to_chat(user, "There's no space to put \the [I] on \the [src]!") return var/obj/item/weapon/reagent_containers/food/snacks/fruit_slice/FS = I extras += FS @@ -21,7 +21,7 @@ FS.pixel_x = 0 // Reset its pixel offsets so the icons work! FS.pixel_y = 0 FS.loc = src - user << "You add \the [FS] to \the [src]." + to_chat(user, "You add \the [FS] to \the [src].") update_icon() else return ..() @@ -31,7 +31,7 @@ return ..() if(!extras.len) - user << "There's nothing on the glass to remove!" + to_chat(user, "There's nothing on the glass to remove!") return var/choice = input(user, "What would you like to remove from the glass?") as null|anything in extras @@ -39,10 +39,10 @@ return if(user.put_in_active_hand(choice)) - user << "You remove \the [choice] from \the [src]." + to_chat(user, "You remove \the [choice] from \the [src].") extras -= choice else - user << "Something went wrong, please try again." + to_chat(user, "Something went wrong, please try again.") update_icon() diff --git a/code/modules/food/drinkingglass/glass_boxes.dm b/code/modules/food/drinkingglass/glass_boxes.dm index d534c10cad..7157e209c1 100644 --- a/code/modules/food/drinkingglass/glass_boxes.dm +++ b/code/modules/food/drinkingglass/glass_boxes.dm @@ -78,3 +78,11 @@ /obj/item/weapon/storage/box/glass_extras/sticks name = "box of drink sticks" extra_type = /obj/item/weapon/glass_extra/stick + +/obj/item/weapon/storage/box/glasses/coffeecup + name = "box of coffee cups" + glass_type = /obj/item/weapon/reagent_containers/food/drinks/cup + +/obj/item/weapon/storage/box/glasses/coffeemug + name = "box of coffee mugs" + glass_type = /obj/item/weapon/reagent_containers/food/drinks/britcup \ No newline at end of file diff --git a/code/modules/food/drinkingglass/metaglass.dm b/code/modules/food/drinkingglass/metaglass.dm index ec9397b115..31ad0b7330 100644 --- a/code/modules/food/drinkingglass/metaglass.dm +++ b/code/modules/food/drinkingglass/metaglass.dm @@ -9,6 +9,12 @@ matter = list("glass" = 500) icon = 'icons/obj/drinks.dmi' +/obj/item/weapon/reagent_containers/food/drinks/metaglass/metapint + name = "metamorphic pint glass" + desc = "This glass changes shape and form depending on the drink inside... fancy!" + icon_state = "pglass_empty" + volume = 60 + /obj/item/weapon/reagent_containers/food/drinks/metaglass/on_reagent_change() if (reagents.reagent_list.len > 0) var/datum/reagent/R = reagents.get_master_reagent() @@ -38,11 +44,17 @@ else price_tag = null else - icon_state = "glass_empty" - name = "metamorphic glass" - desc = "This glass changes shape and form depending on the drink inside... fancy!" - center_of_mass = list("x"=16, "y"=10) - return + if(type == /obj/item/weapon/reagent_containers/food/drinks/metaglass/metapint) + icon_state = "pglass_empty" + name = "metamorphic pint glass" + desc = "This glass changes shape and form depending on the drink inside... fancy!" + center_of_mass = list("x"=16, "y"=10) + else + icon_state = "glass_empty" + name = "metamorphic glass" + desc = "This glass changes shape and form depending on the drink inside... fancy!" + center_of_mass = list("x"=16, "y"=10) + return /* @@ -390,7 +402,7 @@ Drinks Data glass_icon_state = "gintonicglass" /datum/reagent/ethanol/goldschlager - glass_icon_state = "ginvodkaglass" + glass_icon_state = "goldschlagerglass" glass_center_of_mass = list("x"=16, "y"=12) /datum/reagent/ethanol/hippies_delight @@ -635,6 +647,10 @@ Drinks Data glass_icon_state = "appleade" glass_center_of_mass = list("x"=16, "y"=8) +/datum/reagent/drink/mintapplesparkle + glass_icon_state = "mintapplesparkle" + glass_center_of_mass = list("x"=16, "y"=8) + /datum/reagent/drink/soda/pineappleade glass_icon_state = "pineappleade" glass_center_of_mass = list("x"=16, "y"=8) @@ -645,4 +661,7 @@ Drinks Data /datum/reagent/drink/arnold_palmer glass_icon_state = "arnoldpalmer" - glass_center_of_mass = list("x"=16, "y"=8) \ No newline at end of file + glass_center_of_mass = list("x"=16, "y"=8) + +/datum/reagent/ethanol/mojito + glass_icon_state = "mojito" \ No newline at end of file diff --git a/code/modules/food/drinkingglass/serving_glasses.dm b/code/modules/food/drinkingglass/serving_glasses.dm new file mode 100644 index 0000000000..16c0c15cc9 --- /dev/null +++ b/code/modules/food/drinkingglass/serving_glasses.dm @@ -0,0 +1,20 @@ +/obj/item/weapon/reagent_containers/food/drinks/glass2/carafe + name = "glass carafe" + base_name = "carafe" + base_icon = "carafe" + icon = 'icons/obj/drinks_vr.dmi' + filling_states = list(10, 20, 30, 40, 50, 60, 70, 80, 90, 100) + volume = 120 + possible_transfer_amounts = list(5,15,30) + matter = list("glass" = 50) + +/obj/item/weapon/reagent_containers/food/drinks/glass2/pitcher + name = "plastic pitcher" + base_name = "pitcher" + base_icon = "pitcher" + icon = 'icons/obj/drinks_vr.dmi' + filling_states = list(15, 30, 50, 70, 85, 100) + volume = 120 + possible_transfer_amounts = list(5,15,30) + matter = list("plastic" = 50) + \ No newline at end of file diff --git a/code/modules/food/food.dm b/code/modules/food/food.dm index 1e91e48ae0..c33389ad15 100644 --- a/code/modules/food/food.dm +++ b/code/modules/food/food.dm @@ -8,8 +8,7 @@ possible_transfer_amounts = null volume = 50 //Sets the default container amount for all food items. var/filling_color = "#FFFFFF" //Used by sandwiches. - - var/list/center_of_mass = list() // Used for table placement + drop_sound = 'sound/items/drop/food.ogg' /obj/item/weapon/reagent_containers/food/Initialize() . = ..() diff --git a/code/modules/food/food/cans.dm b/code/modules/food/food/cans.dm index 1c0071e47e..66a25d8dad 100644 --- a/code/modules/food/food/cans.dm +++ b/code/modules/food/food/cans.dm @@ -2,6 +2,7 @@ volume = 40 //just over one and a half cups amount_per_transfer_from_this = 5 flags = 0 //starts closed + drop_sound = 'sound/items/drop/soda.ogg' //DRINKS @@ -19,7 +20,8 @@ name = "bottled water" desc = "Introduced to the vending machines by Skrellian request, this water comes straight from the Martian poles." icon_state = "waterbottle" - center_of_mass = list("x"=15, "y"=8) + center_of_mass = list("x"=16, "y"=8) + drop_sound = 'sound/items/drop/food.ogg' /obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle/Initialize() . = ..() @@ -29,7 +31,7 @@ name = "\improper Space Mountain Wind" desc = "Blows right through you like a space wind." icon_state = "space_mountain_wind" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/space_mountain_wind/Initialize() ..() @@ -39,7 +41,7 @@ name = "\improper Thirteen Loko" desc = "The CMO has advised crew members that consumption of Thirteen Loko may result in seizures, blindness, drunkeness, or even death. Please Drink Responsibly." icon_state = "thirteen_loko" - center_of_mass = list("x"=16, "y"=8) + center_of_mass = list("x"=16, "y"=10) /obj/item/weapon/reagent_containers/food/drinks/cans/thirteenloko/Initialize() . = ..() @@ -49,7 +51,7 @@ name = "\improper Dr. Gibb" desc = "A delicious mixture of 42 different flavors." icon_state = "dr_gibb" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/dr_gibb/Initialize() ..() @@ -59,7 +61,7 @@ name = "\improper Star-kist" desc = "The taste of a star in liquid form. And, a bit of tuna...?" icon_state = "starkist" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/starkist/Initialize() . = ..() @@ -69,7 +71,7 @@ name = "\improper Space-Up" desc = "Tastes like a hull breach in your mouth." icon_state = "space-up" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/space_up/Initialize() ..() @@ -79,7 +81,7 @@ name = "\improper Lemon-Lime" desc = "You wanted ORANGE. It gave you Lemon Lime." icon_state = "lemon-lime" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/lemon_lime/Initialize() ..() @@ -89,7 +91,7 @@ name = "\improper Vrisk Serket Iced Tea" desc = "That sweet, refreshing southern earthy flavor. That's where it's from, right? South Earth?" icon_state = "ice_tea_can" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea/Initialize() ..() @@ -99,7 +101,7 @@ name = "\improper Grapel Juice" desc = "500 pages of rules of how to appropriately enter into a combat with this juice!" icon_state = "purple_can" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice/Initialize() ..() @@ -109,7 +111,7 @@ name = "\improper T-Borg's Tonic Water" desc = "Quinine tastes funny, but at least it'll keep that Space Malaria away." icon_state = "tonic" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/tonic/Initialize() . = ..() @@ -119,7 +121,7 @@ name = "soda water" desc = "A can of soda water. Still water's more refreshing cousin." icon_state = "sodawater" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/sodawater/Initialize() . = ..() @@ -129,7 +131,7 @@ name = "\improper Classic Ginger Ale" desc = "For when you need to be more retro than NanoTrasen already pays you for." icon_state = "gingerale" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/drinks/cans/gingerale/Initialize() . = ..() diff --git a/code/modules/food/food/condiment.dm b/code/modules/food/food/condiment.dm index 522f79b89b..5ed00e00f6 100644 --- a/code/modules/food/food/condiment.dm +++ b/code/modules/food/food/condiment.dm @@ -33,15 +33,15 @@ if(istype(target, /obj/item/weapon/reagent_containers/food/snacks)) // These are not opencontainers but we can transfer to them if(!reagents || !reagents.total_volume) - user << "There is no condiment left in \the [src]." + to_chat(user, "There is no condiment left in \the [src].") return if(!target.reagents.get_free_space()) - user << "You can't add more condiment to \the [target]." + to_chat(user, "You can't add more condiment to \the [target].") return var/trans = reagents.trans_to_obj(target, amount_per_transfer_from_this) - user << "You add [trans] units of the condiment to \the [target]." + to_chat(user, "You add [trans] units of the condiment to \the [target].") else ..() @@ -49,7 +49,7 @@ playsound(user.loc, 'sound/items/drink.ogg', rand(10, 50), 1) /obj/item/weapon/reagent_containers/food/condiment/self_feed_message(var/mob/user) - user << "You swallow some of contents of \the [src]." + to_chat(user, "You swallow some of contents of \the [src].") /obj/item/weapon/reagent_containers/food/condiment/on_reagent_change() if(reagents.reagent_list.len > 0) @@ -83,12 +83,12 @@ name = "Salt Shaker" desc = "Salt. From space oceans, presumably." icon_state = "saltshaker" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=17, "y"=11) if("blackpepper") name = "Pepper Mill" desc = "Often used to flavor food or make people sneeze." icon_state = "peppermillsmall" - center_of_mass = list("x"=16, "y"=10) + center_of_mass = list("x"=17, "y"=11) if("cornoil") name = "Corn Oil" desc = "A delicious oil used in cooking. Made from corn." @@ -98,6 +98,21 @@ name = "Sugar" desc = "Tastey space sugar!" center_of_mass = list("x"=16, "y"=6) + if("peanutbutter") + name = "Peanut Butter" + desc = "A jar of smooth peanut butter." + icon_state = "peanutbutter" + center_of_mass = list("x"=16, "y"=6) + if("mayo") + name = "Mayonnaise" + desc = "A jar of mayonnaise!" + icon_state = "mayo" + center_of_mass = list("x"=16, "y"=6) + if("yeast") + name = "Yeast" + desc = "This is what you use to make bread fluffy." + icon_state = "yeast" + center_of_mass = list("x"=16, "y"=6) else name = "Misc Condiment Bottle" if (reagents.reagent_list.len==1) @@ -134,6 +149,9 @@ . = ..() reagents.add_reagent("capsaicin", 50) +/obj/item/weapon/reagent_containers/food/condiment/cornoil + name = "Corn Oil" + /obj/item/weapon/reagent_containers/food/condiment/cornoil/Initialize() . = ..() reagents.add_reagent("cornoil", 50) @@ -146,6 +164,13 @@ . = ..() reagents.add_reagent("soysauce", 50) +/obj/item/weapon/reagent_containers/food/condiment/yeast + name = "Yeast" + +/obj/item/weapon/reagent_containers/food/condiment/yeast/Initialize() + . = ..() + reagents.add_reagent("yeast", 50) + /obj/item/weapon/reagent_containers/food/condiment/small possible_transfer_amounts = list(1,20) amount_per_transfer_from_this = 1 @@ -159,6 +184,7 @@ name = "salt shaker" // a large one. desc = "Salt. From space oceans, presumably." icon_state = "saltshakersmall" + center_of_mass = list("x"=17, "y"=11) /obj/item/weapon/reagent_containers/food/condiment/small/saltshaker/Initialize() . = ..() @@ -168,6 +194,7 @@ name = "pepper mill" desc = "Often used to flavor food or make people sneeze." icon_state = "peppermillsmall" + center_of_mass = list("x"=17, "y"=11) /obj/item/weapon/reagent_containers/food/condiment/small/peppermill/Initialize() . = ..() @@ -379,6 +406,7 @@ desc = "A big bag of flour. Good for baking!" icon = 'icons/obj/food.dmi' icon_state = "flour" + center_of_mass = list("x"=16, "y"=8) /obj/item/weapon/reagent_containers/food/condiment/flour/on_reagent_change() return @@ -386,5 +414,4 @@ /obj/item/weapon/reagent_containers/food/condiment/flour/Initialize() . = ..() reagents.add_reagent("flour", 30) - src.pixel_x = rand(-10.0, 10) - src.pixel_y = rand(-10.0, 10) + randpixel_xy() \ No newline at end of file diff --git a/code/modules/food/food/drinks.dm b/code/modules/food/food/drinks.dm index e86539c427..d8023dbedc 100644 --- a/code/modules/food/food/drinks.dm +++ b/code/modules/food/food/drinks.dm @@ -5,6 +5,7 @@ name = "drink" desc = "yummy" icon = 'icons/obj/drinks.dmi' + drop_sound = 'sound/items/drop/bottle.ogg' icon_state = null flags = OPENCONTAINER amount_per_transfer_from_this = 5 @@ -25,7 +26,7 @@ /obj/item/weapon/reagent_containers/food/drinks/proc/open(mob/user) playsound(loc,"canopen", rand(10,50), 1) - user << "You open [src] with an audible pop!" + to_chat(user, "You open [src] with an audible pop!") flags |= OPENCONTAINER /obj/item/weapon/reagent_containers/food/drinks/attack(mob/M as mob, mob/user as mob, def_zone) @@ -48,24 +49,24 @@ /obj/item/weapon/reagent_containers/food/drinks/standard_feed_mob(var/mob/user, var/mob/target) if(!is_open_container()) - user << "You need to open [src]!" + to_chat(user, "You need to open [src]!") return 1 return ..() /obj/item/weapon/reagent_containers/food/drinks/standard_dispenser_refill(var/mob/user, var/obj/structure/reagent_dispensers/target) if(!is_open_container()) - user << "You need to open [src]!" + to_chat(user, "You need to open [src]!") return 1 return ..() /obj/item/weapon/reagent_containers/food/drinks/standard_pour_into(var/mob/user, var/atom/target) if(!is_open_container()) - user << "You need to open [src]!" + to_chat(user, "You need to open [src]!") return 1 return ..() /obj/item/weapon/reagent_containers/food/drinks/self_feed_message(var/mob/user) - user << "You swallow a gulp from \the [src]." + to_chat(user, "You swallow a gulp from \the [src].") /obj/item/weapon/reagent_containers/food/drinks/feed_sound(var/mob/user) playsound(user.loc, 'sound/items/drink.ogg', rand(10, 50), 1) @@ -74,15 +75,15 @@ if(!..(user, 1)) return if(!reagents || reagents.total_volume == 0) - user << "\The [src] is empty!" + to_chat(user, "\The [src] is empty!") else if (reagents.total_volume <= volume * 0.25) - user << "\The [src] is almost empty!" + to_chat(user, "\The [src] is almost empty!") else if (reagents.total_volume <= volume * 0.66) - user << "\The [src] is half full!" + to_chat(user, "\The [src] is half full!") else if (reagents.total_volume <= volume * 0.90) - user << "\The [src] is almost full!" + to_chat(user, "\The [src] is almost full!") else - user << "\The [src] is full!" + to_chat(user, "\The [src] is full!") //////////////////////////////////////////////////////////////////////////////// @@ -116,6 +117,7 @@ icon_state = "milk" item_state = "carton" center_of_mass = list("x"=16, "y"=9) + drop_sound = 'sound/items/drop/box.ogg' /obj/item/weapon/reagent_containers/food/drinks/milk/Initialize() . = ..() @@ -127,6 +129,8 @@ icon_state = "soymilk" item_state = "carton" center_of_mass = list("x"=16, "y"=9) + drop_sound = 'sound/items/drop/box.ogg' + /obj/item/weapon/reagent_containers/food/drinks/soymilk/Initialize() . = ..() reagents.add_reagent("soymilk", 50) @@ -138,6 +142,8 @@ icon_state = "mini-milk" item_state = "carton" center_of_mass = list("x"=16, "y"=9) + drop_sound = 'sound/items/drop/box.ogg' + /obj/item/weapon/reagent_containers/food/drinks/smallmilk/Initialize() . = ..() reagents.add_reagent("milk", 30) @@ -149,6 +155,8 @@ icon_state = "mini-milk_choco" item_state = "carton" center_of_mass = list("x"=16, "y"=9) + drop_sound = 'sound/items/drop/box.ogg' + /obj/item/weapon/reagent_containers/food/drinks/smallchocmilk/Initialize() . = ..() reagents.add_reagent("chocolate_milk", 30) @@ -158,6 +166,8 @@ desc = "Careful, the beverage you're about to enjoy is extremely hot." icon_state = "coffee" center_of_mass = list("x"=15, "y"=10) + drop_sound = 'sound/items/drop/box.ogg' + /obj/item/weapon/reagent_containers/food/drinks/coffee/Initialize() . = ..() reagents.add_reagent("coffee", 30) @@ -209,6 +219,7 @@ possible_transfer_amounts = null volume = 10 center_of_mass = list("x"=16, "y"=12) + drop_sound = 'sound/items/drop/paper.ogg' /obj/item/weapon/reagent_containers/food/drinks/sillycup/Initialize() . = ..() @@ -226,7 +237,7 @@ var/obj/structure/reagent_dispensers/water_cooler/W = over_object if(W.cupholder && W.cups < 10) W.cups++ - usr << "You put the [src] in the cup dispenser." + to_chat(usr, "You put the [src] in the cup dispenser.") qdel(src) W.update_icon() else diff --git a/code/modules/food/food/drinks/bottle.dm b/code/modules/food/food/drinks/bottle.dm index 0a56f946d5..28915dff7e 100644 --- a/code/modules/food/food/drinks/bottle.dm +++ b/code/modules/food/food/drinks/bottle.dm @@ -16,7 +16,9 @@ /obj/item/weapon/reagent_containers/food/drinks/bottle/Initialize() . = ..() - if(isGlass) unacidable = 1 + if(isGlass) + unacidable = 1 + drop_sound = 'sound/items/drop/bottle.ogg' /obj/item/weapon/reagent_containers/food/drinks/bottle/Destroy() if(rag) @@ -87,12 +89,12 @@ if(!choice) return if(!(choice.density && usr.Adjacent(choice))) - usr << "You must stay close to your target! You moved away from \the [choice]" + to_chat(usr, "You must stay close to your target! You moved away from \the [choice]") return usr.put_in_hands(src.smash(usr.loc, choice)) usr.visible_message("\The [usr] smashed \the [src] on \the [choice]!") - usr << "You smash \the [src] on \the [choice]!" + to_chat(usr, "You smash \the [src] on \the [choice]!") /obj/item/weapon/reagent_containers/food/drinks/bottle/attackby(obj/item/W, mob/user) if(!rag && istype(W, /obj/item/weapon/reagent_containers/glass/rag)) @@ -112,7 +114,7 @@ /obj/item/weapon/reagent_containers/food/drinks/bottle/proc/insert_rag(obj/item/weapon/reagent_containers/glass/rag/R, mob/user) if(!isGlass || rag) return if(user.unEquip(R)) - user << "You stuff [R] into [src]." + to_chat(user, "You stuff [R] into [src].") rag = R rag.forceMove(src) flags &= ~OPENCONTAINER diff --git a/code/modules/food/food/sandwich.dm b/code/modules/food/food/sandwich.dm index f4d9ac5fd0..13663af81a 100644 --- a/code/modules/food/food/sandwich.dm +++ b/code/modules/food/food/sandwich.dm @@ -23,16 +23,16 @@ sandwich_limit += 4 if(istype(W,/obj/item/weapon/material/shard)) - user << "You hide [W] in \the [src]." + to_chat(user, "You hide [W] in \the [src].") user.drop_item() W.loc = src update() return else if(istype(W,/obj/item/weapon/reagent_containers/food/snacks)) if(src.contents.len > sandwich_limit) - user << "If you put anything else on \the [src] it's going to collapse." + to_chat(user, "If you put anything else on \the [src] it's going to collapse.") return - user << "You layer [W] over \the [src]." + to_chat(user, "You layer [W] over \the [src].") var/obj/item/weapon/reagent_containers/F = W F.reagents.trans_to_obj(src, F.reagents.total_volume) user.drop_item() @@ -81,7 +81,7 @@ /obj/item/weapon/reagent_containers/food/snacks/csandwich/examine(mob/user) ..(user) var/obj/item/O = pick(contents) - user << "You think you can see [O.name] in there." + to_chat(user, "You think you can see [O.name] in there.") /obj/item/weapon/reagent_containers/food/snacks/csandwich/attack(mob/M as mob, mob/user as mob, def_zone) @@ -96,6 +96,6 @@ H = M if(H && shard && M == user) //This needs a check for feeding the food to other people, but that could be abusable. - H << "You lacerate your mouth on a [shard.name] in the sandwich!" + to_chat(H, "You lacerate your mouth on a [shard.name] in the sandwich!") H.adjustBruteLoss(5) //TODO: Target head if human. //This TODO has been here for 4 years. ..() diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm index 393d81505e..ada6870827 100644 --- a/code/modules/food/food/snacks.dm +++ b/code/modules/food/food/snacks.dm @@ -525,6 +525,25 @@ src.name = "Frosted Jelly Donut" reagents.add_reagent("sprinkles", 2) +/obj/item/weapon/reagent_containers/food/snacks/donut/poisonberry + name = "Jelly Donut" + desc = "You jelly?" + icon_state = "jdonut1" + filling_color = "#ED1169" + center_of_mass = list("x"=16, "y"=11) + nutriment_amt = 3 + +/obj/item/weapon/reagent_containers/food/snacks/donut/poisonberry/Initialize() + . = ..() + reagents.add_reagent("sprinkles", 1) + reagents.add_reagent("poisonberryjuice", 5) + bitesize = 5 + if(prob(30)) + src.icon_state = "jdonut2" + src.overlay_state = "box-donut2" + src.name = "Frosted Jelly Donut" + reagents.add_reagent("sprinkles", 2) + /obj/item/weapon/reagent_containers/food/snacks/donut/slimejelly name = "Jelly Donut" desc = "You jelly?" @@ -580,7 +599,7 @@ return ..() if(!(proximity && O.is_open_container())) return - user << "You crack \the [src] into \the [O]." + to_chat(user, "You crack \the [src] into \the [O].") reagents.trans_to(O, reagents.total_volume) user.drop_from_inventory(src) qdel(src) @@ -598,10 +617,10 @@ var/clr = C.colourName if(!(clr in list("blue","green","mime","orange","purple","rainbow","red","yellow"))) - usr << "The egg refuses to take on this color!" + to_chat(usr, "The egg refuses to take on this color!") return - usr << "You color \the [src] [clr]" + to_chat(usr, "You color \the [src] [clr]") icon_state = "egg-[clr]" else ..() @@ -896,12 +915,12 @@ /obj/item/weapon/reagent_containers/food/snacks/donkpocket/sinpocket/attack_self(mob/user) if(has_been_heated) - user << "The heating chemicals have already been spent." + to_chat(user, "The heating chemicals have already been spent.") return has_been_heated = 1 user.visible_message("[user] crushes \the [src] package.", "You crush \the [src] package and feel a comfortable heat build up.") spawn(200) - user << "You think \the [src] is ready to eat about now." + to_chat(user, "You think \the [src] is ready to eat about now.") heat() /obj/item/weapon/reagent_containers/food/snacks/brainburger @@ -1120,9 +1139,16 @@ /obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/Initialize() . = ..() - reagents.add_reagent("berryjuice", 5) bitesize = 3 +/obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/berry/Initialize() + . = ..() + reagents.add_reagent("berryjuice", 5) + +/obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/poison/Initialize() + . = ..() + reagents.add_reagent("poisonberryjuice", 5) + /obj/item/weapon/reagent_containers/food/snacks/waffles name = "waffles" desc = "Mmm, waffles" @@ -1342,7 +1368,7 @@ /obj/item/weapon/reagent_containers/food/snacks/popcorn/On_Consume() if(prob(unpopped)) //lol ...what's the point? - usr << "You bite down on an un-popped kernel!" + to_chat(usr, "You bite down on an un-popped kernel!") unpopped = max(0, unpopped-1) ..() @@ -1644,7 +1670,7 @@ /obj/item/weapon/reagent_containers/food/snacks/slimesoup name = "slime soup" desc = "If no water is available, you may substitute tears." - icon_state = "slimesoup" //nonexistant? + icon_state = "rorosoup" //nonexistant? - 3/1/2020 FIXED. roro's live on. filling_color = "#C4DBA0" /obj/item/weapon/reagent_containers/food/snacks/slimesoup/Initialize() @@ -3264,6 +3290,7 @@ desc = "A box suited for pizzas." icon = 'icons/obj/food.dmi' icon_state = "pizzabox1" + center_of_mass = list("x" = 16,"y" = 6) var/open = 0 // Is the box open? var/ismessy = 0 // Fancy mess on the lid @@ -3435,6 +3462,10 @@ pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza(src) boxtag = "Meatlover's Supreme" +/obj/item/pizzabox/pineapple/Initialize() + pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/pineapple(src) + boxtag = "Hawaiian Sunrise" + /obj/item/pizzabox/old/Initialize() pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/oldpizza(src) boxtag = "Deluxe Gourmet" @@ -3475,7 +3506,7 @@ /obj/item/weapon/reagent_containers/food/snacks/dough/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W,/obj/item/weapon/material/kitchen/rollingpin)) new /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough(src) - user << "You flatten the dough." + to_chat(user, "You flatten the dough.") qdel(src) // slicable into 3xdoughslices @@ -3525,21 +3556,21 @@ // Bun + meatball = burger if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/meatball)) new /obj/item/weapon/reagent_containers/food/snacks/monkeyburger(src) - user << "You make a burger." + to_chat(user, "You make a burger.") qdel(W) qdel(src) // Bun + cutlet = hamburger else if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/cutlet)) new /obj/item/weapon/reagent_containers/food/snacks/monkeyburger(src) - user << "You make a burger." + to_chat(user, "You make a burger.") qdel(W) qdel(src) // Bun + sausage = hotdog else if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/sausage)) new /obj/item/weapon/reagent_containers/food/snacks/hotdog(src) - user << "You make a hotdog." + to_chat(user, "You make a hotdog.") qdel(W) qdel(src) @@ -3547,7 +3578,7 @@ /obj/item/weapon/reagent_containers/food/snacks/monkeyburger/attackby(obj/item/weapon/reagent_containers/food/snacks/cheesewedge/W as obj, mob/user as mob) if(istype(W))// && !istype(src,/obj/item/weapon/reagent_containers/food/snacks/cheesewedge)) new /obj/item/weapon/reagent_containers/food/snacks/cheeseburger(src) - user << "You make a cheeseburger." + to_chat(user, "You make a cheeseburger.") qdel(W) qdel(src) return @@ -3558,7 +3589,7 @@ /obj/item/weapon/reagent_containers/food/snacks/human/burger/attackby(obj/item/weapon/reagent_containers/food/snacks/cheesewedge/W as obj, mob/user as mob) if(istype(W)) new /obj/item/weapon/reagent_containers/food/snacks/cheeseburger(src) - user << "You make a cheeseburger." + to_chat(user, "You make a cheeseburger.") qdel(W) qdel(src) return @@ -3898,13 +3929,21 @@ filling_color = "#E0CF9B" center_of_mass = list("x"=17, "y"=4) nutriment_amt = 6 - nutriment_desc = list("sweetness" = 2, "muffin" = 2, "berries" = 2) + nutriment_desc = list("sweetness" = 2, "muffin" = 2) /obj/item/weapon/reagent_containers/food/snacks/berrymuffin/Initialize() . = ..() reagents.add_reagent("nutriment", 6) bitesize = 2 +/obj/item/weapon/reagent_containers/food/snacks/berrymuffin/berry/Initialize() + . = ..() + reagents.add_reagent("berryjuice", 3) + +/obj/item/weapon/reagent_containers/food/snacks/berrymuffin/poison/Initialize() + . = ..() + reagents.add_reagent("poisonberryjuice", 3) + /obj/item/weapon/reagent_containers/food/snacks/ghostmuffin name = "booberry muffin" desc = "My stomach is a graveyard! No living being can quench my bloodthirst!" @@ -3919,6 +3958,16 @@ reagents.add_reagent("nutriment", 6) bitesize = 2 +/obj/item/weapon/reagent_containers/food/snacks/ghostmuffin/berry/Initialize() + . = ..() + reagents.add_reagent("berryjuice", 3) + bitesize = 2 + +/obj/item/weapon/reagent_containers/food/snacks/ghostmuffin/poison/Initialize() + . = ..() + reagents.add_reagent("poisonberryjuice", 3) + bitesize = 2 + /obj/item/weapon/reagent_containers/food/snacks/eggroll name = "egg roll" desc = "Free with orders over 10 thalers." @@ -4288,11 +4337,10 @@ icon_state = "bagelplain" /obj/item/weapon/reagent_containers/food/snacks/bageltwo/Initialize() - . = ..() + ..() //Not returning . because asking to be qdel'd below. spawn_bagels() spawn_bagels() - sleep(30) - qdel(src) + return INITIALIZE_HINT_QDEL /obj/item/weapon/reagent_containers/food/snacks/bageltwo/proc/spawn_bagels() var/build_path = /obj/item/weapon/reagent_containers/food/snacks/bagelplain diff --git a/code/modules/food/food/snacks/meat.dm b/code/modules/food/food/snacks/meat.dm index 9d472b8c34..c2c5753eda 100644 --- a/code/modules/food/food/snacks/meat.dm +++ b/code/modules/food/food/snacks/meat.dm @@ -16,7 +16,7 @@ new /obj/item/weapon/reagent_containers/food/snacks/rawcutlet(src) new /obj/item/weapon/reagent_containers/food/snacks/rawcutlet(src) new /obj/item/weapon/reagent_containers/food/snacks/rawcutlet(src) - user << "You cut the meat into thin strips." + to_chat(user, "You cut the meat into thin strips.") qdel(src) else ..() diff --git a/code/modules/food/food/snacks_vr.dm b/code/modules/food/food/snacks_vr.dm index 65db2fe560..31d0134e7c 100644 --- a/code/modules/food/food/snacks_vr.dm +++ b/code/modules/food/food/snacks_vr.dm @@ -490,6 +490,7 @@ name = "wolpin cube" monkey_type = "Wolpin" +/* /obj/item/weapon/reagent_containers/food/snacks/pizza/margfrozen name = "frozen margherita pizza" desc = "It's frozen rock solid, better thaw it in a microwave." @@ -669,18 +670,7 @@ /obj/item/weapon/reagent_containers/food/snacks/slice/vegcargo/filled filled = TRUE - -/obj/item/pizzabox/margherita/Initialize() - pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margcargo(src) - -/obj/item/pizzabox/vegetable/Initialize() - pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegcargo(src) - -/obj/item/pizzabox/mushroom/Initialize() - pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushcargo(src) - -/obj/item/pizzabox/meat/Initialize() - pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatcargo(src) +*/ // food cubes /obj/item/weapon/reagent_containers/food/snacks/cube @@ -760,3 +750,14 @@ ) can_hold = list(/obj/item/weapon/reagent_containers/food/snacks/cube/protein, /obj/item/weapon/reagent_containers/food/snacks/cube/nutriment) + +/obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif //Making fish meat non-toxic! As advised by Ascian! + toxin_type = null + toxin_amount = null + +/obj/item/weapon/reagent_containers/food/snacks/carpmeat/sif/murkfish + toxin_type = null + +/obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish + toxin_type = null + toxin_amount = null diff --git a/code/modules/food/food/thecake.dm b/code/modules/food/food/thecake.dm index 5de17b36c6..6c5b1ad580 100644 --- a/code/modules/food/food/thecake.dm +++ b/code/modules/food/food/thecake.dm @@ -1,280 +1,279 @@ -// Chaos cake - -/datum/recipe/microwave/chaoscake_layerone - reagents = list("flour" = 300,"milk" = 200, "sugar" = 100, "egg" = 30) - fruit = list("poisonberries" = 15, "cherries" = 15) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/meat/, - /obj/item/weapon/reagent_containers/food/snacks/meat/, - /obj/item/weapon/reagent_containers/food/snacks/meat/, - /obj/item/weapon/reagent_containers/food/snacks/meat/ - ) - result = /obj/structure/chaoscake - -/datum/recipe/microwave/chaoscake_layertwo - reagents = list("flour" = 300, "milk" = 200, "sugar" = 100, "egg" = 30, ) - fruit = list("vanilla" = 15, "banana" = 15) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/chaoscake_layer - -/datum/recipe/microwave/chaoscake_layerthree - reagents = list("flour" = 240, "milk" = 150, "sugar" = 80, "egg" = 24, "deathbell" = 100) - fruit = list("grapes" = 30) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/chaoscake_layer/three - -/datum/recipe/microwave/chaoscake_layerfour - reagents = list("flour" = 240, "milk" = 150, "sugar" = 80, "egg" = 24, "milkshake" = 300) - fruit = list("rice" = 30) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/chaoscake_layer/four - -/datum/recipe/microwave/chaoscake_layerfive - reagents = list("flour" = 180, "milk" = 100, "sugar" = 60, "egg" = 18, "blood" = 300) - fruit = list("tomato" = 20) - items = list() //supposed to be made with lobster, still has to be ported. - result = /obj/item/weapon/chaoscake_layer/five - -/datum/recipe/microwave/chaoscake_layersix - reagents = list("flour" = 180, "milk" = 100, "sugar" = 60, "egg" = 18, "sprinkles" = 10) - fruit = list("apple" = 30) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/chocolatebar, - /obj/item/weapon/reagent_containers/food/snacks/chocolatebar, - /obj/item/weapon/reagent_containers/food/snacks/chocolatebar - ) - result = /obj/item/weapon/chaoscake_layer/six - -/datum/recipe/microwave/chaoscake_layerseven - reagents = list("flour" = 120, "milk" = 50, "sugar" = 40, "egg" = 12, "devilskiss" = 200) - fruit = list("potato" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/chaoscake_layer/seven - -/datum/recipe/microwave/chaoscake_layereight - reagents = list("flour" = 120, "milk" = 50, "sugar" = 40, "egg" = 12, "cream" = 200) - fruit = list("lemon" = 10) - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/dough - ) - result = /obj/item/weapon/chaoscake_layer/eight - -/datum/recipe/microwave/chaoscake_layernine - reagents = list("water" = 100, "blood" = 100) - fruit = list("goldapple" = 50) - items = list() - result = /obj/item/weapon/chaoscake_layer/nine - -/obj/structure/chaoscake - name = "An unfinished cake" - desc = "A single layer of a strange cake, you can see the cherry paste ooze, but it feels very incomplete..." - - icon = 'icons/obj/food64x64.dmi' - icon_state = "chaoscake_unfinished-1" - pixel_x = -16 - - var/slices = 6 - var/maxslices = 6 - var/stage = 1 - var/maxstages = 9 - var/edible = 0 - - var/regentime = 1000 - var/interval = 0 - - var/static/list/desclist2 = list( - "The first layer of a strange cake, you can see the cherry paste ooze.", - "The second layer of the cake sits in place now, smelling of pear with delicious colourful cream.", - "The third layer of cake adds a strange purple layer, glazed over with frosting. It smells of grapes, but with a hint of something foul underneath.", - "With the fourth layer added the cake looks happier again. Reeking of vanilla, it brings up memories of childhood joy.", - "The fifth layer is extremely disturbing on that cake. Smelling of pure copper, it seems that bright blood clots are forming on top.", - "The cake is getting closer with the sixth layer added, the pink hue smelling of chocolate, with colourful sprinkles on top.", - "The first pair of triplets rest on the cake, despite being mostly similar to the first three, an evil aura becomes noticable.", - "The second pair of triplets rest on the cake, if you stand on the bright side, you can feel a good aura lifting your mood.", - "A chaos cake. Both a creation of dark and light, the two cakes are kept in a careful balance by that mystical coin in the middle. It's said its effects would dissipate if the balance is ever tipped in favour of one side too much, so both sides much be cut equally." - ) - -/obj/item/weapon/chaoscake_layer - name = "A layer of cake" - desc = "a layer of cake, it is made out of colourful cream." - icon = 'icons/obj/food.dmi' - icon_state = "chaoscake_layer-2" - var/layer_stage = 1 - -/obj/item/weapon/chaoscake_layer/three - desc = "a layer of cake, glazed in purple." - icon_state = "chaoscake_layer-3" - layer_stage = 2 - -/obj/item/weapon/chaoscake_layer/four - desc = "a layer of cake, reminding you of a colouring book." - icon_state = "chaoscake_layer-4" - layer_stage = 3 - -/obj/item/weapon/chaoscake_layer/five - desc = "A layer of cake, smells like copper." - icon_state = "chaoscake_layer-5" - layer_stage = 4 - -/obj/item/weapon/chaoscake_layer/six - desc = "A layer of cake, featuring colourful sprinkles." - icon_state = "chaoscake_layer-6" - layer_stage = 5 - -/obj/item/weapon/chaoscake_layer/seven - desc = "A triplet of evil cake parts." - icon_state = "chaoscake_layer-7" - layer_stage = 6 - -/obj/item/weapon/chaoscake_layer/eight - desc = "A triplet of good cake parts." - icon_state = "chaoscake_layer-8" - layer_stage = 7 - -/obj/item/weapon/chaoscake_layer/nine - name = "A coin of balance" - desc = "A very peculiar coin, it seems to stabilise the air around it." - icon_state = "chaoscake_layer-9" - layer_stage = 8 - -/obj/structure/chaoscake/proc/HasSliceMissing() - ..() - if(slices < maxslices) - if(interval >= regentime) - interval = 0 - slices++ - HasSliceMissing() - else - interval++ - HasSliceMissing() - else - return - -/obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice - name = "The Chaos Cake Slice" - desc = "A slice from The Chaos Cake, it pulses weirdly, as if angry to be seperated from the whole" - icon_state = "chaoscake_slice-1" - - center_of_mass = list("x"=16, "y"=10) - nutriment_desc = list() - nutriment_amt = 4 - volume = 80 - -/obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice/Initialize() - ..() - var/i = rand(1,6) - icon_state = "chaoscake_slice-[i]" - switch(i) - if(1) - name = "Slice Of Evil" //Pretty damn poisonous, takes a lot of work to make safe for consumption, useful for medical. - desc = "An odd slice, despite the grease and cherries oozing off the top, it smells delicious." - nutriment_desc = list("The desire to consume" = 10) // You won't even taste the poison. - reagents.add_reagent("neurotoxic_protein", 2) - reagents.add_reagent("shockchem", 2) - reagents.add_reagent("amatoxin", 2) - reagents.add_reagent("carpotoxin", 2) - reagents.add_reagent("spidertoxin", 2) - bitesize = 7 - if(2) - name = "Slice Of Evil" //A bad trip - desc = "A mysterious slice, coated in purple frosting that smells like grapes." - nutriment_desc = list("The desire to show off an party" = 10) - reagents.add_reagent("stoxin", 2) - reagents.add_reagent("space_drugs", 10) - reagents.add_reagent("serotrotium", 4) - reagents.add_reagent("cryptobiolin", 8) - reagents.add_reagent("mindbreaker", 10) - reagents.add_reagent("psilocybin", 10) - bitesize = 30 //even a single bite won't make you escape fate. - if(3) - name = "Slice Of Evil" //acidic - desc = "A menacing slice, smelling clearly of copper, blood clots float on top." - nutriment_desc = list("Infernal Rage" = 10) - reagents.add_reagent("blood", 20) - reagents.add_reagent("stomacid", 10) - reagents.add_reagent("mutagen", 4) - reagents.add_reagent("thirteenloko", 20) - reagents.add_reagent("hyperzine", 10) - bitesize = 30 - if(4) - name = "Slice Of Good" //anti-tox - desc = "A colourful slice, smelling of pear and coated in delicious cream." - nutriment_desc = list("Hapiness" = 10) - reagents.add_reagent("anti_toxin", 2) - reagents.add_reagent("tricordrazine", 2) - bitesize = 3 - if(5) - name = "Slice Of Good" //anti-oxy - desc = "A light slice, it's pretty to look at and smells of vanilla." - nutriment_desc = list("Freedom" = 10) - reagents.add_reagent("dexalinp", 2) - reagents.add_reagent("tricordrazine", 2) - bitesize = 3 - if(6) - name = "Slice Of Good" //anti-burn/brute - desc = "A hearty slice, it smells of chocolate and strawberries." - nutriment_desc = list("Love" = 10) - reagents.add_reagent("bicaridine", 2) - reagents.add_reagent("tricordrazine", 2) - reagents.add_reagent("kelotane", 2) - bitesize = 4 - -/obj/structure/chaoscake/attackby(var/obj/item/weapon/W, var/mob/living/user) - if(istype(W,/obj/item/weapon/material/knife)) - if(edible == 1) - HasSliceMissing() - if(slices <= 0) - user << "The cake hums away quietly as the chaos powered goodness slowly recovers the large amount of lost mass, best to give it a moment before cutting another slice." - return - else - user << "You cut a slice of the cake. The slice looks like the cake was just baked, and you can see before your eyes as the spot where you cut the slice slowly regenerates!" - slices = slices - 1 - icon_state = "chaoscake-[slices]" - new /obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice(src.loc) - - else - to_chat(user, "It looks so good... But it feels so wrong to eat it before it's finished...") - return - if(istype(W,/obj/item/weapon/chaoscake_layer)) - var/obj/item/weapon/chaoscake_layer/C = W - if(C.layer_stage == 8) - user << "Finally! The coin on the top, the almighty chaos cake is complete!" - qdel(W) - stage++ - desc = desclist2[stage] - icon_state = "chaoscake-6" - edible = 1 - name = "The Chaos Cake!" - else if(stage == maxstages) - user << "The cake is already done!" - else if(stage == C.layer_stage) - user << "You add another layer to the cake, nice." - qdel(W) - stage++ - desc = desclist2[stage] - icon_state = "chaoscake_stage-[stage]" - else - user << "Hmm, doesnt seem like this layer is supposed to be added there?" - +// Chaos cake + +/datum/recipe/microwave/chaoscake_layerone + reagents = list("flour" = 300,"milk" = 200, "sugar" = 100, "egg" = 30) + fruit = list("poisonberries" = 15, "cherries" = 15) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/meat/, + /obj/item/weapon/reagent_containers/food/snacks/meat/, + /obj/item/weapon/reagent_containers/food/snacks/meat/, + /obj/item/weapon/reagent_containers/food/snacks/meat/ + ) + result = /obj/structure/chaoscake + +/datum/recipe/microwave/chaoscake_layertwo + reagents = list("flour" = 300, "milk" = 200, "sugar" = 100, "egg" = 30, ) + fruit = list("vanilla" = 15, "banana" = 15) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/chaoscake_layer + +/datum/recipe/microwave/chaoscake_layerthree + reagents = list("flour" = 240, "milk" = 150, "sugar" = 80, "egg" = 24, "deathbell" = 100) + fruit = list("grapes" = 30) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/chaoscake_layer/three + +/datum/recipe/microwave/chaoscake_layerfour + reagents = list("flour" = 240, "milk" = 150, "sugar" = 80, "egg" = 24, "milkshake" = 300) + fruit = list("rice" = 30) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/chaoscake_layer/four + +/datum/recipe/microwave/chaoscake_layerfive + reagents = list("flour" = 180, "milk" = 100, "sugar" = 60, "egg" = 18, "blood" = 300) + fruit = list("tomato" = 20) + items = list() //supposed to be made with lobster, still has to be ported. + result = /obj/item/weapon/chaoscake_layer/five + +/datum/recipe/microwave/chaoscake_layersix + reagents = list("flour" = 180, "milk" = 100, "sugar" = 60, "egg" = 18, "sprinkles" = 10) + fruit = list("apple" = 30) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar, + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar, + /obj/item/weapon/reagent_containers/food/snacks/chocolatebar + ) + result = /obj/item/weapon/chaoscake_layer/six + +/datum/recipe/microwave/chaoscake_layerseven + reagents = list("flour" = 120, "milk" = 50, "sugar" = 40, "egg" = 12, "devilskiss" = 200) + fruit = list("potato" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/chaoscake_layer/seven + +/datum/recipe/microwave/chaoscake_layereight + reagents = list("flour" = 120, "milk" = 50, "sugar" = 40, "egg" = 12, "cream" = 200) + fruit = list("lemon" = 10) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/chaoscake_layer/eight + +/datum/recipe/microwave/chaoscake_layernine + reagents = list("water" = 100, "blood" = 100) + fruit = list("goldapple" = 50) + items = list() + result = /obj/item/weapon/chaoscake_layer/nine + +/obj/structure/chaoscake + name = "An unfinished cake" + desc = "A single layer of a strange cake, you can see the cherry paste ooze, but it feels very incomplete..." + + icon = 'icons/obj/food64x64.dmi' + icon_state = "chaoscake_unfinished-1" + pixel_x = -16 + + var/slices = 6 + var/maxslices = 6 + var/stage = 1 + var/maxstages = 9 + var/edible = 0 + + var/regentime = 1000 + var/interval = 0 + + var/static/list/desclist2 = list( + "The first layer of a strange cake, you can see the cherry paste ooze.", + "The second layer of the cake sits in place now, smelling of pear with delicious colourful cream.", + "The third layer of cake adds a strange purple layer, glazed over with frosting. It smells of grapes, but with a hint of something foul underneath.", + "With the fourth layer added the cake looks happier again. Reeking of vanilla, it brings up memories of childhood joy.", + "The fifth layer is extremely disturbing on that cake. Smelling of pure copper, it seems that bright blood clots are forming on top.", + "The cake is getting closer with the sixth layer added, the pink hue smelling of chocolate, with colourful sprinkles on top.", + "The first pair of triplets rest on the cake, despite being mostly similar to the first three, an evil aura becomes noticable.", + "The second pair of triplets rest on the cake, if you stand on the bright side, you can feel a good aura lifting your mood.", + "A chaos cake. Both a creation of dark and light, the two cakes are kept in a careful balance by that mystical coin in the middle. It's said its effects would dissipate if the balance is ever tipped in favour of one side too much, so both sides much be cut equally." + ) + +/obj/item/weapon/chaoscake_layer + name = "A layer of cake" + desc = "a layer of cake, it is made out of colourful cream." + icon = 'icons/obj/food.dmi' + icon_state = "chaoscake_layer-2" + var/layer_stage = 1 + +/obj/item/weapon/chaoscake_layer/three + desc = "a layer of cake, glazed in purple." + icon_state = "chaoscake_layer-3" + layer_stage = 2 + +/obj/item/weapon/chaoscake_layer/four + desc = "a layer of cake, reminding you of a colouring book." + icon_state = "chaoscake_layer-4" + layer_stage = 3 + +/obj/item/weapon/chaoscake_layer/five + desc = "A layer of cake, smells like copper." + icon_state = "chaoscake_layer-5" + layer_stage = 4 + +/obj/item/weapon/chaoscake_layer/six + desc = "A layer of cake, featuring colourful sprinkles." + icon_state = "chaoscake_layer-6" + layer_stage = 5 + +/obj/item/weapon/chaoscake_layer/seven + desc = "A triplet of evil cake parts." + icon_state = "chaoscake_layer-7" + layer_stage = 6 + +/obj/item/weapon/chaoscake_layer/eight + desc = "A triplet of good cake parts." + icon_state = "chaoscake_layer-8" + layer_stage = 7 + +/obj/item/weapon/chaoscake_layer/nine + name = "A coin of balance" + desc = "A very peculiar coin, it seems to stabilise the air around it." + icon_state = "chaoscake_layer-9" + layer_stage = 8 + +/obj/structure/chaoscake/proc/HasSliceMissing() + ..() + if(slices < maxslices) + if(interval >= regentime) + interval = 0 + slices++ + HasSliceMissing() + else + interval++ + HasSliceMissing() + else + return + +/obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice + name = "The Chaos Cake Slice" + desc = "A slice from The Chaos Cake, it pulses weirdly, as if angry to be seperated from the whole" + icon_state = "chaoscake_slice-1" + + center_of_mass = list("x"=16, "y"=10) + nutriment_desc = list() + nutriment_amt = 4 + volume = 80 + +/obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice/Initialize() + ..() + var/i = rand(1,6) + icon_state = "chaoscake_slice-[i]" + switch(i) + if(1) + name = "Slice Of Evil" //Pretty damn poisonous, takes a lot of work to make safe for consumption, useful for medical. + desc = "An odd slice, despite the grease and cherries oozing off the top, it smells delicious." + nutriment_desc = list("The desire to consume" = 10) // You won't even taste the poison. + reagents.add_reagent("neurotoxic_protein", 2) + reagents.add_reagent("shockchem", 2) + reagents.add_reagent("amatoxin", 2) + reagents.add_reagent("carpotoxin", 2) + reagents.add_reagent("spidertoxin", 2) + bitesize = 7 + if(2) + name = "Slice Of Evil" //A bad trip + desc = "A mysterious slice, coated in purple frosting that smells like grapes." + nutriment_desc = list("The desire to show off an party" = 10) + reagents.add_reagent("stoxin", 2) + reagents.add_reagent("space_drugs", 10) + reagents.add_reagent("serotrotium", 4) + reagents.add_reagent("cryptobiolin", 8) + reagents.add_reagent("mindbreaker", 10) + reagents.add_reagent("psilocybin", 10) + bitesize = 30 //even a single bite won't make you escape fate. + if(3) + name = "Slice Of Evil" //acidic + desc = "A menacing slice, smelling clearly of copper, blood clots float on top." + nutriment_desc = list("Infernal Rage" = 10) + reagents.add_reagent("blood", 20) + reagents.add_reagent("stomacid", 10) + reagents.add_reagent("mutagen", 4) + reagents.add_reagent("thirteenloko", 20) + reagents.add_reagent("hyperzine", 10) + bitesize = 30 + if(4) + name = "Slice Of Good" //anti-tox + desc = "A colourful slice, smelling of pear and coated in delicious cream." + nutriment_desc = list("Hapiness" = 10) + reagents.add_reagent("anti_toxin", 2) + reagents.add_reagent("tricordrazine", 2) + bitesize = 3 + if(5) + name = "Slice Of Good" //anti-oxy + desc = "A light slice, it's pretty to look at and smells of vanilla." + nutriment_desc = list("Freedom" = 10) + reagents.add_reagent("dexalinp", 2) + reagents.add_reagent("tricordrazine", 2) + bitesize = 3 + if(6) + name = "Slice Of Good" //anti-burn/brute + desc = "A hearty slice, it smells of chocolate and strawberries." + nutriment_desc = list("Love" = 10) + reagents.add_reagent("bicaridine", 2) + reagents.add_reagent("tricordrazine", 2) + reagents.add_reagent("kelotane", 2) + bitesize = 4 + +/obj/structure/chaoscake/attackby(var/obj/item/weapon/W, var/mob/living/user) + if(istype(W,/obj/item/weapon/material/knife)) + if(edible == 1) + HasSliceMissing() + if(slices <= 0) + to_chat(user, "The cake hums away quietly as the chaos powered goodness slowly recovers the large amount of lost mass, best to give it a moment before cutting another slice.") + return + else + to_chat(user, "You cut a slice of the cake. The slice looks like the cake was just baked, and you can see before your eyes as the spot where you cut the slice slowly regenerates!") + slices = slices - 1 + icon_state = "chaoscake-[slices]" + new /obj/item/weapon/reagent_containers/food/snacks/chaoscakeslice(src.loc) + + else + to_chat(user, "It looks so good... But it feels so wrong to eat it before it's finished...") + return + if(istype(W,/obj/item/weapon/chaoscake_layer)) + var/obj/item/weapon/chaoscake_layer/C = W + if(C.layer_stage == 8) + to_chat(user, "Finally! The coin on the top, the almighty chaos cake is complete!") + qdel(W) + stage++ + desc = desclist2[stage] + icon_state = "chaoscake-6" + edible = 1 + name = "The Chaos Cake!" + else if(stage == maxstages) + to_chat(user, "The cake is already done!") + else if(stage == C.layer_stage) + to_chat(user, "You add another layer to the cake, nice.") + qdel(W) + stage++ + desc = desclist2[stage] + icon_state = "chaoscake_unfinished-[stage]" + else + to_chat(user, "Hmm, doesnt seem like this layer is supposed to be added there?") diff --git a/code/modules/food/food/z_custom_food_vr.dm b/code/modules/food/food/z_custom_food_vr.dm index b9ea0d2cef..16bff57f0d 100644 --- a/code/modules/food/food/z_custom_food_vr.dm +++ b/code/modules/food/food/z_custom_food_vr.dm @@ -44,7 +44,7 @@ var/global/ingredientLimit = 20000 to_chat(user, "As uniquely original as that idea is, you can't figure out how to perform it.") return /*if(!user.drop_item()) - user << "\The [I] is stuck to your hands!" + to_chat(user, "\The [I] is stuck to your hands!") return*/ user.drop_item() I.forceMove(src) diff --git a/code/modules/food/kitchen/cooking_machines/_cooker.dm b/code/modules/food/kitchen/cooking_machines/_cooker.dm index a5215a747e..008910c253 100644 --- a/code/modules/food/kitchen/cooking_machines/_cooker.dm +++ b/code/modules/food/kitchen/cooking_machines/_cooker.dm @@ -10,7 +10,7 @@ icon = 'icons/obj/cooking_machines.dmi' density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 var/on_icon // Icon state used when cooking. @@ -42,16 +42,16 @@ /obj/machinery/cooker/examine() ..() if(cooking_obj && Adjacent(usr)) - usr << "You can see \a [cooking_obj] inside." + to_chat(usr, "You can see \a [cooking_obj] inside.") /obj/machinery/cooker/attackby(var/obj/item/I, var/mob/user) if(!cook_type || (stat & (NOPOWER|BROKEN))) - user << "\The [src] is not working." + to_chat(user, "\The [src] is not working.") return if(cooking) - user << "\The [src] is running!" + to_chat(user, "\The [src] is running!") return if(default_unfasten_wrench(user, I, 20)) @@ -62,11 +62,11 @@ if(istype(G)) if(!can_cook_mobs) - user << "That's not going to fit." + to_chat(user, "That's not going to fit.") return if(!isliving(G.affecting)) - user << "You can't cook that." + to_chat(user, "You can't cook that.") return cook_mob(G.affecting, user) @@ -75,33 +75,33 @@ // We're trying to cook something else. Check if it's valid. var/obj/item/weapon/reagent_containers/food/snacks/check = I if(istype(check) && islist(check.cooked) && (cook_type in check.cooked)) - user << "\The [check] has already been [cook_type]." + to_chat(user, "\The [check] has already been [cook_type].") return 0 else if(istype(check, /obj/item/weapon/reagent_containers/glass)) - user << "That would probably break [src]." + to_chat(user, "That would probably break [src].") return 0 else if(istype(check, /obj/item/weapon/disk/nuclear)) - user << "Central Command would kill you if you [cook_type] that." + to_chat(user, "Central Command would kill you if you [cook_type] that.") return 0 else if(!istype(check) && !istype(check, /obj/item/weapon/holder) && !istype(check, /obj/item/organ)) //Gripper check has to go here, else it still just cuts it off. ~Mechoid // Is it a borg using a gripper? if(istype(check, /obj/item/weapon/gripper)) // Grippers. ~Mechoid. var/obj/item/weapon/gripper/B = check //B, for Borg. if(!B.wrapped) - user << "\The [B] is not holding anything." + to_chat(user, "\The [B] is not holding anything.") return 0 else var/B_held = B.wrapped - user << "You use \the [B] to put \the [B_held] into \the [src]." + to_chat(user, "You use \the [B] to put \the [B_held] into \the [src].") return 0 else - user << "That's not edible." + to_chat(user, "That's not edible.") return 0 if(istype(I, /obj/item/organ)) var/obj/item/organ/O = I if(O.robotic) - user << "That would probably break [src]." + to_chat(user, "That would probably break [src].") return // Gotta hurt. @@ -203,7 +203,7 @@ /obj/machinery/cooker/attack_hand(var/mob/user) if(cooking_obj && user.Adjacent(src)) //Fixes borgs being able to teleport food in these machines to themselves. - user << "You grab \the [cooking_obj] from \the [src]." + to_chat(user, "You grab \the [cooking_obj] from \the [src].") user.put_in_hands(cooking_obj) set_cooking(FALSE) cooking_obj = null @@ -213,7 +213,7 @@ if(output_options.len) if(cooking) - user << "\The [src] is in use!" + to_chat(user, "\The [src] is in use!") return var/choice = input("What specific food do you wish to make with \the [src]?") as null|anything in output_options+"Default" @@ -221,10 +221,10 @@ return if(choice == "Default") selected_option = null - user << "You decide not to make anything specific with \the [src]." + to_chat(user, "You decide not to make anything specific with \the [src].") else selected_option = choice - user << "You prepare \the [src] to make \a [selected_option]." + to_chat(user, "You prepare \the [src] to make \a [selected_option].") ..() diff --git a/code/modules/food/kitchen/cooking_machines/fryer.dm b/code/modules/food/kitchen/cooking_machines/fryer.dm index e0dc15cc45..e3753f0f48 100644 --- a/code/modules/food/kitchen/cooking_machines/fryer.dm +++ b/code/modules/food/kitchen/cooking_machines/fryer.dm @@ -42,7 +42,7 @@ return if(!victim || !victim.Adjacent(user)) - user << "Your victim slipped free!" + to_chat(user, "Your victim slipped free!") cooking = 0 icon_state = off_icon fry_loop.stop() @@ -71,10 +71,10 @@ victim.apply_damage(rand(30,40), BURN, user.zone_sel.selecting) if(!nopain) - victim << "Agony consumes you as searing hot oil scorches your [E ? E.name : "flesh"] horribly!" + to_chat(victim, "Agony consumes you as searing hot oil scorches your [E ? E.name : "flesh"] horribly!") victim.emote("scream") else - victim << "Searing hot oil scorches your [E ? E.name : "flesh"]!" + to_chat(victim, "Searing hot oil scorches your [E ? E.name : "flesh"]!") if(victim.client) add_attack_logs(user,victim,"[cook_type] in [src]") diff --git a/code/modules/food/kitchen/gibber.dm b/code/modules/food/kitchen/gibber.dm index db1c13ebde..e44c484c54 100644 --- a/code/modules/food/kitchen/gibber.dm +++ b/code/modules/food/kitchen/gibber.dm @@ -14,7 +14,7 @@ var/gib_time = 40 // Time from starting until meat appears var/gib_throw_dir = WEST // Direction to spit meat and gibs in. - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 500 @@ -22,21 +22,27 @@ /obj/machinery/gibber/autogibber var/turf/input_plate -/obj/machinery/gibber/autogibber/New() - ..() - spawn(5) - for(var/i in cardinal) - var/obj/machinery/mineral/input/input_obj = locate( /obj/machinery/mineral/input, get_step(src.loc, i) ) - if(input_obj) - if(isturf(input_obj.loc)) - input_plate = input_obj.loc - gib_throw_dir = i - qdel(input_obj) - break +/obj/machinery/gibber/autogibber/Initialize() + . = ..() + for(var/i in cardinal) + var/obj/machinery/mineral/input/input_obj = locate( /obj/machinery/mineral/input, get_step(src.loc, i) ) + if(input_obj) + if(isturf(input_obj.loc)) + input_plate = input_obj.loc + gib_throw_dir = i + qdel(input_obj) + break - if(!input_plate) - log_misc("a [src] didn't find an input plate.") - return + if(!input_plate) + log_misc("a [src] didn't find an input plate.") + +/obj/machinery/gibber/Destroy() + occupant = null + return ..() + +/obj/machinery/gibber/autogibber/Destroy() + input_plate = null + return ..() /obj/machinery/gibber/autogibber/Bumped(var/atom/A) if(!input_plate) return @@ -75,18 +81,18 @@ if(stat & (NOPOWER|BROKEN)) return if(operating) - user << "The gibber is locked and running, wait for it to finish." + to_chat(user, "The gibber is locked and running, wait for it to finish.") return else src.startgibbing(user) /obj/machinery/gibber/examine() ..() - usr << "The safety guard is [emagged ? "disabled" : "enabled"]." + to_chat(usr, "The safety guard is [emagged ? "disabled" : "enabled"].") /obj/machinery/gibber/emag_act(var/remaining_charges, var/mob/user) emagged = !emagged - user << "You [emagged ? "disable" : "enable"] the gibber safety guard." + to_chat(user, "You [emagged ? "disable" : "enable"] the gibber safety guard.") return 1 /obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) @@ -99,7 +105,7 @@ return ..() if(G.state < 2) - user << "You need a better grip to do that!" + to_chat(user, "You need a better grip to do that!") return move_into_gibber(user,G.affecting) @@ -113,24 +119,24 @@ /obj/machinery/gibber/proc/move_into_gibber(var/mob/user,var/mob/living/victim) if(src.occupant) - user << "The gibber is full, empty it first!" + to_chat(user, "The gibber is full, empty it first!") return if(operating) - user << "The gibber is locked and running, wait for it to finish." + to_chat(user, "The gibber is locked and running, wait for it to finish.") return if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_mob)) ) - user << "This is not suitable for the gibber!" + to_chat(user, "This is not suitable for the gibber!") return if(istype(victim,/mob/living/carbon/human) && !emagged) - user << "The gibber safety guard is engaged!" + to_chat(user, "The gibber safety guard is engaged!") return if(victim.abiotic(1)) - user << "Subject may not have abiotic items on." + to_chat(user, "Subject may not have abiotic items on.") return user.visible_message("[user] starts to put [victim] into the gibber!") diff --git a/code/modules/food/kitchen/icecream.dm b/code/modules/food/kitchen/icecream.dm index 821e118c01..2b53b657ce 100644 --- a/code/modules/food/kitchen/icecream.dm +++ b/code/modules/food/kitchen/icecream.dm @@ -14,7 +14,7 @@ icon_state = "icecream_vat" density = 1 anchored = 0 - use_power = 0 + use_power = USE_POWER_OFF flags = OPENCONTAINER | NOREACT var/list/product_types = list() @@ -92,7 +92,7 @@ var/obj/item/weapon/reagent_containers/food/snacks/icecream/I = O if(!I.ice_creamed) if(product_types[dispense_flavour] > 0) - src.visible_message("\icon[src] [user] scoops delicious [flavour_name] icecream into [I].") + src.visible_message("[bicon(src)] [user] scoops delicious [flavour_name] icecream into [I].") product_types[dispense_flavour] -= 1 I.add_ice_cream(flavour_name) // if(beaker) @@ -100,9 +100,9 @@ if(I.reagents.total_volume < 10) I.reagents.add_reagent("sugar", 10 - I.reagents.total_volume) else - user << "There is not enough icecream left!" + to_chat(user, "There is not enough icecream left!") else - user << "[O] already has icecream in it." + to_chat(user, "[O] already has icecream in it.") return 1 else if(O.is_open_container()) return @@ -125,7 +125,7 @@ else src.visible_message("[user] whips up some [flavour] icecream.") else - user << "You don't have the ingredients to make this." + to_chat(user, "You don't have the ingredients to make this.") /obj/machinery/icecream_vat/Topic(href, href_list) @@ -148,7 +148,7 @@ I.desc = "Delicious [cone_name] cone, but no ice cream." src.visible_message("[usr] dispenses a crunchy [cone_name] cone from [src].") else - usr << "There are no [cone_name] cones left!" + to_chat(usr, "There are no [cone_name] cones left!") if(href_list["make"]) var/amount = (text2num(href_list["amount"])) diff --git a/code/modules/food/kitchen/microwave.dm b/code/modules/food/kitchen/microwave.dm index 9898524add..b9beeed09b 100644 --- a/code/modules/food/kitchen/microwave.dm +++ b/code/modules/food/kitchen/microwave.dm @@ -5,9 +5,11 @@ icon_state = "mw" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 100 + clicksound = "button" + clickvol = "30" flags = OPENCONTAINER | NOREACT circuit = /obj/item/weapon/circuitboard/microwave var/operating = 0 // Is it on? @@ -236,7 +238,7 @@ Eject ingredients!
    \ "} - to_chat(user, browse("Microwave Controls[dat]", "window=microwave")) + user << browse("Microwave Controls[dat]", "window=microwave") onclose(user, "microwave") return @@ -351,7 +353,7 @@ if (src.reagents.total_volume) src.dirty++ src.reagents.clear_reagents() - usr << "You dispose of the microwave contents." + to_chat(usr, "You dispose of the microwave contents.") src.updateUsrDialog() /obj/machinery/microwave/proc/muck_start() @@ -414,12 +416,12 @@ /obj/machinery/microwave/advanced // specifically for complex recipes name = "deluxe microwave" - icon = 'icons/obj/kitchen.dmi' - icon_state = "mw-deluxe" + icon = 'icons/obj/deluxemicrowave.dmi' + icon_state = "mw" circuit = /obj/item/weapon/circuitboard/microwave/advanced circuit_item_capacity = 100 item_level = 1 /obj/machinery/microwave/advanced/Initialize() ..() - reagents.maximum_volume = 1000 + reagents.maximum_volume = 1000 diff --git a/code/modules/food/kitchen/smartfridge.dm b/code/modules/food/kitchen/smartfridge.dm index 0213ef089b..c798ed3875 100644 --- a/code/modules/food/kitchen/smartfridge.dm +++ b/code/modules/food/kitchen/smartfridge.dm @@ -2,19 +2,19 @@ */ /obj/machinery/smartfridge name = "\improper SmartFridge" + desc = "For storing all sorts of perishable foods!" icon = 'icons/obj/vending.dmi' - icon_state = "smartfridge" + icon_state = "fridge_food" + var/icon_base = "fridge_food" //Iconstate to base all the broken/deny/etc on + var/icon_contents = "food" //Overlay to put on glass to show contents density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 100 flags = NOREACT var/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. //VOREStation Edit - Non-global //var/global/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. - var/icon_on = "smartfridge" - var/icon_off = "smartfridge-off" - var/icon_panel = "smartfridge-panel" var/list/item_records = list() var/datum/stored_item/currently_vending = null //What we're putting out of the machine. var/seconds_electrified = 0; @@ -27,6 +27,9 @@ /obj/machinery/smartfridge/secure is_secure = 1 + icon_state = "fridge_sci" + icon_base = "fridge_sci" + icon_contents = "chem" /obj/machinery/smartfridge/New() ..() @@ -50,10 +53,7 @@ /obj/machinery/smartfridge/seeds name = "\improper MegaSeed Servitor" desc = "When you need seeds fast!" - icon = 'icons/obj/vending.dmi' - icon_state = "seeds" - icon_on = "seeds" - icon_off = "seeds-off" + icon_contents = "chem" /obj/machinery/smartfridge/seeds/accept_check(var/obj/item/O as obj) if(istype(O,/obj/item/seeds/)) @@ -63,6 +63,7 @@ /obj/machinery/smartfridge/secure/extract name = "\improper Biological Sample Storage" desc = "A refrigerated storage unit for xenobiological samples." + icon_contents = "slime" req_access = list(access_research) /obj/machinery/smartfridge/secure/extract/accept_check(var/obj/item/O as obj) @@ -72,12 +73,9 @@ return TRUE return FALSE - /obj/machinery/smartfridge/secure/medbay name = "\improper Refrigerated Medicine Storage" desc = "A refrigerated storage unit for storing medicine and chemicals." - icon_state = "smartfridge" //To fix the icon in the map editor. - icon_on = "smartfridge_chem" req_one_access = list(access_medical,access_chemistry) /obj/machinery/smartfridge/secure/medbay/accept_check(var/obj/item/O as obj) @@ -92,10 +90,8 @@ /obj/machinery/smartfridge/secure/virology name = "\improper Refrigerated Virus Storage" desc = "A refrigerated storage unit for storing viral material." + icon_contents = "drink" req_access = list(access_virology) - icon_state = "smartfridge_virology" - icon_on = "smartfridge_virology" - icon_off = "smartfridge_virology-off" /obj/machinery/smartfridge/secure/virology/accept_check(var/obj/item/O as obj) if(istype(O,/obj/item/weapon/reagent_containers/glass/beaker/vial/)) @@ -104,23 +100,26 @@ return 1 return 0 -/obj/machinery/smartfridge/chemistry +/obj/machinery/smartfridge/chemistry //Is this used anywhere? It's not secure. name = "\improper Smart Chemical Storage" desc = "A refrigerated storage unit for medicine and chemical storage." + icon_contents = "chem" /obj/machinery/smartfridge/chemistry/accept_check(var/obj/item/O as obj) if(istype(O,/obj/item/weapon/storage/pill_bottle) || istype(O,/obj/item/weapon/reagent_containers)) return 1 return 0 -/obj/machinery/smartfridge/chemistry/virology +/obj/machinery/smartfridge/chemistry/virology //Same name = "\improper Smart Virus Storage" desc = "A refrigerated storage unit for volatile sample storage." - /obj/machinery/smartfridge/drinks name = "\improper Drink Showcase" desc = "A refrigerated storage unit for tasty tasty alcohol." + icon_state = "fridge_drinks" + icon_base = "fridge_drinks" + icon_contents = "drink" /obj/machinery/smartfridge/drinks/accept_check(var/obj/item/O as obj) if(istype(O,/obj/item/weapon/reagent_containers/glass) || istype(O,/obj/item/weapon/reagent_containers/food/drinks) || istype(O,/obj/item/weapon/reagent_containers/food/condiment)) @@ -131,9 +130,7 @@ desc = "A machine for drying plants." wrenchable = 1 icon_state = "drying_rack" - icon_on = "drying_rack_on" - icon_off = "drying_rack" - icon_panel = "drying_rack-panel" + icon_base = "drying_rack" /obj/machinery/smartfridge/drying_rack/accept_check(var/obj/item/O as obj) if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/)) @@ -151,21 +148,22 @@ update_icon() /obj/machinery/smartfridge/drying_rack/update_icon() - overlays.Cut() var/not_working = stat & (BROKEN|NOPOWER) - if(not_working) - icon_state = icon_off - else - icon_state = icon_on var/hasItems for(var/datum/stored_item/I in item_records) if(I.get_amount()) hasItems = 1 break if(hasItems) - overlays += "drying_rack_filled" - if(!not_working) - overlays += "drying_rack_drying" + if(not_working) + icon_state = "[icon_base]-plant-off" + else + icon_state = "[icon_base]-plant" + else + if(not_working) + icon_state = "[icon_base]-off" + else + icon_state = "[icon_base]" /obj/machinery/smartfridge/drying_rack/proc/dry() for(var/datum/stored_item/I in item_records) @@ -199,10 +197,41 @@ update_icon() /obj/machinery/smartfridge/update_icon() + cut_overlays() if(stat & (BROKEN|NOPOWER)) - icon_state = icon_off + icon_state = "[icon_base]-off" else - icon_state = icon_on + icon_state = icon_base + + if(is_secure) + add_overlay("[icon_base]-sidepanel") + + if(panel_open) + add_overlay("[icon_base]-panel") + + var/is_off = "" + if(inoperable()) + is_off = "-off" + + // Fridge contents + if(contents) //VOREStation Edit - Some fridges do not have visible contents + switch(contents.len) + if(0) + add_overlay("empty[is_off]") + if(1 to 2) + add_overlay("[icon_contents]-1[is_off]") + if(3 to 5) + add_overlay("[icon_contents]-2[is_off]") + if(6 to 8) + add_overlay("[icon_contents]-3[is_off]") + else + add_overlay("[icon_contents]-4[is_off]") + + // Fridge top + var/image/top = image(icon, "[icon_base]-top") + top.pixel_z = 32 + top.layer = ABOVE_WINDOW_LAYER + add_overlay(top) /******************* * Item Adding @@ -213,9 +242,7 @@ panel_open = !panel_open user.visible_message("[user] [panel_open ? "opens" : "closes"] the maintenance panel of \the [src].", "You [panel_open ? "open" : "close"] the maintenance panel of \the [src].") playsound(src, O.usesound, 50, 1) - overlays.Cut() - if(panel_open) - overlays += image(icon, icon_panel) + update_icon() SSnanoui.update_uis(src) return diff --git a/code/modules/food/recipes_microwave.dm b/code/modules/food/recipes_microwave.dm index eca7a1ae32..0877869870 100644 --- a/code/modules/food/recipes_microwave.dm +++ b/code/modules/food/recipes_microwave.dm @@ -55,6 +55,13 @@ I said no! ) result = /obj/item/weapon/reagent_containers/food/snacks/donut/jelly +/datum/recipe/microwave/jellydonut/poisonberry + reagents = list("poisonberryjuice" = 5, "sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + result = /obj/item/weapon/reagent_containers/food/snacks/donut/poisonberry + /datum/recipe/microwave/jellydonut/slime reagents = list("slimejelly" = 5, "sugar" = 5) items = list( @@ -308,7 +315,14 @@ I said no! items = list( /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, ) - result = /obj/item/weapon/reagent_containers/food/snacks/berryclafoutis + result = /obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/berry + +/datum/recipe/microwave/poisonberryclafoutis + fruit = list("poisonberries" = 1) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + ) + result = /obj/item/weapon/reagent_containers/food/snacks/berryclafoutis/poison /datum/recipe/microwave/wingfangchu reagents = list("soysauce" = 5) @@ -597,7 +611,7 @@ I said no! result = /obj/item/weapon/reagent_containers/food/snacks/monkeysdelight /datum/recipe/microwave/baguette - reagents = list("sodiumchloride" = 1, "blackpepper" = 1) + reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "yeast" = 5) items = list( /obj/item/weapon/reagent_containers/food/snacks/dough, /obj/item/weapon/reagent_containers/food/snacks/dough, @@ -605,7 +619,7 @@ I said no! result = /obj/item/weapon/reagent_containers/food/snacks/baguette /datum/recipe/microwave/croissant - reagents = list("sodiumchloride" = 1, "water" = 5, "milk" = 5) + reagents = list("sodiumchloride" = 1, "water" = 5, "milk" = 5, "yeast" = 5) items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) result = /obj/item/weapon/reagent_containers/food/snacks/croissant @@ -617,10 +631,8 @@ I said no! result = /obj/item/weapon/reagent_containers/food/snacks/fishandchips /datum/recipe/microwave/bread - items = list( - /obj/item/weapon/reagent_containers/food/snacks/dough, - /obj/item/weapon/reagent_containers/food/snacks/egg - ) + reagents = list("yeast" = 5) + items = list(/obj/item/weapon/reagent_containers/food/snacks/dough) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bread /datum/recipe/microwave/sandwich @@ -639,7 +651,7 @@ I said no! result = /obj/item/weapon/reagent_containers/food/snacks/toastedsandwich /datum/recipe/microwave/peanutbutterjellysandwich - reagents = list("berryjuice" = 5, "peanutbutter" = 5) + reagents = list("cherryjelly" = 5, "peanutbutter" = 5) items = list( /obj/item/weapon/reagent_containers/food/snacks/slice/bread, /obj/item/weapon/reagent_containers/food/snacks/slice/bread @@ -1106,7 +1118,15 @@ I said no! /obj/item/weapon/reagent_containers/food/snacks/dough ) fruit = list("berries" = 1) - result = /obj/item/weapon/reagent_containers/food/snacks/berrymuffin + result = /obj/item/weapon/reagent_containers/food/snacks/berrymuffin/berry + +/datum/recipe/microwave/poisonberrymuffin + reagents = list("milk" = 5, "sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough + ) + fruit = list("poisonberries" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/berrymuffin/poison /datum/recipe/microwave/ghostmuffin reagents = list("milk" = 5, "sugar" = 5) @@ -1115,7 +1135,16 @@ I said no! /obj/item/weapon/ectoplasm ) fruit = list("berries" = 1) - result = /obj/item/weapon/reagent_containers/food/snacks/ghostmuffin + result = /obj/item/weapon/reagent_containers/food/snacks/ghostmuffin/berry + +/datum/recipe/microwave/poisonghostmuffin + reagents = list("milk" = 5, "sugar" = 5) + items = list( + /obj/item/weapon/reagent_containers/food/snacks/dough, + /obj/item/weapon/ectoplasm + ) + fruit = list("poisonberries" = 1) + result = /obj/item/weapon/reagent_containers/food/snacks/ghostmuffin/poison /datum/recipe/microwave/eggroll reagents = list("soysauce" = 10) @@ -1201,7 +1230,7 @@ I said no! /datum/recipe/microwave/piginblanket items = list( - /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, + /obj/item/weapon/reagent_containers/food/snacks/doughslice, /obj/item/weapon/reagent_containers/food/snacks/sausage ) result = /obj/item/weapon/reagent_containers/food/snacks/piginblanket diff --git a/code/modules/food/recipes_microwave_vr.dm b/code/modules/food/recipes_microwave_vr.dm index 14fe3a0b98..b4feb63488 100644 --- a/code/modules/food/recipes_microwave_vr.dm +++ b/code/modules/food/recipes_microwave_vr.dm @@ -196,6 +196,7 @@ ) result = /obj/item/weapon/reagent_containers/food/snacks/sharkmeatcubes +/* /datum/recipe/microwave/margheritapizzacargo reagents = list() items = list( @@ -223,10 +224,11 @@ /obj/item/weapon/reagent_containers/food/snacks/pizza/vegfrozen ) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegcargo +*/ //// food cubes /datum/recipe/microwave/foodcubes - reagents = list("enzyme" = 20,"virusfood" = 5, "nutriment" = 15, "protein" = 15) // labor intensive + reagents = list("enzyme" = 20, "virusfood" = 5, "nutriment" = 15, "protein" = 15) // labor intensive items = list() result = /obj/item/weapon/storage/box/wings/tray \ No newline at end of file diff --git a/code/modules/gamemaster/actions/action.dm b/code/modules/gamemaster/actions/action.dm deleted file mode 100644 index f8ea16994e..0000000000 --- a/code/modules/gamemaster/actions/action.dm +++ /dev/null @@ -1,27 +0,0 @@ -/datum/gm_action - var/name = "no name" // Simple name, for organization. - var/enabled = TRUE // If not enabled, this action is never taken. - var/departments = list() // What kinds of departments are affected by this action. Multiple departments can be listed. - var/chaotic = 0 // A number showing how chaotic the action may be. If danger is high, the GM will avoid it. - var/reusable = FALSE // If true, the event does not become disabled upon being used. Should be used sparingly. - var/observers_used = FALSE // Determines if the GM should check if ghosts are available before using this. - var/length = 0 // Determines how long the event lasts, until end() is called. - var/datum/game_master/gm = null - var/severity = 1 // The severity of the action. This is here to prevent continued future defining of this var on actions, un-used. - -/datum/gm_action/proc/set_up() - return - -/datum/gm_action/proc/get_weight() - return - -/datum/gm_action/proc/start() - if(!reusable) - enabled = FALSE - return - -/datum/gm_action/proc/end() - return - -/datum/gm_action/proc/announce() - return \ No newline at end of file diff --git a/code/modules/gamemaster/actions/atmos_leak.dm b/code/modules/gamemaster/actions/atmos_leak.dm deleted file mode 100644 index 34772bf1a8..0000000000 --- a/code/modules/gamemaster/actions/atmos_leak.dm +++ /dev/null @@ -1,77 +0,0 @@ -/datum/gm_action/atmos_leak - name = "atmospherics leak" - departments = list(ROLE_ENGINEERING, ROLE_SYNTHETIC) - var/area/target_area // Chosen target area - var/area/target_turf // Chosen target turf in target_area - var/gas_type // Chosen gas to release - // Exclude these types and sub-types from targeting eligibilty - var/list/area/excluded = list( - /area/submap, - /area/shuttle, - /area/crew_quarters, - /area/holodeck, - /area/engineering/engine_room - ) - - severity - -// Decide which area will be targeted! -/datum/gm_action/atmos_leak/set_up() - severity = pickweight(EVENT_LEVEL_MUNDANE = 8, - EVENT_LEVEL_MODERATE = 5, - EVENT_LEVEL_MAJOR = 3 - ) - - var/gas_choices = list("carbon_dioxide", "sleeping_agent") // Annoying - if(severity >= EVENT_LEVEL_MODERATE) - gas_choices += "phoron" // Dangerous - if(severity >= EVENT_LEVEL_MAJOR) - gas_choices += "volatile_fuel" // Dangerous and no default atmos setup! - gas_type = pick(gas_choices) - - var/list/area/grand_list_of_areas = get_station_areas(excluded) - - // Okay, now lets try and pick a target! Lets try 10 times, otherwise give up - for(var/i in 1 to 10) - var/area/A = pick(grand_list_of_areas) - if(is_area_occupied(A)) - log_debug("atmos_leak event: Rejected [A] because it is occupied.") - continue - // A good area, great! Lets try and pick a turf - var/list/turfs = list() - for(var/turf/simulated/floor/F in A) - if(turf_clear(F)) - turfs += F - if(turfs.len == 0) - log_debug("atmos_leak event: Rejected [A] because it has no clear turfs.") - continue - target_area = A - target_turf = pick(turfs) - - // If we can't find a good target, give up - if(!target_area) - log_debug("atmos_leak event: Giving up after too many failures to pick target area") - return - -/datum/gm_action/atmos_leak/announce() - if(target_area) - command_announcement.Announce("Warning, hazardous [gas_data.name[gas_type]] gas leak detected in \the [target_area], evacuate the area.", "Hazard Alert") - -/datum/gm_action/atmos_leak/start() - if(!target_turf) - return - ..() - spawn(rand(0, 600)) - // Okay, time to actually put the gas in the room! - // TODO - Would be nice to break a waste pipe perhaps? - // TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around - - // Fow now just add a bunch of it to the air - var/datum/gas_mixture/air_contents = new - air_contents.temperature = T20C + ((severity - 1) * rand(-50, 50)) - air_contents.gas[gas_type] = 10 * MOLES_CELLSTANDARD - target_turf.assume_air(air_contents) - playsound(target_turf, 'sound/effects/smoke.ogg', 50, 1) - -/datum/gm_action/atmos_leak/get_weight() - return 15 + (metric.count_people_in_department(ROLE_ENGINEERING) * 10 + metric.count_people_in_department(ROLE_SYNTHETIC) * 30) // Synthetics are counted in higher value because they can wirelessly connect to alarms. diff --git a/code/modules/gamemaster/actions/blob.dm b/code/modules/gamemaster/actions/blob.dm deleted file mode 100644 index 31a4518a69..0000000000 --- a/code/modules/gamemaster/actions/blob.dm +++ /dev/null @@ -1,74 +0,0 @@ -/datum/gm_action/blob - name = "blob infestation" - departments = list(ROLE_ENGINEERING, ROLE_SECURITY, ROLE_MEDICAL) - chaotic = 25 - - var/list/area/excluded = list( - /area/submap, - /area/shuttle, - /area/crew_quarters, - /area/holodeck, - /area/engineering/engine_room - ) - - var/area/target_area // Chosen target area - var/turf/target_turf // Chosen target turf in target_area - - var/obj/structure/blob/core/Blob - var/spawn_blob_type = /obj/structure/blob/core/random_medium - -/datum/gm_action/blob/set_up() - severity = pickweight(EVENT_LEVEL_MUNDANE = 4, - EVENT_LEVEL_MODERATE = 2, - EVENT_LEVEL_MAJOR = 1 - ) - - var/list/area/grand_list_of_areas = get_station_areas(excluded) - - for(var/i in 1 to 10) - var/area/A = pick(grand_list_of_areas) - if(is_area_occupied(A)) - log_debug("Blob infestation event: Rejected [A] because it is occupied.") - continue - var/list/turfs = list() - for(var/turf/simulated/floor/F in A) - if(turf_clear(F)) - turfs += F - if(turfs.len == 0) - log_debug("Blob infestation event: Rejected [A] because it has no clear turfs.") - continue - target_area = A - target_turf = pick(turfs) - - if(!target_area) - log_debug("Blob infestation event: Giving up after too many failures to pick target area") - -/datum/gm_action/blob/start() - ..() - var/turf/T - - if(severity == EVENT_LEVEL_MUNDANE || !target_area || !target_turf) - T = pick(blobstart) - else if(severity == EVENT_LEVEL_MODERATE) - T = target_turf - else - T = target_turf - spawn_blob_type = /obj/structure/blob/core/random_hard - - Blob = new spawn_blob_type(T) - -/datum/gm_action/blob/announce() - spawn(rand(600, 3000)) // 1-5 minute leeway for the blob to go un-detected. - command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') - -/datum/gm_action/blob/get_weight() - var/engineers = metric.count_people_in_department(ROLE_ENGINEERING) - var/security = metric.count_people_in_department(ROLE_SECURITY) - var/medical = metric.count_people_in_department(ROLE_MEDICAL) - - var/assigned_staff = engineers + security - if(engineers || security) // Medical only counts if one of the other two exists, and even then they count as half. - assigned_staff += round(medical / 2) - - var/weight = (max(assigned_staff - 2, 0) * 20) // An assigned staff count of 2 must be had to spawn a blob. - return weight diff --git a/code/modules/gamemaster/actions/brand_intelligence.dm b/code/modules/gamemaster/actions/brand_intelligence.dm deleted file mode 100644 index 156e74f3fa..0000000000 --- a/code/modules/gamemaster/actions/brand_intelligence.dm +++ /dev/null @@ -1,69 +0,0 @@ -/datum/gm_action/brand_intelligence - name = "rampant vending machines" - length = 30 MINUTES - departments = list(ROLE_ENGINEERING, ROLE_EVERYONE) - - var/list/obj/machinery/vending/vendingMachines = list() - var/list/obj/machinery/vending/infectedVendingMachines = list() - var/obj/machinery/vending/originMachine - var/start_time = 0 - - var/active = FALSE // Are we currently infecting? - -/datum/gm_action/brand_intelligence/announce() - if(prob(90)) - command_announcement.Announce("An ongoing mass upload of malware for vendors has been detected onboard [station_name()], which appears to transmit \ - to other nearby vendors. The original infected machine is believed to be \a [originMachine.name].", "Vendor Service Alert") - -/datum/gm_action/brand_intelligence/set_up() - vendingMachines.Cut() - infectedVendingMachines.Cut() - - for(var/obj/machinery/vending/V in machines) - if(isNotStationLevel(V.z)) continue - vendingMachines.Add(V) - - if(!vendingMachines.len) - length = 0 - return - - originMachine = pick(vendingMachines) - vendingMachines.Remove(originMachine) - originMachine.shut_up = 0 - originMachine.shoot_inventory = 1 - - start_time = world.time - active = TRUE - -/datum/gm_action/brand_intelligence/start() - ..() - while(originMachine || active) - if(!vendingMachines.len || !originMachine || originMachine.shut_up) //if every machine is infected, or if the original vending machine is missing or has it's voice switch flipped - end() - return - - if(ISMULTIPLE(world.time - start_time, 5)) - if(prob(15)) - var/obj/machinery/vending/infectedMachine = pick(vendingMachines) - vendingMachines.Remove(infectedMachine) - infectedVendingMachines.Add(infectedMachine) - infectedMachine.shut_up = 0 - infectedMachine.shoot_inventory = 1 - - if(ISMULTIPLE(world.time - start_time, 12)) - originMachine.speak(pick("Try our aggressive new marketing strategies!", \ - "You should buy products to feed your lifestyle obsession!", \ - "Consume!", \ - "Your money can buy happiness!", \ - "Engage direct marketing!", \ - "Advertising is legalized lying! But don't let that put you off our great deals!", \ - "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) - -/datum/gm_action/brand_intelligence/end() - active = FALSE - for(var/obj/machinery/vending/infectedMachine in infectedVendingMachines) - infectedMachine.shut_up = 1 - infectedMachine.shoot_inventory = 0 - -/datum/gm_action/brand_intelligence/get_weight() - return 60 + (metric.count_people_in_department(ROLE_ENGINEERING) * 20) diff --git a/code/modules/gamemaster/actions/camera_damage.dm b/code/modules/gamemaster/actions/camera_damage.dm deleted file mode 100644 index 583e57096b..0000000000 --- a/code/modules/gamemaster/actions/camera_damage.dm +++ /dev/null @@ -1,52 +0,0 @@ -/datum/gm_action/camera_damage - name = "random camera damage" - reusable = TRUE - departments = list(ROLE_SYNTHETIC, ROLE_ENGINEERING) - -/datum/gm_action/camera_damage/start() - var/obj/machinery/camera/C = acquire_random_camera() - if(!C) - return - ..() - - var/severity_range = 0 - severity = pickweight(EVENT_LEVEL_MUNDANE = 10, - EVENT_LEVEL_MODERATE = 5, - EVENT_LEVEL_MAJOR = 1 - ) - - switch(severity) - if(EVENT_LEVEL_MUNDANE) - severity_range = 0 - if(EVENT_LEVEL_MODERATE) - severity_range = 7 - if(EVENT_LEVEL_MAJOR) - severity_range = 15 - - for(var/obj/machinery/camera/cam in range(severity_range,C)) - if(is_valid_camera(cam)) - if(prob(2*severity)) - cam.destroy() - else - cam.wires.UpdateCut(CAMERA_WIRE_POWER, 0) - if(prob(5*severity)) - cam.wires.UpdateCut(CAMERA_WIRE_ALARM, 0) - -/datum/gm_action/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) - if(!cameranet.cameras.len) - return - if(!remaining_attempts) - return - - var/obj/machinery/camera/C = pick(cameranet.cameras) - if(is_valid_camera(C)) - return C - return acquire_random_camera(remaining_attempts--) - -/datum/gm_action/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) - // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access - var/turf/T = get_turf(C) - return T && C.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) - -/datum/gm_action/camera_damage/get_weight() - return 40 + (metric.count_people_in_department(ROLE_ENGINEERING) * 20) + (metric.count_people_in_department(ROLE_SYNTHETIC) * 40) diff --git a/code/modules/gamemaster/actions/carp_migration.dm b/code/modules/gamemaster/actions/carp_migration.dm deleted file mode 100644 index eabb2ce79a..0000000000 --- a/code/modules/gamemaster/actions/carp_migration.dm +++ /dev/null @@ -1,58 +0,0 @@ -//carp_migration -/datum/gm_action/carp_migration - name = "carp migration" - departments = list(ROLE_SECURITY, ROLE_EVERYONE) - chaotic = 50 - var/list/spawned_carp = list() - var/carp_amount = 0 - length = 20 MINUTES - -/datum/gm_action/carp_migration/get_weight() - return 50 + (metric.count_people_in_department(ROLE_SECURITY) * 10) + (metric.count_all_space_mobs() * 20) - -/datum/gm_action/carp_migration/announce() - var/announcement = "Unknown biological entities have been detected near [station_name()], please stand-by." - command_announcement.Announce(announcement, "Lifesign Alert") - -/datum/gm_action/carp_migration/set_up() - // Higher filled roles means more groups of fish. - var/station_strength = 0 - station_strength += (metric.count_people_in_department(ROLE_SECURITY) * 3) - station_strength += (metric.count_people_in_department(ROLE_ENGINEERING) * 2) - station_strength += metric.count_people_in_department(ROLE_MEDICAL) - - // Less active emergency response departments tones the event down. - var/activeness = ((metric.assess_department(ROLE_SECURITY) + metric.assess_department(ROLE_ENGINEERING) + metric.assess_department(ROLE_MEDICAL)) / 3) - activeness = max(activeness, 20) - - carp_amount = CEILING(station_strength * (activeness / 100) + 1, 1) - -/datum/gm_action/carp_migration/start() - ..() - var/list/spawn_locations = list() - - var/group_size_min = 3 - var/group_size_max = 5 - - for(var/obj/effect/landmark/C in landmarks_list) - if(C.name == "carpspawn") - spawn_locations.Add(C.loc) - - spawn_locations = shuffle(spawn_locations) - carp_amount = min(carp_amount, spawn_locations.len) - - var/i = 1 - while (i <= carp_amount) - var/group_size = rand(group_size_min, group_size_max) - for (var/j = 1, j <= group_size, j++) - spawned_carp.Add(new /mob/living/simple_mob/animal/space/carp/event(spawn_locations[i])) - i++ - message_admins("[spawned_carp.len] carp spawned by event.") - -/datum/gm_action/carp_migration/end() - for(var/mob/living/simple_mob/animal/space/carp/C in spawned_carp) - if(!C.stat) - var/turf/T = get_turf(C) - if(istype(T, /turf/space)) - if(!prob(25)) - qdel(C) \ No newline at end of file diff --git a/code/modules/gamemaster/actions/comms_blackout.dm b/code/modules/gamemaster/actions/comms_blackout.dm deleted file mode 100644 index 71b172b153..0000000000 --- a/code/modules/gamemaster/actions/comms_blackout.dm +++ /dev/null @@ -1,21 +0,0 @@ -/datum/gm_action/comms_blackout - name = "communications blackout" - departments = list(ROLE_ENGINEERING, ROLE_EVERYONE) - chaotic = 35 - -/datum/gm_action/comms_blackout/get_weight() - return 50 + (metric.count_people_in_department(ROLE_ENGINEERING) * 40) - -/datum/gm_action/comms_blackout/announce() - if(prob(33)) - command_announcement.Announce("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT", new_sound = 'sound/misc/interference.ogg') - // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms - for(var/mob/living/silicon/ai/A in player_list) - A << "
    " - A << "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT" - A << "
    " - -/datum/gm_action/comms_blackout/start() - ..() - for(var/obj/machinery/telecomms/T in telecomms_list) - T.emp_act(1) diff --git a/code/modules/gamemaster/actions/drill_announcement.dm b/code/modules/gamemaster/actions/drill_announcement.dm deleted file mode 100644 index 978d5a4ffd..0000000000 --- a/code/modules/gamemaster/actions/drill_announcement.dm +++ /dev/null @@ -1,10 +0,0 @@ -/datum/gm_action/security_drill - name = "security drills" - departments = list(ROLE_SECURITY, ROLE_EVERYONE) - -/datum/gm_action/security_drill/announce() - spawn(rand(1 MINUTE, 2 MINUTES)) - command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", "a nonstandard", "a bomb", "an emergent intelligence")] drill with the personnel onboard \the [station_name()].", "Security Advisement") - -/datum/gm_action/security_drill/get_weight() - return max(-20, 10 + gm.staleness - (gm.danger * 2)) + (metric.count_people_in_department(ROLE_SECURITY) * 5) + (metric.count_people_in_department(ROLE_EVERYONE) * 1.5) diff --git a/code/modules/gamemaster/actions/dust.dm b/code/modules/gamemaster/actions/dust.dm deleted file mode 100644 index da08dc4f4c..0000000000 --- a/code/modules/gamemaster/actions/dust.dm +++ /dev/null @@ -1,17 +0,0 @@ -/datum/gm_action/dust - name = "dust" - departments = list(ROLE_ENGINEERING) - chaotic = 10 - reusable = TRUE - -/datum/gm_action/dust/announce() - command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching your colony.", "Dust Alert") - -/datum/gm_action/dust/get_weight() - var/engineers = metric.count_people_in_department(ROLE_ENGINEERING) - var/weight = 30 + (engineers * 25) - return weight - -/datum/gm_action/dust/start() - ..() - dust_swarm("norm") diff --git a/code/modules/gamemaster/actions/electrical_storm.dm b/code/modules/gamemaster/actions/electrical_storm.dm deleted file mode 100644 index 008a345a44..0000000000 --- a/code/modules/gamemaster/actions/electrical_storm.dm +++ /dev/null @@ -1,33 +0,0 @@ -/datum/gm_action/electrical_storm - name = "electrical storm" - departments = list(ROLE_EVERYONE) - reusable = TRUE - var/lightsoutAmount = 1 - var/lightsoutRange = 25 - -/datum/gm_action/electrical_storm/announce() - command_announcement.Announce("An electrical issue has been detected in your area, please repair potential electronic overloads.", "Electrical Alert") - -/datum/gm_action/electrical_storm/start() - ..() - var/list/epicentreList = list() - - for(var/i=1, i <= lightsoutAmount, i++) - var/list/possibleEpicentres = list() - for(var/obj/effect/landmark/newEpicentre in landmarks_list) - if(newEpicentre.name == "lightsout" && !(newEpicentre in epicentreList)) - possibleEpicentres += newEpicentre - if(possibleEpicentres.len) - epicentreList += pick(possibleEpicentres) - else - break - - if(!epicentreList.len) - return - - for(var/obj/effect/landmark/epicentre in epicentreList) - for(var/obj/machinery/power/apc/apc in range(epicentre,lightsoutRange)) - apc.overload_lighting() - -/datum/gm_action/electrical_storm/get_weight() - return 30 + (metric.count_people_in_department(ROLE_ENGINEERING) * 15) + (metric.count_people_in_department(ROLE_EVERYONE) * 5) diff --git a/code/modules/gamemaster/actions/electrified_door.dm b/code/modules/gamemaster/actions/electrified_door.dm deleted file mode 100644 index e11c72f345..0000000000 --- a/code/modules/gamemaster/actions/electrified_door.dm +++ /dev/null @@ -1,75 +0,0 @@ -/datum/gm_action/electrified_door - name = "airlock short-circuit" - departments = list(ROLE_ENGINEERING, ROLE_MEDICAL) - chaotic = 10 - var/obj/machinery/door/airlock/chosen_door - var/area/target_area - var/list/area/excluded = list( - /area/submap, - /area/shuttle, - /area/crew_quarters - ) - -/datum/gm_action/electrified_door/set_up() - var/list/area/grand_list_of_areas = get_station_areas(excluded) - - severity = pickweight(EVENT_LEVEL_MUNDANE = 10, - EVENT_LEVEL_MODERATE = 5, - EVENT_LEVEL_MAJOR = 1 - ) - - //try 10 times - for(var/i in 1 to 10) - target_area = pick(grand_list_of_areas) - var/list/obj/machinery/door/airlock/target_doors = list() - for(var/obj/machinery/door/airlock/target_door in target_area.contents) - target_doors += target_door - target_doors = shuffle(target_doors) - - for(var/obj/machinery/door/airlock/target_door in target_doors) - if(!target_door.isElectrified() && target_door.arePowerSystemsOn() && target_door.maxhealth == target_door.health) - chosen_door = target_door - return - -/datum/gm_action/electrified_door/start() - ..() - if(!chosen_door) - return - command_announcement.Announce("An electrical issue has been detected in your area, please repair potential electronic overloads.", "Electrical Alert") - chosen_door.visible_message("\The [chosen_door]'s panel sparks!") - chosen_door.set_safeties(0) - playsound(get_turf(chosen_door), 'sound/machines/buzz-sigh.ogg', 50, 1) - if(severity >= EVENT_LEVEL_MODERATE) - chosen_door.electrify(-1) - spawn(rand(10 SECONDS, 2 MINUTES)) - if(chosen_door && chosen_door.arePowerSystemsOn() && prob(25 + 25 * severity)) - command_announcement.Announce("Overload has been localized to \the [target_area].", "Electrical Alert") - - if(severity >= EVENT_LEVEL_MAJOR) // New Major effect. Hydraulic boom. - spawn() - chosen_door.visible_message("\The [chosen_door] buzzes.") - playsound(get_turf(chosen_door), 'sound/machines/buzz-sigh.ogg', 50, 1) - sleep(rand(10 SECONDS, 3 MINUTES)) - if(!chosen_door || !chosen_door.arePowerSystemsOn()) - return - chosen_door.visible_message("\The [chosen_door]'s hydraulics creak.") - playsound(get_turf(chosen_door), 'sound/machines/airlock_creaking.ogg', 50, 1) - sleep(rand(30 SECONDS, 10 MINUTES)) - if(!chosen_door || !chosen_door.arePowerSystemsOn()) - return - chosen_door.visible_message("\The [chosen_door] emits a hydraulic shriek!") - playsound(get_turf(chosen_door), 'sound/machines/airlock.ogg', 80, 1) - spawn(rand(5 SECONDS, 30 SECONDS)) - if(!chosen_door || !chosen_door.arePowerSystemsOn()) - return - chosen_door.visible_message("\The [chosen_door]'s hydraulics detonate!") - chosen_door.fragmentate(get_turf(chosen_door), rand(5, 10), rand(3, 5), list(/obj/item/projectile/bullet/pellet/fragment/tank/small)) - explosion(get_turf(chosen_door),-1,-1,2,3) - - chosen_door.lock() - chosen_door.health = chosen_door.maxhealth / 6 - chosen_door.aiControlDisabled = 1 - chosen_door.update_icon() - -/datum/gm_action/electrified_door/get_weight() - return 10 + (metric.count_people_in_department(ROLE_ENGINEERING) * 5 + metric.count_people_in_department(ROLE_MEDICAL) * 10) diff --git a/code/modules/gamemaster/actions/gravity.dm b/code/modules/gamemaster/actions/gravity.dm deleted file mode 100644 index e895ca2cfa..0000000000 --- a/code/modules/gamemaster/actions/gravity.dm +++ /dev/null @@ -1,36 +0,0 @@ -/datum/gm_action/gravity - name = "gravity failure" - departments = list(ROLE_EVERYONE) - length = 600 - var/list/zLevels - -/datum/gm_action/gravity/set_up() - length = rand(length, length * 5) - // Setup which levels we will disrupt gravit on. - zLevels = using_map.station_levels.Copy() - for(var/datum/planet/P in SSplanets.planets) - zLevels -= P.expected_z_levels - -/datum/gm_action/gravity/announce() - command_announcement.Announce("Feedback surge detected in mass-distributions systems. Artificial gravity has been disabled whilst the system \ - reinitializes. Please stand by while the gravity system reinitializes.", "Gravity Failure") - -/datum/gm_action/gravity/start() - ..() - gravity_is_on = 0 - for(var/area/A in all_areas) - if(A.z in zLevels) - A.gravitychange(gravity_is_on, A) - -/datum/gm_action/gravity/end() - if(!gravity_is_on) - gravity_is_on = 1 - - for(var/area/A in all_areas) - if(A.z in zLevels) - A.gravitychange(gravity_is_on, A) - - command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") - -/datum/gm_action/gravity/get_weight() - return 30 + (metric.count_people_in_department(ROLE_EVERYONE) * 20) diff --git a/code/modules/gamemaster/actions/grid_check.dm b/code/modules/gamemaster/actions/grid_check.dm deleted file mode 100644 index dcf35a230f..0000000000 --- a/code/modules/gamemaster/actions/grid_check.dm +++ /dev/null @@ -1,23 +0,0 @@ -// New grid check event: -// Very similar to the old one, power goes out in most of the colony, however the new feature is the ability for engineering to -// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so, -// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting -// the powernet when this event fires. - -/datum/gm_action/grid_check - name = "grid check" - departments = list(ROLE_ENGINEERING, ROLE_EVERYONE) - chaotic = 20 - -/datum/gm_action/grid_check/get_weight() - return 50 + (metric.count_people_in_department(ROLE_ENGINEERING) * 30) - -/datum/gm_action/grid_check/start() - ..() - // This sets off a chain of events that lead to the actual grid check (or perhaps worse). - // First, the Supermatter engine makes a power spike. - for(var/obj/machinery/power/generator/engine in machines) - engine.power_spike() - break // Just one engine, please. - // After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout. - // If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code. \ No newline at end of file diff --git a/code/modules/gamemaster/actions/infestation.dm b/code/modules/gamemaster/actions/infestation.dm deleted file mode 100644 index 2c8ddac566..0000000000 --- a/code/modules/gamemaster/actions/infestation.dm +++ /dev/null @@ -1,116 +0,0 @@ -#define LOC_KITCHEN 0 -#define LOC_ATMOS 1 -#define LOC_CHAPEL 2 -#define LOC_LIBRARY 3 -#define LOC_HYDRO 4 -#define LOC_VAULT 5 -#define LOC_CONSTR 6 -#define LOC_TECH 7 -#define LOC_GARDEN 8 - -#define VERM_MICE 0 -#define VERM_LIZARDS 1 -#define VERM_SPIDERS 2 - -/datum/gm_action/infestation - name = "animal infestation" - departments = list(ROLE_EVERYONE) - var/location - var/locstring - var/vermin - var/vermstring - - var/list/turf/simulated/floor/turfs = list() - - var/spawn_types - var/max_number - -/datum/gm_action/infestation/set_up() - location = rand(0,8) - turfs.Cut() - var/spawn_area_type - switch(location) - if(LOC_KITCHEN) - spawn_area_type = /area/crew_quarters/kitchen - locstring = "the kitchen" - if(LOC_ATMOS) - spawn_area_type = /area/engineering/atmos - locstring = "atmospherics" - if(LOC_CHAPEL) - spawn_area_type = /area/chapel/main - locstring = "the chapel" - if(LOC_LIBRARY) - spawn_area_type = /area/library - locstring = "the library" - if(LOC_HYDRO) - spawn_area_type = /area/hydroponics - locstring = "hydroponics" - if(LOC_VAULT) - spawn_area_type = /area/security/nuke_storage - locstring = "the vault" - if(LOC_CONSTR) - spawn_area_type = /area/construction - locstring = "the construction area" - if(LOC_TECH) - spawn_area_type = /area/storage/tech - locstring = "technical storage" - if(LOC_GARDEN) - spawn_area_type = /area/hydroponics/garden - locstring = "the public garden" - - for(var/areapath in typesof(spawn_area_type)) - var/area/A = locate(areapath) - for(var/turf/simulated/floor/F in A.contents) - if(turf_clear(F)) - turfs += F - - spawn_types = list() - max_number = 0 - vermin = rand(0,2) - switch(vermin) - if(VERM_MICE) - spawn_types = list(/mob/living/simple_mob/animal/passive/mouse/gray, /mob/living/simple_mob/animal/passive/mouse/brown, /mob/living/simple_mob/animal/passive/mouse/white, /mob/living/simple_mob/animal/passive/mouse/rat) - max_number = 12 - vermstring = "mice" - if(VERM_LIZARDS) - spawn_types = list(/mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/lizard/large, /mob/living/simple_mob/animal/passive/lizard/large/defensive) - max_number = 6 - vermstring = "lizards" - if(VERM_SPIDERS) - spawn_types = list(/obj/effect/spider/spiderling) - max_number = 3 - vermstring = "spiders" - -/datum/gm_action/infestation/start() - spawn() - var/num = rand(2,max_number) - while(turfs.len > 0 && num > 0) - var/turf/simulated/floor/T = pick(turfs) - turfs.Remove(T) - num-- - - if(vermin == VERM_SPIDERS) - var/obj/effect/spider/spiderling/S = new(T) - S.amount_grown = -1 - else - var/spawn_type = pick(spawn_types) - new spawn_type(T) - -/datum/gm_action/infestation/announce() - command_announcement.Announce("Bioscans indicate that [vermstring] have been breeding in [locstring]. Clear them out, before this starts to affect productivity.", "Vermin infestation") - -/datum/gm_action/infestation/get_weight() - return 5 + (metric.count_people_in_department(ROLE_EVERYONE) * 20) - -#undef LOC_KITCHEN -#undef LOC_ATMOS -#undef LOC_CHAPEL -#undef LOC_LIBRARY -#undef LOC_HYDRO -#undef LOC_VAULT -#undef LOC_TECH -#undef LOC_GARDEN - -#undef VERM_MICE -#undef VERM_LIZARDS -#undef VERM_SPIDERS \ No newline at end of file diff --git a/code/modules/gamemaster/actions/ion_storm.dm b/code/modules/gamemaster/actions/ion_storm.dm deleted file mode 100644 index 9285b2ad99..0000000000 --- a/code/modules/gamemaster/actions/ion_storm.dm +++ /dev/null @@ -1,50 +0,0 @@ -/datum/gm_action/ionstorm - name = "ion storm" - departments = list(ROLE_SYNTHETIC) - var/botEmagChance = 0.5 - var/list/players = list() - var/active = FALSE - length = 1 MINUTE - -/datum/gm_action/ionstorm/set_up() - length = rand(length, length * 10) -// command_alert("The station has entered an ion storm. Monitor all electronic equipment for malfunctions", "Anomaly Alert") - for (var/mob/living/carbon/human/player in player_list) - if( !player.mind || player_is_antag(player.mind, only_offstation_roles = 1) || player.client.inactivity > MinutesToTicks(10)) - continue - players += player.real_name - - for (var/mob/living/silicon/ai/target in silicon_mob_list) - var/law = target.generate_ion_law() - target << "You have detected a change in your laws information:" - target << law - target.add_ion_law(law) - target.show_laws() - -/datum/gm_action/ionstorm/announce() - if(message_servers) - for (var/obj/machinery/message_server/MS in message_servers) - MS.spamfilter.Cut() - var/i - for (i = 1, i <= MS.spamfilter_limit, i++) - MS.spamfilter += pick("kitty","HONK","rev","malf","liberty","freedom","drugs", "[using_map.station_short]", \ - "admin","ponies","heresy","meow","Pun Pun","monkey","Ian","moron","pizza","message","spam",\ - "director", "Hello", "Hi!"," ","nuke","crate","dwarf","xeno") - -/datum/gm_action/ionstorm/start() - while(active) - sleep(1) - if(botEmagChance) - for(var/mob/living/bot/bot in mob_list) - if(prob(botEmagChance)) - bot.emag_act(1) - -/datum/gm_action/ionstorm/end() - spawn(rand(5000,8000)) - if(prob(50)) - ion_storm_announcement() - -/datum/gm_action/ionstorm/get_weight() - var/bots = metric.count_people_in_department(ROLE_SYNTHETIC) - var/weight = 5 + (bots * 20) - return weight diff --git a/code/modules/gamemaster/actions/manifest_malfunction.dm b/code/modules/gamemaster/actions/manifest_malfunction.dm deleted file mode 100644 index 0ef9067671..0000000000 --- a/code/modules/gamemaster/actions/manifest_malfunction.dm +++ /dev/null @@ -1,52 +0,0 @@ -/datum/gm_action/manifest_malfunction - name = "manifest malfunction" - enabled = TRUE - departments = list(ROLE_SECURITY, ROLE_SYNTHETIC, ROLE_EVERYONE) - chaotic = 3 - reusable = FALSE - length = 0 - - var/recordtype - -/datum/gm_action/manifest_malfunction/set_up() - severity = pickweight(EVENT_LEVEL_MUNDANE = 6, - EVENT_LEVEL_MODERATE = 2, - EVENT_LEVEL_MAJOR = 1 - ) - - recordtype = pickweight("medical" = 10,"security" = (severity * 15)) - - return - -/datum/gm_action/manifest_malfunction/get_weight() - . = -10 - - var/security = metric.count_people_in_department(ROLE_SECURITY) - - if(security && data_core) - . += (metric.count_people_in_department(ROLE_EVERYONE) * 5) - (metric.count_people_in_department(ROLE_SYNTHETIC) * 5) - - return . - -/datum/gm_action/manifest_malfunction/start() - ..() - - var/manifest_cut_count = 1 * severity - - for(var/I = 1 to manifest_cut_count) - var/datum/data/record/R - - switch(recordtype) - if("security") - R = pick(data_core.security) - - if("medical") - R = pick(data_core.medical) - - qdel(R) - -/datum/gm_action/manifest_malfunction/announce() - if(prob(30 * severity)) - spawn(rand(5 MINUTES, 10 MINUTES)) - command_announcement.Announce("An ongoing mass upload of malware for [recordtype] record cores has been detected onboard [station_name()]", "Data Breach Alert") - return diff --git a/code/modules/gamemaster/actions/meteor_defense.dm b/code/modules/gamemaster/actions/meteor_defense.dm deleted file mode 100644 index d0a0d40a3f..0000000000 --- a/code/modules/gamemaster/actions/meteor_defense.dm +++ /dev/null @@ -1,71 +0,0 @@ -// This event gives the station an advance warning about meteors, so that they can prepare in various ways. - -/datum/gm_action/meteor_defense - name = "meteor defense" - departments = list(ROLE_ENGINEERING, ROLE_CARGO) - chaotic = 50 - var/direction = null - var/dir_text = null - var/waves = 0 - - var/meteor_types - -/datum/gm_action/meteor_defense/get_weight() - var/engineers = metric.count_people_in_department(ROLE_ENGINEERING) - var/cargo = metric.count_people_in_department(ROLE_CARGO) - var/bots = metric.count_people_in_department(ROLE_SYNTHETIC) - var/weight = (max(engineers - 1, 0) * 20) // If only one engineer exists, no meteors for now. - - if(engineers >= 2) - weight += ((cargo - 1) * 10) - weight += (bots * 15) - - return weight - -/datum/gm_action/meteor_defense/set_up() - severity = pickweight(EVENT_LEVEL_MUNDANE = 10, - EVENT_LEVEL_MODERATE = 3 - ) - - switch(severity) - if(EVENT_LEVEL_MUNDANE) - meteor_types = meteors_threatening.Copy() - - if(EVENT_LEVEL_MODERATE) - meteor_types = meteors_catastrophic.Copy() - - direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately. - waves = rand(5, 8) - switch(direction) - if(NORTH) - dir_text = "aft" // For some reason this is needed. - if(SOUTH) - dir_text = "fore" - if(EAST) - dir_text = "port" - if(WEST) - dir_text = "starboard" - -/datum/gm_action/meteor_defense/announce() - var/announcement = "Alert! Two asteroids have collided near [station_name()]. Chunks of it are expected to approach from the [dir_text] side. ETA to arrival is \ - approximately [round(5 * severity * 2)] minutes." - command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') - -/datum/gm_action/meteor_defense/start() - ..() - spawn(0) - sleep(round(5 * severity) MINUTES) - var/announcement = "The incoming debris are expected to approach from the [dir_text] side. ETA to arrival is approximately [round(5 * severity)] minutes." - command_announcement.Announce(announcement, "Meteor Alert - Update") - sleep(round(5 * severity) MINUTES) - announcement = "Incoming debris approaches from the [dir_text] side!" - command_announcement.Announce(announcement, "Meteor Alert - Update") - while(waves) - message_admins("[waves] more wave\s of meteors remain.") - spawn(1) // Dir is reversed because the direction describes where meteors are going, not what side it's gonna hit. - spawn_meteors(rand(8, 12), meteors_threatening, reverse_dir[direction]) - waves-- - sleep(30 SECONDS) - announcement = "The station has cleared the incoming debris." - command_announcement.Announce(announcement, "Meteor Alert - Update") - message_admins("Meteor defense event has ended.") diff --git a/code/modules/gamemaster/actions/money_hacker.dm b/code/modules/gamemaster/actions/money_hacker.dm deleted file mode 100644 index 69c3e906c7..0000000000 --- a/code/modules/gamemaster/actions/money_hacker.dm +++ /dev/null @@ -1,79 +0,0 @@ -/var/global/account_hack_attempted = 0 - -/datum/gm_action/money_hacker - name = "bank account hacker" - departments = list(ROLE_EVERYONE) - reusable = TRUE - var/datum/money_account/affected_account - var/active - var/activeFor - var/end_time - -/datum/gm_action/money_hacker/set_up() - active = TRUE - end_time = world.time + 6000 - if(all_money_accounts.len) - affected_account = pick(all_money_accounts) - - account_hack_attempted = 1 - -/datum/gm_action/money_hacker/announce() - var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[affected_account.account_number], \ - without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \ - Notifications will be sent as updates occur.
    " - var/my_department = "[station_name()] firewall subroutines" - - for(var/obj/machinery/message_server/MS in machines) - if(!MS.active) continue - MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) - - -/datum/gm_action/money_hacker/start() - ..() - spawn(0) - while(active) - sleep(1) - activeFor++ - if(world.time >= end_time) - length = activeFor - else - length = activeFor + 10 - -/datum/gm_action/money_hacker/end() - active = FALSE - var/message - if(affected_account && !affected_account.suspended) - //hacker wins - message = "The hack attempt has succeeded." - - //subtract the money - var/lost = affected_account.money * 0.8 + (rand(2,4) - 2) / 10 - affected_account.money -= lost - - //create a taunting log entry - var/datum/transaction/T = new() - T.target_name = pick("","yo brotha from anotha motha","el Presidente","chieF smackDowN","Nobody") - T.purpose = pick("Ne$ ---ount fu%ds init*&lisat@*n","PAY BACK YOUR MUM","Funds withdrawal","pWnAgE","l33t hax","liberationez","Hit","Nothing") - T.amount = pick("","([rand(0,99999)])","alla money","9001$","HOLLA HOLLA GET DOLLA","([lost])") - var/date1 = "31 December, 1999" - var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]" - T.date = pick("", current_date_string, date1, date2,"Nowhen") - var/time1 = rand(0, 99999999) - var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]" - T.time = pick("", stationtime2text(), time2, "Never") - T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere") - - affected_account.transaction_log.Add(T) - - else - //crew wins - message = "The attack has ceased, the affected accounts can now be brought online." - - var/my_department = "[station_name()] firewall subroutines" - - for(var/obj/machinery/message_server/MS in machines) - if(!MS.active) continue - MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) - -/datum/gm_action/money_hacker/get_weight() - return 30 * all_money_accounts.len diff --git a/code/modules/gamemaster/actions/money_lotto.dm b/code/modules/gamemaster/actions/money_lotto.dm deleted file mode 100644 index 1cb7f92e1e..0000000000 --- a/code/modules/gamemaster/actions/money_lotto.dm +++ /dev/null @@ -1,39 +0,0 @@ -/datum/gm_action/money_lotto - name = "lottery win" - departments = list(ROLE_EVERYONE) - var/winner_name = "John Smith" - var/winner_sum = 0 - var/deposit_success = 0 - -/datum/gm_action/money_lotto/start() - ..() - winner_sum = pick(5000, 10000, 50000, 100000, 500000, 1000000, 1500000) - if(all_money_accounts.len) - var/datum/money_account/D = pick(all_money_accounts) - winner_name = D.owner_name - if(!D.suspended) - D.money += winner_sum - - var/datum/transaction/T = new() - T.target_name = "The [using_map.starsys_name] Times Grand Slam -Stellar- Lottery" - T.purpose = "Winner!" - T.amount = winner_sum - T.date = current_date_string - T.time = stationtime2text() - T.source_terminal = "Sif TCD Terminal #[rand(111,333)]" - D.transaction_log.Add(T) - - deposit_success = 1 - -/datum/gm_action/money_lotto/announce() - var/author = "[using_map.company_name] Editor" - var/channel = "The [using_map.starsys_name] Times" - - var/body = "The [using_map.starsys_name] Times wishes to congratulate [winner_name] for recieving the [using_map.starsys_name] Stellar Slam Lottery, and receiving the out of this world sum of [winner_sum] credits!" - if(!deposit_success) - body += "
    Unfortunately, we were unable to verify the account details provided, so we were unable to transfer the money. Send a cheque containing the sum of 5000 Thalers to ND 'Stellar Slam' office on the The [using_map.starsys_name] Times gateway containing updated details, and your winnings'll be re-sent within the month." - - news_network.SubmitArticle(body, author, channel, null, 1) - -/datum/gm_action/money_lotto/get_weight() - return 25 * metric.count_people_in_department(ROLE_EVERYONE) diff --git a/code/modules/gamemaster/actions/money_spam.dm b/code/modules/gamemaster/actions/money_spam.dm deleted file mode 100644 index e11baec090..0000000000 --- a/code/modules/gamemaster/actions/money_spam.dm +++ /dev/null @@ -1,131 +0,0 @@ -/datum/gm_action/pda_spam - name = "PDA spam" - departments = list(ROLE_EVERYONE) - reusable = TRUE - var/last_spam_time = 0 - var/obj/machinery/message_server/useMS - var/obj/machinery/exonet_node/node - -/datum/gm_action/pda_spam/set_up() - last_spam_time = world.time - pick_message_server() - -/datum/gm_action/pda_spam/proc/pick_message_server() - if(message_servers) - for (var/obj/machinery/message_server/MS in message_servers) - if(MS.active) - useMS = MS - break - -/datum/gm_action/pda_spam/start() - ..() - while(world.time < last_spam_time + 3000) - if(!node) - node = get_exonet_node() - - if(!node || !node.on || !node.allow_external_PDAs) - return - - if(!useMS || !useMS.active) - useMS = null - pick_message_server() - - if(useMS) - if(prob(5)) - // /obj/machinery/message_server/proc/send_pda_message(var/recipient = "",var/sender = "",var/message = "") - var/obj/item/device/pda/P - var/list/viables = list() - for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) - if (!check_pda.owner||check_pda.toff||check_pda == src||check_pda.hidden) - continue - viables.Add(check_pda) - - if(!viables.len) - return - P = pick(viables) - - var/sender - var/message - switch(pick(1,2,3,4,5,6,7)) - if(1) - sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us") - message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\ - "You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\ - "Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\ - "You will be able to enjoy over 450 top-flight casino games at MaxBet.") - if(2) - sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides") - message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\ - "If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\ - "I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\ - "You have (1) new message!",\ - "You have (2) new profile views!") - if(3) - sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas") - message = pick("Luxury watches for Blowout sale prices!",\ - "Watches, Jewelry & Accessories, Bags & Wallets !",\ - "Deposit 100$ and get 300$ totally free!",\ - " 100K NT.|WOWGOLD nly $89 ",\ - "We have been filed with a complaint from one of your customers in respect of their business relations with you.",\ - "We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..") - if(4) - sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?") - message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\ - "Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\ - "After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\ - "Men of all species report AMAZING increases in length, width and stamina.") - if(5) - sender = pick("Dr","Crown prince","King Regent","Professor","Captain") - sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi") - sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi") - message = pick("YOUR FUND HAS BEEN MOVED TO [pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus")] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\ - "We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\ - "Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\ - "Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\ - "Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits") - if(6) - sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt") - message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\ - "WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\ - "Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\ - "Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!") - if(7) - sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!") - message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\ - "You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\ - "You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\ - "You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!") - - if (useMS.send_pda_message("[P.owner]", sender, message)) //Message been filtered by spam filter. - return - - last_spam_time = world.time - - /* //VOREStation Removal: no need to spam the AI tenfold - if (prob(50)) //Give the AI an increased chance to intercept the message - for(var/mob/living/silicon/ai/ai in mob_list) - // Allows other AIs to intercept the message but the AI won't intercept their own message. - if(ai.aiPDA != P && ai.aiPDA != src) - ai.show_message("Intercepted message from [sender] (Unknown / spam?) to [P:owner]: [message]") - */ - - //Commented out because we don't send messages like this anymore. Instead it will just popup in their chat window. - //P.tnote += "← From [sender] (Unknown / spam?):
    [message]
    " - - if (!P.message_silent) - playsound(P.loc, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(3, P.loc)) - if(!P.message_silent) O.show_message(text("\icon[P] *[P.ttone]*")) - //Search for holder of the PDA. - var/mob/living/L = null - if(P.loc && isliving(P.loc)) - L = P.loc - //Maybe they are a pAI! - else - L = get(P, /mob/living/silicon) - - if(L) - L << "\icon[P] Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)" - -/datum/gm_action/pda_spam/get_weight() - return 25 * metric.count_people_in_department(ROLE_EVERYONE) diff --git a/code/modules/gamemaster/actions/planet_weather_change.dm b/code/modules/gamemaster/actions/planet_weather_change.dm deleted file mode 100644 index 0faf80cf87..0000000000 --- a/code/modules/gamemaster/actions/planet_weather_change.dm +++ /dev/null @@ -1,35 +0,0 @@ -/datum/gm_action/planet_weather_shift - name = "sudden weather shift" - enabled = TRUE - departments = list(ROLE_EVERYONE) - reusable = TRUE - var/datum/planet/target_planet - - var/list/banned_weathers = list( -/* //yawn Wider Edit - Borealis 2 Weather, - /datum/weather/borealis2/ash_storm, - /datum/weather/borealis2/emberfall, - /datum/weather/borealis2/blood_moon, - /datum/weather/borealis2/fallout*/) - //Yawn wider Edit End - var/list/possible_weathers = list() - -/datum/gm_action/planet_weather_shift/set_up() - if(!target_planet || isnull(target_planet)) - target_planet = pick(SSplanets.planets) - possible_weathers |= target_planet.weather_holder.allowed_weather_types - possible_weathers -= banned_weathers - return - -/datum/gm_action/planet_weather_shift/get_weight() - return max(0, -15 + (metric.count_all_outdoor_mobs() * 20)) - -/datum/gm_action/planet_weather_shift/start() - ..() - var/new_weather = pick(possible_weathers) - target_planet.weather_holder.change_weather(new_weather) - -/datum/gm_action/planet_weather_shift/announce() - spawn(rand(3 SECONDS, 2 MINUTES)) - command_announcement.Announce("Local weather patterns on [target_planet.name] suggest that a sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of rapidly deteriorating conditions.", "Weather Alert") - return diff --git a/code/modules/gamemaster/actions/prison_break.dm b/code/modules/gamemaster/actions/prison_break.dm deleted file mode 100644 index c034c7e223..0000000000 --- a/code/modules/gamemaster/actions/prison_break.dm +++ /dev/null @@ -1,93 +0,0 @@ -/datum/gm_action/prison_break - name = "prison break" - departments = list(ROLE_SECURITY, ROLE_SYNTHETIC) - - var/start_time = 0 - var/active = FALSE // Are we doing stuff? - var/releaseWhen = 60 // The delay for the breakout to occur. - var/list/area/areas = list() // List of areas to affect. Filled by start() - - var/eventDept = "Security" // Department name in announcement - var/list/areaName = list("Brig") // Names of areas mentioned in AI and Engineering announcements - var/list/areaType = list(/area/security/prison, /area/security/brig) // Area types to include. - var/list/areaNotType = list() // Area types to specifically exclude. - -/datum/gm_action/prison_break/get_weight() - var/afflicted_staff = 0 - var/assigned_staff = metric.count_people_in_department(ROLE_ENGINEERING) - for(var/department in departments) - afflicted_staff += round(metric.count_people_in_department(department) / 2) - - var/weight = 20 + (assigned_staff * 10) - - if(assigned_staff) - weight += afflicted_staff - - return weight - -/datum/gm_action/prison_break/virology - name = "virology breakout" - departments = list(ROLE_MEDICAL, ROLE_SYNTHETIC) - eventDept = "Medical" - areaName = list("Virology") - areaType = list(/area/medical/virology, /area/medical/virologyaccess) - -/datum/gm_action/prison_break/xenobiology - name = "xenobiology breakout" - departments = list(ROLE_RESEARCH, ROLE_SYNTHETIC) - eventDept = "Science" - areaName = list("Xenobiology") - areaType = list(/area/rnd/xenobiology) - areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) - -/datum/gm_action/prison_break/station - name = "station-wide breakout" - departments = list(ROLE_SECURITY, ROLE_MEDICAL, ROLE_RESEARCH, ROLE_SYNTHETIC) - eventDept = "Station" - areaName = list("Brig","Virology","Xenobiology") - areaType = list(/area/security/prison, /area/security/brig, /area/medical/virology, /area/medical/virologyaccess, /area/rnd/xenobiology) - areaNotType = list(/area/rnd/xenobiology/xenoflora, /area/rnd/xenobiology/xenoflora_storage) - -/datum/gm_action/prison_break/set_up() - releaseWhen = rand(60, 90) - start_time = world.time - active = TRUE - length = releaseWhen + 1 SECOND - -/datum/gm_action/prison_break/announce() - if(areas && areas.len > 0) - command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] detected in [station_name()] [(eventDept == "Security")? "imprisonment":"containment"] subroutines. Secure any compromised areas immediately. Station AI involvement is recommended.", "[eventDept] Alert") - -/datum/gm_action/prison_break/start() - ..() - for(var/area/A in all_areas) - if(is_type_in_list(A,areaType) && !is_type_in_list(A,areaNotType)) - areas += A - - if(areas && areas.len > 0) - var/my_department = "[station_name()] firewall subroutines" - var/rc_message = "An unknown malicious program has been detected in the [english_list(areaName)] lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised within approximately three minutes. Direct intervention is required immediately.
    " - for(var/obj/machinery/message_server/MS in machines) - MS.send_rc_message("Engineering", my_department, rc_message, "", "", 2) - for(var/mob/living/silicon/ai/A in player_list) - A << "Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department]." - - else - world.log << "ERROR: Could not initate grey-tide. Unable to find suitable containment area." - - if(areas && areas.len > 0) - spawn() - while(active) - sleep(1) - if(world.time >= releaseWhen + start_time) - var/obj/machinery/power/apc/theAPC = null - for(var/area/A in areas) - theAPC = A.get_apc() - if(theAPC.operating) //If the apc's off, it's a little hard to overload the lights. - for(var/obj/machinery/light/L in A) - L.flicker(10) - -/datum/gm_action/prison_break/end() - active = FALSE - for(var/area/A in shuffle(areas)) - A.prison_break() diff --git a/code/modules/gamemaster/actions/radiation_storm.dm b/code/modules/gamemaster/actions/radiation_storm.dm deleted file mode 100644 index 678ad16ab6..0000000000 --- a/code/modules/gamemaster/actions/radiation_storm.dm +++ /dev/null @@ -1,67 +0,0 @@ -/datum/gm_action/radiation_storm - name = "radiation storm" - departments = list(ROLE_EVERYONE) - reusable = TRUE - - var/enterBelt = 30 - var/radIntervall = 5 - var/leaveBelt = 80 - var/revokeAccess = 165 - var/activeFor = 0 - var/postStartTicks = 0 - var/active = FALSE - -/datum/gm_action/radiation_storm/announce() - command_announcement.Announce("High levels of radiation detected near \the [station_name()]. Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') - -/datum/gm_action/radiation_storm/set_up() - active = TRUE - -/datum/gm_action/radiation_storm/start() - ..() - make_maint_all_access() - - while(active) - sleep(1 SECOND) - activeFor ++ - if(activeFor == enterBelt) - command_announcement.Announce("The station has entered the radiation belt. Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") - radiate() - - if(activeFor >= enterBelt && activeFor <= leaveBelt) - postStartTicks++ - - if(postStartTicks == radIntervall) - postStartTicks = 0 - radiate() - - else if(activeFor == leaveBelt) - command_announcement.Announce("The station has passed the radiation belt. Please allow for up to one minute while radiation levels dissipate, and report to medbay if you experience any unusual symptoms. Maintenance will lose all access again shortly.", "Anomaly Alert") - -/datum/gm_action/radiation_storm/proc/radiate() - var/radiation_level = rand(15, 35) - for(var/z in using_map.station_levels) - SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) - - for(var/mob/living/carbon/C in living_mob_list) - var/area/A = get_area(C) - if(!A) - continue - if(A.flags & RAD_SHIELDED) - continue - if(istype(C,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = C - if(prob(5)) - if (prob(75)) - randmutb(H) // Applies bad mutation - domutcheck(H,null,MUTCHK_FORCED) - else - randmutg(H) // Applies good mutation - domutcheck(H,null,MUTCHK_FORCED) - -/datum/gm_action/radiation_storm/end() - spawn(revokeAccess SECONDS) - revoke_maint_all_access() - -/datum/gm_action/radiation_storm/get_weight() - return 20 + (metric.count_people_in_department(ROLE_MEDICAL) * 10) + (metric.count_all_space_mobs() * 40) + (metric.count_people_in_department(ROLE_EVERYONE) * 20) diff --git a/code/modules/gamemaster/actions/random_antagonist.dm b/code/modules/gamemaster/actions/random_antagonist.dm deleted file mode 100644 index b1cb7b01d8..0000000000 --- a/code/modules/gamemaster/actions/random_antagonist.dm +++ /dev/null @@ -1,23 +0,0 @@ -// The random spawn proc on the antag datum will handle announcing the spawn and whatnot. -/datum/gm_action/random_antag - name = "random antagonist" - departments = list(ROLE_EVERYONE) - chaotic = 30 - reusable = TRUE - -/datum/gm_action/random_antag/start() - ..() - var/list/valid_types = list() - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(antag.flags & ANTAG_RANDSPAWN) - valid_types |= antag - if(valid_types.len) - var/datum/antagonist/antag = pick(valid_types) - antag.attempt_random_spawn() - -/datum/gm_action/random_antag/get_weight() - . = ..() - if(gm) - var/weight = max(0, (metric.count_people_in_department(ROLE_SECURITY) * 20) + (metric.count_people_in_department(ROLE_EVERYONE) * 5) + gm.staleness) - return weight diff --git a/code/modules/gamemaster/actions/rogue_drones.dm b/code/modules/gamemaster/actions/rogue_drones.dm deleted file mode 100644 index 983aa87842..0000000000 --- a/code/modules/gamemaster/actions/rogue_drones.dm +++ /dev/null @@ -1,63 +0,0 @@ -/datum/gm_action/rogue_drone - name = "rogue drones" - departments = list(ROLE_SECURITY) - chaotic = 60 - length = 20 MINUTES - var/list/drones_list = list() - -/datum/gm_action/rogue_drone/start() - ..() - //spawn them at the same place as carp - var/list/possible_spawns = list() - for(var/obj/effect/landmark/C in landmarks_list) - if(C.name == "carpspawn") - possible_spawns.Add(C) - - //25% chance for this to be a false alarm - var/num - if(prob(25)) - num = 0 - else - num = rand(2,6) - for(var/i=0, i drones_list.len * 0.75) - command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert") - else - command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert") - -/datum/gm_action/rogue_drone/get_weight() - return 20 + (metric.count_people_in_department(ROLE_SECURITY) * 10) + (metric.count_all_space_mobs() * 30) diff --git a/code/modules/gamemaster/actions/security_advisement.dm b/code/modules/gamemaster/actions/security_advisement.dm deleted file mode 100644 index b5c8d82912..0000000000 --- a/code/modules/gamemaster/actions/security_advisement.dm +++ /dev/null @@ -1,49 +0,0 @@ -/datum/gm_action/security_screening - name = "security screening" - departments = list(ROLE_SECURITY, ROLE_EVERYONE) - - var/list/species_weights = list( - SPECIES_SKRELL = 9, - SPECIES_UNATHI = 15, - SPECIES_HUMAN_VATBORN = 6, - SPECIES_TESHARI = 2, - SPECIES_TAJ = 3, - SPECIES_DIONA = 1, - SPECIES_ZADDAT = 25, - SPECIES_HUMAN = 3, - SPECIES_PROMETHEAN = 30 - ) - - var/list/synth_weights = list( - "Cybernetic" = 15, - "Drone" = 30, - "Positronic" = 25 - ) - - var/list/end_weights = list() - -/datum/gm_action/security_screening/set_up() - for(var/species_name in species_weights) - var/giveweight = 0 - - for(var/datum/data/record/R in data_core.general) - if(R.fields["species"] == species_name) - giveweight += species_weights[species_name] - - end_weights[species_name] = giveweight - - for(var/bottype in synth_weights) - var/giveweight = 0 - - for(var/datum/data/record/R in data_core.general) - if(R.fields["brain_type"] == bottype) - giveweight += synth_weights[bottype] - - end_weights[bottype] = giveweight - -/datum/gm_action/security_screening/announce() - spawn(rand(1 MINUTE, 2 MINUTES)) - command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has requested the screening of [pick("every other", "every", "suspicious", "willing")] [pickweight(end_weights)] personnel onboard \the [station_name()].", "Security Advisement") - -/datum/gm_action/security_screening/get_weight() - return max(-20, 10 + round(gm.staleness * 1.5) - (gm.danger * 2)) + (metric.count_people_in_department(ROLE_SECURITY) * 10) + (metric.count_people_in_department(ROLE_EVERYONE) * 1.5) diff --git a/code/modules/gamemaster/actions/shipping_error.dm b/code/modules/gamemaster/actions/shipping_error.dm deleted file mode 100644 index 33520f13a3..0000000000 --- a/code/modules/gamemaster/actions/shipping_error.dm +++ /dev/null @@ -1,17 +0,0 @@ -/datum/gm_action/shipping_error - name = "shipping error" - departments = list(ROLE_CARGO) - reusable = TRUE - -/datum/gm_action/shipping_error/get_weight() - var/cargo = metric.count_people_in_department(ROLE_CARGO) - var/weight = (cargo * 40) - return weight - -/datum/gm_action/shipping_error/start() - ..() - var/datum/supply_order/O = new /datum/supply_order() - O.ordernum = supply_controller.ordernum - O.object = supply_controller.supply_pack[pick(supply_controller.supply_pack)] - O.ordered_by = random_name(pick(MALE,FEMALE), species = "Human") - supply_controller.shoppinglist += O \ No newline at end of file diff --git a/code/modules/gamemaster/actions/solar_storm.dm b/code/modules/gamemaster/actions/solar_storm.dm deleted file mode 100644 index 046b93639b..0000000000 --- a/code/modules/gamemaster/actions/solar_storm.dm +++ /dev/null @@ -1,56 +0,0 @@ -/datum/gm_action/solar_storm - name = "solar storm" - var/rad_interval = 1 SECOND - var/base_solar_gen_rate - length = 3 MINUTES - var/duration // Duration for the storm - var/start_time = 0 - - reusable = TRUE - -/datum/gm_action/solar_storm/set_up() - start_time = world.time - duration = length - duration += rand(-1 * 1 MINUTE, 1 MINUTE) - -/datum/gm_action/solar_storm/announce() - command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') - adjust_solar_output(1.5) - -/datum/gm_action/solar_storm/proc/adjust_solar_output(var/mult = 1) - if(isnull(base_solar_gen_rate)) base_solar_gen_rate = GLOB.solar_gen_rate - GLOB.solar_gen_rate = mult * base_solar_gen_rate - -/datum/gm_action/solar_storm/start() - ..() - length = duration - command_announcement.Announce("The solar storm has reached the station. Please refain from EVA and remain inside the station until it has passed.", "Anomaly Alert") - adjust_solar_output(5) - - var/start_time = world.time - - spawn() - while(world.time <= start_time + duration) - sleep(rad_interval) - radiate() - -/datum/gm_action/solar_storm/get_weight() - return 20 + (metric.count_people_in_department(ROLE_ENGINEERING) * 10) + (metric.count_all_space_mobs() * 30) - -/datum/gm_action/solar_storm/proc/radiate() - // Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case. - for(var/mob/living/L in player_list) - var/turf/T = get_turf(L) - if(!T) - continue - - if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf - continue - - //Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation. - L.rad_act(rand(15, 30)) - -/datum/gm_action/solar_storm/end() - command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. Please report to medbay if you experience any unusual symptoms. ", "Anomaly Alert") - adjust_solar_output() - length = initial(length) diff --git a/code/modules/gamemaster/actions/spacevine.dm b/code/modules/gamemaster/actions/spacevine.dm deleted file mode 100644 index c9e5b4135e..0000000000 --- a/code/modules/gamemaster/actions/spacevine.dm +++ /dev/null @@ -1,14 +0,0 @@ -/datum/gm_action/spacevine - name = "space-vine infestation" - departments = list(ROLE_ENGINEERING) - chaotic = 2 - -/datum/gm_action/spacevine/start() - ..() - spacevine_infestation() - -/datum/gm_action/spacevine/announce() - level_seven_announcement() - -/datum/gm_action/spacevine/get_weight() - return 20 + (metric.count_people_in_department(ROLE_ENGINEERING) * 20) + (metric.count_people_in_department(ROLE_EVERYONE) * 10) diff --git a/code/modules/gamemaster/actions/spider_infestation.dm b/code/modules/gamemaster/actions/spider_infestation.dm deleted file mode 100644 index c641c86167..0000000000 --- a/code/modules/gamemaster/actions/spider_infestation.dm +++ /dev/null @@ -1,56 +0,0 @@ -/datum/gm_action/spider_infestation - name = "spider infestation" - departments = list(ROLE_SECURITY, ROLE_MEDICAL, ROLE_EVERYONE) - chaotic = 30 - - severity = 1 - - var/spawncount = 1 - - var/spawntype = /obj/effect/spider/spiderling - -/datum/gm_action/spider_infestation/set_up() - severity = pickweight(EVENT_LEVEL_MUNDANE = max(1,(12 - (3 * metric.count_people_in_department(ROLE_SECURITY)))), - EVENT_LEVEL_MODERATE = (7 + (2 * metric.count_people_in_department(ROLE_SECURITY))), - EVENT_LEVEL_MAJOR = (1 + (2 * metric.count_people_in_department(ROLE_SECURITY))) - ) - - switch(severity) - if(EVENT_LEVEL_MUNDANE) - spawntype = /obj/effect/spider/spiderling/stunted - if(EVENT_LEVEL_MODERATE) - spawntype = /obj/effect/spider/spiderling - if(EVENT_LEVEL_MAJOR) - spawntype = /obj/effect/spider/spiderling - - spawncount = rand(4 * severity, 6 * severity) - -/datum/gm_action/spider_infestation/announce() - command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') - - if(severity >= EVENT_LEVEL_MAJOR) - spawn(rand(600, 3000)) - command_announcement.Announce("Unidentified lifesigns previously detected coming aboard [station_name()] have been classified as a swarm of arachnids. Extreme caution is advised.", "Arachnid Alert") - -/datum/gm_action/spider_infestation/start() - ..() - var/list/vents = list() - for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) - if(!temp_vent.welded && temp_vent.network && temp_vent.loc.z in using_map.station_levels) - if(temp_vent.network.normal_members.len > 50) - vents += temp_vent - - while((spawncount >= 1) && vents.len) - var/obj/vent = pick(vents) - new /obj/effect/spider/spiderling(vent.loc) - vents -= vent - spawncount-- - -/datum/gm_action/spider_infestation/get_weight() - var/security = metric.count_people_in_department(ROLE_SECURITY) - var/medical = metric.count_people_in_department(ROLE_MEDICAL) - var/engineering = metric.count_people_in_department(ROLE_ENGINEERING) - - var/assigned_staff = security + round(medical / 2) + round(engineering / 2) - - return 10 + (assigned_staff * 15) diff --git a/code/modules/gamemaster/actions/spontaneous_appendicitis.dm b/code/modules/gamemaster/actions/spontaneous_appendicitis.dm deleted file mode 100644 index 1758709164..0000000000 --- a/code/modules/gamemaster/actions/spontaneous_appendicitis.dm +++ /dev/null @@ -1,13 +0,0 @@ -/datum/gm_action/spontaneous_appendicitis - name = "appendicitis" - departments = list(ROLE_MEDICAL, ROLE_EVERYONE) - chaotic = 1 - -/datum/gm_action/spontaneous_appendicitis/start() - ..() - for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) - if(H.client && !player_is_antag(H.mind) && H.appendicitis()) - break - -/datum/gm_action/spontaneous_appendicitis/get_weight() - return max(0, -5 + (metric.count_people_in_department(ROLE_MEDICAL) * 10)) diff --git a/code/modules/gamemaster/actions/station_fundraise.dm b/code/modules/gamemaster/actions/station_fundraise.dm deleted file mode 100644 index 2a516d178a..0000000000 --- a/code/modules/gamemaster/actions/station_fundraise.dm +++ /dev/null @@ -1,14 +0,0 @@ -/datum/gm_action/station_fund_raise - name = "local funding drive" - departments = list(ROLE_SECURITY, ROLE_CARGO, ROLE_EVERYONE) - -/datum/gm_action/station_fund_raise/announce() - spawn(rand(1 MINUTE, 2 MINUTES)) - command_announcement.Announce("Due to [pick("recent", "unfortunate", "possible future")] budget [pick("changes", "issues")], in-system stations are now advised to increase funding income.", "Security & Supply Advisement") - -/datum/gm_action/station_fund_raise/get_weight() - var/weight_modifier = 0.5 - if(station_account.money <= 80000) - weight_modifier = 1 - - return (max(-20, 10 + gm.staleness) + ((metric.count_people_in_department(ROLE_SECURITY) + (metric.count_people_in_department(ROLE_CARGO))) * 5) + (metric.count_people_in_department(ROLE_EVERYONE) * 3)) * weight_modifier diff --git a/code/modules/gamemaster/actions/stowaway.dm b/code/modules/gamemaster/actions/stowaway.dm deleted file mode 100644 index c75df494e2..0000000000 --- a/code/modules/gamemaster/actions/stowaway.dm +++ /dev/null @@ -1,80 +0,0 @@ -/datum/gm_action/stowaway - name = "stowaway pod" - departments = list(ROLE_EVERYONE, ROLE_SECURITY) - chaotic = 10 - observers_used = TRUE - var/area/target_area // Chosen target area - var/area/target_turf // Chosen target turf in target_area - var/list/area/excluded = list( - /area/submap, - /area/shuttle, - /area/crew_quarters, - /area/holodeck, - /area/engineering/engine_room - ) - - var/list/area/included = list( - /area/maintenance - ) - -/datum/gm_action/stowaway/set_up() - severity = pickweight(EVENT_LEVEL_MUNDANE = 20, - EVENT_LEVEL_MODERATE = 5, - EVENT_LEVEL_MAJOR = 1 - ) - - var/list/area/grand_list_of_areas = get_station_areas(excluded) - - for(var/area/Incl in included) - for(var/area/A in grand_list_of_areas) - if(!istype(A, Incl)) - grand_list_of_areas -= A - - // Okay, now lets try and pick a target! Lets try 10 times, otherwise give up - for(var/i in 1 to 10) - var/area/A = pick(grand_list_of_areas) - if(is_area_occupied(A)) - log_debug("[name] event: Rejected [A] because it is occupied.") - continue - // A good area, great! Lets try and pick a turf - var/list/turfs = list() - for(var/turf/simulated/floor/F in A) - if(turf_clear(F)) - turfs += F - if(turfs.len == 0) - log_debug("[name] event: Rejected [A] because it has no clear turfs.") - continue - target_area = A - target_turf = pick(turfs) - - if(!target_area) - log_debug("[name] event: Giving up after too many failures to pick target area") - return - -/datum/gm_action/stowaway/start() - if(!target_turf) - return - ..() - - var/obj/structure/ghost_pod/ghost_activated/human/HP = new (target_turf) - - if(severity == EVENT_LEVEL_MUNDANE || istype(ticker.mode, /datum/game_mode/extended)) - HP.make_antag = MODE_STOWAWAY - - else if(severity == EVENT_LEVEL_MODERATE) - HP.make_antag = MODE_RENEGADE - HP.occupant_type = "renegade [HP.occupant_type]" - - else if(severity == EVENT_LEVEL_MAJOR) - HP.make_antag = MODE_INFILTRATOR - HP.occupant_type = "volatile [HP.occupant_type]" - - say_dead_object("A [HP.occupant_type] pod is now available in \the [target_area].", HP) - -/datum/gm_action/stowaway/get_weight() - return -20 + (metric.count_people_in_department(ROLE_SECURITY) * 15 + metric.count_people_in_department(ROLE_SYNTHETIC) * 5 + metric.count_people_in_department(ROLE_EVERYONE) * 1) - -/datum/gm_action/stowaway/announce() - spawn(rand(15 MINUTES, 30 MINUTES)) - if(prob(20) && severity >= EVENT_LEVEL_MODERATE && atc && !atc.squelched) - atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/actions/supply_conversion.dm b/code/modules/gamemaster/actions/supply_conversion.dm deleted file mode 100644 index 40ed820462..0000000000 --- a/code/modules/gamemaster/actions/supply_conversion.dm +++ /dev/null @@ -1,65 +0,0 @@ -/datum/gm_action/nanotrasen_budget_allocation - name = "supply point to cargo budget" - enabled = TRUE - departments = list(ROLE_CARGO) - chaotic = 0 - reusable = TRUE - - var/datum/controller/supply/SC - var/running = FALSE - var/last_run - - var/thaler_earned - -/datum/gm_action/nanotrasen_budget_allocation/New() - ..() - SC = supply_controller - -/datum/gm_action/nanotrasen_budget_allocation/set_up() - running = TRUE - return - -/datum/gm_action/nanotrasen_budget_allocation/get_weight() - . = round(SC.points / 15) - - var/cargo = metric.count_people_in_department(ROLE_CARGO) - var/personnel = metric.count_people_in_department(ROLE_EVERYONE) - if(cargo) - . = round(SC.points / (10 + personnel)) + cargo * 10 - - if(running || ( world.time < (last_run + 30 MINUTES))) - . = 0 - - return . - -/datum/gm_action/nanotrasen_budget_allocation/start() - . = ..() - - last_run = world.time - - var/point_difference = SC.points - - if(SC.points >= 1000) - SC.points = round(SC.points / 3) - point_difference -= SC.points - - else if(SC.points >= 500) - SC.points -= 100 * (rand(5, 20) / 10) - point_difference -= SC.points - - else - SC.points = round(SC.points / 1.25) - point_difference -= SC.points - - if(point_difference > 0) - thaler_earned = round(point_difference / SC.points_per_money) - -/datum/gm_action/nanotrasen_budget_allocation/end() - spawn(5 MINUTES) - running = FALSE - return - -/datum/gm_action/nanotrasen_budget_allocation/announce() - spawn(rand(1 MINUTE, 5 MINUTES)) - command_announcement.Announce("[station_name()] Supply Department has earned a converted thaler budget of [thaler_earned] due to their backlogged daily requisition tokens.", "Supply Budget Conversion") - return diff --git a/code/modules/gamemaster/actions/supplyrequest.dm b/code/modules/gamemaster/actions/supplyrequest.dm deleted file mode 100644 index ae2e597fcd..0000000000 --- a/code/modules/gamemaster/actions/supplyrequest.dm +++ /dev/null @@ -1,11 +0,0 @@ -/datum/gm_action/request - name = "general request" - departments = list(ROLE_CARGO) - -/datum/gm_action/request/announce() - spawn(rand(1 MINUTE, 2 MINUTES)) - command_announcement.Announce("[pick("A nearby vessel", "A Solar contractor", "A Skrellian contractor", "A NanoTrasen board director")] has requested the delivery of [pick("one","two","three","several")] [pick("medical","engineering","research","civilian")] supply packages. The [station_name()] has been tasked with completing this request.", "Supply Request") - -/datum/gm_action/request/get_weight() - return max(15, 15 + round(gm.staleness / 2) - gm.danger) + (metric.count_people_in_department(ROLE_CARGO) * 10) - diff --git a/code/modules/gamemaster/actions/surprise_carp_attack.dm b/code/modules/gamemaster/actions/surprise_carp_attack.dm deleted file mode 100644 index dce6121b61..0000000000 --- a/code/modules/gamemaster/actions/surprise_carp_attack.dm +++ /dev/null @@ -1,45 +0,0 @@ -// This event sends a few carp after someone in space. - -/datum/gm_action/surprise_carp_attack - name = "surprise carp attack" - departments = list(ROLE_EVERYONE) - reusable = TRUE - chaotic = 10 - var/mob/living/victim = null - -/datum/gm_action/surprise_carp_attack/get_weight() - return metric.count_all_space_mobs() * 50 - -/datum/gm_action/surprise_carp_attack/set_up() - var/list/potential_victims = list() - victim = null - for(var/mob/living/L in player_list) - if(!(L.z in using_map.station_levels)) - continue // Not on the right z-level. - if(L.stat) - continue // Don't want dead people. - var/turf/T = get_turf(L) - if(istype(T, /turf/space) && istype(T.loc,/area/space)) - potential_victims.Add(L) - if(potential_victims.len) - victim = pick(potential_victims) - - -/datum/gm_action/surprise_carp_attack/start() - ..() - if(!victim) - message_admins("Surprise carp attack failed to find a target.") - return - var/number_of_carp = rand(1, 2) - message_admins("Sending [number_of_carp] carp\s after [victim].") - while(number_of_carp) - var/turf/spawning_turf = null - var/list/nearby_things = oview(10, victim) - for(var/turf/space/space in nearby_things) - if(get_dist(space, victim) <= 7) - continue - spawning_turf = space - break - if(spawning_turf) - new /mob/living/simple_mob/animal/space/carp(spawning_turf) - number_of_carp-- \ No newline at end of file diff --git a/code/modules/gamemaster/actions/surprise_meteor.dm b/code/modules/gamemaster/actions/surprise_meteor.dm deleted file mode 100644 index 682a8287a6..0000000000 --- a/code/modules/gamemaster/actions/surprise_meteor.dm +++ /dev/null @@ -1,17 +0,0 @@ -// This event sends one wave of meteors unannounced. - -/datum/gm_action/surprise_meteors - name = "surprise meteors" - departments = list(ROLE_ENGINEERING) - chaotic = 25 - -/datum/gm_action/surprise_meteors/get_weight() - var/engineers = metric.count_people_in_department(ROLE_ENGINEERING) - var/weight = (max(engineers - 1, 0) * 25) // If only one engineer exists, no meteors for now. - return weight - -/datum/gm_action/surprise_meteors/start() - ..() - spawn(1) - spawn_meteors(rand(4, 8), meteors_normal, pick(cardinal)) - message_admins("Surprise meteors event has ended.") \ No newline at end of file diff --git a/code/modules/gamemaster/actions/swarmboarder.dm b/code/modules/gamemaster/actions/swarmboarder.dm deleted file mode 100644 index 8a257b9d4b..0000000000 --- a/code/modules/gamemaster/actions/swarmboarder.dm +++ /dev/null @@ -1,75 +0,0 @@ -/datum/gm_action/swarm_boarder - name = "swarmer shell" - departments = list(ROLE_EVERYONE, ROLE_SECURITY, ROLE_ENGINEERING) - chaotic = 60 - observers_used = TRUE - var/area/target_area // Chosen target area - var/area/target_turf // Chosen target turf in target_area - var/list/area/excluded = list( - /area/submap, - /area/shuttle, - /area/crew_quarters, - /area/holodeck, - /area/engineering/engine_room - ) - - var/list/area/included = list( - /area/maintenance - ) - -/datum/gm_action/swarm_boarder/set_up() - severity = pickweight(EVENT_LEVEL_MUNDANE = 30, - EVENT_LEVEL_MODERATE = 10, - EVENT_LEVEL_MAJOR = 1 - ) - - var/list/area/grand_list_of_areas = get_station_areas(excluded) - - for(var/area/Incl in included) - for(var/area/A in grand_list_of_areas) - if(!istype(A, Incl)) - grand_list_of_areas -= A - - // Okay, now lets try and pick a target! Lets try 10 times, otherwise give up - for(var/i in 1 to 10) - var/area/A = pick(grand_list_of_areas) - if(is_area_occupied(A)) - log_debug("[name] event: Rejected [A] because it is occupied.") - continue - // A good area, great! Lets try and pick a turf - var/list/turfs = list() - for(var/turf/simulated/floor/F in A) - if(turf_clear(F)) - turfs += F - if(turfs.len == 0) - log_debug("[name] event: Rejected [A] because it has no clear turfs.") - continue - target_area = A - target_turf = pick(turfs) - - if(!target_area) - log_debug("[name] event: Giving up after too many failures to pick target area") - return - -/datum/gm_action/swarm_boarder/start() - if(!target_turf) - return - ..() - - var/swarmertype = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event - - if(severity == EVENT_LEVEL_MODERATE) - swarmertype = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee - - if(severity == EVENT_LEVEL_MAJOR) - swarmertype = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner - - new swarmertype(target_turf) - -/datum/gm_action/swarm_boarder/get_weight() - return max(0, -60 + (metric.count_people_in_department(ROLE_SECURITY) * 10 + metric.count_people_in_department(ROLE_SYNTHETIC) * 5)) - -/datum/gm_action/swarm_boarder/announce() - spawn(rand(5 MINUTES, 15 MINUTES)) - if(prob(80) && severity >= EVENT_LEVEL_MODERATE && atc && !atc.squelched) - atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/actions/viral_infection.dm b/code/modules/gamemaster/actions/viral_infection.dm deleted file mode 100644 index 9b42e2df46..0000000000 --- a/code/modules/gamemaster/actions/viral_infection.dm +++ /dev/null @@ -1,83 +0,0 @@ -/var/global/list/event_viruses = list() // so that event viruses are kept around for admin logs, rather than being GCed - -/datum/gm_action/viral_infection - name = "viral infection" - departments = list(ROLE_MEDICAL) - chaotic = 5 - var/list/viruses = list() - severity = 1 - -/datum/gm_action/viral_infection/set_up() - severity = pickweight(EVENT_LEVEL_MUNDANE = 20, - EVENT_LEVEL_MODERATE = 10, - EVENT_LEVEL_MAJOR = 3 - ) - - //generate 1-3 viruses. This way there's an upper limit on how many individual diseases need to be cured if many people are initially infected - var/num_diseases = rand(1,3) - for (var/i=0, i < num_diseases, i++) - var/datum/disease2/disease/D = new /datum/disease2/disease - - var/strength = 1 //whether the disease is of the greater or lesser variety - if (severity >= EVENT_LEVEL_MAJOR && prob(75)) - strength = 2 - D.makerandom(strength) - viruses += D - -/datum/gm_action/viral_infection/announce() - var/level - if (severity == EVENT_LEVEL_MUNDANE) - return - else if (severity == EVENT_LEVEL_MODERATE) - level = pick("one", "two", "three", "four") - else - level = "five" - - spawn(rand(0, 3000)) - if(severity == EVENT_LEVEL_MAJOR || prob(60)) - command_announcement.Announce("Confirmed outbreak of level [level] biohazard aboard \the [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak5.ogg') - -/datum/gm_action/viral_infection/start() - if(!viruses.len) return - - ..() - - var/list/candidates = list() //list of candidate keys - for(var/mob/living/carbon/human/G in player_list) - if(G.mind && G.stat != DEAD && G.is_client_active(5) && !player_is_antag(G.mind)) - var/turf/T = get_turf(G) - if(T.z in using_map.station_levels) - candidates += G - if(!candidates.len) return - candidates = shuffle(candidates)//Incorporating Donkie's list shuffle - - var/list/used_viruses = list() - var/list/used_candidates = list() - severity = max(EVENT_LEVEL_MUNDANE, severity - 1) - var/actual_severity = severity * rand(1, 3) - while(actual_severity > 0 && candidates.len) - var/datum/disease2/disease/D = pick(viruses) - infect_mob(candidates[1], D.getcopy()) - used_candidates += candidates[1] - candidates.Remove(candidates[1]) - actual_severity-- - used_viruses |= D - - event_viruses |= used_viruses - var/list/used_viruses_links = list() - var/list/used_viruses_text = list() - for(var/datum/disease2/disease/D in used_viruses) - used_viruses_links += "
    [D.name()]" - used_viruses_text += D.name() - - var/list/used_candidates_links = list() - var/list/used_candidates_text = list() - for(var/mob/M in used_candidates) - used_candidates_links += key_name_admin(M) - used_candidates_text += key_name(M) - - log_admin("Virus event affecting [english_list(used_candidates_text)] started; Viruses: [english_list(used_viruses_text)]") - message_admins("Virus event affecting [english_list(used_candidates_links)] started; Viruses: [english_list(used_viruses_links)]") - -/datum/gm_action/viral_infection/get_weight() - return (metric.count_people_in_department(ROLE_MEDICAL) * 20) diff --git a/code/modules/gamemaster/actions/viral_outbreak.dm b/code/modules/gamemaster/actions/viral_outbreak.dm deleted file mode 100644 index f0eb27b41a..0000000000 --- a/code/modules/gamemaster/actions/viral_outbreak.dm +++ /dev/null @@ -1,44 +0,0 @@ -/datum/gm_action/viral_outbreak - name = "viral outbreak" - departments = list(ROLE_MEDICAL, ROLE_EVERYONE) - chaotic = 30 - severity = 1 - var/list/candidates = list() - -/datum/gm_action/viral_outbreak/set_up() - candidates.Cut() // Incase we somehow get run twice. - severity = rand(2, 4) - for(var/mob/living/carbon/human/G in player_list) - if(G.client && G.stat != DEAD) - candidates += G - if(!candidates.len) return - candidates = shuffle(candidates)//Incorporating Donkie's list shuffle - -/datum/gm_action/viral_outbreak/announce() - command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') - -/datum/gm_action/viral_outbreak/start() - ..() - while(severity > 0 && candidates.len) - if(prob(33)) - infect_mob_random_lesser(candidates[1]) - else - infect_mob_random_greater(candidates[1]) - - candidates.Remove(candidates[1]) - severity-- - -/datum/gm_action/viral_outbreak/get_weight() - var/medical = metric.count_people_in_department(ROLE_MEDICAL) - var/security = metric.count_people_in_department(ROLE_SECURITY) - var/everyone = metric.count_people_in_department(ROLE_EVERYONE) - - var/assigned_staff = medical + round(security / 2) - - if(!medical) // No docs, no staff. - assigned_staff = 0 - - if(assigned_staff < round(everyone / 6) - assigned_staff) // A doc or half an officer per roughly six people. Any less, and assigned staff is halved. - assigned_staff /= 2 - - return (assigned_staff * 10) diff --git a/code/modules/gamemaster/actions/wallrot.dm b/code/modules/gamemaster/actions/wallrot.dm deleted file mode 100644 index e2c05aed74..0000000000 --- a/code/modules/gamemaster/actions/wallrot.dm +++ /dev/null @@ -1,43 +0,0 @@ -/datum/gm_action/wallrot - name = "wall rot" - departments = list(ROLE_ENGINEERING) - reusable = TRUE - var/turf/simulated/wall/center - severity = 1 - -/datum/gm_action/wallrot/set_up() - severity = rand(1,3) - center = null - // 100 attempts - for(var/i=0, i<100, i++) - var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), 1) - if(istype(candidate, /turf/simulated/wall)) - center = candidate - return 1 - return 0 - -/datum/gm_action/wallrot/announce() - if(center && prob(min(90,40 * severity))) - command_announcement.Announce("Harmful fungi detected on \the [station_name()] nearby [center.loc.name]. Station structures may be contaminated.", "Biohazard Alert") - -/datum/gm_action/wallrot/start() - ..() - spawn() - if(center) - // Make sure at least one piece of wall rots! - center.rot() - - // Have a chance to rot lots of other walls. - var/rotcount = 0 - var/actual_severity = severity * rand(5, 10) - for(var/turf/simulated/wall/W in range(5, center)) - if(prob(50)) - W.rot() - rotcount++ - - // Only rot up to severity walls - if(rotcount >= actual_severity) - break - -/datum/gm_action/wallrot/get_weight() - return 60 + (metric.count_people_in_department(ROLE_ENGINEERING) * 35) diff --git a/code/modules/gamemaster/actions/waste_disposal.dm b/code/modules/gamemaster/actions/waste_disposal.dm deleted file mode 100644 index e7ba856e78..0000000000 --- a/code/modules/gamemaster/actions/waste_disposal.dm +++ /dev/null @@ -1,9 +0,0 @@ -// A shuttle full of junk docks, and cargo is tasked with sifting through it all to find valuables, or just dispose of it. - -/datum/gm_action/waste_disposal - name = "waste disposal" - departments = list(ROLE_CARGO) - chaotic = 0 - -/datum/gm_action/waste_disposal/get_weight() - return metric.count_people_in_department(ROLE_CARGO) * 50 \ No newline at end of file diff --git a/code/modules/gamemaster/actions/window_break.dm b/code/modules/gamemaster/actions/window_break.dm deleted file mode 100644 index 3cb16cba58..0000000000 --- a/code/modules/gamemaster/actions/window_break.dm +++ /dev/null @@ -1,78 +0,0 @@ -/datum/gm_action/window_break - name = "window breach" - departments = list(ROLE_ENGINEERING) - chaotic = 5 - var/obj/structure/window/chosen_window - var/list/obj/structure/window/collateral_windows - var/turf/chosen_location - var/list/area/excluded = list( - /area/shuttle, - /area/crew_quarters - ) - -/datum/gm_action/window_break/set_up() - var/list/area/grand_list_of_areas = get_station_areas(excluded) - //try 10 times - for(var/i in 1 to 10) - var/area/A = pick(grand_list_of_areas) - var/list/obj/structure/window/possible_target_windows = list() - for(var/obj/structure/window/target_window in A.contents) - possible_target_windows += target_window - possible_target_windows = shuffle(possible_target_windows) - - for(var/obj/structure/window/target_window in possible_target_windows) - //if() don't have any conditions, for isn't strictly necessary - chosen_window = target_window - chosen_location = chosen_window.loc - collateral_windows = gather_collateral_windows(chosen_window) - return - -//TL;DR: breadth first search for all connected turfs with windows -/datum/gm_action/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window) - var/list/turf/frontier_set = list(target_window.loc) - var/list/obj/structure/window/result_set = list() - var/list/turf/explored_set = list() - - while(frontier_set.len > 0) - var/turf/current = frontier_set[1] - frontier_set -= current - explored_set += current - - var/contains_windows = 0 - for(var/obj/structure/window/to_add in current.contents) - contains_windows = 1 - result_set += to_add - - if(contains_windows) - //add adjacent turfs to be checked for windows as well - var/turf/neighbor = locate(current.x + 1, current.y, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x - 1, current.y, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x, current.y + 1, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - neighbor = locate(current.x, current.y - 1, current.z) - if(!(neighbor in frontier_set) && !(neighbor in explored_set)) - frontier_set += neighbor - return result_set - -/datum/gm_action/window_break/start() - if(!chosen_window) - return - ..() - chosen_window.shatter(0) - - spawn() - for(var/obj/structure/window/current_collateral in collateral_windows) - sleep(rand(1,20)) - current_collateral.take_damage(current_collateral.health - (current_collateral.maxhealth / 5)) //set to 1/5th health - -/datum/gm_action/window_break/announce() - if(chosen_window) - command_announcement.Announce("Structural integrity of windows at [chosen_location.loc.name] is failing. Immediate repair or replacement is advised.", "Structural Alert") - -/datum/gm_action/window_break/get_weight() - return 20 * metric.count_people_in_department(ROLE_ENGINEERING) diff --git a/code/modules/gamemaster/actions/wormholes.dm b/code/modules/gamemaster/actions/wormholes.dm deleted file mode 100644 index f2a90f5539..0000000000 --- a/code/modules/gamemaster/actions/wormholes.dm +++ /dev/null @@ -1,23 +0,0 @@ -/datum/gm_action/wormholes - name = "space-time anomalies" - chaotic = 70 - length = 12 MINUTES - departments = list(ROLE_EVERYONE) - severity = 1 - -/datum/gm_action/wormholes/set_up() // 1 out of 5 will be full-duration wormholes, meaning up to a minute long. - severity = pickweight(list( - 3 = 5, - 2 = 7, - 1 = 13 - )) - -/datum/gm_action/wormholes/start() - ..() - wormhole_event(length / 2, (severity / 3)) - -/datum/gm_action/wormholes/get_weight() - return 10 + max(0, -30 + (metric.count_people_in_department(ROLE_EVERYONE) * 5) + (metric.count_people_in_department(ROLE_ENGINEERING) + 10) + (metric.count_people_in_department(ROLE_MEDICAL) * 20)) - -/datum/gm_action/wormholes/end() - command_announcement.Announce("There are no more space-time anomalies detected on the station.", "Anomaly Alert") diff --git a/code/modules/gamemaster/controller.dm b/code/modules/gamemaster/controller.dm deleted file mode 100644 index 343f05842d..0000000000 --- a/code/modules/gamemaster/controller.dm +++ /dev/null @@ -1,92 +0,0 @@ -/client/proc/show_gm_status() - set category = "Debug" - set name = "Show GM Status" - set desc = "Shows you what the GM is thinking. If only that existed in real life..." - - game_master.interact(usr) - -/datum/game_master/proc/interact(var/client/user) - if(!user) - return - - var/HTML = "Game Master AI" - - HTML += "\[Toggle Time Restrictions\] | \ - \[Toggle GM\] | \ - \[Force Event Decision\]
    " - - HTML += "Status: [pre_action_checks() ? "Ready" : "Suppressed"]

    " - - HTML += "Staleness: [staleness] \[Adjust\]
    " - HTML += "Danger: [danger] \[Adjust\]

    " - - HTML += "Actions available;
    " - for(var/datum/gm_action/action in available_actions) - if(action.enabled == FALSE) - continue - HTML += "[action.name] ([english_list(action.departments)]) (weight: [action.get_weight()]) \[Force\]
    " - - HTML += "
    " - HTML += "All living mobs activity: [metric.assess_all_living_mobs()]%
    " - HTML += "All ghost activity: [metric.assess_all_dead_mobs()]%
    " - - HTML += "
    " - HTML += "Departmental activity;
    " - for(var/department in metric.departments) - HTML += " [department] : [metric.assess_department(department)]%
    " - - HTML += "
    " - HTML += "Activity of players;
    " - for(var/mob/player in player_list) - HTML += " [player] ([player.key]) : [metric.assess_player_activity(player)]%
    " - - - - HTML +="" - user << browse(HTML, "window=log;size=400x450;border=1;can_resize=1;can_close=1;can_minimize=1") - -/datum/game_master/Topic(href, href_list) - if(..()) - return - - if(!is_admin(usr)) - message_admins("[usr] has attempted to modify the Game Master values without being an admin.") - return - - if(href_list["toggle_time_restrictions"]) - ignore_time_restrictions = !ignore_time_restrictions - message_admins("GM event time restrictions was [ignore_time_restrictions ? "dis" : "en"]abled by [usr.key].") - - if(href_list["force_choose_event"]) - start_action() - message_admins("[usr.key] forced the Game Master to choose an event immediately.") - - if(href_list["suspend"]) - suspended = !suspended - message_admins("GM was [suspended ? "dis" : "en"]abled by [usr.key].") - - if(href_list["adjust_staleness"]) - var/amount = input(usr, "How much staleness should be added or subtracted?", "Game Master") as null|num - if(amount) - adjust_staleness(amount) - message_admins("GM staleness was adjusted by [amount] by [usr.key].") - - if(href_list["adjust_danger"]) - var/amount = input(usr, "How much danger should be added or subtracted?", "Game Master") as null|num - if(amount) - adjust_danger(amount) - message_admins("GM danger was adjusted by [amount] by [usr.key].") - - interact(usr) // To refresh the UI. - -/datum/gm_action/Topic(href, href_list) - if(..()) - return - - if(!is_admin(usr)) - message_admins("[usr] has attempted to force an event without being an admin.") - return - - if(href_list["force"]) - gm.run_action(src) - message_admins("GM event [name] was forced by [usr.key].") \ No newline at end of file diff --git a/code/modules/gamemaster/defines.dm b/code/modules/gamemaster/defines.dm index 2e486ee23b..5bc17b462c 100644 --- a/code/modules/gamemaster/defines.dm +++ b/code/modules/gamemaster/defines.dm @@ -1 +1,3 @@ -#define EVENT_BASELINE_WEIGHT 200 \ No newline at end of file +#define EVENT_CHAOS_THRESHOLD_HIGH_IMPACT 25 +#define EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT 50 +#define EVENT_CHAOS_THRESHOLD_LOW_IMPACT 100 \ No newline at end of file diff --git a/code/modules/gamemaster/event2/event.dm b/code/modules/gamemaster/event2/event.dm new file mode 100644 index 0000000000..7c69f23c96 --- /dev/null +++ b/code/modules/gamemaster/event2/event.dm @@ -0,0 +1,230 @@ +// This object holds the code that is needed to execute an event. +// Code for judging whether doing that event is a good idea or not belongs inside its meta event object. + + +/* + +Important: DO NOT `sleep()` in any of the procs here, or the GM will get stuck. Use callbacks insead. +Also please don't use spawn(), but use callbacks instead. + +Note that there is an important distinction between an event being ended, and an event being finished. +- Ended is for when the actual event is over, regardless of whether an announcement happened or not. +- Finished is for when both the event itself is over, and it was announced. The event will stop being +processed after it is finished. + +For an event to finish, it must have done two things: +- Go through its entire cycle, of start() -> end(), and +- Have the event be announced. +If an event has ended, but the announcement didn't happen, the event will not be finished. +This allows for events that have their announcement happen after the end itself. + +*/ + +// +/datum/event2/event + var/announced = FALSE // Is set to TRUE when `announce()` is called by `process()`. + var/started = FALSE // Is set to TRUE when `start()` is called by `process()`. + var/ended = FALSE // Is set to TRUE when `end()` is called by `process()`. + var/finished = FALSE // Is set to TRUE when `ended` and `announced` are TRUE. + + // `world.time`s when this event started, and finished, for bookkeeping. + var/time_started = null + var/time_finished = null + + // If these are set, the announcement will be delayed by a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // Note that this is independant of the event itself, so you can have the announcement happen long after the event ended. + // This may not work if should_announce() is overrided. + var/announce_delay_lower_bound = null + var/announce_delay_upper_bound = null + + // If these are set, the event will be delayed by a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // This may not work if should_start() is overrided. + var/start_delay_lower_bound = null + var/start_delay_upper_bound = null + + // If these are set, the event will automatically end at a random time between the lower and upper bounds. + // If the upper bound is not defined, then it will use the lower bound instead. + // This may not work if should_end() is overrided. + var/length_lower_bound = null + var/length_upper_bound = null + + // Set automatically, don't touch. + var/time_to_start = null + var/time_to_announce = null + var/time_to_end = null + + // These are also set automatically, and are provided for events to know what RNG decided for the various durations. + var/start_delay = null + var/announce_delay = null + var/length = null + +// Returns the name of where the event is taking place. +// In the future this might be handy for off-station events. +/datum/event2/event/proc/location_name() + return station_name() + +// Returns the z-levels that are involved with the event. +// In the future this might be handy for off-station events. +/datum/event2/event/proc/get_location_z_levels() + return using_map.station_levels + +// Returns a list of empty turfs in the same area. +/datum/event2/event/proc/find_random_turfs(minimum_free_space = 5, list/specific_areas = list(), ignore_occupancy = FALSE) + var/list/area/grand_list_of_areas = find_random_areas(specific_areas) + + if(!LAZYLEN(grand_list_of_areas)) + return list() + + for(var/thing in grand_list_of_areas) + var/list/A = thing + var/list/turfs = list() + for(var/turf/T in A) + if(!T.check_density()) + turfs += T + + if(turfs.len < minimum_free_space) + continue // Not enough free space. + return turfs + + return list() + +/datum/event2/event/proc/find_random_areas(list/specific_areas = list(), ignore_occupancy = FALSE) + if(!LAZYLEN(specific_areas)) + specific_areas = global.the_station_areas.Copy() + + var/list/area/grand_list_of_areas = get_all_existing_areas_of_types(specific_areas) + . = list() + for(var/thing in shuffle(grand_list_of_areas)) + var/area/A = thing + if(A.forbid_events) + continue + if(!(A.z in get_location_z_levels())) + continue + if(!ignore_occupancy && is_area_occupied(A)) + continue // Occupied. + . += A + +// Starts the event. +/datum/event2/event/proc/execute() + time_started = world.time + + if(announce_delay_lower_bound) + announce_delay = rand(announce_delay_lower_bound, announce_delay_upper_bound ? announce_delay_upper_bound : announce_delay_lower_bound) + time_to_announce = world.time + announce_delay + + if(start_delay_lower_bound) + start_delay = rand(start_delay_lower_bound, start_delay_upper_bound ? start_delay_upper_bound : start_delay_lower_bound) + time_to_start = world.time + start_delay + + if(length_lower_bound) + var/starting_point = time_to_start ? time_to_start : world.time + length = rand(length_lower_bound, length_upper_bound ? length_upper_bound : length_lower_bound) + time_to_end = starting_point + length + + set_up() + +// Called at the very end of the event's lifecycle, or when aborted. +// Don't override this, use `end()` for cleanup instead. +/datum/event2/event/proc/finish() + finished = TRUE + time_finished = world.time + +// Called by admins wanting to stop an event immediately. +/datum/event2/event/proc/abort() + if(!announced) + announce() + if(!ended) // `end()` generally has cleanup procs, so call that. + end() + finish() + +// Called by the GM processer. +/datum/event2/event/process() + // Handle announcement track. + if(!announced && should_announce()) + announced = TRUE + announce() + + // Handle event track. + if(!started) + if(should_start()) + started = TRUE + start() + else + wait_tick() + + if(started && !ended) + if(should_end()) + ended = TRUE + end() + else + event_tick() + + // In order to be finished, the event needs to end, and be announced. + if(ended && announced) + finish() + +/datum/event2/event/Topic(href, href_list) + if(..()) + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") + return + + if(href_list["abort"]) + abort() + message_admins("Event '[type]' was aborted by [usr.key].") + + // SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet. + +/* + * Procs to Override + */ + +// Override this for code to be ran before the event is started. +/datum/event2/event/proc/set_up() + +// Called every tick from the GM system, and determines if the announcement should happen. +// Override this for special logic on when it should be announced, e.g. after `ended` is set to TRUE, +// however be aware that the event cannot finish until this returns TRUE at some point. +/datum/event2/event/proc/should_announce() + if(!time_to_announce) + return TRUE + return time_to_announce <= world.time + +// Override this for code that alerts the crew that the event is happening in some form, e.g. a centcom announcement or some other message. +// If you want them to not know, you can just not override it. +/datum/event2/event/proc/announce() + +// Override for code that runs every few seconds, while the event is waiting for `should_start()` to return TRUE. +// Note that events that have `should_start()` return TRUE at the start will never have this proc called. +/datum/event2/event/proc/wait_tick() + +// Called every tick from the GM system, and determines if the event should offically start. +// Override this for special logic on when it should start. +/datum/event2/event/proc/should_start() + if(!time_to_start) + return TRUE + return time_to_start <= world.time + +// Override this for code to do the actual event. +/datum/event2/event/proc/start() + + +// Override for code that runs every few seconds, while the event is waiting for `should_end()` to return TRUE. +// Note that events that have `should_end()` return TRUE at the start will never have this proc called. +/datum/event2/event/proc/event_tick() + + +// Called every tick from the GM system, and determines if the event should end. +// If this returns TRUE at the very start, then the event ends instantly and `tick()` will never be called. +// Override this for special logic on when it should end, e.g. blob core has to die before event ends. +/datum/event2/event/proc/should_end() + if(!time_to_end) + return TRUE + return time_to_end <= world.time + +// Override this for code to run when the event is over, e.g. cleanup. +/datum/event2/event/proc/end() diff --git a/code/modules/gamemaster/event2/events/cargo/shipping_error.dm b/code/modules/gamemaster/event2/events/cargo/shipping_error.dm new file mode 100644 index 0000000000..eaab32cb8a --- /dev/null +++ b/code/modules/gamemaster/event2/events/cargo/shipping_error.dm @@ -0,0 +1,16 @@ +/datum/event2/meta/shipping_error + name = "shipping error" + departments = list(DEPARTMENT_CARGO) + chaos = -10 // A helpful event. + reusable = TRUE + event_type = /datum/event2/event/shipping_error + +/datum/event2/meta/shipping_error/get_weight() + return (metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm)) * 30 + +/datum/event2/event/shipping_error/start() + var/datum/supply_order/O = new /datum/supply_order() + O.ordernum = SSsupply.ordernum + O.object = SSsupply.supply_pack[pick(SSsupply.supply_pack)] + O.ordered_by = random_name(pick(MALE,FEMALE), species = "Human") + SSsupply.shoppinglist += O diff --git a/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm b/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm new file mode 100644 index 0000000000..0a19ec7a65 --- /dev/null +++ b/code/modules/gamemaster/event2/events/command/manifest_malfunction.dm @@ -0,0 +1,61 @@ +/datum/event2/meta/manifest_malfunction + name = "manifest_malfunction" + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/manifest_malfunction + +/datum/event2/meta/manifest_malfunction/get_weight() + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + + if(!security || !data_core) + return 0 + + var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) + var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (synths + security + command) // So they don't get counted twice. + + return (security * 10) + (synths * 20) + (command * 20) + (everyone * 5) + + + +/datum/event2/event/manifest_malfunction + announce_delay_lower_bound = 5 MINUTES + announce_delay_upper_bound = 10 MINUTES + var/records_to_delete = 2 + var/record_class_to_delete = null + +/datum/event2/event/manifest_malfunction/set_up() + record_class_to_delete = pickweight(list("medical" = 10, "security" = 30)) + +/datum/event2/event/manifest_malfunction/announce() + if(prob(30)) + var/message = null + var/author = null + var/rng = rand(1, 2) + switch(rng) + if(1) + author = "Data Breach Alert" + message = "The [record_class_to_delete] record database has suffered from an attack by one or more hackers. \ + They appear to have wiped several records, before disconnecting." + if(2) + author = "Downtime Alert" + message = "The [record_class_to_delete] record database server has suffered a hardware failure, and is no longer functional. \ + A temporary replacement server has been activated, containing recovered data from the main server. \ + A few records became corrupted, and could not be transferred." + command_announcement.Announce(message, author) + +/datum/event2/event/manifest_malfunction/start() + for(var/i = 1 to records_to_delete) + var/datum/data/record/R + + switch(record_class_to_delete) + if("security") + R = safepick(data_core.security) + + if("medical") + R = safepick(data_core.medical) + + if(R) + log_debug("Manifest malfunction event is now deleting [R.fields["name"]]'s [record_class_to_delete] record.") + qdel(R) diff --git a/code/modules/gamemaster/event2/events/command/money_hacker.dm b/code/modules/gamemaster/event2/events/command/money_hacker.dm new file mode 100644 index 0000000000..a1dbd1e18b --- /dev/null +++ b/code/modules/gamemaster/event2/events/command/money_hacker.dm @@ -0,0 +1,110 @@ +/datum/event2/meta/money_hacker + name = "money hacker" + departments = list(DEPARTMENT_COMMAND) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/money_hacker + +/datum/event2/meta/money_hacker/get_weight() + var/command = metric.count_people_with_job(/datum/job/hop) + metric.count_people_with_job(/datum/job/captain) + + if(!command) + return 0 + return 30 + (command * 20) + (all_money_accounts.len * 5) + + + +/datum/event2/event/money_hacker + length_lower_bound = 8 MINUTES + length_upper_bound = 12 MINUTES + var/datum/money_account/targeted_account = null + +/datum/event2/event/money_hacker/set_up() + if(LAZYLEN(all_money_accounts)) + targeted_account = pick(all_money_accounts) + + if(!targeted_account) + log_debug("Money hacker event could not find an account to hack. Aborting.") + abort() + return + +/datum/event2/event/money_hacker/announce() + var/message = "A brute force hack has been detected (in progress since [stationtime2text()]). The target of the attack is: Financial account #[targeted_account.account_number], \ + without intervention this attack will succeed in approximately 10 minutes. Required intervention: temporary suspension of affected accounts until the attack has ceased. \ + Notifications will be sent as updates occur." + var/my_department = "[location_name()] Firewall Subroutines" + + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) + continue + MS.send_rc_message("Head of Personnel's Desk", my_department, "[message]
    ", "", "", 2) + + // Nobody reads the requests consoles so lets use the radio as well. + global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) + +/datum/event2/event/money_hacker/end() + var/message = null + if(targeted_account && !targeted_account.suspended) // Hacker wins. + message = "The hack attempt has succeeded." + hack_account(targeted_account) + log_debug("Money hacker event managed to hack the targeted account.") + + else // Crew wins. + message = "The attack has ceased, the affected accounts can now be brought online." + log_debug("Money hacker event failed to hack the targeted account due to intervention by the crew.") + + var/my_department = "[location_name()] Firewall Subroutines" + + for(var/obj/machinery/message_server/MS in machines) + if(!MS.active) continue + MS.send_rc_message("Head of Personnel's Desk", my_department, message, "", "", 2) + + global_announcer.autosay(message, my_department, DEPARTMENT_COMMAND) + +/datum/event2/event/money_hacker/proc/hack_account(datum/money_account/A) + // Subtract the money. + var/lost = A.money * 0.8 + (rand(2,4) - 2) / 10 + A.money -= lost + + // Create a taunting log entry. + var/datum/transaction/T = new() + T.target_name = pick(list( + "", + "yo brotha from anotha motha", + "el Presidente", + "chieF smackDowN", + "Nobody" + )) + + T.purpose = pick(list( + "Ne$ ---ount fu%ds init*&lisat@*n", + "PAY BACK YOUR MUM", + "Funds withdrawal", + "pWnAgE", + "l33t hax", + "liberationez", + "Hit", + "Nothing" + )) + + T.amount = pick(list( + "", + "([rand(0,99999)])", + "alla money", + "9001$", + "HOLLA HOLLA GET DOLLA", + "([lost])", + "69,420t" + )) + + var/date1 = "1 January 1970" // Unix epoch. + var/date2 = "[num2text(rand(1,31))] [pick("January","February","March","April","May","June","July","August","September","October","November","December")], [rand(1000,3000)]" + T.date = pick("", current_date_string, date1, date2,"Nowhen") + + var/time1 = rand(0, 99999999) + var/time2 = "[round(time1 / 36000)+12]:[(time1 / 600 % 60) < 10 ? add_zero(time1 / 600 % 60, 1) : time1 / 600 % 60]" + T.time = pick("", stationtime2text(), time2, "Never") + + T.source_terminal = pick("","[pick("Biesel","New Gibson")] GalaxyNet Terminal #[rand(111,999)]","your mums place","nantrasen high CommanD","Angessa's Pearl","Nowhere") + + A.transaction_log.Add(T) diff --git a/code/modules/gamemaster/event2/events/command/raise_funds.dm b/code/modules/gamemaster/event2/events/command/raise_funds.dm new file mode 100644 index 0000000000..31e8816350 --- /dev/null +++ b/code/modules/gamemaster/event2/events/command/raise_funds.dm @@ -0,0 +1,96 @@ +/datum/event2/meta/raise_funds + name = "local funding drive" + enabled = FALSE // There isn't really any suitable way for the crew to generate thalers right now, if that gets fixed feel free to turn this event on. + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_CARGO) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/raise_funds + +/datum/event2/meta/raise_funds/get_weight() + var/command = metric.count_people_in_department(DEPARTMENT_COMMAND) + if(!command) // Need someone to read the centcom message. + return 0 + + var/cargo = metric.count_people_in_department(DEPARTMENT_CARGO) + return (command * 20) + (cargo * 20) + + + +/datum/event2/event/raise_funds + length_lower_bound = 30 MINUTES + length_upper_bound = 45 MINUTES + var/money_at_start = 0 + +/datum/event2/event/raise_funds/announce() + var/message = "Due to [pick("recent", "unfortunate", "possible future")] budget \ + [pick("changes", "issues")], in-system stations are now advised to increase funding income." + + send_command_report("Budget Advisement", message) + +/datum/event2/event/raise_funds/start() + // Note that the event remembers the amount of money when it started. If an issue develops where people try to scam centcom by + // taking out loads of money before the event, then depositing it back in after the event fires, feel free to make this check for + // roundstart money instead. + money_at_start = count_money() + log_debug("Funding Drive event logged a sum of [money_at_start] thalers in all station accounts at the start of the event.") + +/datum/event2/event/raise_funds/end() + var/money_at_end = count_money() + log_debug("Funding Drive event logged a sum of [money_at_end] thalers in all station accounts at the end of the event, compared \ + to [money_at_start] thalers. A difference of [money_at_end / money_at_start] was calculated.") + + // A number above 1 indicates money was made, while below 1 does the opposite. + var/budget_shift = money_at_end / money_at_start + + // Centcom will say different things based on if they gained or lost money. + var/message = null + switch(budget_shift) + if(0 to 0.02) // Abyssmal response. + message = "We are very interested in learning where [round(money_at_start, 1000)] thaler went in \ + just half an hour. We highly recommend rectifying this issue before the end of the shift, otherwise a \ + discussion regarding your future employment prospects will occur.

    \ + Your facility's current balance of requisition tokens has been revoked." + SSsupply.points = 0 + log_debug("Funding Drive event ended with an abyssmal response, and the loss of all cargo points.") + + if(0.02 to 0.98) // Bad response. + message = "We're very disappointed that \the [location_name()] has ran a deficit since our request. \ + As such, we will be taking away some requisition tokens to cover the cost of operating your facility." + var/points_lost = round(SSsupply.points * rand(0.5, 0.8)) + SSsupply.points -= points_lost + log_debug("Funding Drive event ended with a bad response, and [points_lost] cargo points was taken away.") + + if(0.98 to 1.02) // Neutral response. + message = "It is unfortunate that \the [location_name()]'s finances remain at a standstill, however \ + that is still preferred over having a decicit. We hope that in the future, your facility will be able to be \ + more profitable." + log_debug("Funding Drive event ended with a neutral response.") + + if(1.02 to INFINITY) // Good response. + message = "We appreciate the efforts made by \the [location_name()] to run at a surplus. \ + Together, along with the other facilities present in the [using_map.starsys_name] system, \ + the company is expected to meet the quota.

    \ + We will allocate additional requisition tokens for the cargo department as a reward." + + // If cargo is ever made to use station funds instead of cargo points, then a new kind of reward will be needed. + // Otherwise it would be weird for centcom to go 'thanks for not spending money, your reward is money to spend'. + var/point_reward = rand(100, 200) + SSsupply.points += point_reward + log_debug("Funding Drive event ended with a good response and a bonus of [point_reward] cargo points.") + + send_command_report("Budget Followup", message) + + + +// Returns the sum of the station account and all the departmental accounts. +/datum/event2/event/raise_funds/proc/count_money() + . = 0 + . += station_account.money + for(var/i = 1 to SSjob.department_datums.len) + var/datum/money_account/account = LAZYACCESS(department_accounts, SSjob.department_datums[i]) + if(istype(account)) + . += account.money + +/datum/event2/event/raise_funds/proc/send_command_report(title, message) + post_comm_message(title, message) + to_world(span("danger", "New [using_map.company_name] Update available at all communication consoles.")) + SEND_SOUND(world, 'sound/AI/commandreport.ogg') diff --git a/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm b/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm new file mode 100644 index 0000000000..69b596d799 --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/airlock_failure.dm @@ -0,0 +1,100 @@ +/datum/event2/meta/airlock_failure + event_class = "airlock failure" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_MEDICAL) + chaos = 15 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/airlock_failure + +/datum/event2/meta/airlock_failure/emag + name = "airlock failure - emag" + event_type = /datum/event2/event/airlock_failure/emag + +/datum/event2/meta/airlock_failure/door_crush + name = "airlock failure - crushing" + event_type = /datum/event2/event/airlock_failure/door_crush + +/datum/event2/meta/airlock_failure/shock + name = "airlock failure - shock" + chaos = 30 + event_type = /datum/event2/event/airlock_failure/shock + + +/datum/event2/meta/airlock_failure/get_weight() + var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + + // Synths are good both for fixing the doors and getting blamed for the doors zapping people. + var/synths = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + if(!engineering && !synths) // Nobody's around to fix the door. + return 0 + + // Medical might be needed for some of the more violent airlock failures. + var/medical = metric.count_people_in_department(DEPARTMENT_MEDICAL) + + return (engineering * 20) + (medical * 20) + (synths * 20) + + + +/datum/event2/event/airlock_failure + announce_delay_lower_bound = 20 SECONDS + announce_delay_upper_bound = 40 SECONDS + var/announce_odds = 0 + var/doors_to_break = 1 + var/list/affected_areas = list() + +/datum/event2/event/airlock_failure/emag + announce_odds = 10 // To make people wonder if the emagged door was from a baddie or from this event. + doors_to_break = 2 // Replacing emagged doors really sucks for engineering so don't overdo it. + +/datum/event2/event/airlock_failure/door_crush + announce_odds = 30 + doors_to_break = 5 + +/datum/event2/event/airlock_failure/shock + announce_odds = 70 + +/datum/event2/event/airlock_failure/start() + var/list/areas = find_random_areas() + if(!LAZYLEN(areas)) + log_debug("Airlock Failure event could not find any areas. Aborting.") + abort() + return + + while(areas.len) + var/area/area = pick(areas) + areas -= area + + for(var/obj/machinery/door/airlock/door in area.contents) + if(can_break_door(door)) + addtimer(CALLBACK(src, .proc/break_door, door), 1) // Emagging proc is actually a blocking proc and that's bad for the ticker. + door.visible_message(span("danger", "\The [door]'s panel sparks!")) + playsound(door, "sparks", 50, 1) + log_debug("Airlock Failure event has broken \the [door] airlock in [area].") + affected_areas |= area + doors_to_break-- + + if(doors_to_break <= 0) + return + +/datum/event2/event/airlock_failure/announce() + if(prob(announce_odds)) + command_announcement.Announce("An electrical issue has been detected in the airlock grid at [english_list(affected_areas)]. \ + Some airlocks may require servicing by a qualified technician.", "Electrical Alert") + + +/datum/event2/event/airlock_failure/proc/can_break_door(obj/machinery/door/airlock/door) + if(istype(door, /obj/machinery/door/airlock/lift)) + return FALSE + return door.arePowerSystemsOn() + +// Override this for door busting. +/datum/event2/event/airlock_failure/proc/break_door(obj/machinery/door/airlock/door) + +/datum/event2/event/airlock_failure/emag/break_door(obj/machinery/door/airlock/door) + door.emag_act(1) + +/datum/event2/event/airlock_failure/door_crush/break_door(obj/machinery/door/airlock/door) + door.normalspeed = FALSE + door.safe = FALSE + +/datum/event2/event/airlock_failure/shock/break_door(obj/machinery/door/airlock/door) + door.electrify(-1) diff --git a/code/modules/gamemaster/event2/events/engineering/blob.dm b/code/modules/gamemaster/event2/events/engineering/blob.dm new file mode 100644 index 0000000000..3ace8f0894 --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/blob.dm @@ -0,0 +1,163 @@ +/datum/event2/meta/blob + name = "blob" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_class = "blob" // This makes it so there is no potential for multiple blob events of different types happening in the same round. + event_type = /datum/event2/event/blob + // In the distant future, if a mechanical skill system were to come into being, these vars could be replaced with skill checks so off duty people could count. + var/required_fighters = 2 // Fighters refers to engineering OR security. + var/required_support = 1 // Support refers to doctors AND roboticists, depending on fighter composition. + +/datum/event2/meta/blob/hard + name = "harder blob" + chaos = 40 + event_type = /datum/event2/event/blob/hard_blob + required_fighters = 3 + +/datum/event2/meta/blob/multi_blob + name = "multi blob" + chaos = 60 + event_type = /datum/event2/event/blob/multi_blob + required_fighters = 4 + required_support = 2 + +// For bussing only. +/datum/event2/meta/blob/omni_blob + name = "omni blob" + chaos = 200 + event_type = /datum/event2/event/blob/omni_blob + enabled = FALSE + +/datum/event2/meta/blob/get_weight() + // Count the 'fighters'. + var/list/engineers = metric.get_people_in_department(DEPARTMENT_ENGINEERING) + var/list/security = metric.get_people_in_department(DEPARTMENT_SECURITY) + + if(engineers.len + security.len < required_fighters) + return 0 + + // Now count the 'support'. + var/list/medical = metric.get_people_in_department(DEPARTMENT_MEDICAL) + var/need_medical = FALSE + + var/list/robotics = metric.get_people_with_job(/datum/job/roboticist) + var/need_robotics = FALSE + + // Determine what kind of support might be needed. + for(var/mob/living/L in engineers|security) + if(L.isSynthetic()) + need_robotics = TRUE + else + need_medical = TRUE + + // Medical is more important than robotics, since robits tend to not suffer slow deaths if there isn't a roboticist. + if(medical.len < required_support && need_medical) + return 0 + + // Engineers can sometimes fill in as robotics. This is done in the interest of the event having a chance of not being super rare. + // In the uncertain future, a mechanical skill system check could replace this check here. + if(robotics.len + engineers.len < required_support && need_robotics) + return 0 + + var/fighter_weight = (engineers.len + security.len) * 20 + var/support_weight = (medical.len + robotics.len) * 10 // Not counting engineers as support so they don't cause 30 weight each. + var/chaos_weight = chaos / 2 // Chaos is added as a weight in order to make more chaotic variants be preferred if they are allowed to be picked. + + return fighter_weight + support_weight + chaos_weight + + + +/datum/event2/event/blob + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 5 MINUTES + // This could be made into a GLOB accessible list for reuse if needed. + var/list/area/excluded = list( + /area/submap, + /area/shuttle, + /area/crew_quarters, + /area/holodeck, + /area/engineering/engine_room + ) + var/list/open_turfs = list() + var/spawn_blob_type = /obj/structure/blob/core/random_medium + var/number_of_blobs = 1 + var/list/blobs = list() // A list containing weakrefs to blob cores created. Weakrefs mean this event won't interfere with qdel. + +/datum/event2/event/blob/hard_blob + spawn_blob_type = /obj/structure/blob/core/random_hard + +/datum/event2/event/blob/multi_blob + spawn_blob_type = /obj/structure/blob/core/random_hard // Lethargic blobs are boring. + number_of_blobs = 2 + +// For adminbus only. +/datum/event2/event/blob/omni_blob + number_of_blobs = 16 // Someday maybe we can get this to specifically spawn every blob. + +/datum/event2/event/blob/set_up() + open_turfs = find_random_turfs(5 + number_of_blobs) + + if(!open_turfs.len) + log_debug("Blob infestation event: Giving up after failure to find blob spots.") + abort() + +/datum/event2/event/blob/start() + for(var/i = 1 to number_of_blobs) + var/turf/T = pick(open_turfs) + var/obj/structure/blob/core/new_blob = new spawn_blob_type(T) + blobs += weakref(new_blob) + open_turfs -= T // So we can't put two cores on the same tile if doing multiblob. + log_debug("Spawned [new_blob.overmind.blob_type.name] blob at [get_area(new_blob)].") + +/datum/event2/event/blob/should_end() + for(var/WR in blobs) + var/weakref/weakref = WR + if(weakref.resolve()) // If the weakref is resolvable, that means the blob hasn't been deleted yet. + return FALSE + return TRUE // Only end if all blobs die. + +// Normally this does nothing, but is useful if aborted by an admin. +/datum/event2/event/blob/end() + for(var/WR in blobs) + var/weakref/weakref = WR + var/obj/structure/blob/core/B = weakref.resolve() + if(istype(B)) + qdel(B) + +/datum/event2/event/blob/announce() + if(!ended) // Don't announce if the blobs die early. + var/danger_level = 0 + var/list/blob_type_names = list() + var/multiblob = FALSE + for(var/WR in blobs) + var/weakref/weakref = WR + var/obj/structure/blob/core/B = weakref.resolve() + if(!istype(B)) + continue + var/datum/blob_type/blob_type = B.overmind.blob_type + + blob_type_names += blob_type.name + if(danger_level > blob_type.difficulty) // The highest difficulty is used, if multiple blobs are present. + danger_level = blob_type.difficulty + + if(blob_type_names.len > 1) // More than one blob is harder. + danger_level += blob_type_names.len + multiblob = TRUE + + var/list/lines = list() + lines += "Confirmed outbreak of level [7 + danger_level] biohazard[multiblob ? "s": ""] \ + aboard [location_name()]. All personnel must contain the outbreak." + + if(danger_level >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough. + lines += "The biohazard[multiblob ? "s have": " has"] been identified as [english_list(blob_type_names)]." + + if(danger_level >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster. + var/turf/T = open_turfs[1] + var/area/A = T.loc + lines += "[multiblob ? "It is": "They are"] suspected to have originated from \the [A]." + + if(danger_level >= BLOB_DIFFICULTY_SUPERHARD) + lines += "Extreme caution is advised." + + command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') diff --git a/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm b/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm new file mode 100644 index 0000000000..bf1a8dfed6 --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/brand_intelligence.dm @@ -0,0 +1,90 @@ +/datum/event2/meta/brand_intelligence + name = "vending machine malware" + departments = list(DEPARTMENT_EVERYONE, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/brand_intelligence + +/datum/event2/meta/brand_intelligence/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + + + +/datum/event2/event/brand_intelligence + var/malware_spread_cooldown = 30 SECONDS + + var/list/vending_machines = list() // List of venders that can potentially be infected. + var/list/infected_vending_machines = list() // List of venders that have been infected. + var/obj/machinery/vending/vender_zero = null // The first vending machine infected. If that one gets fixed, all other infected machines will be cured. + var/last_malware_spread_time = null + +/datum/event2/event/brand_intelligence/set_up() + for(var/obj/machinery/vending/V in machines) + if(!(V.z in using_map.station_levels)) + continue + vending_machines += V + + if(!vending_machines.len) + log_debug("Could not find any vending machines on station Z levels. Aborting.") + abort() + return + + vender_zero = pick(vending_machines) + +/datum/event2/event/brand_intelligence/announce() + if(prob(90)) + command_announcement.Announce("An ongoing mass upload of malware for venders has been detected onboard \the [location_name()], \ + which appears to transmit to nearby vendors. The original infected machine is believed to be \a [vender_zero].", "Vender Service Alert") + +/datum/event2/event/brand_intelligence/start() + infect_vender(vender_zero) + +/datum/event2/event/brand_intelligence/event_tick() + if(last_malware_spread_time + malware_spread_cooldown > world.time) + return // Still on cooldown. + last_malware_spread_time = world.time + + if(vending_machines.len) + var/next_victim = pick(vending_machines) + infect_vender(next_victim) + + // Every time Vender Zero infects, it says something. + vender_zero.speak(pick("Try our aggressive new marketing strategies!", \ + "You should buy products to feed your lifestyle obsession!", \ + "Consume!", \ + "Your money can buy happiness!", \ + "Engage direct marketing!", \ + "Advertising is legalized lying! But don't let that put you off our great deals!", \ + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")) + + +/datum/event2/event/brand_intelligence/should_end() + if(!vending_machines.len) + return TRUE + if(!can_propagate(vender_zero)) + return TRUE + return FALSE + +/datum/event2/event/brand_intelligence/end() + if(can_propagate(vender_zero)) // The crew failed and all the machines are infected! + return + // Otherwise Vender Zero was taken out in some form. + if(vender_zero) + vender_zero.visible_message(span("notice", "\The [vender_zero]'s network activity light flickers wildly \ + for a few seconds as a small screen reads: 'Rolling out firmware reset to networked machines'.")) + for(var/obj/machinery/vending/vender in infected_vending_machines) + cure_vender(vender) + +/datum/event2/event/brand_intelligence/proc/infect_vender(obj/machinery/vending/V) + vending_machines -= V + infected_vending_machines += V + V.shut_up = FALSE + V.shoot_inventory = TRUE + +/datum/event2/event/brand_intelligence/proc/cure_vender(obj/machinery/vending/V) + infected_vending_machines -= V + V.shut_up = TRUE + V.shoot_inventory = FALSE + +/datum/event2/event/brand_intelligence/proc/can_propagate(obj/machinery/vending/V) + return V && V.shut_up == FALSE diff --git a/code/modules/gamemaster/event2/events/engineering/camera_damage.dm b/code/modules/gamemaster/event2/events/engineering/camera_damage.dm new file mode 100644 index 0000000000..c24fbb06f3 --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/camera_damage.dm @@ -0,0 +1,41 @@ +/datum/event2/meta/camera_damage + name = "random camera damage" + departments = list(DEPARTMENT_SYNTHETIC, DEPARTMENT_ENGINEERING) + chaos = 5 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/camera_damage + +/datum/event2/meta/camera_damage/get_weight() + return 30 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + (metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 40) + +/datum/event2/event/camera_damage + var/camera_range = 7 + +/datum/event2/event/camera_damage/start() + var/obj/machinery/camera/C = acquire_random_camera() + if(!C) + return + + for(var/obj/machinery/camera/cam in range(camera_range, C)) + if(is_valid_camera(cam)) + cam.wires.UpdateCut(CAMERA_WIRE_POWER, 0) + if(prob(25)) + cam.wires.UpdateCut(CAMERA_WIRE_ALARM, 0) + +/datum/event2/event/camera_damage/proc/acquire_random_camera(var/remaining_attempts = 5) + if(!cameranet.cameras.len) + return + if(!remaining_attempts) + return + + var/obj/machinery/camera/C = pick(cameranet.cameras) + if(is_valid_camera(C)) + return C + // It is very important to use --var and not var-- for recursive calls, as var-- will cause an infinite loop. + return acquire_random_camera(--remaining_attempts) + +/datum/event2/event/camera_damage/proc/is_valid_camera(var/obj/machinery/camera/C) + // Only return a functional camera, not installed in a silicon/hardsuit/circuit/etc, and that exists somewhere players have access + var/turf/T = get_turf(C) + return T && C?.can_use() && istype(C.loc, /turf) && (T.z in using_map.player_levels) \ No newline at end of file diff --git a/code/modules/gamemaster/actions/canister_leak.dm b/code/modules/gamemaster/event2/events/engineering/canister_leak.dm similarity index 58% rename from code/modules/gamemaster/actions/canister_leak.dm rename to code/modules/gamemaster/event2/events/engineering/canister_leak.dm index a294f95736..a48823151c 100644 --- a/code/modules/gamemaster/actions/canister_leak.dm +++ b/code/modules/gamemaster/event2/events/engineering/canister_leak.dm @@ -1,24 +1,26 @@ -// -// This event chooses a random canister on player levels and breaks it, releasing its contents! -// - -/datum/gm_action/canister_leak - name = "Canister Leak" - departments = list(ROLE_ENGINEERING) - chaotic = 20 - -/datum/gm_action/canister_leak/get_weight() - return metric.count_people_in_department(ROLE_ENGINEERING) * 30 - -/datum/gm_action/canister_leak/start() - ..() - // List of all non-destroyed canisters on station levels - var/list/all_canisters = list() - for(var/obj/machinery/portable_atmospherics/canister/C in machines) - if(!C.destroyed && (C.z in using_map.station_levels) && C.air_contents.total_moles >= MOLES_CELLSTANDARD) - all_canisters += C - var/obj/machinery/portable_atmospherics/canister/C = pick(all_canisters) - log_debug("canister_leak event: Canister [C] ([C.x],[C.y],[C.z]) destroyed.") - C.health = 0 - C.healthcheck() - return +// +// This event chooses a random canister on player levels and breaks it, releasing its contents! +// + +/datum/event2/meta/canister_leak + name = "canister leak" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/canister_leak + +/datum/event2/meta/canister_leak/get_weight() + return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 30 + +/datum/event2/event/canister_leak/start() + // List of all non-destroyed canisters on station levels + var/list/all_canisters = list() + for(var/obj/machinery/portable_atmospherics/canister/C in machines) + if(!C.destroyed && (C.z in using_map.station_levels) && C.air_contents.total_moles >= MOLES_CELLSTANDARD) + all_canisters += C + var/obj/machinery/portable_atmospherics/canister/C = pick(all_canisters) + log_debug("canister_leak event: Canister [C] ([C.x],[C.y],[C.z]) destroyed.") + C.health = 0 + C.healthcheck() + diff --git a/code/modules/gamemaster/actions/carp_migration_vr.dm b/code/modules/gamemaster/event2/events/engineering/carp_migration_vr.dm similarity index 100% rename from code/modules/gamemaster/actions/carp_migration_vr.dm rename to code/modules/gamemaster/event2/events/engineering/carp_migration_vr.dm diff --git a/code/modules/gamemaster/event2/events/engineering/dust.dm b/code/modules/gamemaster/event2/events/engineering/dust.dm new file mode 100644 index 0000000000..1a26118fbc --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/dust.dm @@ -0,0 +1,19 @@ +/datum/event2/meta/dust + name = "dust" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/dust + +/datum/event2/meta/dust/get_weight() + return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20 + + + +/datum/event2/event/dust/announce() + if(prob(33)) + command_announcement.Announce("Dust has been detected on a collision course with \the [location_name()].") + +/datum/event2/event/dust/start() + dust_swarm("norm") diff --git a/code/modules/gamemaster/event2/events/engineering/gas_leak.dm b/code/modules/gamemaster/event2/events/engineering/gas_leak.dm new file mode 100644 index 0000000000..fb8dc41f86 --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/gas_leak.dm @@ -0,0 +1,47 @@ +/datum/event2/meta/gas_leak + name = "gas leak" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_SYNTHETIC) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/gas_leak + +/datum/event2/meta/gas_leak/get_weight() + // Synthetics are counted in higher value because they can wirelessly connect to alarms. + var/engineering_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/synthetic_factor = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) * 30 + return (15 + engineering_factor + synthetic_factor) / (times_ran + 1) + + + +/datum/event2/event/gas_leak + var/potential_gas_choices = list("carbon_dioxide", "sleeping_agent", "phoron", "volatile_fuel") + var/chosen_gas = null + var/turf/chosen_turf = null + +/datum/event2/event/gas_leak/set_up() + chosen_gas = pick(potential_gas_choices) + + var/list/turfs = find_random_turfs() + if(!turfs.len) + log_debug("Gas Leak event failed to find any available turfs to leak into. Aborting.") + abort() + return + chosen_turf = pick(turfs) + +/datum/event2/event/gas_leak/announce() + if(chosen_turf) + command_announcement.Announce("Warning, hazardous [lowertext(gas_data.name[chosen_gas])] gas leak detected in \the [chosen_turf.loc], evacuate the area.", "Hazard Alert") + +/datum/event2/event/gas_leak/start() + // Okay, time to actually put the gas in the room! + // TODO - Would be nice to break a waste pipe perhaps? + // TODO - Maybe having it released from a single point and thus causing airflow to blow stuff around + + // Fow now just add a bunch of it to the air + + var/datum/gas_mixture/air_contents = new + air_contents.temperature = T20C + rand(-50, 50) + air_contents.gas[chosen_gas] = 10 * MOLES_CELLSTANDARD + chosen_turf.assume_air(air_contents) + playsound(chosen_turf, 'sound/effects/smoke.ogg', 75, 1) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/engineering/grid_check.dm b/code/modules/gamemaster/event2/events/engineering/grid_check.dm new file mode 100644 index 0000000000..8b081f29e2 --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/grid_check.dm @@ -0,0 +1,52 @@ +// New grid check event: +// Very similar to the old one, power goes out in most of the station, however the new feature is the ability for engineering to +// get power back on sooner, if they are able to reach a special machine and initiate a manual reboot. If no one is able to do so, +// it will reboot itself after a few minutes, just like the old one. Bad things happen if there is no grid checker machine protecting +// the powernet when this event fires. + +/datum/event2/meta/grid_check + name = "grid check" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/grid_check + +// Having the turbines be way over their rated limit makes grid checks more likely. +/datum/event2/meta/grid_check/proc/get_overpower() + var/highest_overpower = 0 + for(var/T in GLOB.all_turbines) + var/obj/machinery/power/generator/turbine = T + var/overpower = max((turbine.effective_gen / turbine.max_power) - 1, 0) + if(overpower > highest_overpower) + highest_overpower = overpower + return highest_overpower + +/datum/event2/meta/grid_check/get_weight() + var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/overpower_factor = 50 * get_overpower() // Will be 0 if not overloaded at all, and 50 if turbines are outputting twice as much as rated. + return (20 + population_factor + overpower_factor) / (times_ran + 1) + + + +/datum/event2/event/grid_check + var/obj/machinery/power/generator/engine // The turbine that will send a power spike. + +/datum/event2/event/grid_check/set_up() + // Find the turbine being pushed the most. + var/obj/machinery/power/generator/most_stressed_turbine = null + for(var/T in GLOB.all_turbines) + var/obj/machinery/power/generator/turbine = T + if(!most_stressed_turbine) + most_stressed_turbine = turbine + else if(turbine.effective_gen > most_stressed_turbine.effective_gen) + most_stressed_turbine = turbine + engine = most_stressed_turbine + +/datum/event2/event/grid_check/start() + // This sets off a chain of events that lead to the actual grid check (or perhaps worse). + // First, the Supermatter engine makes a power spike. + if(engine) + engine.power_spike() + // After that, the engine checks if a grid checker exists on the same powernet, and if so, it triggers a blackout. + // If not, lots of stuff breaks. See code/modules/power/generator.dm for that piece of code. diff --git a/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm b/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm new file mode 100644 index 0000000000..378c6b42fe --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/meteor_defense.dm @@ -0,0 +1,83 @@ +// This event gives the station an advance warning about meteors, so that they can prepare in various ways. + +/datum/event2/meta/meteor_defense + name = "meteor defense" + departments = list(DEPARTMENT_ENGINEERING, DEPARTMENT_CARGO) + chaos = 50 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_class = "meteor defense" + event_type = /datum/event2/event/meteor_defense + +/datum/event2/meta/meteor_defense/get_weight() + // Engineers count as 20. + var/engineers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + if(engineers < 3) // There -must- be at least three engineers for this to be possible. + return 0 + + . = engineers * 20 + + // Cargo and AI/borgs count as 10. + var/cargo = metric.count_people_with_job(/datum/job/cargo_tech) + metric.count_people_with_job(/datum/job/qm) + var/bots = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + + . += (cargo + bots) * 10 + + +/datum/event2/event/meteor_defense + start_delay_lower_bound = 10 MINUTES + start_delay_upper_bound = 15 MINUTES + var/soon_announced = FALSE + var/direction = null // Actual dir used for which side the meteors come from. + var/dir_text = null // Direction shown in the announcement. + var/list/meteor_types = null + var/waves = null // How many times to send meteors. + var/last_wave_time = null // world.time of latest wave. + var/wave_delay = 10 SECONDS + var/wave_upper_bound = 8 // Max amount of meteors per wave. + var/wave_lower_bound = 4 // Min amount. + +/datum/event2/event/meteor_defense/proc/set_meteor_types() + meteor_types = meteors_threatening.Copy() + +/datum/event2/event/meteor_defense/set_up() + direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately. + waves = rand(3, 6) + switch(direction) + if(NORTH) + dir_text = "aft" // For some reason this is needed. + if(SOUTH) + dir_text = "fore" + if(EAST) + dir_text = "port" + if(WEST) + dir_text = "starboard" + set_meteor_types() + +/datum/event2/event/meteor_defense/announce() + var/announcement = "Meteors are expected to approach from the [dir_text] side, in approximately [DisplayTimeText(time_to_start - world.time, 60)]." + command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') + +/datum/event2/event/meteor_defense/wait_tick() + if(!soon_announced) + if((time_to_start - world.time) <= 5 MINUTES) + soon_announced = TRUE + var/announcement = "The incoming meteors are expected to approach from the [dir_text] side. \ + ETA to arrival is approximately [DisplayTimeText(time_to_start - world.time, 60)]." + command_announcement.Announce(announcement, "Meteor Alert - Update") + +/datum/event2/event/meteor_defense/start() + command_announcement.Announce("Incoming meteors approach from \the [dir_text] side!", "Meteor Alert - Update") + +/datum/event2/event/meteor_defense/event_tick() + if(world.time > last_wave_time + wave_delay) + last_wave_time = world.time + waves-- + message_admins("[waves] more wave\s of meteors remain.") + // Dir is reversed because the direction describes where meteors are going, not what side it's gonna hit. + spawn_meteors(rand(wave_upper_bound, wave_lower_bound), meteor_types, reverse_dir[direction]) + +/datum/event2/event/meteor_defense/should_end() + return waves <= 0 + +/datum/event2/event/meteor_defense/end() + command_announcement.Announce("\The [location_name()] will clear the incoming meteors in a moment.", "Meteor Alert - Update") diff --git a/code/modules/gamemaster/event2/events/engineering/spacevine.dm b/code/modules/gamemaster/event2/events/engineering/spacevine.dm new file mode 100644 index 0000000000..5dd5d6b3a1 --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/spacevine.dm @@ -0,0 +1,17 @@ +/datum/event2/meta/spacevine + name = "space-vine infestation" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 // There's a really rare chance of vines getting something awful like phoron atmosphere but thats not really controllable. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/spacevine + +/datum/event2/meta/spacevine/get_weight() + return 20 + (metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20) + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 10) + + + +/datum/event2/event/spacevine/announce() + level_seven_announcement() + +/datum/event2/event/spacevine/start() + spacevine_infestation() diff --git a/code/modules/gamemaster/event2/events/engineering/wallrot.dm b/code/modules/gamemaster/event2/events/engineering/wallrot.dm new file mode 100644 index 0000000000..29d12c8524 --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/wallrot.dm @@ -0,0 +1,46 @@ +/datum/event2/meta/wallrot + name = "wall-rot" + departments = list(DEPARTMENT_ENGINEERING) + reusable = TRUE + event_type = /datum/event2/event/wallrot + +/datum/event2/meta/wallrot/get_weight() + return (10 + metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10) / (times_ran + 1) + + + + +/datum/event2/event/wallrot + var/turf/simulated/wall/origin = null + +/datum/event2/event/wallrot/set_up() + for(var/i = 1 to 100) + var/turf/candidate = locate(rand(1, world.maxx), rand(1, world.maxy), pick(get_location_z_levels()) ) + if(istype(candidate, /turf/simulated/wall)) + origin = candidate + log_debug("Wall-rot event has chosen \the [origin] ([origin.loc]) as the origin for the wallrot infestation.") + return + + log_debug("Wall-rot event failed to find a valid wall after one hundred tries. Aborting.") + abort() + +/datum/event2/event/wallrot/announce() + if(origin && prob(80)) + command_announcement.Announce("Harmful fungi detected on \the [location_name()], near \the [origin.loc]. \ + Station structural integrity may be compromised.", "Biohazard Alert") + +/datum/event2/event/wallrot/start() + if(origin) + origin.rot() + + var/rot_count = 0 + var/target_rot = rand(5, 20) + for(var/turf/simulated/wall/W in range(7, origin)) + if(prob(50)) + if(W.rot()) + rot_count++ + if(rot_count >= target_rot) + break + + + diff --git a/code/modules/gamemaster/event2/events/engineering/window_break.dm b/code/modules/gamemaster/event2/events/engineering/window_break.dm new file mode 100644 index 0000000000..0b02e78b3d --- /dev/null +++ b/code/modules/gamemaster/event2/events/engineering/window_break.dm @@ -0,0 +1,148 @@ +// This event causes a random window near space to become damaged. +// If that window is not fixed in a certain amount of time, +// that window and nearby windows will shatter, causing a breach. + +/datum/event2/meta/window_break + name = "window break" + departments = list(DEPARTMENT_ENGINEERING) + chaos = 10 + reusable = TRUE + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/window_break + +/datum/event2/meta/window_break/get_weight() + return metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 20 + + + +/datum/event2/event/window_break + announce_delay_lower_bound = 10 SECONDS + announce_delay_upper_bound = 20 SECONDS + length_lower_bound = 8 MINUTES + length_upper_bound = 12 MINUTES + var/turf/chosen_turf_with_windows = null + var/obj/structure/window/chosen_window = null + var/list/collateral_windows = list() + +/datum/event2/event/window_break/set_up() + var/list/areas = find_random_areas() + if(!LAZYLEN(areas)) + log_debug("Window Break event could not find any areas. Aborting.") + abort() + return + + while(areas.len) + var/area/area = pick(areas) + areas -= area + + for(var/obj/structure/window/W in area.contents) + if(!is_window_to_space(W)) + continue + chosen_turf_with_windows = get_turf(W) + collateral_windows = gather_collateral_windows(W) + break // Break out of the inner loop. + + if(chosen_turf_with_windows) + log_debug("Window Break event has chosen turf '[chosen_turf_with_windows.name]' in [chosen_turf_with_windows.loc].") + break // Then the outer loop. + + if(!chosen_turf_with_windows) + log_debug("Window Break event could not find a turf with valid windows to break. Aborting.") + abort() + return + +/datum/event2/event/window_break/announce() + if(chosen_window) + command_announcement.Announce("Structural integrity of space-facing windows at \the [get_area(chosen_turf_with_windows)] are failing. \ + Repair of the damaged window is advised. Personnel without EVA suits in the area should leave until repairs are complete.", "Structural Alert") + +/datum/event2/event/window_break/start() + if(!chosen_turf_with_windows) + return + + for(var/obj/structure/window/W in chosen_turf_with_windows.contents) + if(W.is_fulltile()) // Full tile windows are simple and can always be used. + chosen_window = W + break + else // Otherwise we only want the window that is on the inside side of the station. + var/turf/T = get_step(W, W.dir) + if(T.is_space()) + continue + if(T.check_density()) + continue + chosen_window = W + break + + if(!chosen_window) + return + + chosen_window.take_damage(chosen_window.maxhealth * 0.8) + playsound(chosen_window, 'sound/effects/Glasshit.ogg', 100, 1) + chosen_window.visible_message(span("danger", "\The [chosen_window] suddenly begins to crack!")) + +/datum/event2/event/window_break/should_end() + . = ..() + if(!.) // If the timer didn't expire, we can still end it early if someone messes up. + if(!chosen_window || !chosen_window.anchored || chosen_window.health == chosen_window.maxhealth) + // If the window got deconstructed/moved/etc, immediately end and make the breach happen. + // Also end early if it was repaired. + return TRUE + +/datum/event2/event/window_break/end() + // If someone fixed the window, then everything is fine. + if(chosen_window && chosen_window.anchored && chosen_window.health == chosen_window.maxhealth) + log_debug("Window Break event ended with window repaired.") + return + + // Otherwise a bunch of windows shatter. + chosen_window?.shatter() + + var/windows_to_shatter = min(rand(4, 10), collateral_windows.len) + for(var/i = 1 to windows_to_shatter) + var/obj/structure/window/W = collateral_windows[i] + W?.shatter() + + log_debug("Window Break event ended with [windows_to_shatter] shattered windows and a breach.") + +// Checks if a window is adjacent to a space tile, and also that the opposite direction is open. +// This is done to avoid getting caught in corner parts of windows. +/datum/event2/event/window_break/proc/is_window_to_space(obj/structure/window/W) + for(var/direction in GLOB.cardinal) + var/turf/T = get_step(W, direction) + if(T.is_space()) + var/turf/opposite_T = get_step(W, GLOB.reverse_dir[direction]) + if(!opposite_T.check_density()) + return TRUE + return FALSE + +//TL;DR: breadth first search for all connected turfs with windows +/datum/event2/event/window_break/proc/gather_collateral_windows(var/obj/structure/window/target_window) + var/list/turf/frontier_set = list(target_window.loc) + var/list/obj/structure/window/result_set = list() + var/list/turf/explored_set = list() + + while(frontier_set.len > 0) + var/turf/current = frontier_set[1] + frontier_set -= current + explored_set += current + + var/contains_windows = 0 + for(var/obj/structure/window/to_add in current.contents) + contains_windows = 1 + result_set += to_add + + if(contains_windows) + //add adjacent turfs to be checked for windows as well + var/turf/neighbor = locate(current.x + 1, current.y, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x - 1, current.y, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x, current.y + 1, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + neighbor = locate(current.x, current.y - 1, current.z) + if(!(neighbor in frontier_set) && !(neighbor in explored_set)) + frontier_set += neighbor + return result_set diff --git a/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm b/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm new file mode 100644 index 0000000000..6da19c32cc --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/comms_blackout.dm @@ -0,0 +1,43 @@ +/datum/event2/meta/comms_blackout + name = "communications blackout" + departments = list(DEPARTMENT_EVERYONE) // It's not an engineering event because engineering can't do anything to help . . . for now. + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + reusable = TRUE + event_type = /datum/event2/event/comms_blackout + +/datum/event2/meta/comms_blackout/get_weight() + return 50 + metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 + + + +/datum/event2/event/comms_blackout/announce() + var/alert = pick("Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ + "Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v¬-BZZZT", \ + "Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \ + "Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \ + "Ionospheri:%£ MCayj^j<.3-BZZZZZZT", \ + "#4nd%;f4y6,>£%-BZZZZZZZT") + if(prob(33)) + command_announcement.Announce(alert, new_sound = 'sound/misc/interference.ogg') + // AIs will always know if there's a comm blackout, rogue AIs could then lie about comm blackouts in the future while they shutdown comms + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, "
    ") + to_chat(A, "[alert]") + to_chat(A, "
    ") + +/datum/event2/event/comms_blackout/start() + if(prob(50)) + // One in two chance for the radios to turn i%t# t&_)#%, which can be more alarming than radio silence. + log_debug("Doing partial outage of telecomms.") + for(var/obj/machinery/telecomms/processor/P in telecomms_list) + P.emp_act(1) + else + // Otherwise just shut everything down, madagascar style. + log_debug("Doing complete outage of telecomms.") + for(var/obj/machinery/telecomms/T in telecomms_list) + T.emp_act(1) + + // Communicators go down no matter what. + for(var/obj/machinery/exonet_node/N in machines) + N.emp_act(1) diff --git a/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm b/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm new file mode 100644 index 0000000000..6467646f1b --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/electrical_fault.dm @@ -0,0 +1,99 @@ +// Makes a spooky electrical thing happen, that can blow the lights or make the APCs turn off for a short period of time. +// Doesn't do any permanent damage beyond the small chance to emag an APC, which just unlocks it forever. As such, this is free to occur even with no engineers. +// Since this is an 'external' thing, the Grid Checker can't stop it. + +/datum/event2/meta/electrical_fault + name = "electrical fault" + departments = list(DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/electrical_fault + +/datum/event2/meta/electrical_fault/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5) + + +/datum/event2/event/electrical_fault + start_delay_lower_bound = 30 SECONDS + start_delay_upper_bound = 1 MINUTE + length_lower_bound = 20 SECONDS + length_upper_bound = 40 SECONDS + var/max_apcs_per_tick = 6 + + var/list/valid_apcs = null + var/list/valid_z_levels = null + + var/apcs_disabled = 0 + var/apcs_overloaded = 0 + var/apcs_emagged = 0 + +/datum/event2/event/electrical_fault/announce() + // Trying to be vague to avoid 'space lightning storms'. + // This could be re-flavored to be a solar flare or something and have robots outside be sad. + command_announcement.Announce("External conditions near \the [location_name()] are likely \ + to cause voltage spikes and other electrical issues very soon. Please secure sensitive electrical equipment until the situation passes.", "[location_name()] Sensor Array") + +/datum/event2/event/electrical_fault/set_up() + valid_z_levels = get_location_z_levels() + valid_z_levels -= using_map.sealed_levels // Space levels only please! + + valid_apcs = list() + for(var/obj/machinery/power/apc/A in global.machines) + if(A.z in valid_z_levels) + valid_apcs += A + +/datum/event2/event/electrical_fault/start() + command_announcement.Announce("Irregularities detected in \the [location_name()] power grid.", "[location_name()] Power Grid Monitoring") + +/datum/event2/event/electrical_fault/event_tick() + if(!valid_apcs.len) + log_debug("ELECTRICAL EVENT: No valid APCs found for electrical fault event. Aborting.") + abort() + return + + var/list/picked_apcs = list() + for(var/i = 1 to max_apcs_per_tick) + picked_apcs |= pick(valid_apcs) + + for(var/A in picked_apcs) + affect_apc(A) + +/datum/event2/event/electrical_fault/end() + command_announcement.Announce("The irregular electrical conditions inside \the [location_name()] power grid has ceased.", "[location_name()] Power Grid Monitoring") + log_debug("Electrical Fault event caused [apcs_disabled] APC\s to shut off, \ + [apcs_overloaded] APC\s to overload lighting, and [apcs_emagged] APC\s to be emagged.") + +/datum/event2/event/electrical_fault/proc/affect_apc(obj/machinery/power/apc/A) + // Main breaker is turned off or is Special(tm). Consider it protected. + // Important APCs like the AI or the engine core shouldn't get shut off by this event. + if((!A.operating || A.failure_timer > 0) || A.is_critical) + return + + // In reality this would probably make the lights get brighter but oh well. + for(var/obj/machinery/light/L in get_area(A)) + L.flicker(rand(10, 20)) + + // Chance to make the APC turn off for awhile. + // This will actually protect it from further damage. + if(prob(25)) + A.energy_fail(rand(60, 120)) + log_debug("ELECTRICAL EVENT: Disabled \the [A]'s power for a temporary amount of time.") + playsound(A, 'sound/machines/defib_success.ogg', 50, 1) + apcs_disabled++ + return + + // Decent chance to overload lighting circuit. + if(prob(30)) + A.overload_lighting() + log_debug("ELECTRICAL EVENT: Overloaded \the [A]'s lighting.") + playsound(A, 'sound/effects/lightningshock.ogg', 50, 1) + apcs_overloaded++ + + // Relatively small chance to emag the apc as apc_damage event does. + if(prob(5)) + A.emagged = TRUE + A.update_icon() + log_debug("ELECTRICAL EVENT: Emagged \the [A].") + playsound(A, 'sound/machines/chime.ogg', 50, 1) + apcs_emagged++ + diff --git a/code/modules/gamemaster/event2/events/everyone/gravity.dm b/code/modules/gamemaster/event2/events/everyone/gravity.dm new file mode 100644 index 0000000000..c57f3bd919 --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/gravity.dm @@ -0,0 +1,34 @@ +/datum/event2/meta/gravity + name = "gravity failure" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + reusable = TRUE + event_type = /datum/event2/event/gravity + +/datum/event2/meta/gravity/get_weight() + return (20 + (metric.count_people_in_department(DEPARTMENT_EVERYONE) * 20)) / (times_ran + 1) + + + + +/datum/event2/event/gravity + length_lower_bound = 5 MINUTES + length_upper_bound = 10 MINUTES + +/datum/event2/event/gravity/announce() + command_announcement.Announce("Feedback surge detected in mass-distributions systems. \ + Artificial gravity has been disabled whilst the system reinitializes. \ + Please stand by while the gravity system reinitializes.", "Gravity Failure") + +/datum/event2/event/gravity/start() + for(var/area/A in world) + if(A.z in get_location_z_levels()) + A.gravitychange(FALSE) + +/datum/event2/event/gravity/end() + for(var/area/A in world) + if(A.z in get_location_z_levels()) + A.gravitychange(TRUE) + + command_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.", "Gravity Restored") \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/everyone/infestation.dm b/code/modules/gamemaster/event2/events/everyone/infestation.dm new file mode 100644 index 0000000000..f51456f362 --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/infestation.dm @@ -0,0 +1,72 @@ +/datum/event2/meta/infestation + event_class = "infestation" + departments = list(DEPARTMENT_EVERYONE) + +/datum/event2/meta/infestation/get_weight() + return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 10 + +/datum/event2/meta/infestation/rodents + name = "infestation - rodents" + event_type = /datum/event2/event/infestation/rodents + +/datum/event2/meta/infestation/lizards + name = "infestation - lizards" + event_type = /datum/event2/event/infestation/lizards + +/datum/event2/meta/infestation/spiderlings + name = "infestation - spiders" + event_type = /datum/event2/event/infestation/spiderlings + + +/datum/event2/event/infestation + var/vermin_string = null + var/max_vermin = 0 + var/list/things_to_spawn = list() + + var/list/turfs = list() + +/datum/event2/event/infestation/rodents + vermin_string = "rodents" + max_vermin = 12 + things_to_spawn = list( + /mob/living/simple_mob/animal/passive/mouse/gray, + /mob/living/simple_mob/animal/passive/mouse/brown, + /mob/living/simple_mob/animal/passive/mouse/white, + /mob/living/simple_mob/animal/passive/mouse/rat + ) + +/datum/event2/event/infestation/lizards + vermin_string = "lizards" + max_vermin = 6 + things_to_spawn = list( + /mob/living/simple_mob/animal/passive/lizard, + /mob/living/simple_mob/animal/passive/lizard/large, + /mob/living/simple_mob/animal/passive/lizard/large/defensive + ) + +/datum/event2/event/infestation/spiderlings + vermin_string = "spiders" + max_vermin = 3 + things_to_spawn = list(/obj/effect/spider/spiderling/non_growing) + + +/datum/event2/event/infestation/set_up() + turfs = find_random_turfs(max_vermin) + if(!turfs.len) + log_debug("Infestation event failed to find any valid turfs. Aborting.") + abort() + return + +/datum/event2/event/infestation/announce() + var/turf/T = turfs[1] + command_announcement.Announce("Bioscans indicate that [vermin_string] have been breeding \ + in \the [T.loc]. Clear them out, before this starts to affect productivity.", "Vermin infestation") + + +/datum/event2/event/infestation/start() + var/vermin_to_spawn = rand(2, max_vermin) + for(var/i = 1 to vermin_to_spawn) + var/turf/T = pick(turfs) + turfs -= T + var/spawn_type = pick(things_to_spawn) + new spawn_type(T) diff --git a/code/modules/gamemaster/event2/events/everyone/pda_spam.dm b/code/modules/gamemaster/event2/events/everyone/pda_spam.dm new file mode 100644 index 0000000000..b56687283c --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/pda_spam.dm @@ -0,0 +1,137 @@ +/datum/event2/meta/pda_spam + name = "pda spam" + departments = list(DEPARTMENT_EVERYONE) + event_type = /datum/event2/event/pda_spam + +/datum/event2/meta/pda_spam/get_weight() + return metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 + + +/datum/event2/event/pda_spam + length_lower_bound = 30 MINUTES + length_upper_bound = 1 HOUR + var/spam_debug = FALSE // If true, notices of the event sending spam go to `log_debug()`. + var/last_spam_time = null // world.time of most recent spam. + var/next_spam_attempt_time = 0 // world.time of next attempt to try to spam. + var/give_up_after = 5 MINUTES + var/obj/machinery/message_server/MS = null + var/obj/machinery/exonet_node/node = null + +/datum/event2/event/pda_spam/set_up() + last_spam_time = world.time // So it won't immediately give up. + MS = pick_message_server() + node = get_exonet_node() + +/datum/event2/event/pda_spam/event_tick() + if(!can_spam()) + return + + if(world.time < next_spam_attempt_time) + return + + next_spam_attempt_time = world.time + rand(30 SECONDS, 2 MINUTES) + + var/obj/item/device/pda/P = null + var/list/viables = list() + + for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) + if(!check_pda.owner || check_pda.toff || check_pda.hidden || check_pda.spam_proof) + continue + viables += check_pda + + if(!viables.len) + return + + P = pick(viables) + var/list/spam = generate_spam() + + if(MS.send_pda_message("[P.owner]", spam[1], spam[2])) // Message been filtered by spam filter. + return + + send_spam(P, spam[1], spam[2]) + + +/datum/event2/event/pda_spam/should_end() + . = ..() + if(!.) + // Give up if nobody was reachable for five minutes. + if(last_spam_time + give_up_after < world.time) + log_debug("PDA Spam event giving up after not being able to spam for awhile.") + return TRUE + +/datum/event2/event/pda_spam/proc/can_spam() + if(!node || !node.on || !node.allow_external_PDAs) + node = get_exonet_node() + return FALSE + + if(!MS || !MS.active) + MS = pick_message_server() + return FALSE + + return TRUE + +// Returns a list containing two items, the sender and message. +/datum/event2/event/pda_spam/proc/generate_spam() + var/sender = null + var/message = null + switch(rand(1, 7)) + if(1) + sender = pick("MaxBet","MaxBet Online Casino","There is no better time to register","I'm excited for you to join us") + message = pick("Triple deposits are waiting for you at MaxBet Online when you register to play with us.",\ + "You can qualify for a 200% Welcome Bonus at MaxBet Online when you sign up today.",\ + "Once you are a player with MaxBet, you will also receive lucrative weekly and monthly promotions.",\ + "You will be able to enjoy over 450 top-flight casino games at MaxBet.") + if(2) + sender = pick(300;"QuickDatingSystem",200;"Find your russian bride",50;"Tajaran beauties are waiting",50;"Find your secret skrell crush",50;"Beautiful unathi brides") + message = pick("Your profile caught my attention and I wanted to write and say hello (QuickDating).",\ + "If you will write to me on my email [pick(first_names_female)]@[pick(last_names)].[pick("ru","ck","tj","ur","nt")] I shall necessarily send you a photo (QuickDating).",\ + "I want that we write each other and I hope, that you will like my profile and you will answer me (QuickDating).",\ + "You have (1) new message!",\ + "You have (2) new profile views!") + if(3) + sender = pick("Galactic Payments Association","Better Business Bureau","[using_map.starsys_name] E-Payments","NAnoTransen Finance Deparmtent","Luxury Replicas") + message = pick("Luxury watches for Blowout sale prices!",\ + "Watches, Jewelry & Accessories, Bags & Wallets !",\ + "Deposit 100$ and get 300$ totally free!",\ + " 100K NT.|WOWGOLD nly $89 ",\ + "We have been filed with a complaint from one of your customers in respect of their business relations with you.",\ + "We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..") + if(4) + sender = pick("Buy Dr. Maxman","Having dysfuctional troubles?") + message = pick("DR MAXMAN: REAL Doctors, REAL Science, REAL Results!",\ + "Dr. Maxman was created by George Acuilar, M.D, a [using_map.boss_short] Certified Urologist who has treated over 70,000 patients sector wide with 'male problems'.",\ + "After seven years of research, Dr Acuilar and his team came up with this simple breakthrough male enhancement formula.",\ + "Men of all species report AMAZING increases in length, width and stamina.") + if(5) + sender = pick("Dr","Crown prince","King Regent","Professor","Captain") + sender += " " + pick("Robert","Alfred","Josephat","Kingsley","Sehi","Zbahi") + sender += " " + pick("Mugawe","Nkem","Gbatokwia","Nchekwube","Ndim","Ndubisi") + message = pick("YOUR FUND HAS BEEN MOVED TO [uppertext(pick("Salusa","Segunda","Cepheus","Andromeda","Gruis","Corona","Aquila","ARES","Asellus"))] DEVELOPMENTARY BANK FOR ONWARD REMITTANCE.",\ + "We are happy to inform you that due to the delay, we have been instructed to IMMEDIATELY deposit all funds into your account",\ + "Dear fund beneficiary, We have please to inform you that overdue funds payment has finally been approved and released for payment",\ + "Due to my lack of agents I require an off-world financial account to immediately deposit the sum of 1 POINT FIVE MILLION credits.",\ + "Greetings sir, I regretfully to inform you that as I lay dying here due to my lack ofheirs I have chosen you to recieve the full sum of my lifetime savings of 1.5 billion credits") + if(6) + sender = pick("[using_map.company_name] Morale Divison","Feeling Lonely?","Bored?","www.wetskrell.nt") + message = pick("The [using_map.company_name] Morale Division wishes to provide you with quality entertainment sites.",\ + "WetSkrell.nt is a xenophillic website endorsed by NT for the use of male crewmembers among it's many stations and outposts.",\ + "Wetskrell.nt only provides the higest quality of male entertaiment to [using_map.company_name] Employees.",\ + "Simply enter your [using_map.company_name] Bank account system number and pin. With three easy steps this service could be yours!") + if(7) + sender = pick("You have won free tickets!","Click here to claim your prize!","You are the 1000th vistor!","You are our lucky grand prize winner!") + message = pick("You have won tickets to the newest ACTION JAXSON MOVIE!",\ + "You have won tickets to the newest crime drama DETECTIVE MYSTERY IN THE CLAMITY CAPER!",\ + "You have won tickets to the newest romantic comedy 16 RULES OF LOVE!",\ + "You have won tickets to the newest thriller THE CULT OF THE SLEEPING ONE!") + return list(sender, message) + +/datum/event2/event/pda_spam/proc/send_spam(obj/item/device/pda/P, sender, message) + last_spam_time = world.time + P.spam_message(sender, message) + if(spam_debug) + log_debug("PDA Spam event sent spam to \the [P].") + + +/datum/event2/event/pda_spam/proc/pick_message_server() + if(LAZYLEN(message_servers)) + return pick(message_servers) diff --git a/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm b/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm new file mode 100644 index 0000000000..ebd137b404 --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/radiation_storm.dm @@ -0,0 +1,49 @@ +/datum/event2/meta/radiation_storm + name = "radiation storm" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/radiation_storm + +/datum/event2/meta/radiation_storm/get_weight() + var/medical_factor = metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 + var/population_factor = metric.count_people_in_department(DEPARTMENT_EVERYONE) * 5 // Note medical people will get counted twice at 25 weight. + return 20 + medical_factor + population_factor + + + +/datum/event2/event/radiation_storm + start_delay_lower_bound = 1 MINUTE + length_lower_bound = 1 MINUTE + +/datum/event2/event/radiation_storm/announce() + command_announcement.Announce("High levels of radiation detected near \the [location_name()]. \ + Please evacuate into one of the shielded maintenance tunnels.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + make_maint_all_access() + +/datum/event2/event/radiation_storm/start() + command_announcement.Announce("The station has entered the radiation belt. \ + Please remain in a sheltered area until we have passed the radiation belt.", "Anomaly Alert") + +/datum/event2/event/radiation_storm/event_tick() + radiate() + +/datum/event2/event/radiation_storm/proc/radiate() + var/radiation_level = rand(15, 35) + for(var/z in using_map.station_levels) + SSradiation.z_radiate(locate(1, 1, z), radiation_level, 1) + +/datum/event2/event/radiation_storm/end() + command_announcement.Announce("The station has passed the radiation belt. \ + Please allow for up to one minute while radiation levels dissipate, and report to \ + medbay if you experience any unusual symptoms. Maintenance will lose all \ + access again shortly.", "Anomaly Alert") + addtimer(CALLBACK(src, .proc/maint_callback), 2 MINUTES) + +/datum/event2/event/radiation_storm/proc/maint_callback() + revoke_maint_all_access() + + +// There is no actual radiation during a fake storm. +/datum/event2/event/radiation_storm/fake/radiate() + return diff --git a/code/modules/gamemaster/event2/events/everyone/random_antag.dm b/code/modules/gamemaster/event2/events/everyone/random_antag.dm new file mode 100644 index 0000000000..fe3b58be8c --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/random_antag.dm @@ -0,0 +1,31 @@ +// No idea if this is needed for autotraitor or not. +// If it is, it shouldn't depend on the event system, but fixing that would be it's own project. +// If not, it can stay off until an admin wants to play with it. + +/datum/event2/meta/random_antagonist + name = "random antagonist" + enabled = FALSE + reusable = TRUE + chaos = 0 // This is zero due to the event system not being able to know if an antag actually got spawned or not. + departments = list(DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/random_antagonist + +// This has an abnormally high weight due to antags being very important for the round, +// however the weight will decay with more antags, and more attempts to add antags. +/datum/event2/meta/random_antagonist/get_weight() + var/antags = metric.count_all_antags() + return 200 / (antags + times_ran + 1) + + + +// The random spawn proc on the antag datum will handle announcing the spawn and whatnot, in theory. +/datum/event2/event/random_antagonist/start() + var/list/valid_types = list() + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag.flags & ANTAG_RANDSPAWN) + valid_types |= antag + if(valid_types.len) + var/datum/antagonist/antag = pick(valid_types) + antag.attempt_random_spawn() diff --git a/code/modules/gamemaster/event2/events/everyone/solar_storm.dm b/code/modules/gamemaster/event2/events/everyone/solar_storm.dm new file mode 100644 index 0000000000..b73df66076 --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/solar_storm.dm @@ -0,0 +1,52 @@ +/datum/event2/meta/solar_storm + name = "solar storm" + reusable = TRUE + event_type = /datum/event2/event/solar_storm + +/datum/event2/meta/solar_storm/get_weight() + var/population_factor = metric.count_people_in_department(DEPARTMENT_ENGINEERING) * 10 + var/space_factor = metric.count_all_space_mobs() * 50 + return (20 + population_factor + space_factor) / (times_ran + 1) + + +/datum/event2/event/solar_storm + start_delay_lower_bound = 1 MINUTE + start_delay_upper_bound = 1 MINUTE + length_lower_bound = 2 MINUTES + length_upper_bound = 4 MINUTES + var/base_solar_gen_rate = null + +/datum/event2/event/solar_storm/announce() + command_announcement.Announce("A solar storm has been detected approaching \the [station_name()]. \ + Please halt all EVA activites immediately and return to the interior of the station.", "Anomaly Alert", new_sound = 'sound/AI/radiation.ogg') + adjust_solar_output(1.5) + +/datum/event2/event/solar_storm/start() + command_announcement.Announce("The solar storm has reached the station. Please refain from EVA and remain inside the station until it has passed.", "Anomaly Alert") + adjust_solar_output(5) + +/datum/event2/event/solar_storm/event_tick() + radiate() + +/datum/event2/event/solar_storm/end() + command_announcement.Announce("The solar storm has passed the station. It is now safe to resume EVA activities. \ + Please report to medbay if you experience any unusual symptoms.", "Anomaly Alert") + adjust_solar_output(1) + +/datum/event2/event/solar_storm/proc/adjust_solar_output(var/mult = 1) + if(isnull(base_solar_gen_rate)) + base_solar_gen_rate = GLOB.solar_gen_rate + GLOB.solar_gen_rate = mult * base_solar_gen_rate + +/datum/event2/event/solar_storm/proc/radiate() + // Note: Too complicated to be worth trying to use the radiation system for this. Its only in space anyway, so we make an exception in this case. + for(var/mob/living/L in player_list) + var/turf/T = get_turf(L) + if(!T) + continue + + if(!istype(T.loc,/area/space) && !istype(T,/turf/space)) //Make sure you're in a space area or on a space turf + continue + + //Todo: Apply some burn damage from the heat of the sun. Until then, enjoy some moderate radiation. + L.rad_act(rand(15, 30)) diff --git a/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm b/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm new file mode 100644 index 0000000000..2d83209612 --- /dev/null +++ b/code/modules/gamemaster/event2/events/everyone/sudden_weather_shift.dm @@ -0,0 +1,45 @@ +/datum/event2/meta/sudden_weather_shift + name = "sudden weather shift" + departments = list(DEPARTMENT_EVERYONE) + reusable = TRUE + event_type = /datum/event2/event/sudden_weather_shift + +/datum/event2/meta/sudden_weather_shift/get_weight() + // The proc name is a bit misleading, it only counts players outside, not all mobs. + return (metric.count_all_outdoor_mobs() * 20) / (times_ran + 1) + +/datum/event2/event/sudden_weather_shift + start_delay_lower_bound = 30 SECONDS + start_delay_upper_bound = 1 MINUTE + var/datum/planet/chosen_planet = null + +/datum/event2/event/sudden_weather_shift/set_up() + if(!LAZYLEN(SSplanets.planets)) + log_debug("Weather shift event was ran when no planets exist. Aborting.") + abort() + return + + chosen_planet = pick(SSplanets.planets) + +/datum/event2/event/sudden_weather_shift/announce() + if(!chosen_planet) + return + command_announcement.Announce("Local weather patterns on [chosen_planet.name] suggest that a \ + sudden atmospheric fluctuation has occurred. All groundside personnel should be wary of \ + rapidly deteriorating conditions.", "Weather Alert") + +/datum/event2/event/sudden_weather_shift/start() + // Using the roundstart weather list is handy, because it avoids the chance of choosing a bus-only weather. + // It also makes this event generic and suitable for other planets besides the main one, with no additional code needed. + // Only flaw is that roundstart weathers are -usually- safe ones, but we can fix that by tweaking a copy of it. + var/list/weather_choices = chosen_planet.weather_holder.roundstart_weather_chances.Copy() + var/list/new_weather_weights = list() + + // A lazy way of inverting the odds is to use some division. + for(var/weather in weather_choices) + new_weather_weights[weather] = 100 / weather_choices[weather] + + // Now choose a new weather. + var/new_weather = pickweight(new_weather_weights) + log_debug("Sudden weather shift event is now changing [chosen_planet.name]'s weather to [new_weather].") + chosen_planet.weather_holder.change_weather(new_weather) diff --git a/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm b/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm new file mode 100644 index 0000000000..4c462d06d6 --- /dev/null +++ b/code/modules/gamemaster/event2/events/ghost_pod_spawner.dm @@ -0,0 +1,21 @@ +// Generic subtype for events that make ghost pods. + +/datum/event2/event/ghost_pod_spawner + var/pod_type = null + var/list/desired_turf_areas = list() // If this is left empty, it will default to a global list of 'station' turfs. + var/list/free_turfs = list() + +/datum/event2/event/ghost_pod_spawner/set_up() + free_turfs = find_random_turfs(5, desired_turf_areas) + + if(!free_turfs.len) + log_debug("Ghost Pod Spawning event failed to find a place to spawn. Aborting.") + abort() + return + +/datum/event2/event/ghost_pod_spawner/start() + var/obj/structure/ghost_pod/pod = new pod_type(pick(free_turfs)) + post_pod_creation(pod) + +// Override to do things to the pod after it's spawned. +/datum/event2/event/ghost_pod_spawner/proc/post_pod_creation(obj/structure/ghost_pod/pod) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/legacy/legacy.dm b/code/modules/gamemaster/event2/events/legacy/legacy.dm new file mode 100644 index 0000000000..6f70cafa85 --- /dev/null +++ b/code/modules/gamemaster/event2/events/legacy/legacy.dm @@ -0,0 +1,63 @@ +// This is a somewhat special type of event, that bridges to the old event datum and makes it work with the new system. +// It acts as a compatability layer between the old event, and the new GM system. +// This is possible because the new datum is mostly a superset of the old one. +/datum/event2/event/legacy + var/datum/event/legacy_event = null + + // Used to emulate legacy's `activeFor` tick counter. + var/tick_count = 0 + + // How 'severe' the legacy event should be. This should only be used for legacy events, as severity is an outdated concept for the GM system. + var/severity = EVENT_LEVEL_MODERATE + +/datum/event2/meta/legacy/get_weight() + return 50 + +/datum/event2/event/legacy/process() + ..() + tick_count++ + +/datum/event2/event/legacy/set_up() + legacy_event = new legacy_event(null, external_use = TRUE) + legacy_event.severity = severity + legacy_event.setup() + +/datum/event2/event/legacy/should_announce() + return tick_count >= legacy_event.announceWhen + +/datum/event2/event/legacy/announce() + legacy_event.announce() + + +// Legacy events don't tick before they start, so we don't need to do `wait_tick()`. + +/datum/event2/event/legacy/should_start() + return tick_count >= legacy_event.startWhen + +/datum/event2/event/legacy/start() + legacy_event.start() + +/datum/event2/event/legacy/event_tick() + legacy_event.tick() + + +/datum/event2/event/legacy/should_end() + return tick_count >= legacy_event.endWhen + +/datum/event2/event/legacy/end() + legacy_event.end() + +/datum/event2/event/legacy/finish() + legacy_event.kill(external_use = TRUE) + ..() + +// Proof of concept. +/* +/datum/event2/meta/legacy_gravity + name = "gravity (legacy)" + reusable = TRUE + event_type = /datum/event2/event/legacy/gravity + +/datum/event2/event/legacy/gravity + legacy_event = /datum/event/gravity +*/ \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/medical/appendicitis.dm b/code/modules/gamemaster/event2/events/medical/appendicitis.dm new file mode 100644 index 0000000000..27f5284020 --- /dev/null +++ b/code/modules/gamemaster/event2/events/medical/appendicitis.dm @@ -0,0 +1,36 @@ +/datum/event2/meta/appendicitis + name = "appendicitis" + departments = list(DEPARTMENT_MEDICAL) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/appendicitis + +/datum/event2/meta/appendicitis/get_weight() + var/list/doctors = metric.get_people_with_job(/datum/job/doctor) + + doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/nurse) + doctors -= metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) + doctors += metric.get_people_with_job(/datum/job/cmo) + + return doctors.len * 10 + + + +/datum/event2/event/appendicitis/start() + for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) + // Don't do it to SSD people. + if(!H.client) + continue + + // Or antags. + if(player_is_antag(H.mind)) + continue + + // Or doctors (otherwise it could be possible for the only surgeon to need surgery). + if(H in metric.get_people_with_job(/datum/job/doctor) ) + continue + + if(H.appendicitis()) + log_debug("Appendicitis event gave appendicitis to \the [H].") + return + log_debug("Appendicitis event could not find a valid victim.") diff --git a/code/modules/gamemaster/event2/events/medical/virus.dm b/code/modules/gamemaster/event2/events/medical/virus.dm new file mode 100644 index 0000000000..58791d2ad1 --- /dev/null +++ b/code/modules/gamemaster/event2/events/medical/virus.dm @@ -0,0 +1,69 @@ +/datum/event2/meta/virus + name = "viral infection" + event_class = "virus" + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + event_type = /datum/event2/event/virus + +/datum/event2/meta/virus/superbug + name = "viral superbug" + chaos = 60 + event_type = /datum/event2/event/virus/superbug + +/datum/event2/meta/virus/outbreak + name = "viral outbreak" + chaos = 60 + event_type = /datum/event2/event/virus/outbreak + +/datum/event2/meta/virus/get_weight() + var/list/virologists = metric.get_people_with_alt_title(/datum/job/doctor, /datum/alt_title/virologist) + virologists += metric.get_people_with_job(/datum/job/cmo) + + return virologists.len * 25 + + + +/datum/event2/event/virus + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 3 MINUTES + var/number_of_viruses = 1 + var/virus_power = 2 // Ranges from 1 to 3, with 1 being the weakest. + var/list/candidates = list() + +// A single powerful virus. +/datum/event2/event/virus/superbug + virus_power = 3 + +// A lot of weaker viruses. +/datum/event2/event/virus/outbreak + virus_power = 1 + number_of_viruses = 3 + + + +/datum/event2/event/virus/set_up() + for(var/mob/living/carbon/human/H in player_list) + if(H.client && !H.isSynthetic() && H.stat != DEAD && !player_is_antag(H.mind)) + candidates += H + candidates = shuffle(candidates) + +/datum/event2/event/virus/announce() + command_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard \the [location_name()]. \ + All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg') + +/datum/event2/event/virus/start() + if(!candidates.len) + log_debug("Virus event could not find any valid targets to infect. Aborting.") + abort() + return + + for(var/i = 1 to number_of_viruses) + var/mob/living/carbon/human/H = LAZYACCESS(candidates, 1) + if(!H) + return + var/datum/disease2/disease/D = new() + D.makerandom(virus_power) + log_debug("Virus event is now infecting \the [H] with a new random virus.") + infect_mob(H, D) + candidates -= H diff --git a/code/modules/gamemaster/event2/events/mob_spawning.dm b/code/modules/gamemaster/event2/events/mob_spawning.dm new file mode 100644 index 0000000000..78b1eac69c --- /dev/null +++ b/code/modules/gamemaster/event2/events/mob_spawning.dm @@ -0,0 +1,99 @@ +// A subtype that involves spawning mobs like carp, rogue drones, spiders, etc. + +/datum/event2/event/mob_spawning + var/list/spawned_mobs = list() + var/use_map_edge_with_landmarks = TRUE // Use both landmarks and spawning from the "edge" of the map. Otherise uses landmarks over map edge. + var/landmark_name = "carpspawn" // Which landmark to use for spawning. + +// Spawns a specific mob from the "edge" of the map, and makes them go towards the station. +// Can also use landmarks, if desired. +/datum/event2/event/mob_spawning/proc/spawn_mobs_in_space(mob_type, number_of_groups, min_size_of_group, max_size_of_group, dir) + if(isnull(dir)) + dir = pick(GLOB.cardinal) + + var/list/valid_z_levels = get_location_z_levels() + valid_z_levels -= using_map.sealed_levels // Space levels only please! + + // Check if any landmarks exist! + var/list/spawn_locations = list() + for(var/obj/effect/landmark/C in landmarks_list) + if(C.name == landmark_name && (C.z in valid_z_levels)) + spawn_locations.Add(C.loc) + + var/prioritize_landmarks = TRUE + if(use_map_edge_with_landmarks && prob(50)) + prioritize_landmarks = FALSE // One in two chance to come from the edge instead. + + if(spawn_locations.len && prioritize_landmarks) // Okay we've got landmarks, lets use those! + shuffle_inplace(spawn_locations) + number_of_groups = min(number_of_groups, spawn_locations.len) + var/i = 1 + while (i <= number_of_groups) + var/group_size = rand(min_size_of_group, max_size_of_group) + for (var/j = 0, j < group_size, j++) + spawn_one_mob(spawn_locations[i], mob_type) + i++ + return + + // Okay we did *not* have any landmarks, or we're being told to do both, so lets do our best! + var/i = 1 + while(i <= number_of_groups) + var/z_level = pick(valid_z_levels) + var/group_size = rand(min_size_of_group, max_size_of_group) + var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), z_level) + var/turf/group_center = pick_random_edge_turf(dir, z_level, TRANSITIONEDGE + 2) + var/list/turfs = getcircle(group_center, 2) + for(var/j = 0, j < group_size, j++) + // On larger maps, BYOND gets in the way of letting simple_mobs path to the closest edge of the station. + // So instead we need to simulate the mob's travel, then spawn them somewhere still hopefully off screen. + + // Find a turf to be the edge of the map. + var/turf/edge_of_map = turfs[(i % turfs.len) + 1] + + // Now walk a straight line towards the center of the map, until we find a non-space tile. + var/turf/edge_of_station = null + + var/list/space_line = list() // This holds all space tiles on the line. Will be used a bit later. + for(var/turf/T in getline(edge_of_map, map_center)) + if(!T.is_space()) + break // We found the station! + space_line += T + edge_of_station = T + + // Now put the mob somewhere on the line, hopefully off screen. + // I wish this was higher than 8 but the BYOND internal A* algorithm gives up sometimes when using + // 16 or more. + // In the future, a new AI stance that handles long distance travel using getline() could work. + var/max_distance = 8 + var/turf/spawn_turf = null + for(var/P in space_line) + var/turf/point = P + if(get_dist(point, edge_of_station) <= max_distance) + spawn_turf = P + break + + if(spawn_turf) + // Finally, make the simple_mob go towards the edge of the station. + var/mob/living/simple_mob/M = spawn_one_mob(spawn_turf, mob_type) + if(edge_of_station) + M.ai_holder?.give_destination(edge_of_station) // Ask simple_mobs to fly towards the edge of the station. + i++ + +/datum/event2/event/mob_spawning/proc/spawn_one_mob(new_loc, mob_type) + var/mob/living/simple_mob/M = new mob_type(new_loc) + GLOB.destroyed_event.register(M, src, .proc/on_mob_destruction) + spawned_mobs += M + return M + +// Counts living simple_mobs spawned by this event. +/datum/event2/event/mob_spawning/proc/count_spawned_mobs() + . = 0 + for(var/I in spawned_mobs) + var/mob/living/simple_mob/M = I + if(!QDELETED(M) && M.stat != DEAD) + . += 1 + +// If simple_mob is bomphed, remove it from the list. +/datum/event2/event/mob_spawning/proc/on_mob_destruction(mob/M) + spawned_mobs -= M + GLOB.destroyed_event.unregister(M, src, .proc/on_mob_destruction) \ No newline at end of file diff --git a/code/modules/gamemaster/event2/events/security/carp_migration.dm b/code/modules/gamemaster/event2/events/security/carp_migration.dm new file mode 100644 index 0000000000..12f08f3edd --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/carp_migration.dm @@ -0,0 +1,49 @@ +/datum/event2/meta/carp_migration + name = "carp migration" + event_class = "carp" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/mob_spawning/carp_migration + +/datum/event2/meta/carp_migration/get_weight() + return 10 + (metric.count_people_in_department(DEPARTMENT_SECURITY) * 20) + (metric.count_all_space_mobs() * 40) + + +/datum/event2/event/mob_spawning/carp_migration + announce_delay_lower_bound = 1 MINUTE + announce_delay_upper_bound = 2 MINUTES + length_lower_bound = 30 SECONDS + length_upper_bound = 1 MINUTE + var/carp_cap = 30 // No more than this many (living) carp can exist from this event. + var/carp_smallest_group = 3 + var/carp_largest_group = 5 + var/carp_wave_cooldown = 10 SECONDS + + var/last_carp_wave_time = null // Last world.time we spawned a carp wave. + +/datum/event2/event/mob_spawning/carp_migration/announce() + var/announcement = "Unknown biological entities been detected near \the [location_name()], please stand-by." + command_announcement.Announce(announcement, "Lifesign Alert") + +/datum/event2/event/mob_spawning/carp_migration/event_tick() + if(last_carp_wave_time + carp_wave_cooldown > world.time) + return + last_carp_wave_time = world.time + + if(count_spawned_mobs() < carp_cap) + spawn_mobs_in_space( + mob_type = /mob/living/simple_mob/animal/space/carp/event, + number_of_groups = rand(1, 4), + min_size_of_group = carp_smallest_group, + max_size_of_group = carp_largest_group + ) + +/datum/event2/event/mob_spawning/carp_migration/end() + // Clean up carp that died in space for some reason. + for(var/mob/living/simple_mob/SM in spawned_mobs) + if(SM.stat == DEAD) + var/turf/T = get_turf(SM) + if(istype(T, /turf/space)) + if(prob(75)) + qdel(SM) diff --git a/code/modules/gamemaster/event2/events/security/drill_announcement.dm b/code/modules/gamemaster/event2/events/security/drill_announcement.dm new file mode 100644 index 0000000000..271e2d75f1 --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/drill_announcement.dm @@ -0,0 +1,22 @@ +/datum/event2/meta/security_drill + name = "security drill" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // Don't run if we just got hit by meteors. + event_type = /datum/event2/event/security_drill + +/datum/event2/meta/security_drill/get_weight() + var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) + + if(!sec) // If there's no security, then there is no drill. + return 0 + if(everyone - sec < 0) // If there's no non-sec, then there is no drill. + return 0 + + // Each security player adds +5 weight, while non-security adds +1.5. + return (sec * 5) + ((everyone - sec) * 1.5) + +/datum/event2/event/security_drill/announce() + command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] \ + has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", \ + "a bomb", "an emergent intelligence")] drill with the personnel onboard \the [location_name()].", "Security Advisement") diff --git a/code/modules/gamemaster/event2/events/security/prison_break.dm b/code/modules/gamemaster/event2/events/security/prison_break.dm new file mode 100644 index 0000000000..c178d1f990 --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/prison_break.dm @@ -0,0 +1,227 @@ + +// Type for inheritence. +// It has a null name, so it won't be ran. +/datum/event2/meta/prison_break + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + // The weight system can check if people are in these areas. + // This isn't the same list as what the event itself will break, as the event will also + // break open areas inbetween the holding area and the public hallway, like the brig area verses + // the prison area. + var/list/relevant_areas = list() + var/list/irrelevant_areas = list() + +/datum/event2/meta/prison_break/get_weight() + // First, don't do this if nobody can fix the doors. + var/door_fixers = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + if(!door_fixers) + return 0 + var/list/afflicted_departments = departments.Copy() + var/afflicted_crew = 0 + + afflicted_departments -= DEPARTMENT_SYNTHETIC + for(var/D in afflicted_departments) + afflicted_crew += metric.count_people_in_department(D) + + // Don't do it if nobody is around to ""appreciate"" it. + if(!afflicted_crew) + return 0 + + var/trapped = get_odds_from_trapped_mobs() + + return 10 + (door_fixers * 20) + (afflicted_crew * 10) + trapped + +// This is overriden to have specific events trigger more often based on who is trapped in where, if applicable. +/datum/event2/meta/prison_break/proc/get_odds_from_trapped_mobs() + return 0 + +/datum/event2/meta/prison_break/proc/is_mob_in_relevant_area(mob/living/L) + var/area/A = get_area(L) + if(!A) + return FALSE + if(is_type_in_list(A, relevant_areas) && !is_type_in_list(A, irrelevant_areas)) + return TRUE + return FALSE + +/datum/event2/meta/prison_break/brig + name = "prison break - brig" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) + event_type = /datum/event2/event/prison_break/brig + relevant_areas = list( + /area/security/prison, + /area/security/security_cell_hallway, + /area/security/security_processing, + /area/security/interrogation + ) + +/datum/event2/meta/prison_break/brig/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/L in player_list) + if(is_mob_in_relevant_area(L)) + // Don't count them if they're in security. + if(!(L in metric.count_people_in_department(DEPARTMENT_SECURITY))) + . += 40 + + +/datum/event2/meta/prison_break/armory + name = "prison break - armory" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_SYNTHETIC) + chaos = 40 // Potentially free guns. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/armory + +/datum/event2/meta/prison_break/bridge + name = "prison break - bridge" + departments = list(DEPARTMENT_COMMAND, DEPARTMENT_SYNTHETIC) + chaos = 40 // Potentially free spare ID. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/bridge + +/datum/event2/meta/prison_break/xenobio + name = "prison break - xenobio" + departments = list(DEPARTMENT_RESEARCH, DEPARTMENT_SYNTHETIC) + chaos = 20 // This one is more likely to actually kill someone. + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/prison_break/xenobio + relevant_areas = list(/area/rnd/xenobiology) + irrelevant_areas = list( + /area/rnd/xenobiology/xenoflora, + /area/rnd/xenobiology/xenoflora_storage + ) + +/datum/event2/meta/prison_break/xenobio/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/simple_mob/slime/xenobio/X in living_mob_list) + if(is_mob_in_relevant_area(X)) + . += 5 + + +/datum/event2/meta/prison_break/virology + name = "prison break - virology" + departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SYNTHETIC) + event_type = /datum/event2/event/prison_break/virology + relevant_areas = list( + /area/medical/virology, + /area/medical/virologyaccess + ) + +/datum/event2/meta/prison_break/virology/get_odds_from_trapped_mobs() + . = 0 + for(var/mob/living/L in player_list) + if(is_mob_in_relevant_area(L)) + // Don't count them if they're in medical. + if(!(L in metric.count_people_in_department(DEPARTMENT_MEDICAL))) + . += 40 + + + + +/datum/event2/event/prison_break + start_delay_lower_bound = 3 MINUTES + start_delay_upper_bound = 4 MINUTES + length_lower_bound = 40 SECONDS + length_upper_bound = 1 MINUTE + var/area_display_name = null // A string used to describe the area being messed with. + var/containment_display_desc = null + var/list/areas_to_break = list() + var/list/area_types_to_break = null // Area types to include. + var/list/area_types_to_ignore = null // Area types to exclude, usually due to undesired inclusion from inheritence. + +/datum/event2/event/prison_break/brig + area_display_name = "Brig" + containment_display_desc = "imprisonment" + area_types_to_break = list( + /area/security/prison, + /area/security/brig, + /area/security/security_cell_hallway, + /area/security/security_processing, + /area/security/interrogation + ) + +/datum/event2/event/prison_break/armory + area_display_name = "Armory" + containment_display_desc = "protection" + area_types_to_break = list( + /area/security/brig, + /area/security/warden, + /area/security/evidence_storage, + /area/security/security_equiptment_storage, + /area/security/armoury, + /area/security/tactical + ) + +/datum/event2/event/prison_break/bridge + area_display_name = "Bridge" + containment_display_desc = "isolation" + area_types_to_break = list( + /area/bridge, + /area/bridge_hallway + ) + +/datum/event2/event/prison_break/xenobio + area_display_name = "Xenobiology" + containment_display_desc = "containment" + area_types_to_break = list(/area/rnd/xenobiology) + area_types_to_ignore = list( + /area/rnd/xenobiology/xenoflora, + /area/rnd/xenobiology/xenoflora_storage + ) + +/datum/event2/event/prison_break/virology + area_display_name = "Virology" + containment_display_desc = "quarantine" + area_types_to_break = list( + /area/medical/virology, + /area/medical/virologyaccess + ) + + +/datum/event2/event/prison_break/set_up() + for(var/area/A in world) + if(is_type_in_list(A, area_types_to_break) && !is_type_in_list(A, area_types_to_ignore)) + areas_to_break += A + + if(!areas_to_break.len) + log_debug("Prison Break event failed to find any areas to break. Aborting.") + abort() + return + +/datum/event2/event/prison_break/announce() + var/my_department = "[location_name()] Firewall Subroutines" + var/message = "An unknown malicious program has been detected in the [area_display_name] \ + lighting and airlock control systems at [stationtime2text()]. Systems will be fully compromised \ + within approximately three minutes. Direct intervention is required immediately. Disabling the \ + main breaker in the APCs will protect the APC's room from being compromised." + + for(var/obj/machinery/message_server/MS in machines) + MS.send_rc_message(DEPARTMENT_ENGINEERING, my_department, "[message]
    ", "", "", 2) + + // Nobody reads the requests consoles so lets use the radio as well. + global_announcer.autosay(message, my_department, DEPARTMENT_ENGINEERING) + + for(var/mob/living/silicon/ai/A in player_list) + to_chat(A, span("danger", "Malicious program detected in the [area_display_name] lighting and airlock control systems by [my_department]. \ + Disabling the main breaker in the APCs will protect the APC's room from being compromised.")) + + var/time_to_flicker = start_delay - 10 SECONDS + addtimer(CALLBACK(src, .proc/flicker_area), time_to_flicker) + + +/datum/event2/event/prison_break/proc/flicker_area() + for(var/area/A in areas_to_break) + var/obj/machinery/power/apc/apc = A.get_apc() + if(apc.operating) //If the apc's off, it's a little hard to overload the lights. + for(var/obj/machinery/light/L in A) + L.flicker(10) + +/datum/event2/event/prison_break/start() + for(var/area/A in areas_to_break) + spawn(0) // So we don't block the ticker. + A.prison_break() + +// There's between 40 seconds and one minute before the whole station knows. +// If there's a baddie engineer, they can choose to keep their early announcement to themselves and get a minute to exploit it. +/datum/event2/event/prison_break/end() + command_announcement.Announce("[pick("Gr3y.T1d3 virus","Malignant trojan")] was detected \ + in \the [location_name()] [area_display_name] [containment_display_desc] subroutines. Secure any compromised \ + areas immediately. AI involvement is recommended.", "[capitalize(containment_display_desc)] Alert") diff --git a/code/modules/gamemaster/event2/events/security/rogue_drones.dm b/code/modules/gamemaster/event2/events/security/rogue_drones.dm new file mode 100644 index 0000000000..da40b3262f --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/rogue_drones.dm @@ -0,0 +1,70 @@ +/datum/event2/meta/rogue_drones + name = "rogue drones" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/mob_spawning/rogue_drones + +/datum/event2/meta/rogue_drones/get_weight() + . = 10 // Start with a base weight, since this event does provide some value even if no sec is around. + . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 + . += metric.count_all_space_mobs() * 40 + + +/datum/event2/event/mob_spawning/rogue_drones + length_lower_bound = 15 MINUTES + length_upper_bound = 20 MINUTES + var/drones_to_spawn = 6 + +/datum/event2/event/mob_spawning/rogue_drones/set_up() + if(prob(10)) // Small chance for a false alarm. + drones_to_spawn = 0 + +/datum/event2/event/mob_spawning/rogue_drones/announce() + var/msg = null + var/rng = rand(1,5) + switch(rng) + if(1) + msg = "A combat drone wing operating in close orbit above Sif has failed to return from a anti-piracy sweep. \ + If any are sighted, approach with caution." + if(2) + msg = "Contact has been lost with a combat drone wing in Sif orbit. \ + If any are sighted in the area, approach with caution." + if(3) + msg = "Unidentified hackers have targeted a combat drone wing deployed around Sif. \ + If any are sighted in the area, approach with caution." + if(4) + msg = "A passing derelict ship's drone defense systems have just activated. \ + If any are sighted in the area, use caution." + if(5) + msg = "We're detecting a swarm of small objects approaching your station. \ + Most likely a bunch of drones. Please exercise caution if you see any." + + command_announcement.Announce(msg, "Rogue drone alert") + +/datum/event2/event/mob_spawning/rogue_drones/start() + for(var/i = 1 to drones_to_spawn) + spawn_mobs_in_space( + mob_type = /mob/living/simple_mob/mechanical/combat_drone/event, + number_of_groups = 1, + min_size_of_group = 1, + max_size_of_group = 1 + ) + +/datum/event2/event/mob_spawning/rogue_drones/end() + if(drones_to_spawn) + var/number_recovered = 0 + for(var/mob/living/simple_mob/mechanical/combat_drone/D in spawned_mobs) + var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() + sparks.set_up(3, 0, D.loc) + sparks.start() + D.z = using_map.admin_levels[1] + D.loot_list = list() + + qdel(D) + number_recovered++ + + if(number_recovered > spawned_mobs.len * 0.75) + command_announcement.Announce("The drones that were malfunctioning have been recovered safely.", "Rogue drone alert") + else + command_announcement.Announce("We're disappointed at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert") diff --git a/code/modules/gamemaster/event2/events/security/security_advisement.dm b/code/modules/gamemaster/event2/events/security/security_advisement.dm new file mode 100644 index 0000000000..6a3764e40e --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/security_advisement.dm @@ -0,0 +1,93 @@ +/datum/event2/meta/security_screening + name = "security screening" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT // So this won't get called in the middle of a crisis. + event_type = /datum/event2/event/security_screening + +/datum/event2/meta/security_screening/get_weight() + . = 0 + var/sec = metric.count_people_in_department(DEPARTMENT_SECURITY) + if(!sec < 2) + return 0 // Can't screen with no security. + . += sec * 10 + . += metric.count_people_in_department(DEPARTMENT_EVERYONE) * 2 + + // Having ""suspecious"" people present makes this more likely to be picked. + var/suspicious_people = 0 + suspicious_people += metric.count_all_of_specific_species(SPECIES_PROMETHEAN) * 20 + suspicious_people += metric.count_all_of_specific_species(SPECIES_UNATHI) * 10 + suspicious_people += metric.count_all_of_specific_species(SPECIES_ZADDAT) * 10 + suspicious_people += metric.count_all_of_specific_species(SPECIES_SKRELL) * 5 // Not sure why skrell are so high. + suspicious_people += metric.count_all_of_specific_species(SPECIES_TAJ) * 5 + suspicious_people += metric.count_all_of_specific_species(SPECIES_TESHARI) * 5 + suspicious_people += metric.count_all_of_specific_species(SPECIES_HUMAN_VATBORN) * 5 + suspicious_people += metric.count_all_FBPs_of_kind(FBP_DRONE) * 20 + suspicious_people += metric.count_all_FBPs_of_kind(FBP_POSI) * 10 + if(!suspicious_people) + return 0 + . += suspicious_people + +/datum/event2/event/security_screening + var/victim = null + var/list/species_weights = list( + SPECIES_SKRELL = 9, + SPECIES_UNATHI = 15, + SPECIES_HUMAN_VATBORN = 6, + SPECIES_TESHARI = 2, + SPECIES_TAJ = 3, + SPECIES_DIONA = 1, + SPECIES_ZADDAT = 25, + SPECIES_PROMETHEAN = 30 + ) + + var/list/synth_weights = list( + FBP_CYBORG = 15, + FBP_DRONE = 30, + FBP_POSI = 25 + ) + +/datum/event2/event/security_screening/set_up() + var/list/end_weights = list() + + // First pass makes popular things more likely to get picked, e.g. 5 prommies vs 1 drone. + for(var/species_name in species_weights) + var/give_weight = 0 + for(var/datum/data/record/R in data_core.general) + if(R.fields["species"] == species_name) + give_weight += species_weights[species_name] + + end_weights[species_name] = give_weight + + for(var/bot_type in synth_weights) + var/give_weight = 0 + for(var/datum/data/record/R in data_core.general) + if(R.fields["brain_type"] == bot_type) + give_weight += synth_weights[bot_type] + + end_weights[bot_type] = give_weight + + // Second pass eliminates things that don't exist on the station. + // It's possible to choose something like drones when all the drones are AFK. This prevents that from happening. + while(end_weights.len) // Keep at it until we find someone or run out of possibilities. + var/victim_chosen = pickweight(end_weights) + + if(victim_chosen in synth_weights) + if(metric.count_all_FBPs_of_kind(victim_chosen) > 0) + victim = victim_chosen + break + else + if(metric.count_all_of_specific_species(victim_chosen) > 0) + victim = victim_chosen + break + if(!victim) + end_weights -= victim_chosen + + if(!victim) + log_debug("Security Screening event failed to find anyone to screen. Aborting.") + abort() + return + +/datum/event2/event/security_screening/announce() + command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has \ + requested the screening of [pick("every other", "every", "suspicious", "willing")] [victim] \ + personnel onboard \the [location_name()].", "Security Advisement") diff --git a/code/modules/gamemaster/event2/events/security/spider_infestation.dm b/code/modules/gamemaster/event2/events/security/spider_infestation.dm new file mode 100644 index 0000000000..23dc61c673 --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/spider_infestation.dm @@ -0,0 +1,49 @@ +/datum/event2/meta/spider_infestation + name = "spider infestation" + event_class = "spiders" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_MEDICAL, DEPARTMENT_EVERYONE) + chaos = 30 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/spider_infestation + +/datum/event2/meta/spider_infestation/weak + name = "weak spider infestation" + chaos = 20 + event_type = /datum/event2/event/spider_infestation/weak + + +/datum/event2/meta/spider_infestation/get_weight() + . = 10 + . += metric.count_people_in_department(DEPARTMENT_SECURITY) * 20 + . += metric.count_people_in_department(DEPARTMENT_MEDICAL) * 10 + + +// This isn't a /mob_spawning subtype since spiderlings aren't actually mobs. + +/datum/event2/event/spider_infestation + var/spiders_to_spawn = 8 + var/spiderling_to_spawn = /obj/effect/spider/spiderling + +/datum/event2/event/spider_infestation/weak + spiders_to_spawn = 5 + spiderling_to_spawn = /obj/effect/spider/spiderling/stunted + + + +/datum/event2/event/spider_infestation/announce() + command_announcement.Announce("Unidentified lifesigns detected coming aboard \the [location_name()]. \ + Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + +/datum/event2/event/spider_infestation/start() + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in machines) + if(!temp_vent.welded && temp_vent.network && temp_vent.loc.z in get_location_z_levels()) + if(temp_vent.network.normal_members.len > 50) + vents += temp_vent + + while((spiders_to_spawn >= 1) && vents.len) + var/obj/vent = pick(vents) + new spiderling_to_spawn(vent.loc) + log_debug("Spider infestation event spawned a spiderling at [get_area(vent)].") + vents -= vent + spiders_to_spawn-- diff --git a/code/modules/gamemaster/event2/events/security/stowaway.dm b/code/modules/gamemaster/event2/events/security/stowaway.dm new file mode 100644 index 0000000000..d04d350234 --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/stowaway.dm @@ -0,0 +1,64 @@ +// Base type used for inheritence. +/datum/event2/meta/stowaway + event_class = "stowaway" + departments = list(DEPARTMENT_SECURITY, DEPARTMENT_EVERYONE) + chaos = 10 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_LOW_IMPACT + event_type = /datum/event2/event/ghost_pod_spawner/stowaway + var/safe_for_extended = FALSE + +/datum/event2/meta/stowaway/normal + name = "stowaway - normal" + safe_for_extended = TRUE + +/datum/event2/meta/stowaway/renegade + name = "stowaway - renegade" + chaos = 30 + event_type = /datum/event2/event/ghost_pod_spawner/stowaway/renegade + +/datum/event2/meta/stowaway/infiltrator + name = "stowaway - infiltrator" + chaos = 60 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/ghost_pod_spawner/stowaway/infiltrator + +/datum/event2/meta/stowaway/get_weight() + if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) + return 0 + + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - security + var/ghost_activity = metric.assess_all_dead_mobs() / 100 + + return ( (security * 20) + (everyone * 2) ) * ghost_activity + + +/datum/event2/event/ghost_pod_spawner/stowaway + pod_type = /obj/structure/ghost_pod/ghost_activated/human + desired_turf_areas = list(/area/maintenance) + announce_delay_lower_bound = 15 MINUTES + announce_delay_upper_bound = 30 MINUTES + var/antag_type = MODE_STOWAWAY + var/announce_odds = 20 + +/datum/event2/event/ghost_pod_spawner/stowaway/renegade + antag_type = MODE_RENEGADE + announce_odds = 33 + +/datum/event2/event/ghost_pod_spawner/stowaway/infiltrator + antag_type = MODE_INFILTRATOR + announce_odds = 50 + +/datum/event2/event/ghost_pod_spawner/stowaway/post_pod_creation(obj/structure/ghost_pod/ghost_activated/human/pod) + pod.make_antag = antag_type + pod.occupant_type = "[pod.make_antag] [pod.occupant_type]" + + say_dead_object("[span("notice", pod.occupant_type)] pod is now available in \the [get_area(pod)].", pod) + +/datum/event2/event/ghost_pod_spawner/stowaway/announce() + if(prob(announce_odds)) + if(atc?.squelched) + return + atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as \ + [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ + has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/event2/events/security/surprise_carp.dm b/code/modules/gamemaster/event2/events/security/surprise_carp.dm new file mode 100644 index 0000000000..39be6d8412 --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/surprise_carp.dm @@ -0,0 +1,71 @@ +// This event sends a few carp after someone hanging around in space, unannounced. + +/datum/event2/meta/surprise_carp + name = "surprise carp" + departments = list(DEPARTMENT_EVERYONE) + chaos = 20 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/surprise_carp + +/datum/event2/meta/surprise_carp/get_weight() + return metric.count_all_space_mobs() * 50 + + +/datum/event2/event/surprise_carp + var/mob/living/victim = null + +/datum/event2/event/surprise_carp/set_up() + var/list/potential_victims = list() + for(var/mob/living/L in player_list) + if(!(L.z in get_location_z_levels())) + continue // Not on the right z-level. + if(L.stat) + continue // Don't want dead people. + if(istype(get_turf(L), /turf/space) && istype(get_area(L),/area/space)) + potential_victims += L + + if(potential_victims.len) + victim = pick(potential_victims) + +/datum/event2/event/surprise_carp/start() + if(!victim) + log_debug("Failed to find a target for surprise carp attack. Aborting.") + abort() + return + + var/number_of_carp = rand(1, 2) + log_debug("Sending [number_of_carp] carp\s after \the [victim].") + // Getting off screen tiles is kind of tricky due to potential edge cases that could arise. + // The method we're gonna do is make a big square around the victim, then + // subtract a smaller square in the middle for the default vision range. + var/list/outer_square = get_safe_square(victim, world.view + 3) + var/list/inner_square = get_safe_square(victim, world.view) + + var/list/donut = outer_square - inner_square + for(var/T in donut) + if(!istype(T, /turf/space)) + donut -= T + + for(var/i = 1 to number_of_carp) + var/turf/spawning_turf = pick(donut) + + if(spawning_turf) + var/mob/living/simple_mob/animal/space/carp/C = new(spawning_turf) + // Ask carp to swim onto the victim's screen. The AI will then switch to hostile and try to eat them. + C.ai_holder?.give_destination(get_turf(victim)) + else + log_debug("Surprise carp attack failed to find any space turfs offscreen to the victim.") + +// Gets suitable spots for carp to spawn, without risk of going off the edge of the map. +// If there is demand for this proc, then it can easily be made independant and moved into one of the helper files. +/datum/event2/event/surprise_carp/proc/get_safe_square(atom/center, radius) + var/lower_left_x = max(center.x - radius, 1 + TRANSITIONEDGE) + var/lower_left_y = max(center.y - radius, 1 + TRANSITIONEDGE) + + var/upper_right_x = min(center.x + radius, world.maxx - TRANSITIONEDGE) + var/upper_right_y = min(center.y + radius, world.maxy - TRANSITIONEDGE) + + var/turf/lower_left = locate(lower_left_x, lower_left_y, victim.z) + var/turf/upper_right = locate(upper_right_x, upper_right_y, victim.z) + + return block(lower_left, upper_right) diff --git a/code/modules/gamemaster/event2/events/security/swarm_boarder.dm b/code/modules/gamemaster/event2/events/security/swarm_boarder.dm new file mode 100644 index 0000000000..283333c679 --- /dev/null +++ b/code/modules/gamemaster/event2/events/security/swarm_boarder.dm @@ -0,0 +1,55 @@ +// This is just porting the event to the new new event system, it's not been balanced in any way +// so don't @ me if these things are grossly OP. +/datum/event2/meta/swarm_boarder + event_class = "swarm boarder" + departments = list(DEPARTMENT_EVERYONE, DEPARTMENT_SECURITY, DEPARTMENT_ENGINEERING) + chaos = 60 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_HIGH_IMPACT + var/safe_for_extended = FALSE + +/datum/event2/meta/swarm_boarder/get_weight() + if(istype(ticker.mode, /datum/game_mode/extended) && !safe_for_extended) + return 0 + + var/security = metric.count_people_in_department(DEPARTMENT_SECURITY) + var/engineering = metric.count_people_in_department(DEPARTMENT_ENGINEERING) + var/everyone = metric.count_people_in_department(DEPARTMENT_EVERYONE) - (engineering + security) + + var/ghost_activity = metric.assess_all_dead_mobs() / 100 + + return ( (security * 20) + (engineering * 10) + (everyone * 2) ) * ghost_activity + +/datum/event2/meta/swarm_boarder/normal + name = "swarmer shell - normal" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder + +/datum/event2/meta/swarm_boarder/melee + name = "swarmer shell - melee" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/melee + +/datum/event2/meta/swarm_boarder/gunner + name = "swarmer shell - gunner" + event_type = /datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner + + + + +/datum/event2/event/ghost_pod_spawner/swarm_boarder + announce_delay_lower_bound = 5 MINUTES + announce_delay_upper_bound = 15 MINUTES + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event + desired_turf_areas = list(/area/maintenance) + var/announce_odds = 80 + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/melee + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/gunner + pod_type = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner + +/datum/event2/event/ghost_pod_spawner/swarm_boarder/announce() + if(prob(announce_odds)) + if(atc?.squelched) + atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution \ + is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] \ + has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm b/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm new file mode 100644 index 0000000000..afcf20ebc8 --- /dev/null +++ b/code/modules/gamemaster/event2/events/synthetic/ion_storm.dm @@ -0,0 +1,71 @@ +/datum/event2/meta/ion_storm + name = "ion storm" + departments = list(DEPARTMENT_SYNTHETIC) + chaos = 40 + chaotic_threshold = EVENT_CHAOS_THRESHOLD_MEDIUM_IMPACT + event_type = /datum/event2/event/ion_storm + +/datum/event2/meta/ion_storm/get_weight() + var/bots = metric.count_people_in_department(DEPARTMENT_SYNTHETIC) + var/weight = 5 + (bots * 40) // A small chance even if no synths are on, since it can still emag beepsky. + return weight + + +/datum/event2/event/ion_storm + announce_delay_lower_bound = 7 MINUTES + announce_delay_upper_bound = 15 MINUTES + var/bot_emag_chance = 30 // This is rolled once, instead of once a second for a minute like the old version. + var/announce_odds = 50 // Probability of an announcement actually happening after the delay. + +/datum/event2/event/ion_storm/start() + // Ion laws. + for(var/mob/living/silicon/target in silicon_mob_list) + if(target.z in get_location_z_levels()) + // Don't ion law drons. + if(istype(target, /mob/living/silicon/robot/drone)) + continue + + // Or borgs with an AI (they'll get their AI's ion law anyways). + if(istype(target, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/R = target + if(R.connected_ai) + continue + if(R.shell) + continue + + // Crew member names, and excluding off station antags, are handled by `generate_ion_law()` automatically. + var/law = target.generate_ion_law() + target.add_ion_law(law) + target.show_laws() + + // Emag bots. + for(var/mob/living/bot/B in mob_list) + if(B.z in get_location_z_levels()) + if(prob(bot_emag_chance)) + B.emag_act(1) + + // Messaging server spam filters. + // This might be better served as a seperate event since it seems more like a hacker attack than a natural occurance. + if(message_servers) + for(var/obj/machinery/message_server/MS in message_servers) + if(MS.z in get_location_z_levels()) + MS.spamfilter.Cut() + for (var/i = 1, i <= MS.spamfilter_limit, i++) + MS.spamfilter += pick("warble","help","almach","ai","liberty","freedom","drugs", "[using_map.station_short]", \ + "admin","sol","security","meow","_","monkey","-","moron","pizza","message","spam",\ + "director", "Hello", "Hi!"," ","nuke","crate","taj","xeno") + +/datum/event2/event/ion_storm/announce() + if(prob(announce_odds)) + command_announcement.Announce("An ion storm was detected within proximity to \the [location_name()] recently. \ + Check all AI controlled equipment for corruption.", "Anomaly Alert", new_sound = 'sound/AI/ionstorm.ogg') + +// Fake variant used by traitors. +/datum/event2/event/ion_storm/fake + // Fake ion storms announce instantly, so the traitor can time it to make the AI look suspicious. + announce_delay_lower_bound = 0 + announce_delay_upper_bound = 0 + announce_odds = 100 + +/datum/event2/event/ion_storm/fake/start() + return \ No newline at end of file diff --git a/code/modules/gamemaster/event2/meta.dm b/code/modules/gamemaster/event2/meta.dm new file mode 100644 index 0000000000..3923f85744 --- /dev/null +++ b/code/modules/gamemaster/event2/meta.dm @@ -0,0 +1,81 @@ +// The 'meta' object contains information about its assigned 'action' object, like what departments it will affect. +// It is directly held inside the Game Master Event System. + +// The code for actually executing an event should go inside the event object instead. +/datum/event2/meta + // Name used for organization, shown in the debug verb for the GM system. + // If null, the meta event will be discarded when the GM system initializes, so it is safe to use nameless subtypes for inheritence. + var/name = null + + // If FALSE, the GM system won't pick this. + // Some events set this to FALSE after running, to avoid running twice. + var/enabled = TRUE + + // What departments the event attached might affect. + var/list/departments = list(DEPARTMENT_EVERYONE) + + // A guess on how disruptive to a round the event might be. If the action is chosen, the GM's + // 'danger' score is increased by this number. + // Negative numbers could be used to signify helpful events. + var/chaos = 0 + + // A threshold the GM will use alongside its 'danger' score, to determine if it should pass + // over the event associated with this object. The decision is based on + var/chaotic_threshold = null + + // If true, the event won't have it's `enabled` var set to FALSE when ran by the GM system. + var/reusable = FALSE + + // A string used to identify a 'class' of similar events. + // If the event is not reusable, than all events sharing the same class are disabled. + // Useful if you only ever want one event per round while having a lot of different subtypes of the event. + var/event_class = null + + // Counter for how many times this event has been picked by the GM. + // Can be used to make event repeats discouraged but not forbidden by adjusting the weight based on it. + var/times_ran = 0 + + // The type path to the event associated with this meta object. + // When the GM chooses this event, a new instance is made. + // Seperate instances allow for multiple concurrent events without sharing state, e.g. two blobs. + var/event_type = null + + +// Called by the GM system to actually start an event. +/datum/event2/meta/proc/make_event() + var/datum/event2/event/E = new event_type() + E.execute() + return E + +// Returns a TRUE or FALSE for if the GM system should be able to pick this event. +// Can be extended to check for more than just `enabled` later. +/datum/event2/meta/proc/can_pick() + return enabled + +/* + * Procs to Override + */ + +// Returns a number that determines how likely it is for the event to be picked over others. +// Individual events should override this for their own weights. +/datum/event2/meta/proc/get_weight() + return 0 + + +/datum/event2/meta/Topic(href, href_list) + if(..()) + return + + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) + message_admins("[usr] has attempted to manipulate an event without sufficent privilages.") + return + + if(href_list["force"]) + // SSevent_ticker.start_event(event_type) // VOREStation Edit - We don't use SSgame_master yet. + message_admins("Event '[name]' was forced by [usr.key].") + + if(href_list["toggle"]) + enabled = !enabled + message_admins("Event '[name]' was toggled [enabled ? "on" : "off"] by [usr.key].") + + // SSgame_master.interact(usr) // To refresh the UI. // VOREStation Edit - We don't use SSgame_master yet. \ No newline at end of file diff --git a/code/modules/gamemaster/game_master.dm b/code/modules/gamemaster/game_master.dm deleted file mode 100644 index 8814fb362e..0000000000 --- a/code/modules/gamemaster/game_master.dm +++ /dev/null @@ -1,160 +0,0 @@ -// This is a sort of successor to the various event systems created over the years. It is designed to be just a tad smarter than the -// previous ones, checking various things like player count, department size and composition, individual player activity, -// individual player (IC) skill, and such, in order to try to choose the best actions to take in order to add spice or variety to -// the round. - -/datum/game_master - var/suspended = TRUE // If true, it will not do anything. - var/ignore_time_restrictions = FALSE// Useful for debugging without needing to wait 20 minutes each time. - var/list/available_actions = list() // A list of 'actions' that the GM has access to, to spice up a round, such as events. - var/danger = 0 // The GM's best guess at how chaotic the round is. High danger makes it hold back. - var/staleness = -20 // Determines liklihood of the GM doing something, increases over time. - var/danger_modifier = 1 // Multiplier for how much 'danger' is accumulated. - var/staleness_modifier = 1 // Ditto. Higher numbers generally result in more events occuring in a round. - var/ticks_completed = 0 // Counts amount of ticks completed. Note that this ticks once a minute. - var/next_action = 0 // Minimum amount of time of nothingness until the GM can pick something again. - var/last_department_used = null // If an event was done for a specific department, it is written here, so it doesn't do it again. - -/datum/game_master/New() - ..() - available_actions = init_subtypes(/datum/gm_action) - for(var/datum/gm_action/action in available_actions) - action.gm = src - - var/config_setup_delay = TRUE - spawn(0) - while(config_setup_delay) - if(config) - config_setup_delay = FALSE - if(config.enable_game_master) - suspended = FALSE - next_action = world.time + rand(15 MINUTES, 25 MINUTES) - else - sleep(30 SECONDS) - -/datum/game_master/process() - if(ticker && ticker.current_state == GAME_STATE_PLAYING && !suspended) - adjust_staleness(1) - adjust_danger(-1) - ticks_completed++ - - var/global_afk = metric.assess_all_living_mobs() - global_afk -= 100 - global_afk = abs(global_afk) - global_afk = round(global_afk / 100, 0.1) - adjust_staleness(global_afk) // Staleness increases faster if more people are less active. - - if(world.time < next_action && prob(staleness * 2) ) - log_debug("Game Master going to start something.") - start_action() - -// This is run before committing to an action/event. -/datum/game_master/proc/pre_action_checks() - if(!ticker || ticker.current_state != GAME_STATE_PLAYING) - log_debug("Game Master unable to start event: Ticker is nonexistant, or the game is not ongoing.") - return FALSE - if(suspended) - return FALSE - if(ignore_time_restrictions) - return TRUE - if(world.time < next_action) // Sanity. - log_debug("Game Master unable to start event: Time until next action is approximately [round((next_action - world.time) / (1 MINUTE))] minute(s)") - return FALSE - // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. - var/mills = round_duration_in_ticks - var/mins = round((mills % 36000) / 600) - var/hours = round(mills / 36000) - - if(hours < 1 && mins <= 20) // Don't do anything for the first twenty minutes of the round. - log_debug("Game Master unable to start event: It is too early.") - return FALSE - if(hours >= 2 && mins >= 40) // Don't do anything in the last twenty minutes of the round, as well. - log_debug("Game Master unable to start event: It is too late.") - return FALSE - return TRUE - -/datum/game_master/proc/start_action() - if(!pre_action_checks()) // Make sure we're not doing last minute events, or early events. - return - log_debug("Game Master now starting action decision.") - var/list/most_active_departments = metric.assess_all_departments(3, list(last_department_used)) - var/list/best_actions = decide_best_action(most_active_departments) - - if(best_actions && best_actions.len) - var/list/weighted_actions = list() - for(var/datum/gm_action/action in best_actions) - if(action.chaotic > danger) - continue // We skip dangerous events when bad stuff is already occuring. - weighted_actions[action] = action.get_weight() - - var/datum/gm_action/choice = pickweight(weighted_actions) - if(choice) - log_debug("[choice.name] was chosen by the Game Master, and is now being ran.") - run_action(choice) - -/datum/game_master/proc/run_action(var/datum/gm_action/action) - action.set_up() - action.start() - action.announce() - if(action.chaotic) - danger += action.chaotic - if(action.length) - spawn(action.length) - action.end() - next_action = world.time + rand(5 MINUTES, 20 MINUTES) - last_department_used = action.departments[1] - - -/datum/game_master/proc/decide_best_action(var/list/most_active_departments) - if(!most_active_departments.len) // Server's empty? - log_debug("Game Master failed to find any active departments.") - return list() - - var/list/best_actions = list() // List of actions which involve the most active departments. - if(most_active_departments.len >= 2) - for(var/datum/gm_action/action in available_actions) - if(!action.enabled) - continue - // Try to incorporate an action with the top two departments first. - if(most_active_departments[1] in action.departments && most_active_departments[2] in action.departments) - best_actions.Add(action) - log_debug("[action.name] is being considered because both most active departments are involved.") - - if(best_actions.len) // We found something for those two, let's do it. - return best_actions - - // Otherwise we probably couldn't find something for the second highest group, so let's ignore them. - for(var/datum/gm_action/action in available_actions) - if(!action.enabled) - continue - if(most_active_departments[1] in action.departments) - best_actions.Add(action) - log_debug("[action.name] is being considered because the most active department is involved.") - - if(best_actions.len) // Found something for the one guy. - return best_actions - - // At this point we should expand our horizons. - for(var/datum/gm_action/action in available_actions) - if(!action.enabled) - continue - if(ROLE_EVERYONE in action.departments) - best_actions.Add(action) - log_debug("[action.name] is being considered because it involves everyone.") - - if(best_actions.len) // Finally, perhaps? - return best_actions - - // Just give a random event if for some reason it still can't make up its mind. - for(var/datum/gm_action/action in available_actions) - if(!action.enabled) - continue - best_actions.Add(action) - log_debug("[action.name] is being considered because everything else failed.") - - if(best_actions.len) // Finally, perhaps? - return best_actions - else - log_debug("Game Master failed to find a suitable event, something very wrong is going on.") - - diff --git a/code/modules/gamemaster/helpers.dm b/code/modules/gamemaster/helpers.dm deleted file mode 100644 index da05cb1b20..0000000000 --- a/code/modules/gamemaster/helpers.dm +++ /dev/null @@ -1,9 +0,0 @@ -// Tell the game master that something dangerous happened, e.g. someone dying. -/datum/game_master/proc/adjust_danger(var/amt) - amt = amt * danger_modifier - danger = round( CLAMP(danger + amt, 0, 1000), 0.1) - -// Tell the game master that something interesting happened. -/datum/game_master/proc/adjust_staleness(var/amt) - amt = amt * staleness_modifier - staleness = round( CLAMP(staleness + amt, -50, 200), 0.1) \ No newline at end of file diff --git a/code/modules/games/cah_white_cards.dm b/code/modules/games/cah_white_cards.dm index 53644b3099..2c16e0e54c 100644 --- a/code/modules/games/cah_white_cards.dm +++ b/code/modules/games/cah_white_cards.dm @@ -454,7 +454,6 @@ "Natalie Portman.", "Natural male enhancement.", "Natural selection.", - "Nazis.", "Necrophilia.", "Neil Patrick Harris.", "New Age music.", @@ -716,7 +715,7 @@ "Gay aliens.", "The way white people is.", "Reverse cowgirl.", - "The Quesadilla Explosion Salad from Chilis.", + "The Quesadilla Explosion Salad from Chili’s.", "Actually getting shot, for real.", "Not having sex.", "Vietnam flashbacks.", @@ -741,7 +740,7 @@ "A greased-up Matthew McConaughey.", "An unstoppable wave of fire ants.", "Not contributing to society in any meaningful way.", - "An all-midget production of Shakespeares Richard III.", + "An all-midget production of Shakespeare’s Richard III.", "Screaming like a maniac.", "The moist, demanding chasm of his mouth.", "Filling every orifice with butterscotch pudding.", @@ -750,7 +749,7 @@ "Velcro.", "A PowerPoint presentation.", "A surprising amount of hair.", - "Eating Tom Sellecks mustache to gain his powers.", + "Eating Tom Selleck’s mustache to gain his powers.", "Roland the Farter, flatulist to the king.", "A pile of squirming bodies.", "Buying the right pants to be cool.", @@ -762,7 +761,7 @@ "A lamprey swimming up the toilet and latching onto your taint.", "Jumping out at people.", "A black male in his early 20s, last seen wearing a hoodie.", - "Mufasas death scene.", + "Mufasa’s death scene.", "Bill Clinton, naked on a bearskin rug with a saxophone.", "Demonic possession.", "The Harlem Globetrotters.", @@ -776,7 +775,7 @@ "Getting your dick stuck in a Chinese finger trap with another dick.", "Fisting.", "The thin veneer of situational causality that underlies porn.", - "Girls that always be textin.", + "Girls that always be textin’.", "Blowing some dudes in an alley.", "Drinking ten 5-hour ENERGYs to get fifty continuous hours of energy.", "Sneezing, farting, and coming at the same time." diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm index 08d762e093..fbe703868f 100644 --- a/code/modules/games/cards.dm +++ b/code/modules/games/cards.dm @@ -453,4 +453,5 @@ update_icon() /obj/item/weapon/hand/pickup(mob/user as mob) + ..() src.update_icon() diff --git a/code/modules/ghosttrap/trap.dm b/code/modules/ghosttrap/trap.dm index a3de9fe145..d31b0bffb5 100644 --- a/code/modules/ghosttrap/trap.dm +++ b/code/modules/ghosttrap/trap.dm @@ -26,12 +26,12 @@ proc/populate_ghost_traps() if(!istype(candidate) || !candidate.client || !candidate.ckey) return 0 if(!candidate.MayRespawn()) - candidate << "You have made use of the AntagHUD and hence cannot enter play as \a [object]." + to_chat(candidate, "You have made use of the AntagHUD and hence cannot enter play as \a [object].") return 0 if(islist(ban_checks)) for(var/bantype in ban_checks) if(jobban_isbanned(candidate, "[bantype]")) - candidate << "You are banned from one or more required roles and hence cannot enter play as \a [object]." + to_chat(candidate, "You are banned from one or more required roles and hence cannot enter play as \a [object].") return 0 return 1 @@ -49,7 +49,7 @@ proc/populate_ghost_traps() if(pref_check && !(O.client.prefs.be_special & pref_check)) continue if(O.client) - O << "[request_string]Click here if you wish to play as this option." + to_chat(O, "[request_string]Click here if you wish to play as this option.") // Handles a response to request_player(). /datum/ghosttrap/Topic(href, href_list) @@ -78,10 +78,10 @@ proc/populate_ghost_traps() // Fluff! /datum/ghosttrap/proc/welcome_candidate(var/mob/target) - target << "You are a positronic brain, brought into existence on [station_name()]." - target << "As a synthetic intelligence, you answer to all crewmembers, as well as the AI." - target << "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm." - target << "Use say #b to speak to other artificial intelligences." + to_chat(target, "You are a positronic brain, brought into existence on [station_name()].") + to_chat(target, "As a synthetic intelligence, you answer to all crewmembers, as well as the AI.") + to_chat(target, "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm.") + to_chat(target, "Use say #b to speak to other artificial intelligences.") var/turf/T = get_turf(target) T.visible_message("\The [src] chimes quietly.") var/obj/item/device/mmi/digital/posibrain/P = target.loc @@ -107,8 +107,8 @@ proc/populate_ghost_traps() ghost_trap_role = "Plant" /datum/ghosttrap/plant/welcome_candidate(var/mob/target) - target << "You awaken slowly, stirring into sluggish motion as the air caresses you." + to_chat(target, "You awaken slowly, stirring into sluggish motion as the air caresses you.") // This is a hack, replace with some kind of species blurb proc. if(istype(target,/mob/living/carbon/alien/diona)) - target << "You are \a [target], one of a race of drifting interstellar plantlike creatures that sometimes share their seeds with human traders." - target << "Too much darkness will send you into shock and starve you, but light will help you heal." \ No newline at end of file + to_chat(target, "You are \a [target], one of a race of drifting interstellar plantlike creatures that sometimes share their seeds with human traders.") + to_chat(target, "Too much darkness will send you into shock and starve you, but light will help you heal.") \ No newline at end of file diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index 4cce81a2a6..5783bfc398 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -4,7 +4,7 @@ icon_keyboard = "tech_key" icon_screen = "holocontrol" - use_power = 1 + use_power = USE_POWER_IDLE active_power_usage = 8000 //8kW for the scenery + 500W per holoitem var/item_power_usage = 500 @@ -161,8 +161,8 @@ emagged = 1 safety_disabled = 1 update_projections() - user << "You vastly increase projector power and override the safety and security protocols." - user << "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call [using_map.company_name] maintenance and do not use the simulator." + to_chat(user, "You vastly increase projector power and override the safety and security protocols.") + to_chat(user, "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call [using_map.company_name] maintenance and do not use the simulator.") log_game("[key_name(usr)] emagged the Holodeck Control Computer") return 1 return @@ -187,7 +187,7 @@ current_program = powerdown_program linkedholodeck = locate(projection_area) if(!linkedholodeck) - world << "Holodeck computer at [x],[y],[z] failed to locate projection area." + to_world("Holodeck computer at [x],[y],[z] failed to locate projection area.") //This could all be done better, but it works for now. /obj/machinery/computer/HolodeckControl/Destroy() @@ -224,7 +224,7 @@ damaged = 1 loadProgram(powerdown_program, 0) active = 0 - use_power = 1 + update_use_power(USE_POWER_IDLE) for(var/mob/M in range(10,src)) M.show_message("The holodeck overloads!") @@ -268,10 +268,10 @@ loadProgram(powerdown_program, 0) if(!linkedholodeck.has_gravity) - linkedholodeck.gravitychange(1,linkedholodeck) + linkedholodeck.gravitychange(1) active = 0 - use_power = 1 + update_use_power(USE_POWER_IDLE) /obj/machinery/computer/HolodeckControl/proc/loadProgram(var/prog, var/check_delay = 1) @@ -301,7 +301,7 @@ last_change = world.time active = 1 - use_power = 2 + use_power = USE_POWER_ACTIVE for(var/item in holographic_objs) derez(item) @@ -362,19 +362,19 @@ last_gravity_change = world.time active = 1 - use_power = 1 + use_power = USE_POWER_IDLE if(A.has_gravity) - A.gravitychange(0,A) + A.gravitychange(0) else - A.gravitychange(1,A) + A.gravitychange(1) /obj/machinery/computer/HolodeckControl/proc/emergencyShutdown() //Turn it back to the regular non-holographic room loadProgram(powerdown_program, 0) if(!linkedholodeck.has_gravity) - linkedholodeck.gravitychange(1,linkedholodeck) + linkedholodeck.gravitychange(1) active = 0 - use_power = 1 + use_power = USE_POWER_IDLE diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 35012a262d..ed66db42f3 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -58,8 +58,13 @@ /turf/simulated/floor/holofloor/space icon = 'icons/turf/space.dmi' + plane = SPACE_PLANE name = "\proper space" - icon_state = "0" + icon_state = "white" + +/turf/simulated/floor/holofloor/space/update_icon() + . = ..() + add_overlay(SSskybox.dust_cache["[((x + y) ^ ~(x * y) + z) % 25]"]) /turf/simulated/floor/holofloor/reinforced icon = 'icons/turf/flooring/tiles.dmi' @@ -67,9 +72,6 @@ name = "reinforced holofloor" icon_state = "reinforced" -/turf/simulated/floor/holofloor/space/New() - icon_state = "[((x + y) ^ ~(x * y) + z) % 25]" - /turf/simulated/floor/holofloor/beach desc = "Uncomfortably gritty for a hologram." base_desc = "Uncomfortably gritty for a hologram." @@ -255,7 +257,7 @@ datum/unarmed_attack/holopugilism/unarmed_override(var/mob/living/carbon/human/u //VOREStation Add /obj/structure/bed/holobed/attackby(obj/item/weapon/W as obj, mob/user as mob) if(W.is_wrench()) - user << ("It's a holochair, you can't dismantle it!") + to_chat(user, "It's a holochair, you can't dismantle it!") return //VOREStation Add End /obj/item/weapon/holo @@ -345,6 +347,7 @@ datum/unarmed_attack/holopugilism/unarmed_override(var/mob/living/carbon/human/u name = "basketball" desc = "Here's your chance, do your dance at the Space Jam." w_class = ITEMSIZE_LARGE //Stops people from hiding it in their bags/pockets + drop_sound = 'sound/items/drop/basketball.ogg' /obj/structure/holohoop name = "basketball hoop" @@ -395,7 +398,7 @@ datum/unarmed_attack/holopugilism/unarmed_override(var/mob/living/carbon/human/u var/eventstarted = 0 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 6 power_channel = ENVIRON diff --git a/code/modules/holomap/station_holomap.dm b/code/modules/holomap/station_holomap.dm index d38875f220..39a1c30452 100644 --- a/code/modules/holomap/station_holomap.dm +++ b/code/modules/holomap/station_holomap.dm @@ -8,7 +8,7 @@ icon_state = "station_map" anchored = 1 density = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 500 circuit = /obj/item/weapon/circuitboard/station_map @@ -33,16 +33,15 @@ /obj/machinery/station_map/New() ..() - holomap_datum = new() - original_zLevel = loc.z - SSholomaps.station_holomaps += src flags |= ON_BORDER // Why? It doesn't help if its not density /obj/machinery/station_map/Initialize() . = ..() + holomap_datum = new() + original_zLevel = loc.z + SSholomaps.station_holomaps += src if(SSholomaps.holomaps_initialized) - spawn(1) // Tragically we need to spawn this in order to give the frame construcing us time to set pixel_x/y - setup_holomap() + setup_holomap() /obj/machinery/station_map/Destroy() SSholomaps.station_holomaps -= src @@ -64,14 +63,12 @@ holomap_datum.initialize_holomap(T, reinit = TRUE) small_station_map = image(SSholomaps.extraMiniMaps["[HOLOMAP_EXTRA_STATIONMAPSMALL]_[original_zLevel]"], dir = dir) - // small_station_map.plane = LIGHTING_PLANE // Not until we do planes ~Leshana - // small_station_map.layer = LIGHTING_LAYER+1 // Weird things will happen! floor_markings = image('icons/obj/machines/stationmap.dmi', "decal_station_map") floor_markings.dir = src.dir - // floor_markings.plane = ABOVE_TURF_PLANE // Not until we do planes ~Leshana - // floor_markings.layer = DECAL_LAYER - update_icon() + + spawn(1) //When built from frames, need to allow time for it to set pixel_x and pixel_y + update_icon() /obj/machinery/station_map/attack_hand(var/mob/user) if(watching_mob && (watching_mob != user)) @@ -126,7 +123,7 @@ GLOB.moved_event.register(watching_mob, src, /obj/machinery/station_map/proc/checkPosition) GLOB.dir_set_event.register(watching_mob, src, /obj/machinery/station_map/proc/checkPosition) GLOB.destroyed_event.register(watching_mob, src, /obj/machinery/station_map/proc/stopWatching) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) if(bogus) to_chat(user, "The holomap failed to initialize. This area of space cannot be mapped.") @@ -156,7 +153,7 @@ GLOB.dir_set_event.unregister(watching_mob, src) GLOB.destroyed_event.unregister(watching_mob, src) watching_mob = null - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/station_map/power_change() . = ..() @@ -172,6 +169,9 @@ update_icon() /obj/machinery/station_map/update_icon() + if(!holomap_datum) + return //Not yet. + overlays.Cut() if(stat & BROKEN) icon_state = "station_mapb" diff --git a/code/modules/hydroponics/_hydro_setup.dm b/code/modules/hydroponics/_hydro_setup.dm index ade6cf4ea3..928315df3f 100644 --- a/code/modules/hydroponics/_hydro_setup.dm +++ b/code/modules/hydroponics/_hydro_setup.dm @@ -104,3 +104,11 @@ GLOBAL_LIST_INIT(plant_item_products, list( /obj/item/weapon/spacecash/c1 = 3, /obj/item/weapon/spacecash/c10 = 1 )) + +GLOBAL_LIST_INIT(forbidden_plant_growth_sprites, list( + "gnomes" + )) + +GLOBAL_LIST_INIT(forbidden_plant_product_sprites, list( + "gnomes" + )) \ No newline at end of file diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 428f50e696..982f44e794 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -7,51 +7,43 @@ desc = "Nutritious! Probably." flags = NOCONDUCT slot_flags = SLOT_HOLSTER + drop_sound = 'sound/items/drop/herb.ogg' var/plantname var/datum/seed/seed var/potency = -1 -/obj/item/weapon/reagent_containers/food/snacks/grown/Initialize(newloc,planttype) - ..() +/obj/item/weapon/reagent_containers/food/snacks/grown/Initialize(var/mapload, var/planttype) + . = ..() + if(!dried_type) dried_type = type - src.pixel_x = rand(-5.0, 5) - src.pixel_y = rand(-5.0, 5) + + pixel_x = rand(-5.0, 5) + pixel_y = rand(-5.0, 5) - // Fill the object up with the appropriate reagents. if(planttype) plantname = planttype -/obj/item/weapon/reagent_containers/food/snacks/grown/Initialize() - ..() - spawn() - if(!plantname) - return + if(!plantname) + log_debug("Plantname not provided and and [src] requires it at [x],[y],[z]") + return INITIALIZE_HINT_QDEL - if(!plant_controller) - sleep(250) // ugly hack, should mean roundstart plants are fine. - if(!plant_controller) - world << "Plant controller does not exist and [src] requires it. Aborting." - qdel(src) - return + seed = plant_controller.seeds[plantname] - seed = plant_controller.seeds[plantname] + if(!seed) + log_debug("Plant name '[plantname]' does not exist and [src] requires it at [x],[y],[z]") + return INITIALIZE_HINT_QDEL - if(!seed) - return + name = "[seed.seed_name]" + trash = seed.get_trash_type() - name = "[seed.seed_name]" - trash = seed.get_trash_type() + update_icon() - update_icon() - - if(!seed.chems) - return - - potency = seed.get_trait(TRAIT_POTENCY) + potency = seed.get_trait(TRAIT_POTENCY) + if(seed.chems) for(var/rid in seed.chems) var/list/reagent_data = seed.chems[rid] if(reagent_data && reagent_data.len) @@ -76,7 +68,7 @@ if(!plant_controller) sleep(250) // ugly hack, should mean roundstart plants are fine. if(!plant_controller) - world << "Plant controller does not exist and [src] requires it. Aborting." + to_world("Plant controller does not exist and [src] requires it. Aborting.") qdel(src) return @@ -157,12 +149,8 @@ overlays |= plant_icon /obj/item/weapon/reagent_containers/food/snacks/grown/Crossed(var/mob/living/M) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = M - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(M.is_incorporeal()) + return if(seed && seed.get_trait(TRAIT_JUICY) == 2) if(istype(M)) @@ -175,7 +163,7 @@ return M.stop_pulling() - M << "You slipped on the [name]!" + to_chat(M, "You slipped on the [name]!") playsound(src.loc, 'sound/misc/slip.ogg', 50, 1, -3) M.Stun(8) M.Weaken(5) @@ -195,7 +183,7 @@ var/obj/item/stack/cable_coil/C = W if(C.use(5)) //TODO: generalize this. - user << "You add some cable to the [src.name] and slide it inside the battery casing." + to_chat(user, "You add some cable to the [src.name] and slide it inside the battery casing.") var/obj/item/weapon/cell/potato/pocell = new /obj/item/weapon/cell/potato(get_turf(user)) if(src.loc == user && istype(user,/mob/living/carbon/human)) user.put_in_hands(pocell) @@ -224,26 +212,26 @@ if(G.amount>=G.max_amount) continue G.attackby(NG, user) - user << "You add the newly-formed wood to the stack. It now contains [NG.amount] planks." + to_chat(user, "You add the newly-formed wood to the stack. It now contains [NG.amount] planks.") qdel(src) return else if(!isnull(seed.chems["potato"])) - user << "You slice \the [src] into sticks." + to_chat(user, "You slice \the [src] into sticks.") new /obj/item/weapon/reagent_containers/food/snacks/rawsticks(get_turf(src)) qdel(src) return else if(!isnull(seed.chems["carrotjuice"])) - user << "You slice \the [src] into sticks." + to_chat(user, "You slice \the [src] into sticks.") new /obj/item/weapon/reagent_containers/food/snacks/carrotfries(get_turf(src)) qdel(src) return else if(!isnull(seed.chems["soymilk"])) - user << "You roughly chop up \the [src]." + to_chat(user, "You roughly chop up \the [src].") new /obj/item/weapon/reagent_containers/food/snacks/soydope(get_turf(src)) qdel(src) return else if(seed.get_trait(TRAIT_FLESH_COLOUR)) - user << "You slice up \the [src]." + to_chat(user, "You slice up \the [src].") var/slices = rand(3,5) var/reagents_to_transfer = round(reagents.total_volume/slices) for(var/i=i;i<=slices;i++) @@ -266,7 +254,7 @@ return if(prob(35)) if(user) - user << "\The [src] has fallen to bits." + to_chat(user, "\The [src] has fallen to bits.") user.drop_from_inventory(src) qdel(src) @@ -298,12 +286,12 @@ if(NG.amount>=NG.max_amount) continue NG.attackby(G, user) - user << "You add the newly-formed grass to the stack. It now contains [G.amount] tiles." + to_chat(user, "You add the newly-formed grass to the stack. It now contains [G.amount] tiles.") qdel(src) return if(seed.get_trait(TRAIT_SPREAD) > 0) - user << "You plant the [src.name]." + to_chat(user, "You plant the [src.name].") new /obj/machinery/portable_atmospherics/hydroponics/soil/invisible(get_turf(user),src.seed) qdel(src) return @@ -314,13 +302,13 @@ if("shand") var/obj/item/stack/medical/bruise_pack/tajaran/poultice = new /obj/item/stack/medical/bruise_pack/tajaran(user.loc) poultice.heal_brute = potency - user << "You mash the leaves into a poultice." + to_chat(user, "You mash the leaves into a poultice.") qdel(src) return if("mtear") var/obj/item/stack/medical/ointment/tajaran/poultice = new /obj/item/stack/medical/ointment/tajaran(user.loc) poultice.heal_burn = potency - user << "You mash the petals into a poultice." + to_chat(user, "You mash the petals into a poultice.") qdel(src) return */ @@ -365,6 +353,7 @@ var/list/fruit_icon_cache = list() name = "[S.seed_name] slice" desc = "A slice of \a [S.seed_name]. Tasty, probably." + drop_sound = 'sound/items/drop/herb.ogg' var/rind_colour = S.get_trait(TRAIT_PRODUCT_COLOUR) var/flesh_colour = S.get_trait(TRAIT_FLESH_COLOUR) diff --git a/code/modules/hydroponics/grown_inedible.dm b/code/modules/hydroponics/grown_inedible.dm index 2b02116478..2d3dfbc344 100644 --- a/code/modules/hydroponics/grown_inedible.dm +++ b/code/modules/hydroponics/grown_inedible.dm @@ -46,7 +46,7 @@ /obj/item/weapon/corncob/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() if(istype(W, /obj/item/weapon/surgical/circular_saw) || istype(W, /obj/item/weapon/material/knife/machete/hatchet) || istype(W, /obj/item/weapon/material/knife)) - user << "You use [W] to fashion a pipe out of the corn cob!" + to_chat(user, "You use [W] to fashion a pipe out of the corn cob!") new /obj/item/clothing/mask/smokable/pipe/cobpipe (user.loc) qdel(src) return diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 70b5c99170..775fc13dfe 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -99,12 +99,12 @@ return var/datum/reagents/R = new/datum/reagents(100) - if(chems.len) + if(chems && chems.len) for(var/rid in chems) var/injecting = min(5,max(1,get_trait(TRAIT_POTENCY)/3)) R.add_reagent(rid,injecting) - var/datum/effect/effect/system/smoke_spread/chem/spores/S = new(name) + var/datum/effect/effect/system/smoke_spread/chem/spores/S = new(src) S.attach(T) S.set_up(R, round(get_trait(TRAIT_POTENCY)/4), 0, T) S.start() @@ -193,7 +193,7 @@ if(!flesh_colour) flesh_colour = get_trait(TRAIT_PRODUCT_COLOUR) if(flesh_colour) splat.color = get_trait(TRAIT_PRODUCT_COLOUR) - if(chems) + if(chems && chems.len) for(var/mob/living/M in T.contents) if(!M.reagents) continue @@ -229,6 +229,7 @@ open_turfs |= origin_turf // Flood fill to get affected turfs. + // NOTE: Halfass bugfix implemented using air_blocked() but this really should be redone completely ~Leshana while(open_turfs.len) var/turf/T = pick(open_turfs) open_turfs -= T @@ -246,11 +247,11 @@ var/no_los var/turf/last_turf = origin_turf for(var/turf/target_turf in getline(origin_turf,neighbor)) - if(!last_turf.Enter(target_turf) || target_turf.density) + if(air_master.air_blocked(last_turf, target_turf)) no_los = 1 break last_turf = target_turf - if(!no_los && !origin_turf.Enter(neighbor)) + if(!no_los && air_master.air_blocked(origin_turf, neighbor)) no_los = 1 if(no_los) closed_turfs |= neighbor @@ -409,8 +410,8 @@ seed_noun = pick("spores","nodes","cuttings","seeds") set_trait(TRAIT_POTENCY,rand(5,30),200,0) - set_trait(TRAIT_PRODUCT_ICON,pick(plant_controller.plant_product_sprites)) - set_trait(TRAIT_PLANT_ICON,pick(plant_controller.plant_sprites)) + set_trait(TRAIT_PRODUCT_ICON,pick(plant_controller.accessible_product_sprites)) + set_trait(TRAIT_PLANT_ICON,pick(plant_controller.accessible_plant_sprites)) set_trait(TRAIT_PLANT_COLOUR,"#[get_random_colour(0,75,190)]") set_trait(TRAIT_PRODUCT_COLOUR,"#[get_random_colour(0,75,190)]") update_growth_stages() @@ -452,15 +453,16 @@ var/additional_chems = rand(0,5) if(additional_chems) - //VOREStation Edit Start TFF 24/1/20 - More chems to the blacklist for prefs reasoning. + // VOREStation Edit Start: Modified exclusion list var/list/banned_chems = list( "adminordrazine", "nutriment", "macrocillin", "microcillin", - "normalcillin" + "normalcillin", + "magicdust" ) - //VOREStation Edit End + // VOREStation Edit End: Modified exclusion list for(var/x=1;x<=additional_chems;x++) @@ -693,11 +695,11 @@ for(var/gas in exude_gasses) exude_gasses[gas] = max(1,round(exude_gasses[gas]*0.8)) - set_trait(TRAIT_BENEFICIAL_REAG, gene.values["[TRAIT_BENEFICIAL_REAG]"].Copy()) + set_trait(TRAIT_BENEFICIAL_REAG, gene.values["[TRAIT_BENEFICIAL_REAG]"]?.Copy() || list()) - set_trait(TRAIT_MUTAGENIC_REAG, gene.values["[TRAIT_MUTAGENIC_REAG]"].Copy()) + set_trait(TRAIT_MUTAGENIC_REAG, gene.values["[TRAIT_MUTAGENIC_REAG]"]?.Copy() || list()) - set_trait(TRAIT_TOXIC_REAG, gene.values["[TRAIT_TOXIC_REAG]"].Copy()) + set_trait(TRAIT_TOXIC_REAG, gene.values["[TRAIT_TOXIC_REAG]"]?.Copy() || list()) gene.values["[TRAIT_EXUDE_GASSES]"] = null gene.values["[TRAIT_CHEMS]"] = null @@ -779,9 +781,11 @@ playsound(M, harvest_sound, 50, 1, -1) if(!force_amount && get_trait(TRAIT_YIELD) == 0 && !harvest_sample) - if(istype(user)) user << "You fail to harvest anything useful." + if(istype(user)) + to_chat(user, "You fail to harvest anything useful.") else - if(istype(user)) user << "You [harvest_sample ? "take a sample" : "harvest"] from the [display_name]." + if(istype(user)) + to_chat(user, "You [harvest_sample ? "take a sample" : "harvest"] from the [display_name].") //This may be a new line. Update the global if it is. if(name == "new line" || !(name in plant_controller.seeds)) diff --git a/code/modules/hydroponics/seed_controller.dm b/code/modules/hydroponics/seed_controller.dm index 27e6347e5a..1ca42f4c60 100644 --- a/code/modules/hydroponics/seed_controller.dm +++ b/code/modules/hydroponics/seed_controller.dm @@ -25,16 +25,18 @@ var/global/datum/controller/plants/plant_controller // Set in New(). var/plants_per_tick = PLANTS_PER_TICK var/plant_tick_time = PLANT_TICK_TIME - var/list/product_descs = list() // Stores generated fruit descs. - var/list/plant_queue = list() // All queued plants. - var/list/seeds = list() // All seed data stored here. - var/list/gene_tag_masks = list() // Gene obfuscation for delicious trial and error goodness. - var/list/plant_icon_cache = list() // Stores images of growth, fruits and seeds. - var/list/plant_sprites = list() // List of all harvested product sprites. - var/list/plant_product_sprites = list() // List of all growth sprites plus number of growth stages. - var/processing = 0 // Off/on. - var/list/gene_masked_list = list() // Stored gene masked list, rather than recreating it when needed. - var/list/plant_gene_datums = list() // Stored datum versions of the gene masked list. + var/list/product_descs = list() // Stores generated fruit descs. + var/list/plant_queue = list() // All queued plants. + var/list/seeds = list() // All seed data stored here. + var/list/gene_tag_masks = list() // Gene obfuscation for delicious trial and error goodness. + var/list/plant_icon_cache = list() // Stores images of growth, fruits and seeds. + var/list/plant_sprites = list() // List of all growth sprites plus number of growth stages. + var/list/accessible_plant_sprites = list() // List of all plant sprites allowed to appear in random generation. + var/list/plant_product_sprites = list() // List of all harvested product sprites. + var/list/accessible_product_sprites = list() // List of all product sprites allowed to appear in random generation. + var/processing = 0 // Off/on. + var/list/gene_masked_list = list() // Stored gene masked list, rather than recreating it when needed. + var/list/plant_gene_datums = list() // Stored datum versions of the gene masked list. /datum/controller/plants/New() if(plant_controller && plant_controller != src) @@ -66,11 +68,16 @@ var/global/datum/controller/plants/plant_controller // Set in New(). if(!(plant_sprites[base]) || (plant_sprites[base][host] is malformed and unable to survive. It expires pitifully, leaving behind some seeds.") var/total_yield = rand(1,3) diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index 4424203361..74440c518c 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -62,7 +62,7 @@ GLOBAL_LIST_BOILERPLATE(all_seed_packs, /obj/item/seeds) /obj/item/seeds/examine(mob/user) ..(user) if(seed && !seed.roundstart) - user << "It's tagged as variety #[seed.uid]." + to_chat(user, "It's tagged as variety #[seed.uid].") /obj/item/seeds/cutting name = "cuttings" diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 602947abb7..053ce5a59c 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -20,11 +20,11 @@ /obj/machinery/seed_storage name = "Seed storage" desc = "It stores, sorts, and dispenses seeds." - icon = 'icons/obj/vending_vr.dmi' //VOREStation Edit - Dunno why this isn't a vending subtype + icon = 'icons/obj/vending.dmi' icon_state = "seeds" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 var/seeds_initialized = 0 // Map-placed ones break if seeds are loaded right at the start of the round, so we do it on the first interaction diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index b2e3e28287..e3ac11ed76 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -7,7 +7,7 @@ for(var/areapath in typesof(/area/hallway)) var/area/A = locate(areapath) for(var/turf/simulated/floor/F in A.contents) - if(turf_clear(F)) + if(!F.check_density()) turfs += F if(turfs.len) //Pick a turf to spawn at if we can @@ -92,7 +92,7 @@ if(!plant_controller) sleep(250) // ugly hack, should mean roundstart plants are fine. TODO initialize perhaps? if(!plant_controller) - world << "Plant controller does not exist and [src] requires it. Aborting." + to_world("Plant controller does not exist and [src] requires it. Aborting.") qdel(src) return @@ -242,16 +242,16 @@ if(W.is_wirecutter() || istype(W, /obj/item/weapon/surgical/scalpel)) if(sampled) - user << "\The [src] has already been sampled recently." + to_chat(user, "\The [src] has already been sampled recently.") return if(!is_mature()) - user << "\The [src] is not mature enough to yield a sample yet." + to_chat(user, "\The [src] is not mature enough to yield a sample yet.") return if(!seed) - user << "There is nothing to take a sample from." + to_chat(user, "There is nothing to take a sample from.") return if(sampled) - user << "You cannot take another sample from \the [src]." + to_chat(user, "You cannot take another sample from \the [src].") return if(prob(70)) sampled = 1 diff --git a/code/modules/hydroponics/spreading/spreading_response.dm b/code/modules/hydroponics/spreading/spreading_response.dm index dab4af608f..8c694c8b00 100644 --- a/code/modules/hydroponics/spreading/spreading_response.dm +++ b/code/modules/hydroponics/spreading/spreading_response.dm @@ -20,12 +20,8 @@ manual_unbuckle(user) /obj/effect/plant/Crossed(atom/movable/O) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = O - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(O.is_incorporeal()) + return if(isliving(O)) trodden_on(O) @@ -104,6 +100,6 @@ victim.forceMove(src.loc) buckle_mob(victim) victim.set_dir(pick(cardinal)) - victim << "Tendrils [pick("wind", "tangle", "tighten")] around you!" + to_chat(victim, "Tendrils [pick("wind", "tangle", "tighten")] around you!") victim.Weaken(0.5) seed.do_thorns(victim,src) diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index 2e5d64c244..d5bab16482 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -307,7 +307,8 @@ return if(closed_system) - if(user) user << "You can't harvest from the plant while the lid is shut." + if(user) + to_chat(user, "You can't harvest from the plant while the lid is shut.") return if(user) @@ -334,7 +335,7 @@ if(!user || !dead) return if(closed_system) - user << "You can't remove the dead plant while the lid is shut." + to_chat(user, "You can't remove the dead plant while the lid is shut.") return seed = null @@ -344,7 +345,7 @@ yield_mod = 0 mutation_mod = 0 - user << "You remove the dead plant." + to_chat(user, "You remove the dead plant.") lastproduce = 0 check_health() return @@ -400,11 +401,11 @@ return if(ishuman(usr) || istype(usr, /mob/living/silicon/robot)) if(labelled) - usr << "You remove the label." + to_chat(usr, "You remove the label.") labelled = null update_icon() else - usr << "There is no label to remove." + to_chat(usr, "There is no label to remove.") return /obj/machinery/portable_atmospherics/hydroponics/verb/setlight() @@ -418,7 +419,7 @@ var/new_light = input("Specify a light level.") as null|anything in list(0,1,2,3,4,5,6,7,8,9,10) if(new_light) tray_light = new_light - usr << "You set the tray to a light level of [tray_light] lumens." + to_chat(usr, "You set the tray to a light level of [tray_light] lumens.") return /obj/machinery/portable_atmospherics/hydroponics/proc/check_level_sanity() @@ -466,15 +467,15 @@ if(O.is_wirecutter() || istype(O, /obj/item/weapon/surgical/scalpel)) if(!seed) - user << "There is nothing to take a sample from in \the [src]." + to_chat(user, "There is nothing to take a sample from in \the [src].") return if(sampled) - user << "You have already sampled from this plant." + to_chat(user, "You have already sampled from this plant.") return if(dead) - user << "The plant is dead." + to_chat(user, "The plant is dead.") return // Create a sample. @@ -499,14 +500,14 @@ if(seed) return ..() else - user << "There's no plant to inject." + to_chat(user, "There's no plant to inject.") return 1 else if(seed) //Leaving this in in case we want to extract from plants later. - user << "You can't get any extract out of this plant." + to_chat(user, "You can't get any extract out of this plant.") else - user << "There's nothing to draw something from." + to_chat(user, "There's nothing to draw something from.") return 1 else if (istype(O, /obj/item/seeds)) @@ -517,15 +518,15 @@ user.remove_from_mob(O) if(!S.seed) - user << "The packet seems to be empty. You throw it away." + to_chat(user, "The packet seems to be empty. You throw it away.") qdel(O) return - user << "You plant the [S.seed.seed_name] [S.seed.seed_noun]." + to_chat(user, "You plant the [S.seed.seed_name] [S.seed.seed_noun].") plant_seeds(S) else - user << "\The [src] already has seeds in it!" + to_chat(user, "\The [src] already has seeds in it!") else if (istype(O, /obj/item/weapon/material/minihoe)) // The minihoe @@ -534,7 +535,7 @@ weedlevel = 0 update_icon() else - user << "This plot is completely devoid of weeds. It doesn't need uprooting." + to_chat(user, "This plot is completely devoid of weeds. It doesn't need uprooting.") else if (istype(O, /obj/item/weapon/storage/bag/plants)) @@ -553,7 +554,7 @@ toxins += spray.toxicity pestlevel -= spray.pest_kill_str weedlevel -= spray.weed_kill_str - user << "You spray [src] with [O]." + to_chat(user, "You spray [src] with [O].") playsound(loc, 'sound/effects/spray3.ogg', 50, 1, -6) qdel(O) check_health() @@ -566,7 +567,7 @@ playsound(loc, O.usesound, 50, 1) anchored = !anchored - user << "You [anchored ? "wrench" : "unwrench"] \the [src]." + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") else if(istype(O,/obj/item/device/multitool)) if(!anchored) @@ -611,25 +612,25 @@ ..() if(seed) - usr << "[seed.display_name] are growing here." + to_chat(usr, "[seed.display_name] are growing here.") else - usr << "[src] is empty." + to_chat(usr, "[src] is empty.") if(!Adjacent(usr)) return - usr << "Water: [round(waterlevel,0.1)]/100" - usr << "Nutrient: [round(nutrilevel,0.1)]/10" + to_chat(usr, "Water: [round(waterlevel,0.1)]/100") + to_chat(usr, "Nutrient: [round(nutrilevel,0.1)]/10") if(seed) if(weedlevel >= 5) - usr << "\The [src] is infested with weeds!" + to_chat(usr, "\The [src] is infested with weeds!") if(pestlevel >= 5) - usr << "\The [src] is infested with tiny worms!" + to_chat(usr, "\The [src] is infested with tiny worms!") if(dead) - usr << "The plant is dead." + to_chat(usr, "The plant is dead.") else if(health <= (seed.get_trait(TRAIT_ENDURANCE)/ 2)) - usr << "The plant looks unhealthy." + to_chat(usr, "The plant looks unhealthy.") if(frozen == 1) to_chat(usr, "It is cryogenically frozen.") if(mechanical) @@ -654,7 +655,7 @@ var/light_available = T.get_lumcount() * 5 light_string = "a light level of [light_available] lumens" - usr << "The tray's sensor suite is reporting [light_string] and a temperature of [environment.temperature]K at [environment.return_pressure()] kPa in the [environment_type] environment" + to_chat(usr, "The tray's sensor suite is reporting [light_string] and a temperature of [environment.temperature]K at [environment.return_pressure()] kPa in the [environment_type] environment") /obj/machinery/portable_atmospherics/hydroponics/verb/close_lid_verb() set name = "Toggle Tray Lid" @@ -669,5 +670,5 @@ /obj/machinery/portable_atmospherics/hydroponics/proc/close_lid(var/mob/living/user) closed_system = !closed_system - user << "You [closed_system ? "close" : "open"] the tray's lid." + to_chat(user, "You [closed_system ? "close" : "open"] the tray's lid.") update_icon() diff --git a/code/modules/hydroponics/trays/tray_soil.dm b/code/modules/hydroponics/trays/tray_soil.dm index d6297a6766..76a3417645 100644 --- a/code/modules/hydroponics/trays/tray_soil.dm +++ b/code/modules/hydroponics/trays/tray_soil.dm @@ -2,7 +2,7 @@ name = "soil" icon_state = "soil" density = 0 - use_power = 0 + use_power = USE_POWER_OFF mechanical = 0 tray_light = 0 frozen = -1 diff --git a/code/modules/hydroponics/trays/tray_tools.dm b/code/modules/hydroponics/trays/tray_tools.dm index 12582f01ed..d28db69bae 100644 --- a/code/modules/hydroponics/trays/tray_tools.dm +++ b/code/modules/hydroponics/trays/tray_tools.dm @@ -36,7 +36,7 @@ /obj/item/device/analyzer/plant_analyzer/proc/print_report(var/mob/living/user) if(!last_data) - user << "There is no scan data to print." + to_chat(user, "There is no scan data to print.") return var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(get_turf(src)) P.name = "paper - [form_title]" @@ -84,7 +84,7 @@ grown_reagents = H.reagents if(!grown_seed) - user << "[src] can tell you nothing about \the [target]." + to_chat(user, "[src] can tell you nothing about \the [target].") return form_title = "[grown_seed.seed_name] (#[grown_seed.uid])" diff --git a/code/modules/hydroponics/trays/tray_update_icons.dm b/code/modules/hydroponics/trays/tray_update_icons.dm index cf790269a2..1a816abb3f 100644 --- a/code/modules/hydroponics/trays/tray_update_icons.dm +++ b/code/modules/hydroponics/trays/tray_update_icons.dm @@ -30,7 +30,7 @@ if(!seed.growth_stages) seed.update_growth_stages() if(!seed.growth_stages) - world << "Seed type [seed.get_trait(TRAIT_PLANT_ICON)] cannot find a growth stage value." + to_world("Seed type [seed.get_trait(TRAIT_PLANT_ICON)] cannot find a growth stage value.") return var/overlay_stage = 1 if(age >= seed.get_trait(TRAIT_MATURATION)) diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm index b540aaec2e..896deca111 100644 --- a/code/modules/integrated_electronics/core/assemblies.dm +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -265,9 +265,11 @@ else if(istype(I, /obj/item/device/integrated_electronics/wirer) || istype(I, /obj/item/device/integrated_electronics/debugger) || I.is_screwdriver()) if(opened) interact(user) + return TRUE else to_chat(user, "\The [src] isn't opened, so you can't fiddle with the internal components. \ Try using a crowbar.") + return FALSE else if(istype(I, /obj/item/device/integrated_electronics/detailer)) var/obj/item/device/integrated_electronics/detailer/D = I @@ -365,3 +367,7 @@ /obj/item/device/electronic_assembly/proc/on_unanchored() for(var/obj/item/integrated_circuit/IC in contents) IC.on_unanchored() + +// Returns TRUE if I is something that could/should have a valid interaction. Used to tell circuitclothes to hit the circuit with something instead of the clothes +/obj/item/device/electronic_assembly/proc/is_valid_tool(var/obj/item/I) + return I.is_crowbar() || I.is_screwdriver() || istype(I, /obj/item/integrated_circuit) || istype(I, /obj/item/weapon/cell/device) || istype(I, /obj/item/device/integrated_electronics) \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/assemblies/clothing.dm b/code/modules/integrated_electronics/core/assemblies/clothing.dm index 4415d367f8..2b0b75f69f 100644 --- a/code/modules/integrated_electronics/core/assemblies/clothing.dm +++ b/code/modules/integrated_electronics/core/assemblies/clothing.dm @@ -51,13 +51,12 @@ IC.examine(user) ..() -/obj/item/clothing/attackby(obj/item/I, mob/user) - if(IC) - // This needs to be done in a better way... - if(I.is_crowbar() || I.is_screwdriver() || istype(I, /obj/item/integrated_circuit) || istype(I, /obj/item/weapon/cell/device) || istype(I, /obj/item/device/integrated_electronics) ) - IC.attackby(I, user) - else - ..() +/obj/item/clothing/CtrlShiftClick(mob/user) + var/turf/T = get_turf(src) + if(!T.AdjacentQuick(user)) // So people aren't messing with these from across the room + return FALSE + var/obj/item/I = user.get_active_hand() // ctrl-shift-click doesn't give us the item, we have to fetch it + return IC.attackby(I, user) /obj/item/clothing/attack_self(mob/user) if(IC) @@ -105,6 +104,7 @@ /obj/item/clothing/under/circuitry name = "electronic jumpsuit" desc = "It's a wearable case for electronics. This on is a black jumpsuit with wiring weaved into the fabric." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." icon_state = "circuitry" worn_state = "circuitry" @@ -118,6 +118,7 @@ name = "electronic gloves" desc = "It's a wearable case for electronics. This one is a pair of black gloves, with wires woven into them. A small \ device with a screen is attached to the left glove." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." icon_state = "circuitry" item_state = "circuitry" @@ -131,6 +132,7 @@ name = "electronic goggles" desc = "It's a wearable case for electronics. This one is a pair of goggles, with wiring sticking out. \ Could this augment your vision?" // Sadly it won't, or at least not yet. + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." icon_state = "circuitry" item_state = "night" // The on-mob sprite would be identical anyways. @@ -143,6 +145,7 @@ name = "electronic boots" desc = "It's a wearable case for electronics. This one is a pair of boots, with wires attached to a small \ cover." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." icon_state = "circuitry" item_state = "circuitry" @@ -155,6 +158,7 @@ name = "electronic headwear" desc = "It's a wearable case for electronics. This one appears to be a very technical-looking piece that \ goes around the collar, with a heads-up-display attached on the right." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." icon_state = "circuitry" item_state = "circuitry" @@ -166,6 +170,7 @@ /obj/item/clothing/ears/circuitry name = "electronic earwear" desc = "It's a wearable case for electronics. This one appears to be a technical-looking headset." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." icon = 'icons/obj/clothing/ears.dmi' icon_state = "circuitry" item_state = "circuitry" @@ -179,6 +184,7 @@ name = "electronic chestpiece" desc = "It's a wearable case for electronics. This one appears to be a very technical-looking vest, that \ almost looks professionally made, however the wiring popping out betrays that idea." + description_info = "Control-shift-click on this with an item in hand to use it on the integrated circuit." icon_state = "circuitry" item_state = "circuitry" diff --git a/code/modules/integrated_electronics/core/tools.dm b/code/modules/integrated_electronics/core/tools.dm index 23e8e3f24b..79533bfe25 100644 --- a/code/modules/integrated_electronics/core/tools.dm +++ b/code/modules/integrated_electronics/core/tools.dm @@ -276,60 +276,54 @@ ) cant_hold = list(/obj/item/weapon/tool/screwdriver/power) -/obj/item/weapon/storage/bag/circuits/basic/New() - ..() - spawn(2 SECONDS) // So the list has time to initialize. -// for(var/obj/item/integrated_circuit/IC in all_integrated_circuits) -// if(IC.spawn_flags & IC_SPAWN_DEFAULT) -// for(var/i = 1 to 4) -// new IC.type(src) - new /obj/item/weapon/storage/bag/circuits/mini/arithmetic(src) - new /obj/item/weapon/storage/bag/circuits/mini/trig(src) - new /obj/item/weapon/storage/bag/circuits/mini/input(src) - new /obj/item/weapon/storage/bag/circuits/mini/output(src) - new /obj/item/weapon/storage/bag/circuits/mini/memory(src) - new /obj/item/weapon/storage/bag/circuits/mini/logic(src) - new /obj/item/weapon/storage/bag/circuits/mini/time(src) - new /obj/item/weapon/storage/bag/circuits/mini/reagents(src) - new /obj/item/weapon/storage/bag/circuits/mini/transfer(src) - new /obj/item/weapon/storage/bag/circuits/mini/converter(src) - new /obj/item/weapon/storage/bag/circuits/mini/power(src) +/obj/item/weapon/storage/bag/circuits/basic/Initialize() + . = ..() + new /obj/item/weapon/storage/bag/circuits/mini/arithmetic(src) + new /obj/item/weapon/storage/bag/circuits/mini/trig(src) + new /obj/item/weapon/storage/bag/circuits/mini/input(src) + new /obj/item/weapon/storage/bag/circuits/mini/output(src) + new /obj/item/weapon/storage/bag/circuits/mini/memory(src) + new /obj/item/weapon/storage/bag/circuits/mini/logic(src) + new /obj/item/weapon/storage/bag/circuits/mini/time(src) + new /obj/item/weapon/storage/bag/circuits/mini/reagents(src) + new /obj/item/weapon/storage/bag/circuits/mini/transfer(src) + new /obj/item/weapon/storage/bag/circuits/mini/converter(src) + new /obj/item/weapon/storage/bag/circuits/mini/power(src) - new /obj/item/device/electronic_assembly(src) - new /obj/item/device/assembly/electronic_assembly(src) - new /obj/item/device/assembly/electronic_assembly(src) - new /obj/item/device/multitool(src) - new /obj/item/weapon/tool/screwdriver(src) - new /obj/item/weapon/tool/crowbar(src) - make_exact_fit() + new /obj/item/device/electronic_assembly(src) + new /obj/item/device/assembly/electronic_assembly(src) + new /obj/item/device/assembly/electronic_assembly(src) + new /obj/item/device/multitool(src) + new /obj/item/weapon/tool/screwdriver(src) + new /obj/item/weapon/tool/crowbar(src) + make_exact_fit() -/obj/item/weapon/storage/bag/circuits/all/New() - ..() - spawn(2 SECONDS) // So the list has time to initialize. - new /obj/item/weapon/storage/bag/circuits/mini/arithmetic/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/trig/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/input/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/output/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/memory/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/logic/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/smart/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/manipulation/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/time/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/reagents/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/transfer/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/converter/all(src) - new /obj/item/weapon/storage/bag/circuits/mini/power/all(src) +/obj/item/weapon/storage/bag/circuits/all/Initialize() + . = ..() + new /obj/item/weapon/storage/bag/circuits/mini/arithmetic/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/trig/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/input/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/output/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/memory/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/logic/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/smart/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/manipulation/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/time/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/reagents/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/transfer/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/converter/all(src) + new /obj/item/weapon/storage/bag/circuits/mini/power/all(src) - new /obj/item/device/electronic_assembly(src) - new /obj/item/device/electronic_assembly/medium(src) - new /obj/item/device/electronic_assembly/large(src) - new /obj/item/device/electronic_assembly/drone(src) - new /obj/item/device/integrated_electronics/wirer(src) - new /obj/item/device/integrated_electronics/debugger(src) - new /obj/item/weapon/tool/crowbar(src) - make_exact_fit() + new /obj/item/device/electronic_assembly(src) + new /obj/item/device/electronic_assembly/medium(src) + new /obj/item/device/electronic_assembly/large(src) + new /obj/item/device/electronic_assembly/drone(src) + new /obj/item/device/integrated_electronics/wirer(src) + new /obj/item/device/integrated_electronics/debugger(src) + new /obj/item/weapon/tool/crowbar(src) + make_exact_fit() -/obj/item/weapon/storage/bag/circuits/mini/ +/obj/item/weapon/storage/bag/circuits/mini name = "circuit box" desc = "Used to partition categories of circuits, for a neater workspace." w_class = 2 diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index dc17f3323b..37504035c0 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -416,7 +416,7 @@ if(loc) for(var/mob/O in hearers(1, get_turf(src))) - O.show_message(text("\icon[] *beep* *beep*", src), 3, "*beep* *beep*", 2) + O.show_message("[bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) /obj/item/integrated_circuit/input/EPv2 name = "\improper EPv2 circuit" diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index 3ddc3458d7..0f991aac23 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -43,7 +43,7 @@ var/list/nearby_things = range(0, get_turf(src)) for(var/mob/M in nearby_things) var/obj/O = assembly ? assembly : src - to_chat(M, "\icon[O] [stuff_to_display]") + to_chat(M, "[bicon(O)] [stuff_to_display]") /obj/item/integrated_circuit/output/screen/large name = "large screen" @@ -56,7 +56,7 @@ /obj/item/integrated_circuit/output/screen/large/do_work() ..() var/obj/O = assembly ? loc : assembly - O.visible_message("\icon[O] [stuff_to_display]") + O.visible_message("[bicon(O)] [stuff_to_display]") /obj/item/integrated_circuit/output/light name = "light" @@ -134,7 +134,7 @@ text = get_pin_data(IC_INPUT, 1) if(!isnull(text)) var/obj/O = assembly ? loc : assembly - audible_message("\icon[O] \The [O.name] states, \"[text]\"") + audible_message("[bicon(O)] \The [O.name] states, \"[text]\"") /obj/item/integrated_circuit/output/text_to_speech/advanced name = "advanced text-to-speech circuit" diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 009980bec5..1eb05b7949 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -180,6 +180,7 @@ Book Cart End var/title // The real name of the book. var/carved = 0 // Has the book been hollowed out for use as a secret storage item? var/obj/item/store //What's in the book? + drop_sound = 'sound/bureaucracy/bookclose.ogg' /obj/item/weapon/book/attack_self(var/mob/user as mob) if(carved) @@ -194,7 +195,9 @@ Book Cart End if(src.dat) user << browse("Penned by [author].
    " + "[dat]", "window=book") user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.") + playsound(loc, 'sound/bureaucracy/bookopen.ogg', 50, 1) onclose(user, "book") + playsound(loc, 'sound/bureaucracy/bookclose.ogg', 50, 1) else to_chat(user, "This book is completely blank!") @@ -277,6 +280,8 @@ Book Cart End to_chat(user, "You begin to carve out [title].") if(do_after(user, 30)) to_chat(user, "You carve out the pages from [title]! You didn't want to read it anyway.") + playsound(loc, 'sound/bureaucracy/papercrumple.ogg', 50, 1) + new /obj/item/weapon/shreddedp(get_turf(src)) carved = 1 return else diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index b5b6d8efb6..508c044673 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -186,7 +186,7 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f if(src.arcanecheckout) new /obj/item/weapon/book/tome(src.loc) var/datum/gender/T = gender_datums[user.get_visible_gender()] - user << "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it." + to_chat(user, "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it.") user.visible_message("\The [user] stares at the blank screen for a few moments, [T.his] expression frozen in fear. When [T.he] finally awakens from it, [T.he] looks a lot older.", 2) src.arcanecheckout = 0 if(1) @@ -308,7 +308,7 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f if(istype(W, /obj/item/weapon/barcodescanner)) var/obj/item/weapon/barcodescanner/scanner = W scanner.computer = src - user << "[scanner]'s associated machine has been set to [src]." + to_chat(user, "[scanner]'s associated machine has been set to [src].") for (var/mob/V in hearers(src)) V.show_message("[src] lets out a low, short blip.", 2) else @@ -417,7 +417,7 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f var/sqlcategory = sanitizeSQL(upload_category) var/DBQuery/query = dbcon_old.NewQuery("INSERT INTO library (author, title, content, category) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]')") if(!query.Execute()) - usr << query.ErrorMsg() + to_chat(usr,query.ErrorMsg()) else log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs") alert("Upload Complete.") diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm index 6db6f807b5..ad014b013d 100644 --- a/code/modules/lighting/lighting_setup.dm +++ b/code/modules/lighting/lighting_setup.dm @@ -1,6 +1,6 @@ // Create lighting overlays on all turfs with dynamic lighting in areas with dynamic lighting. /proc/create_all_lighting_overlays() - for(var/area/A in all_areas) + for(var/area/A in world) if(!A.dynamic_lighting) continue for(var/turf/T in A) diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index cc9c678b97..bc3c099091 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -9,12 +9,6 @@ var/tmp/list/datum/lighting_corner/corners var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. -/turf/New() - . = ..() - - if(opacity) - has_opaque_atom = TRUE - // Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() for(var/datum/light_source/L in affecting_lights) @@ -22,7 +16,7 @@ /turf/proc/lighting_clear_overlay() if(lighting_overlay) - qdel(lighting_overlay) + qdel(lighting_overlay, force = TRUE) for(var/datum/lighting_corner/C in corners) C.update_active() diff --git a/code/modules/looking_glass/lg_area.dm b/code/modules/looking_glass/lg_area.dm new file mode 100644 index 0000000000..b17c67449f --- /dev/null +++ b/code/modules/looking_glass/lg_area.dm @@ -0,0 +1,78 @@ +/area/looking_glass + name = "make a subtype" + + var/obj/effect/landmark/looking_glass/our_landmark + var/list/our_turfs = list() + var/list/our_optional_turfs = list() + + var/lg_id + + var/active = FALSE + +/area/looking_glass/Initialize() + . = ..() + our_landmark = locate() in src + if(!our_landmark) + testing("Looking glass area [name] couldn't find a landmark") + for(var/turf/simulated/floor/looking_glass/lgt in src) + our_turfs += lgt + if(lgt.optional) + our_optional_turfs += lgt + +/area/looking_glass/Destroy() + our_landmark = null + our_turfs.Cut() + return ..() + +/area/looking_glass/Entered(var/atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + if(L.client) + our_landmark?.gain_viewer(L.client) + +/area/looking_glass/Exited(var/atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + if(L.client) + our_landmark?.lose_viewer(L.client) + +/area/looking_glass/proc/begin_program(var/image/newimage) + if(!active) + for(var/trf in our_turfs) + var/turf/simulated/floor/looking_glass/lgt = trf + lgt.activate() + + our_landmark.take_image(newimage) + active = TRUE + +/area/looking_glass/proc/end_program() + if(active) + for(var/trf in our_turfs) + var/turf/simulated/floor/looking_glass/lgt = trf + lgt.deactivate() + + active = FALSE + + spawn(2 SECONDS) + our_landmark.drop_image() + +/area/looking_glass/proc/toggle_optional(var/transparent) + for(var/trf in our_optional_turfs) + var/turf/simulated/floor/looking_glass/lgt = trf + lgt.center = !transparent + if(active) + lgt.deactivate() + spawn(3 SECONDS) + lgt.activate() + +/area/looking_glass/lg_1 + name = "looking glass one" + lg_id = "one" + +/area/looking_glass/lg_2 + name = "looking glass two" + lg_id = "two" + +/area/looking_glass/lg_3 + name = "looking glass three" + lg_id = "three" diff --git a/code/modules/looking_glass/lg_console.dm b/code/modules/looking_glass/lg_console.dm new file mode 100644 index 0000000000..410269b8a0 --- /dev/null +++ b/code/modules/looking_glass/lg_console.dm @@ -0,0 +1,170 @@ +/obj/machinery/computer/looking_glass + name = "looking glass control" + desc = "Controls the looking glass displays in this room." + + icon_keyboard = "tech_key" + icon_screen = "holocontrol" + + var/static/list/supported_programs = list() + var/static/list/secret_programs = list() + + use_power = USE_POWER_IDLE + active_power_usage = 8000 + + var/current_program = "Off" + var/area/looking_glass/my_area + var/last_gravity_change = 0 + var/ready = TRUE + var/immersion = FALSE + + var/lg_id = "change_me" + +/obj/machinery/computer/looking_glass/Initialize() + . = ..() + for(var/area/looking_glass/lga in world) + if(lga.lg_id == lg_id) + my_area = lga + break + if(!istype(my_area)) + testing("Looking glass console [x],[y],[x] not in a looking glass area.") + if(!supported_programs.len) + supported_programs["Off"] = null + supported_programs["Diagnostics"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "diagnostic") + supported_programs["Space 1"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space1") + supported_programs["Space 2"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space2") + supported_programs["Space 3"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space3") + supported_programs["Space 4"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space4") + supported_programs["Space 5"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space5") + supported_programs["Space 6"] = image(icon = 'icons/skybox/skybox.dmi', icon_state = "space6") + + secret_programs["Maw"] = image(icon = 'icons/skybox/skybox_vr.dmi', icon_state = "maw") + secret_programs["Flesh"] = image(icon = 'icons/skybox/skybox_vr.dmi', icon_state = "flesh") + secret_programs["Synth Int"] = image(icon = 'icons/skybox/skybox_vr.dmi', icon_state = "synthinsides") + secret_programs["Synth Int 2"] = image(icon = 'icons/skybox/skybox_vr.dmi', icon_state = "synthinsides_active") + secret_programs["Two Teshari"] = image(icon = 'icons/skybox/skybox_vr.dmi', icon_state = "doubletesh") + secret_programs["Teshari 1"] = image(icon = 'icons/skybox/skybox_vr.dmi', icon_state = "sca") + secret_programs["Teshari 2"] = image(icon = 'icons/skybox/skybox_vr.dmi', icon_state = "eis") + +/obj/machinery/computer/looking_glass/Destroy() + my_area = null + return ..() + +/obj/machinery/computer/looking_glass/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/looking_glass/attack_hand(var/mob/user as mob) + if(..()) + return + user.set_machine(src) + + ui_interact(user) + +/obj/machinery/computer/looking_glass/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + user.set_machine(src) + + var/list/data = list() + var/program_list[0] + + for(var/P in supported_programs) + program_list[++program_list.len] = P + + if(emagged) + for(var/P in secret_programs) + program_list[++program_list.len] = P + + data["supportedPrograms"] = program_list + data["currentProgram"] = current_program + data["immersion"] = immersion + if(my_area?.has_gravity) + data["gravity"] = 1 + else + data["gravity"] = null + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "lookingglass.tmpl", src.name, 400, 550) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(20) + +/obj/machinery/computer/looking_glass/Topic(href, href_list) + if(..()) + return 1 + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if(href_list["program"]) + if(ready) + var/prog = href_list["program"] + if(prog == "Off") + current_program = "Off" + unload_program() + else if((prog in supported_programs) || (emagged && (prog in secret_programs))) + current_program = prog + load_program(prog) + else + visible_message("ERROR. Recalibrating displays.") + + else if(href_list["gravity"]) + toggle_gravity(my_area) + + else if(href_list["immersion"]) + immersion = !immersion + my_area.toggle_optional(immersion) + + src.add_fingerprint(usr) + + SSnanoui.update_uis(src) + +/obj/machinery/computer/looking_glass/emag_act(var/remaining_charges, var/mob/user as mob) + if (!emagged) + playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) + emagged = 1 + to_chat(user, "You unlock several programs that were hidden somewhere in memory.") + log_game("[key_name(usr)] emagged the [name]") + return 1 + return + +/obj/machinery/computer/looking_glass/proc/load_program(var/prog_name) + ready = FALSE + VARSET_IN(src, ready, TRUE, 10 SECONDS) + + if(prog_name in supported_programs) + my_area.begin_program(supported_programs[prog_name]) + else if(prog_name in secret_programs) + my_area.begin_program(secret_programs[prog_name]) + +/obj/machinery/computer/looking_glass/proc/unload_program() + ready = FALSE + VARSET_IN(src, ready, TRUE, 10 SECONDS) + + my_area.end_program() + +/obj/machinery/computer/looking_glass/proc/toggle_gravity(var/area/A) + if(world.time < (last_gravity_change + 3 SECONDS)) + if(world.time < (last_gravity_change + 1 SECOND)) + return + visible_message("ERROR. Recalibrating gravity field.") + return + + last_gravity_change = world.time + + if(A.has_gravity) + A.gravitychange(0) + else + A.gravitychange(1) + +//This could all be done better, but it works for now. +/obj/machinery/computer/looking_glass/Destroy() + unload_program() + ..() + +/obj/machinery/computer/looking_glass/ex_act(severity) + unload_program() + ..() + +/obj/machinery/computer/looking_glass/power_change() + var/oldstat = stat + ..() + if (stat != oldstat && (stat & NOPOWER)) + unload_program() \ No newline at end of file diff --git a/code/modules/looking_glass/lg_imageholder.dm b/code/modules/looking_glass/lg_imageholder.dm new file mode 100644 index 0000000000..0d4cf5b157 --- /dev/null +++ b/code/modules/looking_glass/lg_imageholder.dm @@ -0,0 +1,66 @@ +#define LG_IMAGE_SIZE 736 + +/obj/effect/landmark/looking_glass + var/image/holding + + var/list/viewers + + var/lg_id //Area sets this for you + + mouse_opacity = 0 + +/obj/effect/landmark/looking_glass/Initialize() + . = ..() + viewers = list() + +/obj/effect/landmark/looking_glass/proc/gain_viewer(var/client/C) + if(C in viewers) + testing("Looking Glass [x],[y],[z] tried to add a duplicate viewer.") + viewers |= C + if(holding) + show_to(C) + +/obj/effect/landmark/looking_glass/proc/lose_viewer(var/client/C) + if(!(C in viewers)) + testing("Looking Glass [x],[y],[z] tried to remove a viewer it didn't have") + viewers -= C + if(holding) + unshow_to(C) + +/obj/effect/landmark/looking_glass/proc/show_to(var/client/C) + C.images |= holding + +/obj/effect/landmark/looking_glass/proc/unshow_to(var/client/C) + C.images -= holding + +/obj/effect/landmark/looking_glass/proc/take_image(var/image/newimage) + if(!istype(newimage)) + return + + if(holding) + for(var/client in viewers) + unshow_to(client) + + holding = newimage + newimage.plane = PLANE_LOOKINGGLASS_IMG + newimage.blend_mode = BLEND_MULTIPLY + newimage.appearance_flags = RESET_TRANSFORM + newimage.mouse_opacity = 0 + newimage.pixel_y = newimage.pixel_x = (LG_IMAGE_SIZE/-2) + 16 + newimage.loc = src + + for(var/client in viewers) + show_to(client) + +/obj/effect/landmark/looking_glass/proc/drop_image() + if(!holding) + return + + for(var/client in viewers) + unshow_to(client) + + holding.loc = null + holding = null + + +#undef LG_IMAGE_SIZE \ No newline at end of file diff --git a/code/modules/looking_glass/lg_turfs.dm b/code/modules/looking_glass/lg_turfs.dm new file mode 100644 index 0000000000..189790335d --- /dev/null +++ b/code/modules/looking_glass/lg_turfs.dm @@ -0,0 +1,88 @@ +/decl/flooring/looking_glass + name = "looking glass surface" + desc = "Too expensive to replace. Don't break it!" + icon = 'icons/turf/flooring/lg_origin.dmi' + icon_base = "origin" + build_type = null + damage_temperature = T0C+200 + + +/turf/simulated/floor/looking_glass + name = "looking glass surface" + icon = 'icons/turf/flooring/lg_origin.dmi' + icon_state = "origin_arrow" + initial_flooring = /decl/flooring/looking_glass + appearance_flags = TILE_BOUND + dynamic_lighting = FALSE + + var/center = FALSE + var/optional = FALSE + +/turf/simulated/floor/looking_glass/center + center = TRUE + icon_state = "origin_center" + +/turf/simulated/floor/looking_glass/optional + center = TRUE + optional = TRUE + icon_state = "origin_optional_arrow" + +/turf/simulated/floor/looking_glass/proc/activate() + set waitfor = FALSE + + icon_state = "origin_switching" + + animate(src, color = "#000000", time = 3 SECONDS) + + sleep(3 SECONDS) + + var/new_x = 0 + var/new_y = 0 + + if(dir & NORTH) + new_y = 112 + else if(dir & SOUTH) + new_y = -112 + + if(dir & EAST) + new_x = 112 + else if(dir & WEST) + new_x = -112 + + var/matrix/M = matrix() + var/mutable_appearance/MA = new (src) + + if(!center) + var/horizontal = (dir & (WEST|EAST)) + var/vertical = (dir & (NORTH|SOUTH)) + M.Scale(horizontal ? 8 : 1, vertical ? 8 : 1) + M.Translate(new_x, new_y) + MA.opacity = 1 + if(!optional) + MA.density = 1 + + MA.icon_state = "origin_active" + MA.plane = PLANE_LOOKINGGLASS + MA.layer = 0 + appearance = MA + + animate(src, color = "#FFFFFF", transform = M, time = 3 SECONDS) + +/turf/simulated/floor/looking_glass/proc/deactivate() + set waitfor = FALSE + + animate(src, color = "#000000", transform = matrix(), time = 3 SECONDS) + + sleep(3 SECONDS) + var/mutable_appearance/MA = new (src) + MA.opacity = 0 + MA.density = 0 + MA.icon_state = "origin_switching" + MA.plane = initial(plane) + MA.layer = initial(layer) + appearance = MA + + animate(src, color = null, time = 3 SECONDS) + sleep(3 SECONDS) + icon_state = "origin" + diff --git a/code/modules/lore_codex/codex.dm b/code/modules/lore_codex/codex.dm index ca18f742b2..ffed55365e 100644 --- a/code/modules/lore_codex/codex.dm +++ b/code/modules/lore_codex/codex.dm @@ -6,7 +6,7 @@ icon_state = "codex" unique = TRUE var/datum/codex_tree/tree = null - var/root_type = /datum/lore/codex/category/main_virgo_lore //Runtimes on codex_tree.dm, line 18 with a null here + var/root_type = /datum/lore/codex/category/main_borealis_lore //YW EDIT /obj/item/weapon/book/codex/Initialize() tree = new(src, root_type) @@ -23,7 +23,7 @@ desc = "Contains useful information about the world around you. It seems to have been written for travelers to the Borealis system, human or not. It also \ has the words 'Don't Panic' in small, friendly letters on the cover." //YW Edit icon_state = "codex" - root_type = /datum/lore/codex/category/main_virgo_lore + root_type = /datum/lore/codex/category/main_borealis_lore //YW EDIT libcategory = "Reference" /obj/item/weapon/book/codex/lore/robutt @@ -40,6 +40,7 @@ w_class = ITEMSIZE_SMALL root_type = /datum/lore/codex/category/main_news libcategory = "Reference" + drop_sound = 'sound/items/drop/device.ogg' /* //VORESTATION REMOVAL // Combines SOP/Regs/Law diff --git a/code/modules/lore_codex/lore_data_yw/important_locations.dm b/code/modules/lore_codex/lore_data_yw/important_locations.dm new file mode 100644 index 0000000000..c118bd35d5 --- /dev/null +++ b/code/modules/lore_codex/lore_data_yw/important_locations.dm @@ -0,0 +1,75 @@ +/datum/lore/codex/category/important_locations + name = "Important Locations" + data = "There are several locations of interest that you may come across when visiting the system Borealis-Erigone." + children = list( + /datum/lore/codex/page/borealis, + /datum/lore/codex/page/borealis_one, + /datum/lore/codex/page/borealis_majoris, + /datum/lore/codex/page/borealis_majoris_beta, + /datum/lore/codex/page/nsc_cryogaia, + /datum/lore/codex/page/ncs_serenity, + /datum/lore/codex/page/borealis_three_alpha, + /datum/lore/codex/page/borealis_three_beta, + /datum/lore/codex/page/borealis_three_gamma, + /datum/lore/codex/page/borealis_four_alpha, + /datum/lore/codex/page/borealis_four_beta + ) + +/datum/lore/codex/page/borealis/add_content() + name = "Borealis (Star System)" + keywords += list("Borealis") + data = "Survey data incomplete. Check back later!" + +/datum/lore/codex/page/borealis_one/add_content() + name = "Borealis 1 (No Data)" + keywords += list("Borealis 1") + data = "Survey data incomplete. Check back later!" + +/datum/lore/codex/page/borealis_majoris/add_content() + name = "Borealis Majoris (Arctic/Tundra)" + keywords += list("Borealis Majoris") + data = "Situated 10 lightseconds from the system's primary, Borealis Majoris is - in the politest terms - a frozen hellhole. With surface temperatures peaking at a bone-chilling -30 celsius (-22 fahrenheit) but dropping as low as -80 C (-112 F) if not lower on the planet's dark side, the planet's primary redeeming features include its abundance of oxygen and phoron. Its atmosphere hovers between 90 and 110 kPa, and consists of 72% O2, 2% CO2, and 24% N2, with other trace gases. Due to the low temperatures, the vast majority of the planet's phoron is found crystallized underground, or within the flora and fauna, the substance presumably making its way into the wildlife via what little hardy vegetation clings to the planet's surface, mostly mosses and lichens.\ +

    \ + Despite the fact that Borealis Majoris is near-constantly lashed by swirling blizzards, howling winds, vicious hailstorms, and icy rain, it is nonetheless host to a handful of colonies and outposts, the largest and most prosperous of which is the NT-operated NSC Cryogaia." + +/datum/lore/codex/page/borealis_majoris_beta/add_content() + name = "Borealis Majoris Beta (Barren Moon)" + keywords += list("Borealis Majoris Beta") + data = "Borealis Majoris' moon is an unremarkable rocky ball of dust and simple elements. It likely split off from Borealis Majoris as a result of an impactor at some point in the planet's past. Its phoron deposits are located deep underground if they are present at all on the moon, and so mining operations have focused on Borealis Majoris itself rather than on the relatively resource-poor moon." + +/datum/lore/codex/page/nsc_cryogaia/add_content() + name = "NSC Cryogaia (NT Phoron Mining & Research Installation)" + keywords += list("Cryogaia") + data = "The terrestrial half of NT's operations on Borealis Majoris, the NSC Cryogaia Outpost handles the acquisition and low-level refining of phoron extracted from the surrounding planetary environment, as well as extensive research into the material given its abundance. Whilst it's a relatively small operation in the grand scheme of things and is constantly under threat from all manner of angry wildlife, the sheer volume of phoron available makes the installation extremely profitable; enough for NT to maintain a dedicated Asset Response Annex aboard the NCS Serenity, ready to deploy at a moment's notice should a major threat appear.\ +

    \ + The half-subterranean Cryogaia installation is considered a work in progress, and undergoes renovations and updates on a relatively frequent but highly erratic basis. Notably, it is built on one of the few natural hot springs available on the planet, though NT has opted to use another of its Supermatter Research-Reactors backed up by solar arrays, in lieu of a more conventional geothermal power plant for the time being." + +/datum/lore/codex/page/ncs_serenity/add_content() + name = "NCS Serenity (NT Phoron Refinery & Refuelling Station)" + keywords += list("Serenity") + data = "The orbital half of NT's operations on Borealis Majoris, the NCS Serenity installation is responsible for the vast majority of phoron refinery efforts, either by processing purified crystals or cut lumber shipped up from NCS Cryogaia. It also includes crew relief facilities for Cryogaia's personnel, and handles the vast majority of interplanetary traffic in the system. NT does brisk business selling phoron-based fuel directly to passing vessels, but this accounts for only a small fraction of the full profits from their operations in the system." + +/datum/lore/codex/page/borealis_three_alpha/add_content() + name = "Borealis 3 Alpha (No Data)" + keywords += list("Borealis 3 Alpha") + data = "Survey data incomplete. Check back later!" + +/datum/lore/codex/page/borealis_three_beta/add_content() + name = "Borealis 3 Beta (No Data)" + keywords += list("Borealis 3 Beta") + data = "Survey data incomplete. Check back later!" + +/datum/lore/codex/page/borealis_three_gamma/add_content() + name = "Borealis 3 Gamma (No Data)" + keywords += list("Borealis 3 Gamma") + data = "Survey data incomplete. Check back later!" + +/datum/lore/codex/page/borealis_four_alpha/add_content() + name = "Borealis 4 Alpha (No Data)" + keywords += list("Borealis 4 Alpha") + data = "Survey data incomplete. Check back later!" + +/datum/lore/codex/page/borealis_four_beta/add_content() + name = "Borealis 4 Beta (No Data)" + keywords += list("Borealis 4 Beta") + data = "Survey data incomplete. Check back later!" \ No newline at end of file diff --git a/code/modules/lore_codex/lore_data_yw/main.dm b/code/modules/lore_codex/lore_data_yw/main.dm new file mode 100644 index 0000000000..2e9874dd57 --- /dev/null +++ b/code/modules/lore_codex/lore_data_yw/main.dm @@ -0,0 +1,33 @@ +/datum/lore/codex/category/main_borealis_lore // The top-level categories for the Virgo book + name = "Index" + data = "Don't panic!\ +

    \ + The many star systems inhabitied by humanity and friends can seem bewildering to the uninitiated. \ + This guide seeks to provide valuable information to anyone new in the system. This edition is tailored for visitors to the Borealis system, \ + however it also contains useful general information about human space, such as locations you may hear about, the current (as of 2562) political climate, various aliens you \ + may meet in your travels, the big Trans-Stellars, and more." + children = list( + /datum/lore/codex/category/important_locations, + /datum/lore/codex/category/species, + /datum/lore/codex/category/auto_org/tsc, + /datum/lore/codex/category/auto_org/other, + /datum/lore/codex/category/auto_org/gov, + /datum/lore/codex/category/auto_org/mil, + /datum/lore/codex/category/political_factions, + /datum/lore/codex/page/about_lore + ) + +// We're a bird. +/datum/lore/codex/page/about_lore + name = "About" + data = "The Traveler's Guide to Human Space is a series of books detailing a specific location inside a location colonized by humans. \ + This book is for the Borealis System, and was written by Eshi Tache, an explorer whom has visited many star systems, and \ + has personally visited and seen many of the locations described inside this book. Two other people have also assisted in the creation of this \ + book, being Qooqr Volquum, whom is an expert on synthetics, and Damian Fischer, a historian. Together, they provide valuable information and facts that lie outside of Tache's expertise.\ +

    \ + The writings inside this edition are intended to be useful to anyone visiting it for the first time, from someone taking a vacation to beautiful Virgo-Prime, \ + to an immigrant from another system, or even from outside human space, and anyone inbetween. The publisher wishes to note that any opinions expressed \ + in this text does not reflect the opinions of the publisher, and are instead the author's.\ +

    \ + Eshi Tache has also written other The Traveler's Guide books, including Sol Edition, Tau Ceti Edition, Alpha Centauri Edition, Vir Edition, Virgo Edition, and more, \ + which you can find in your local book store, library, or e-reader device." \ No newline at end of file diff --git a/code/modules/lore_codex/lore_data_yw/orgs.dm b/code/modules/lore_codex/lore_data_yw/orgs.dm new file mode 100644 index 0000000000..50f1b2a120 --- /dev/null +++ b/code/modules/lore_codex/lore_data_yw/orgs.dm @@ -0,0 +1,43 @@ +// Pulls data from organizations data +/datum/lore/codex/category/auto_org + var/desired_type = null + var/auto_keywords = list() + +/datum/lore/codex/category/auto_org/New(var/new_holder, var/new_parent) + ..(new_holder, new_parent) + keywords += auto_keywords + for(var/path in loremaster.organizations) + var/datum/lore/organization/O = loremaster.organizations[path] + if(!(istype(O, desired_type))) + continue + var/datum/lore/codex/page/P = new(holder, src) + if(!O.name) // Probably the base type, don't make a page for it. + continue + P.name = O.name + P.keywords.Add(O.name, O.short_name) + if(O.acronym) + P.keywords.Add(O.acronym) + P.data = O.desc + children.Add(P) + +/datum/lore/codex/category/auto_org/tsc + name = "Trans-Stellar Corporations" + auto_keywords = list("TSC","TSCs","Trans-Stellar","Trans-Stellar Corporation") + data = "By definition, TSCs are companies which span multiple star systems, however the term is generally reserved for \ + the biggest and most influential of them all. Some people also categorize the different TSCs into 'major' and 'minor' TSCs." + desired_type = /datum/lore/organization/tsc + +/datum/lore/codex/category/auto_org/other + name = "Local Actors" + auto_keywords = list("SDF","Smuggler","Smugglers","Pirate","Pirates") + desired_type = /datum/lore/organization/other + +/datum/lore/codex/category/auto_org/gov + name = "Governments" + auto_keywords = list("Gov","Government","Governments") + desired_type = /datum/lore/organization/gov + +/datum/lore/codex/category/auto_org/mil + name = "Militaries" + auto_keywords = list("Mil","Military", "Militaries") + desired_type = /datum/lore/organization/mil diff --git a/code/modules/lore_codex/lore_data_yw/political_parties.dm b/code/modules/lore_codex/lore_data_yw/political_parties.dm new file mode 100644 index 0000000000..aaaf85c957 --- /dev/null +++ b/code/modules/lore_codex/lore_data_yw/political_parties.dm @@ -0,0 +1,15 @@ +/datum/lore/codex/category/political_factions + name = "Political Factions" + data = "For some weird reason, this section is blank. This looks like a publishing error." + /*data = "Those wishing to immigrate to somewhere in Borealis, or otherwise plan to stay for a long time should get to know human politics. \ + There are presently three major political parties that exist throughout USG space, being the Icarus Front, the Shadow Coalition, and \ + the Sol Economic Organization, and several smaller ones which tend to align themselves among one of the major parties. In the Borealis system, the \ + Icarus Front's influence is much less than somewhere closer to Sol, and the other two parties being more popular."*/ // ToDo: A lot. + children = list( + /datum/lore/codex/page/blank + ) + +/datum/lore/codex/page/blank/add_content() + name = "Blank Page" + keywords += list("Blank") + data = "For some weird reason, this section is blank. This looks like a publishing error." // ToDo: A lot. diff --git a/code/modules/lore_codex/lore_data_yw/species.dm b/code/modules/lore_codex/lore_data_yw/species.dm new file mode 100644 index 0000000000..8d56070e53 --- /dev/null +++ b/code/modules/lore_codex/lore_data_yw/species.dm @@ -0,0 +1,409 @@ +/datum/lore/codex/category/species + name = "Species" + data = "There are many different types of lifeforms (both alive and artificial) in the galaxy, which you may find inside Vir." + children = list( + /datum/lore/codex/page/human, + /datum/lore/codex/page/skrell, + /datum/lore/codex/page/unathi, + /datum/lore/codex/page/tajaran, + /datum/lore/codex/page/diona, + /datum/lore/codex/page/akula, + /datum/lore/codex/page/nevrean, + /datum/lore/codex/page/sergal, + /datum/lore/codex/page/vulpkanin, + /datum/lore/codex/page/vox, + /datum/lore/codex/page/grey, + /datum/lore/codex/page/zorren, + /datum/lore/codex/category/teshari, + /datum/lore/codex/category/positronic + ) + +/datum/lore/codex/page/human/add_content() + name = "Human" + keywords += list("Humanity") + data = "Humans are a race of 'ape'-like creatures from the continental planet Earth in the Sol system. They are the primary driving \ + force for rapid space expansion, owing to their strong, expansionist central government and opportunistic [quick_link("TSC","Trans-Stellar Corporations")]. \ + The prejudices of their 21st century history have mostly given way to bitter divides on the most important issue of the times- technological \ + expansionism.\ +

    \ + While most humans have accepted the existence of aliens in their communities and workplaces as a fact of life, exceptions abound. \ + While more culturally diverse than most species, humans are generally regarded as somewhat technophobic and isolationist by members \ + of other species." + +/datum/lore/codex/page/skrell + name = "Skrell" + keywords = list("Skrellian") + data = "The Skrell are a species of amphibious humanoids, distinguished by their gelatinous appearance and head tentacles. \ + Skrell come from the world of Sirisai (called Qerr'balak by Skrell), a humid planet with plenty of swamps and jungles. Currently more technologically advanced \ + than the humans, they emphasize the study of the mind above all else.\ +

    \ + Gender has little meaning to Skrell outside of reproduction, and in fact many other species have a difficult time telling the difference \ + between male and female Skrell apart. The most obvious signs (voice in a slightly higher register, longer head-tails for females) are never \ + a guarantee. Due to their scientific focus of the mind and body, Skrell tend to be more peaceful and their colonization has been slow, swiftly \ + outpaced by the humans. For humans, they were their first contact sentient species, and are their longest, and closest, ally in space." + +/datum/lore/codex/page/unathi + name = "Unathi" + data = "The Unathi are a race of tall, reptilian humanoids that possess both crocodile-like and serpent-like features. Their scales are hard and \ + plate-like, save for the softer ones that line the inside of their legs, armpits, and groin. Originating from the planet of Moghes, the Unathi \ + live in an extremely religious society. The Unathi believe in and closely follow a set of Decrees laid out in their belief system, The Unity. \ + Unathi believes in living long, prosperous, honorable and productive lives. They firmly believe in improving the skills of their craft to reach \ + a level where they can be considered to have mastered it. Unathi was humanity's second alien contact, but due to their aggressive nature, engaged \ + in an interstellar war with humanity known as the Sol-Hegemony Conflict. \ +

    \ + Although the conflict has long since ended, and relations have slowly improved, Unathi are still often considered to be 'second-class' citizens \ + and are rarely seen in jobs other than where muscle is needed. It is also still common for racial tensions to run high, although this is less \ + common on the outskirts of SolGov's grasp, such as systems like Virgo-Erigone." + +/datum/lore/codex/page/tajaran + name = "Tajaran" + keywords = list("Tajara") + data = "The Tajaran are a race of humanoid mammalian aliens from Meralar, the fourth planet of the Rarkajar star system. Thickly furred and protected \ + from cold, they thrive on their subarctic planet, where the only terran temperate areas spread across the equator and tropical belt. \ + With their own share of bloody wars and great technological advances, the Tajaran are a proud kind. They fiercely believe they belong \ + among the stars and consider themselves a rightful interstellar nation, even if the humans helped them to actually achieve superluminal \ + speeds with Bluespace FTL drives. Relatively new to the galactic stage, their contacts with other species are aloof, but friendly. \ + Among these bonds, Humans stand out as valued trade partners and maybe even a friend." + +/datum/lore/codex/page/diona/add_content() + name = "Diona" + keywords += list("Dionaea") + data = "The Dionaea are a group of omnivorous, slow-metabolism plantlike organisms that are in fact clusters of individual, smaller organisms. \ + They exhibit a high degree of structural flexibility, and come in a wide variety of shapes and colors to reflect the intelligence of each individual \ + creature. They were discovered by the [quick_link("Skrell")] in 2294CE, not on a planet, but in open space between three stars, a figurative hell that made it \ + difficult to discover, much less contact them.\ +

    \ + Dionaea spread by seeds and are asexual, with no gender. When grown into their small 'nymph' state, they are known to eat large amounts of dead plant \ + matter and fertilize plants while they learn from those around them, and as they grow further, they merge into larger and larger forms. It is not \ + unheard of for Skrell explorers to be traveling in a ship composed of habitat modules and engines of Skrell design and the body formed by their \ + Diona allies to warble across the cosmos.\ +

    \ + Introduced by the Skrell, and quite slow and peaceful, the Diona share good relations with all other species, even the elusive Vox." + +/datum/lore/codex/page/akula/add_content() + name = "Akula" + keywords += list("Akula") + data = "The Akula are a species of amphibious humanoids like the [quick_link("Skrell")], but have an appearance very similar to that of a shark. \ + They were first discovered as a primitive race of underwater dwelling tribal creatures by the Skrell. At first they were not believed to \ + be noteworthy, but the Akula proved to be such swift and clever learners that the Skrell reclassified them as sentients. Allegedly, \ + the Akula were also the first sentient life that the Skrell had ever encountered beside themselves, and thus the two species became swift allies \ + over the next few hundred years.\ +

    \ + With the help of Skrellian technology, the Akula had their genome modified to be capable of surviving in open air for long periods of time. \ + However, Akula even today still require a high humidity environment to avoid drying out after a few days, which would make life on an arid world like \ + Virgo-Prime nearly impossible if it were not for Skrellian technology to aid them." + +/datum/lore/codex/page/nevrean/add_content() + name = "Nevrean" + keywords += list("Nevrean") + data = "An avian species hailing from the planet Eltus in the Vilous system, characterised by their long whiplike \ + tail-feathers. The species is generally matriarchal, with females tending towards duller, brown coloration and \ + bulkier bodies, while males are slight and brightly coloured. In both typical mannerisms, culture and physical \ + build, the sexes are directly reversed when compared to traditional human society.\ +

    \ + Their build is somewhere between a bird and a feathered dinosaur. Females tend to have short feathers on their arms that are not capable \ + of flight, while males often have more developed flight-feathers - on-station, these are often kept tucked away \ + and folded under their jumpsuit sleeves. " + +/datum/lore/codex/page/sergal/add_content() + name = "Sergal" + keywords += list("Sergal") + data = "There are two subspecies of Sergal, Southern and Northern. Northern sergals are a highly aggressive race that \ + lives in the plains and tundra of their homeworld. They are characterized by long, fluffy fur bodies with cold colors; usually with \ + white abdomens, somewhat short ears, and thick faces. Southern sergals are much more docile and live in the Gold Ring City and are scattered around \ + the outskirts in rural areas and small towns. They have short, brown or yellow (or other \"earthy\" colors) fur, long ears, and a long, thin face. \ + They are smaller than their Northern relatives.\ +

    \ + Both have strong racial tensions which has resulted in more than a number of wars and outright attempts at genocide. The southern sergals have mostly been on the losing side \ + of this long conflict. Sergals have an incredibly long lifespan, but due to their lust for violence and a nasty habit of occasional cannibalism, only a handful have \ + ever survived beyond the age of 80, such as the infamous and legendary General Rain Silves who is claimed to have \ + lived to 5000. Although General Rain's historical existence is disputed, she is nonetheless a symbol of the Sergal's warrior culture." + +/datum/lore/codex/page/vulpkanin/add_content() + name = "Vulpkanin" + keywords += list("Vulpkanin") + data = "Vulpkanin or \"Vulpa\" are a species of sharp-witted canid bipeds residing on the planet Altam just barely \ + within the binary Vazzend system. Their politically de-centralized society and independent natures have led them \ + to become a species and culture both feared and respected for their scientific breakthroughs. Discovery, loyalty, \ + and utilitarianism dominates their lifestyles to the degree it can cause conflict with more rigorous and strict \ + authorities. They speak a guttural language known as 'Canilunzt' which has a heavy emphasis on utilizing tail \ + positioning and ear twitches to communicate intent." + +/datum/lore/codex/page/vox/add_content() + name = "Vox" + keywords += list("Vox") + data = "Almost nothing is known about the Vox by the galaxy at large, and the Vox would prefer it remain that way \ + for as long as possible. What little is known has largely been pieced together from spacer's tales and security \ + after-action reports from encounters with the elusive raptor-like creatures. If the TSCs have any exciting data \ + from biopsy or autopsy examinations on Vox or Vox remains, they're not sharing them with everyone else. \ +

    \ + Vox are an infrequent sight in civilized space, as the species is highly insular. The only individuals typically \ + seen leaving the relative safety and isolation of their kin are those who have been cast out for committing some \ + crime against other Vox, and these outcasts sometimes seek employment within the peripheries of civilized space \ + in an effort to simply survive; without a means of acquiring fresh phoron for respiration, a lone vox is liable \ + to suffocate within a matter of weeks at most.\ +

    \ + The marginally more common type of vox one will encounter are the marauders. These opportunistic vox prey on poorly \ + guarded civilian shipping and facilities, either to steal precious items or the phoron they're holding. After all \ + as far as the Vox are concerned, only Vox matter, and only Vox matters matter." + +/datum/lore/codex/page/grey/add_content() + name = "Grey" + keywords += list("Grey","Greys") + data = "The Greys are a relatively new addition to the galactic stage, having only truly made their presence known \ + over the course of the past few decades. This, combined with their appearance and seemingly engineered biology \ + has lead to a number of conspiracy theories and wild ideas about their true origins, with one of the most common \ + proposing that they are extragalactic refugees who have somehow crossed the great divide between their galaxy \ + and our own to hide from some greater threat. \ +

    \ + Theorizing aside, they're perhaps best known for their natural \'telepathic\' abilities, their violent reaction to water \ + (owing to their salt-heavy biology their cells rapidly undergo cytolysis when exposed to water, which can easily be fatal) \ + and their apparent knack for research and development. The TSCs have been quick to court the favor of those that leave the \ + Grey \'homeworld\' of Mauna-b, offering them almost uncharacteristically generous employment packages within administrative \ + and research departments at facilities across civilized space. For their part the Grey seem to be integrating well, despite \ + occasional difficulties with the lower levels of oxygen present outside of their favored ships and habitats." + +/datum/lore/codex/page/zorren/add_content() + name = "Zorren" + keywords += list("Zorren") + data = "The fox-like Zorren are native to Virgo-Prime in the Virgo-Erigone system, however there are two distinct varieties of \ + Zorren one with large ears and shorter fur, and the other with longer fur that is a bit more vibrant. The \ + long-eared, short-furred Zorren have come to be known as \"Flatland\" Zorren as that is where most of their \ + settlements are located. The Flatland Zorren are somewhat tribal and shamanistic as they have only recently \ + started to be hired by the Trans-Stellar Corporations. The other variety of Zorren are known as \"Highland\" \ + Zorren as they frequently settle in hilly and/or mountainous areas, they have a differing societal structure \ + than the Flatland Zorren having a more feudal social structure, like the Flatland Zorren, the Highland Zorren \ + have also only recently been hired by the Trans-Stellar Corporations, but thanks to the different social \ + structure they seem to have adjusted better to their new lives. Though similar fox-like beings have been seen \ + they are different than the Zorren." + +// Bird lore +/datum/lore/codex/category/teshari/add_content() + name = "Teshari" + keywords += list("Teshari") + data = "The Teshari are reptilian pack predators from the [quick_link("Skrell")] homeworld, Sirisai (Qerr'balak). While they evolved alongside the Skrell, their interactions with them \ + tended to be confused and violent, and until peaceful contact was made they largely stayed in their territories on and around the poles, in tundral \ + terrain far too desolate and cold to be of interest to the Skrell. In more enlightened times, the Teshari are a minority culture on many Skrell worlds, \ + maintaining their own settlements and cultures, but often finding themselves standing on the shoulders of their more technologically advanced neighbors \ + when it comes to meeting and exploring the rest of the galaxy.\ +

    \ + It is important to note that Teshari names are unlike standard human names. Their pack name precedes their given name." + children = list( + /datum/lore/codex/page/teshari_packs, + /datum/lore/codex/page/teshari_physical + ) + +/datum/lore/codex/page/teshari_packs/add_content() + name = "Teshari Packs" + keywords += list("Packs") + data = "There are several packs you may come across;\ +

    \ + Eshi
    \ + A large, old, politically neutral pack heavily involved in efforts to get Teshari into space. Probably the most \ + common pack to see outside of a [quick_link("Skrell")] colony, and probably the most numerous Teshari pack outside of Sirisai and associated colonies.\ +

    \ + Nasemari
    \ + A very small pack. Generally focused around supporting and providing for packs on the homeworlds, they have devoted \ + themselves to training as technicians and engineers in order to obtain skills and training to take back to Sirisai. \ + The pack is only around thirty people in size, but owns and maintains a nuclear power plant.\ +

    \ + Schasaraca
    \ + One of the more Skrell-devoted and integrated packs. They tend to be rather sycophantic towards the Skrell and work as \ + scientists and field researchers on a variety of projects, generally biology or technical research. They have a reputation \ + for working as spies and informants for the Skrell governments amongst other Teshari.\ +

    \ + Ceea
    \ + An isolationist pack from the northern tundra of Sirisai; generally known as disliking the Skrell. Small to average in size; \ + only around sixty members. Their regional culture is built around the study culture and anthropology, as well as archaeology, \ + originally for the purposes of recovering history and materials \"lost\" due to Skrell interference. It would be very rare to \ + see them on your travels, however they are listed here for the sake of completeness.\ +

    \ + Resca
    \ + A pack that sold off its small native territory for the chance to get into space. Very musically inclined. They tend towards medical professions.
    " + +/datum/lore/codex/page/teshari_physical/add_content() + name = "Physiology of Teshari" + data = "The Teshari are, relative to other species, smaller than average, rarely reaching more than 2-3'/1m in height, and weigh less than \ + 90lbs/40kg. They have rapid metabolisms and very efficient digestive systems, and thanks to sharing in \ + the medical technology of the [quick_link("Skrell")], they tend to have robust and effective immune systems. They evolved \ + for very cold and very barren areas, generally the polar regions. Because of this, their skin is a fine \ + insulator and many of their internal processes are not particularly energy-efficient; they cannot cope \ + well at all with high temperatures.\ +

    \ + Their hearing is exceptionally sensitive to the point that they can detect a person moving on the other \ + side of a wall, but this comes at a cost. Very loud noises are very painful for Teshari, so be mindful of \ + your indoor voice when speaking with one. The Teshari are omnivorous but generally prefer to eat meat wherever possible." + +// Posi lore +/datum/lore/codex/category/positronic/add_content() + name = "Positronics" + keywords += list("Positronic", "Posi", "Posibrain", "Posibrains") + data = "A Positronic being, is an individual with a positronic brain, manufactured \ + and fostered amongst organic life. Positronic brains enjoy the same legal status as a human in [quick_link("SolGov")] space, although discrimination is \ + still prevalent, and are considered sapient on all accounts. They can be considered a \"synthetic species\". Half-developed and \ + half-discovered in the 2280s by a human black lab studying alien artifacts, the first positronic brain was an inch-wide cube \ + of an palladium-iridium alloy, nano-etched with billions upon billions of conduits and connections. Upon activation, \ + hard-booted with an emitter laser, the brain issued a single sentence before the neural pathways collapsed and \ + it became an inert lump of platinum: \"What is my purpose?\"." + children = list( + /datum/lore/codex/page/positronic_brain_physical, + /datum/lore/codex/page/positronic_memory, + /datum/lore/codex/page/jans_fhriede + ) + +/datum/lore/codex/page/positronic_brain_physical + name = "Physical Structure of a Positronic Brain" + keywords = list("Physical Posibrain", "Physical Positronic") + data = "A positronic brain is a cube of complex metal alloy between two and six inches to a side. They usually weigh just under ten kilograms and are \ + very fragile when exposed to the stresses of heat or cold, as well as physical trauma. The exterior surface is chased with a network of grooves, forming \ + a maze of geometric patterns right down to the molecular level, and the interior is hollow; complex particle generators and densely packed computational \ + arrays form the basis of a self-computing neural network, complex and somewhat poorly understood. Most modern positronic brains are equipped with \ + standardized I/O ports, and all have some interface for imprinting." + +/datum/lore/codex/page/positronic_memory + name = "Positronic Memory" + keywords = list("Posi Memory", "Memory") + data = "Positronic minds learn in a similar manner to humans and other forms of life, although typically more quickly. They are not simple computer storage that holds information \ + verbatim as it is received- instead, they have to repeat activities and train in order to retain memory on complex tasks. Similarly, positronic brains do \ + not have an infinite storage capacity and undergo a natural process of forgetting, albeit in a structured manner, losing unimportant day to day details and \ + ancient information no longer deemed useful. Because of the nature of the positronic brain, its memories cannot simply be stored elsewhere.\ +

    \ + Particularly old positronic minds, over a century plus, that store a great deal of memories have displayed a tendency to become gradually more introspective \ + as more of their mind is co-opted for the task, ending in a state of near-catatonia as their neural networks become clogged with memory. Many choose to avoid \ + this end of self by more aggressively managing their memories, storing a window of their recent existence and most treasured memories rather than their full lifespan." + +/datum/lore/codex/page/jans_fhriede + name = "Jans-Fhriede Test" + keywords = list("Jans-Fhriede", "JF", "Jans", "Fhriede", "Jans Fhriede") + data = "Positronics are eligible to take the \"Jans-Fhriede Test\" after a year of being created, measuring their function in a society and judging if they act \ + socially acceptable and are capable of understanding their actions and the consequences resulting from them. If they successfully pass the test, \ + they are considered legal adults and hold the same basis of rights as a normal human. At that point, Positronics are not allowed to be lawed, \ + unless on a contractual basis or otherwise under their own volition." + +// Drone lore +/* +/datum/lore/codex/category/drone + name = "Drones" + keywords = list("Drone") + data = "While low-level drone intelligences are as old as the oldest human colonies, research into higher-level systems was stymied in human space by precautionist \ + politicians for hundreds of years. Tensions between the corporate rim and the highly conservative core worlds over drone proliferation led to what humans call the \ + Third Cold War, which was defused by the introduction of the positronic brain. After the Icarus Front's loss of the majority in 2504, harsh laws \ + against advanced AI were replaced with the SolGov Emergent Intelligence Oversight commission, the illegality replaced with a steeply sloping \ + system of monetary costs.\ +
    \ + The term \"drone\" was coined by early positronic activists, eager to distinguish themselves from the menial bots that most space-dwellers were \ + familiar with, and avoid the ambiguity of the term \"AI\", which now usually refers to drones." + children = list( + /datum/lore/codex/page/codeline, + /datum/lore/codex/page/emergence, + /datum/lore/codex/page/emergent_intelligence_oversight, + /datum/lore/codex/category/drone_classes, + )*/ + +/datum/lore/codex/page/codeline + name = "Codeline" + keywords = list("fork") + data = "A \"codeline\" is a single type of drone. A codeline represents a significant degree of effort from sapient programmers to realize, as well as \ + a substantial amount of regulatory fees levied by the government. Each copy of a codeline is called a \"fork\", whether the fork is created from the \ + codelines initial state or from a fully realized individual of that codeline. The degree of similarity between forks of the same codeline varies \ + on the intelligence of the codeline, with low-level forks being virtually identical to high-level forks being no more similar than family members." + +/* +/datum/lore/codex/page/emergence + name = "Emergence" + keywords = list("Seed AI") + data = "\"Emergence\" is a term associated with drone intelligences who become more intelligent than they were originally intended to be. While this can \ + extend to financial systems learning language, for instance, it is usually applied to hypothetical intelligences that become more intelligent than humans. \ + Humanity has a long-standing cultural fear of emergent \"seed\" AI, egged on by Icarus memeticists and the occasional very real partial emergence events, where \ + colony-control AI or other powerful systems begin to advance drastically in power, usually ending with the AI being shut down after crashing a handful of major systems." +*/ + +/datum/lore/codex/page/emergent_intelligence_oversight + name = "Emergent Intelligence Oversight" + keywords = list("SG-EIO", "SG EIO", "EIO", "Intelligence Oversight") + data = "SG-EIO, usually just called EIO, is the organization charged with monitoring existing AI for any threat of dangerous emergence. Their perception in the \ + public eye is generally positive, with all but the hardest-line Mercurial humans in favor of protection from the dangers of Seed AI. Some positronic rights \ + groups bristle at the EIOs human-centric viewpoint, but most are glad to have a different boogeyman in the form of drone intelligences. The tiny population \ + of A-class drones are generally frightened of the EIOs total power over them." + +/*/datum/lore/codex/category/drone_classes + name = "Drone Classifications" + keywords = list("Class", "Drone Class") + data = "To aid in its work, the EIO has created a system of classifications corresponding to different levels of drone intelligence. Higher classes are more \ + expensive to deploy and develop, owing to the costs of EIO oversight and political pressure against drone proliferation. EIO classification involves an initial \ + audit of the project's source code by experts and automated systems, and for high-class drones further check-ins throughout the life of the drone. \ + Drone chasses are often branded with their inhabiting intelligence's class, especially those of B or A-class drones, and class is often recorded in security records." + children = list( + /datum/lore/codex/page/class_f, + /datum/lore/codex/page/class_d, + /datum/lore/codex/page/class_c, + /datum/lore/codex/page/class_b, + /datum/lore/codex/page/class_a, + /datum/lore/codex/page/class_aa, + /datum/lore/codex/page/class_aaa, + /datum/lore/codex/page/class_x, + ) + +/datum/lore/codex/page/class_f + name = "F Class" + data = "\"F-class\" drones are an informal term for computer systems that pose absolutely no emergent risk. Most 21st-century software is F-class, as is much of \ + the software used by 26th century humanity. The only regulation on F-class software is the occasional check that it is, in fact, F-Class, and as such has remained \ + the most prevalent form of information-processing technology for centuries. The software powering most F-class drones is either freely available or bundled with the \ + machine it's supposed to run." + +/datum/lore/codex/page/class_d/add_content() + name = "D Class" + data = "D-class drones are conceptually descended from pre-[quick_link("Icarus")] AI and bear a strong resemblance to their forebears. D-class drones are essentially \ + number-crunchers, with virtually nothing in the way of social development. They cannot speak more intelligibly than your average piece of software, \ + using pre-determined messages written by their programmers, and have no capacity for self-improvement. They are D-class intelligence because they \ + work with more complex problems than [quick_link("F class")] software, such as financial forecasting and large-scale data mining and memetics. The creation and \ + deployment of D-class drones requires only a small fee for the required code audit, although some high-power financial and political systems are \ + regularly watched by the [quick_link("EIO")] for signs of emergence. There is no real monopoly on the production of D-class drones." + +/datum/lore/codex/page/class_c/add_content() + name = "C Class" + data = "C-class drones have social protocols for ease of use by organic and positronic laypeople. C-class drones are capable of speech, although \ + it has a strong tendency to be formulaic and repetitive. They are also capable of a limited degree of self-improvement, and over time individual \ + C-class instances tend differ slightly from one-another. C-class drones suffer a moderate fee to development, with automated [quick_link("EIO")] tools ensuring \ + that they are not a long-term emergence risk. However, one a codeline is confirmed safe, deployment is unlimited, encouraging developers to \ + instance many forks of the original drone to recoup their cost. The market for C-class drones is a strange space, dominated by Xion Manufacturing, \ + Ward-Takahashi GMC, and a large number of smaller firms, like the notoriously-cheap Cyber Solutions." + +/datum/lore/codex/page/class_b/add_content() + name = "B Class" + data = "B-class drones have advanced social protocols and are often capable of very intelligible conversation, so long as one sticks to surface \ + topics. B-class drones tend to be specialized but still capable of remarkable growth within their speciality, making them popular for autonomous \ + deployment and even supervision of other classes of drone. The dividing line between [quick_link("A Class", "A")] and B-class drones becomes apparent when they are taken \ + out of their area of specialization, with the B-class drones swiftly becoming useless. They incur a hefty fee for the production of the initial \ + codeline, as their emergent potential is far greater, and a smaller but still substantial fee for the production of forks. The market for B-class \ + drones is a battleground between Ward-Takahashi and NanoTrasen, with other firms usually producing B-classes for in-house needs." + +/datum/lore/codex/page/class_a/add_content() + name = "A Class" + keywords += list("AGI") + data = "A-class drones are also referred to as AGI. A-class drones are capable of performing in many contexts and can learn to solve problems from \ + first principles, with an incredible potential for growth and emergent behavior. However, some abilities fall short of humans, usually those relating \ + to socialization, and they often act in ways that are strange or distressing. There is a small but growing lobby of support for the personhood of A-class \ + drones. The cost of initializing an A-class drone is absolutely massive, as they will be monitored by [quick_link("EIO")] forever. The auditing cost of an A-class drone \ + codeline is even more staggering, making development and deployment of AGI limited to research, highly difficult and high-throughput operations like habitat \ + overwatch, and a few risk-taking firms banking on the associated fees dropping. There is not a proper market for A-class drones, although an appreciable \ + fraction of them are made by [quick_link("NanoTrasen")], with the rest generally being university research projects." + +/datum/lore/codex/page/class_aa + name = "AA Class" + data = "AA-class drones do not yet exist. Hypothetically, they are equal to living in every respect, with psychology that would not be abnormal in a baseline \ + human. The type of AA-class drone most frequently discussed is a hypothetical digitized consciousness of a human, a human brain that is somehow translated into \ + software. Some argue that a small fraction of the A-class drones would more properly be considered AA, but as of yet no action has been taken. Some Mercurials \ + will jokingly refer to themselves or other organics and positronics as AAs. Research into brain uploading is heavily regulated and generally illegal." + +/datum/lore/codex/page/class_aaa + name = "AAA Class" + data = "AAA-class drones do not yet exist, hopefully. They are more competent in every way than humans and pose a threat to the continued existence of sapient life. \ + Anybody creating an AAA-class drone can be classified as a threat to humanity and dealt with very harshly." + +/datum/lore/codex/page/class_x + name = "X Class" + data = "X-class drones emerge from unrated software, are produced by rogue labs, or cross the border from foreign space. They are considered a threat to national \ + security and deleted when encountered in SolGov space, with the producers prosecuted legally if it has a SolGov origin. The few Skrellian drone labs will usually \ + rate their product with EIO to allow their product to be imported." +*/ \ No newline at end of file diff --git a/code/modules/lore_codex/news_data/main.dm b/code/modules/lore_codex/news_data/main.dm index 277c38e175..1d438d43a3 100644 --- a/code/modules/lore_codex/news_data/main.dm +++ b/code/modules/lore_codex/news_data/main.dm @@ -4,6 +4,21 @@ region. Each is labeled by date of publication and title. This list is self-updating, and from time to time the publisher will push new \ articles. You are encouraged to check back frequently." children = list( + /datum/lore/codex/page/article81, + /datum/lore/codex/page/article80, + /datum/lore/codex/page/article79, + /datum/lore/codex/page/article78, + /datum/lore/codex/page/article77, + /datum/lore/codex/page/article76, + /datum/lore/codex/page/article75, + /datum/lore/codex/page/article74, + /datum/lore/codex/page/article73, + /datum/lore/codex/page/article72, + /datum/lore/codex/page/article71, + /datum/lore/codex/page/article70, + /datum/lore/codex/page/article69, + /datum/lore/codex/page/article68, + /datum/lore/codex/page/article67, /datum/lore/codex/page/article66, /datum/lore/codex/page/article65, /datum/lore/codex/page/article64, @@ -781,4 +796,128 @@

    \ Executive Sifat Unar of the Emergent Intelligence Oversight has voiced immediate concern over the motion, criticising the use of 'foreign government' in reference to Almach; 'Our Fleet has suffered a few defeats, but this conflict goes deeper than mere lasers and shells and to surrender to torturers, mind-hackers, and Machiavellian machines at this stage would be insanity. To allow a seccessionist state, particularly one so unabashedly guilty of crimes against humanity that go far beyond even our modern definitions of 'Human Sanctity', to exist unquestioned a stone's throw from some of our most precious member states, would be a failing not only of this government, but of humanity that would echo through history like a great shameful dirge for all to hear.'\

    \ - A communications blackout has been instated on the Gavel system by the Almach Militia, though earlier reports indicate continued strikes on numerous civilian colonies who were unwilling, or unable to deactivate their automated defence systems prior to the invaders arrival." \ No newline at end of file + A communications blackout has been instated on the Gavel system by the Almach Militia, though earlier reports indicate continued strikes on numerous civilian colonies who were unwilling, or unable to deactivate their automated defence systems prior to the invaders arrival." + +/datum/lore/codex/page/article67 + name = "10/08/63 - 'Magnetic Weapon' Designs Released Following Gavel Threat" + data = "Private security and ExoMartian law enforcement agencies are now receiving modernized man-portable magnetic weapon designs produced by Mars Military Industries thanks to increased budget from the S.C.G. The 'hallmarks' of these weapons, as one M.M. Industries spokesperson says, are their incredible ability to launch physical projectiles at velocities rivalling present portable laser technology in practical utility. Many of the designs utilize generalized, easy-to-manufacture compressed matter cartridges as their primary ammunition, meaning no specialized production facilities are required outside of standard shipyard or planetary lathe systems, 'to ensure a cutting edge in the battlefield, down to the last man'.\ +

    \ + Some corporations, such as Hephaestus, NanoTrasen, and the PCRC, are already preparing to utilize these released designs in their own laboratories and stations, undoubtedly providing yet more materiel backing to the Almach front." + +/datum/lore/codex/page/article68 + name = "10/10/63 - Gavel Encircled - Liberation In Sight" + data = "Significant Fleet reinforcements from the Unathi border have 'trapped' the Almach fleet in the Gavel system and are poised for a decisive victory, according to latest reports from the front. Solar vessels from all sides of the war-torn system have closed in to ensure the invading force have no means of retreat. The relief force includes elements of the Hegemony-border fleets and the previously deployed flotilla stationed in Saint Columbia. Speaking at the Colonial Assembly this morning, Rewi Kerehoma of the Sol Economic Organization has stated that the reinforcements will 'beyond a doubt' prevent a repeat of 'embarrassing' errors made in the past month.\ +

    \ + Efforts have been made to re-establish contact with the occupied system, which has been blocked from communication with the rest of the galaxy since the occupation began last week. According to scattered civilian signals from the system, the Association has adopted a 'salted earth' policy to the system following Solar military response, openly demolishing system infrastructure with little regard for its residents. A spokesperson for Grayson Manufactories, who maintain a significant presence in the Gavel system , has proposed that 'The Almachi had no intention of holding this system, this may have been nothing more than a show of force against corporate assets supporting the war effort.'\ +

    \ + In related news, Admiral Ripon Latt has officially retired from his post following immense political pressure from the Assembly. Latt was until this week, commander of the Rim Expeditionary Force - currently in the Saint Columbia system - and has been the primary target for blame in the SCG's defeat in Relan, and failure to prevent the invasion of Gavel. The disgraced admiral will receive a full officer's pension, but no official honours befitting of his previous rank. Latt is to be replaced immediately by Admiral Silvain Barka, an experienced veteran of anti-piracy action in the Rarkajar Rift." + +/datum/lore/codex/page/article69 + name = "10/26/63 - 'Largest Engagement Since The Hegemony War' As Gavel Freed" + data = "The Almach Association invasion force in the Gavel system has been all but annihilated by a successful Solar counter-encirclement, at great cost to both sides. The combined Rim Expeditionary Force in Saint Columbia, along with the newly formed Gavel Relief Fleet - which had been massing in the Vir system over the past week - launched the successful attack this Tuesday evening, leaving no route of escape for Almachi invaders and resulting in 'pitched fighting' between the fleets that lasted several days. Solar forces are currently in the process of performing security sweeps of the system and its scattered habitats and it is expected to be several weeks before the system is declared safe to civilian traffic and for refugees to return home.\ +

    \ + Even as exact causalities remain unconfirmed, the battle has made history as the single largest ship-to-ship engagement by tonnage involving the Sol military since the cessation of hostilities with the Unathi in 2520, involving over one hundred vessels of all sizes across both sides, as well as countless unmanned drones and light craft. Almachi forces, in numbers described as 'far from an insignificant portion of the total fleet' fought fiercely, and 'in manners more reminiscent of mercenary gangs than a single organized force, and with tactics varying from the conventional to the outright mystifying'. Admiral Silvain Barka has commended his own crew for applying lessons learnt from prior 'Aetothean' commando strikes in preventing similar incidents from occuring in the confusion of battle; in a candid interview this morning he stated 'Like any Promethean, (Aetotheans) hate the cold, and my crew are the coldest (expletive) around.' \ +

    \ + The designations of twenty-four Sol Defense Vessels declared 'lost in action' have not yet been released, though next of kin of missing or deceased servicepeople have reportedly been notified." + +/datum/lore/codex/page/article70 + name = "11/11/63 - Gavel Salvation Reveals True Cost of War" + data = "Reports from liberating forces in the Gavel system have confirmed early accounts of 'inhuman' tactics employed by the Almachi invaders during their short occupation and defense of the region. Besides an apparent disregard for sapient life, especially any that gave an outward appearance of defending themselves, the Militia is believed to have employed troops and tactics 'the likes of which had only been imagined', in 'a manner that can only be described as experimental'.\ +

    \ + Most harrowing of the accounts are those of alleged 'kill-switch clone armies' consisting of near-identical vatborn troops deployed to some of New Xanadu's largest surface colonies. According to local residents, the 'uncanny' troops arrived en-masse 'from the depths of the wastes' at the beginning of the invasion, despite wearing no obvious gear that would protect them from the extreme, unbreathable environment beyond the confines of controlled habitats. The clones are said to have targetted infrastructure including life support, with little regard for those who stood in their way. However, what has baffled Fleet analysts is the reaction upon the arrival of Sol surface troops; the clones did not fight back, but rather dropped dead 'all at once, as if a switch had been flipped'.\ +

    \ + Teams from the SCG's top analytical and regulatory bodies including the EIO have been hard at work collecting examples of the unusual Almachi technology from throughout Gavel. Executive Sifat Unar has stated that 'It is vital that we ensure this transgressive technology no longer poses a threat to sapient life, either now or to future generations. A full cleanup operation is underway, and we are working to analyse what we have found and better understand the enemy's limits and the extent of their biological and technological... Practices.'\ +

    \ + The current confirmed death toll, including civilian losses in Gavel has now reached 11,520 and is expected to rise. Debate continues whether to add the so-called 'Kill-switchers' to the tally." + +/datum/lore/codex/page/article71 + name = "11/21/63 - Tajaran Pearlshield Draws Line In The Sand" + data = "Khama Suketa enai-Lutiir, representative of the Tajaran Pearlshield Coalition, has just issued a formal statement:\ +

    \ + 'It's no secret that we members of the Pearlshield Coalition have our differences and conflicts, both within and without. Much as some might call it the spice of life, it's with shame that I admit it has left us somewhat paralyzed over the last few months, even as a war has raged on a mere few jumps away from our borders. But in acknowledgment of our differences, something we have been able to unanimously agree upon is the sanctity of life and common decency, sanctity that Almach has continued to violate in the name of political conquest. Our relationship with SolGov - and indeed, any who would call us 'friend' - should be one of mutual cooperation and benefit, not of hard boundaries delineating 'us' and 'them'. There is only 'we', and we cannot stand idly by.\ +

    \ + 'To this end, the Pearlshield has negotiated with local forces in the Silk system, and are taking over interim protection of the system, to free up Solar military forces so they can assist in the war effort. We have also begun construction of a large residential station to supplement the Silk station itself, alleviating its acknowledged overpopulation issues and providing additional logistical support for our defensive fleet. Finally, our cousins in Mesomori have generously loaned their new flagship, the PCMV Raniira's Grace, with all hands on deck for temporary joint assignment with Solar military forces. They are quite eager to provide a taste of Tajaran firepower and ingenuity.\ +

    \ + 'We wish we could spare more at this time, but alas, we're still finding our feet among the stars, stepping carefully among the proverbial minefield that is our own share of cosmic threats. Know that these contributions represent a grand investment in their own right, and they are only the beginning should this war carry on.\ +

    \ + 'We will have more to say as it comes up. May all our stars shine upon us.'" + +/datum/lore/codex/page/article72 + name = "11/23/63 - Surviving 'Kill-switcher' Assassinated During Vir Interview" + data = "An Almachi 'kill-switcher' clone soldier capturing during the liberation of the Gavel system was yesterday 'forced' to explosively self-terminate during a live TV broadcast with Virite news anchor David Huexqole aboard a SCG prisoner transport craft, allegedly by the statement of a code phrase, clearly audible on the recording. Following the attack, the apparent 'activator' is reported to have escaped to nearby NanoTrasen logistical station, the Southern Cross amidst the chaos where a minor altercation took place, resulting in the injury of one Positronic crew member and death of one Almachi accomplice, which initial reports suggest to have been a 'Vox mercenary'. The search continues for the Almachi agent, and local authorities remain confident that they will be apprehended.\ +

    \ + David Huexqole, a popular local media personality, was the only other immediate victim of the attack, suffering moderate injuries and was rushed to the Southern Cross for emergency surgery where he is reported to have made a full recovery. Huexqole has expressed his gratitude to the 'skilled and charming staff, clearly shaken by the war but nonetheless capable for it.' but has expressed concerns regarding so-called sleeper agents within Sol space, 'Isa (341, the interviewed clone), seemed genuinely reformed. She spoke openly of regret, and struggling with what it meant to be created only to die. Before she blew herself up, I never would have thought her capable - it was like she changed in an instant.'\ +

    \ + Initial blast investigation suggests the presence of a previously unidentified compound in the clone's bloodstream, which was detonated following 'activation' by the as of yet unidentified Association agent, believed to have been present in the room during the recording. Moments prior to termination, the clone's demeanour is visibly altered and the phrase 'Those of Sol must look to the eastern star, Phorcys.' was clearly stated. A spokesperson for the Saint Columbia Telemetry Station has stated that observation of Phorcys, an uninhabited star system located approximately between the Almach Association state of Vounna and Skrellian Far Kingdom space, has revealed 'no immediate anomalous behaviour or presence' but that analysis will continue in the coming weeks 'in order to verify the potential meaning of this statement.'" + +/datum/lore/codex/page/article73 + name = "11/25/63 - Phorcys Star Ignites!" + data = "The Phorcys star system, mentioned cryptically during Friday's dramatic televised Almachi assassination, has 'gone supernova' several thousand millennia ahead of schedule, sparking concerns of 'Almach Superweapon' involvement. The process, which ordinarily takes place over many months and is preceded by millions of years of obvious stellar evolution, began rapidly in the early hours of this morning and has already begun to consume the entire system at a rapid rate - mercifully consisting of only two uninhabitable dwarf planets.\ +

    \ + According to government telemetric sources, who have been observing the system since Friday's message, the 'ignition' was preceded by 'abnormal bluespace readings', but it is impossible at this time to confirm whether the phenomena was a natural anomaly that Almachi sources were able to identify ahead of time, or if the long-rumoured 'Whythe Superweapon' was in fact involved. If direct Almach involvement were the case, the range of such an 'attack' would be unprecedented, with the afflicted star far from any populated area or territorial claims and 'no evidence of sapient passage to the system in several decades.'\ +

    \ + General Secretary Mackenzie West, speaking on behalf of the Colonial Assembly has stated that 'The Solar Confederate Government is taking this matter very seriously. The mere prospect of a weapon of this calibre would indeed pose an existential threat to our society, and perhaps mankind. However, at this time we cannot jump to conclusions, this may in fact be a game of smoke and mirrors; an effort to use a previously unobserved phenomena as a weapon of propaganda, and we cannot allow this to cloud our judgement. We are in close contact with the Skrell in order to determine the nature of this sudden ignition. Know that the Fleet is on high alert, and fighting forth from the Saint Columbia system will continue unaffected.'" + +/datum/lore/codex/page/article74 + name = "11/27/63 - Whythe Superweapon Confirmed In Phorcys Blast" + data = "New government footage from the moment of ignition in the Phorcys system has confirmed the 'brief' presence of an artificial 'bluespace gate' within the star just prior to its rapid expansion and structural collapse. This verifies claims of responsibility made by the Almach Association late yesterday, and casts no doubt on the threat posed by the so-called Whythe Superweapon. The Colonial Assembly has been in continuous session since the initial incident, and one staffer has described the atmosphere as one of 'unhinged panic'.\ +

    \ + The prevailing fear amongst the government, with the assent of top physicists in the private sector is that such a weapon could be used against Solar targets, and that it is no longer a question of 'how', but 'when', and indeed 'where'. The only unknown factor is the superweapon's capability for repeat use, and 'rate of fire' which is yet to be demonstrated but widely considered to be 'not a risk worth taking under any circumstances'.\ +

    \ + Latest reports from the Assembly have are that a 'scramble' is taking place to resolve the 'Almach Situation', and that the ongoing offensive out of Saint Columbia has been redirected towards Relan and Whythe. According to Skrellian correspondent Hasan Drust, official aid has been requested as of this afternoon." + +/datum/lore/codex/page/article75 + name = "12/01/63 - Skrell Defer Aid Citing 'Deliberations'" + data = "Official Solar contacts within the Skrellian government have reportedly delayed tangible aid in the rapidly escalating Almach Crisis, citing 'Internal Deliberations', agreeing only to continue to share telemetric data on the movement of Association forces. The Skrell, who were instrumental in the Solar victory against the Unathi 40 years ago have long been considered taciturn in their internal affairs, but have previously been open in their support of Sol action when it comes to national defence, making their indecisive response greatly unexpected to some.\ +

    \ + Speaker ISA-5, who has been increasingly critical of the war in the past several days, has dismissed concerns that the Skrell are 'siding with the Association', citing the long term close relationship between their governments, 'The Skrell are not war hawks, and they've never shown outward ill-will against other sapients. We can't let a bureaucratic delay allow public opinion to turn against an entire species of people who have always had our backs. If our allies need time for deliberations, we should allow them and not expect mere muscle and military aid when words could have been our solution from the beginning. I've no doubt that they will not allow the situation to become so dire that the Association are able to cause us any serious harm as they have threatened. I'm convinced that diplomacy will prevail, and that if the Skrell are in fact negotiating with the Association, then it is with all of our best interests in mind.'" + +/datum/lore/codex/page/article76 + name = "12/20/63 - Jee Relaunches Libraries Across Vir" + data = "Vir's Colonial Assembly representative and long time education reformist Vani Jee has launched her long planned 'modernization' of exonet-linked library system throughout the Vir system. During her campaign early this year she brought to light the 'staggering unsuitability' of library networks in Vir, particularly those being provided for corporate employees and residents, with particular pressure placed on the NanoTrasen Corporation whose literary collection was described as 'basically smut and whatever an old drone had scraped from the bottom of the exonet barrel.' Following her subsequent election, she reaffirmed her desire for education reform, making 'post-education' providers her first target.\ +

    \ + The new system, which she successfully acquired government funding for this October, is due to be launched this week and includes a 'personally curated' collection in addition to titles recommended by affected institutions, and a particular focus on local authors." + +/datum/lore/codex/page/article77 + name = "01/23/63 - 'Extreme' Environmental Alert Following Ullran Expanse Chemical Leak" + data = "Residents and visitors to Sif's remote Ullran Expanse have been advised to exercise extreme caution when travelling and to avoid consuming local water sources following a 'catastrophic' chemical spill in the mountains. The exact cause and origin of the spill has yet to be confirmed, though it is currently believed to have originated from an improperly disposed of chemical tank. Local rivers and their outflow have been tested with 'extremely lethal' levels of acutely corrosive material.\ +

    \ + Governor Hainirsdottir has issued a statement assuring Sif residents that the cleanup process is already underway, and the contamination which return to safe exposure levels for most sapients within 'Just a few days', but that the risk to local ecosystems in much higher and 'may take decades to recover.' and locals are advised to use only pre-packaged or thoroughly filtered water for the next 'six months, at least.'\ +

    \ + A spokesperson for the Sif Environmental Agency has described this leak as 'At least regionally, perhaps the worst environmental disaster since the introduction of the invasive spider species.' and has expressed concern about similar accidents occurring in more populated areas if stricter regulations are not put in place.\ +

    \ + The Ullran Expanse Chemical Relief Fund has been set up and is collection donations at save-ullran.xo.vr/donate" + +/datum/lore/codex/page/article78 + name = "01/27/64 - NanoTrasen Implicated In Devastating Ullran Chemical Spill" + data = "Files recovered from the contamination site by clean-up crews and submitted to the Sif Environmental Agency suggest that the hazardous materials were discarded in the region by the NanoTrasen Corporation. Though the company is not currently accused of malicious intent, ownership has been proven of several cargo containers filled with Growth Inhibitor 78-1, a compound used in the manufacture of Vatborn humans which is known to severely inhibit cell regeneration in living creatures.\ +

    \ + The documents, which have not been released in full by the SEA, were also leaked onto the exonet from several sources which NanoTrasen initially dismissed as fraudulent but have since admitted to their authenticity. Investigations are currently underway to determine those at fault within the company so that prosecution may begin. Third-party commentators have alleged that, 'The debris field and damaged containers suggests cargo jettisoned from a craft within the Sif atmosphere', though the circumstances are yet to be thoroughly examined.\ +

    \ + Certain members of the civilian clean-up crews responsible for waste disposal have also accused NanoTrasen of failing to provide their employees with sufficient safety information and protective equipment, which led to several minor injuries and one volunteer was reportedly admitted to hospital for 'severe side-effects of exposure' but has since been discharged." + +/datum/lore/codex/page/article79 + name = "02/10/64 - Shelf Fleet Vanishes Without A Trace!" + data = "Government agencies are scrambling to explain the apparent 'total disappearance' of the Almach-aligned Shelf fleet. Initial reports from the fleet's last known location describe a phenomenon not dissimilar to that observed just prior to the ignition of the Phorcys star in November, but the SCG has assured the media that 'No activity has been detected from the Whythe superweapon that would indicate any relation to the ongoing Shelf situation.'\ +

    \ + Independent telemetric data from several sources has confirmed the presence of a 'bluespace anomaly' in the moments preceding the fleet. Currently efforts are focused on determining whether the fleet has been lost with all hands or if survivors may yet be found, but no trace of the fleet or of telltale signs of large fleet movements anywhere within or beyond Almachi space have yet been detected.\ +

    \ + This afternoon, Speaker Mackenzie West of the Icarus Front addressed Sol, and indicated that communication was underway 'across faction lines' with the Association to determine the cause and implications of Shelf's apparent loss. The Almach Assocation has claimed not to be responsible for the 'possible attack' and both sides has expressed concern for the 'astronomical loss of life this may represent.'\ +

    \ + Shelf, a largely Positronic colony fleet, consists of over 1700 vessels including the 'One Leaky Bitch', current headquarters of Morpheus Shelf, Morpheus' non-Solar 'spin-out' corporation established last June. The fleet has continuously denied direct affiliation with the Almach Assocation, but was involved in a major drone attack on Solar vessels just nine months ago, which was claimed to be 'in error'." + +/datum/lore/codex/page/article80 + name = "02/12/64 - Spectralist Wardens to hold Vigils for Lost Fleet" + data = "In the wake of Shelf's sudden disappearence, Wardens across SolGov space have collectively agreed to hold services and vigils throughout dozens of systems for those who are doubtless worried for their loved ones aboard the missing ships. Spectralism, a synthetic-centric religion, finds it's roots within 'Haven', a small vessel and community that travels alongside the wider Shelf fleet, though considers itself a distinct entity. Its ministers, known as Wardens, tend to its adherants wherever they may be found.\ +

    \ + Despite Haven's well-known and somewhat-controversial independence of identity from Shelf, all indications point to the Fleet's disappearance having taken the attached ship with it. All twelve Spectralist Elders are known to have been onboard Haven at the time of its disappearance, as well as numerous other significant Spectralists and other spiritual leaders thought to have been on Shelf. The potential loss of the entire upper organizational body could be devastating to the religion, who have long been instrumental in the synthetic rights movement.\ +

    \ + Ceramica, a Warden operating out of Nyx, reached out for comment with the following: 'We are as worried as everyone else who calls Shelf their home, or who has lost contact with friends or family. You need not believe in the First Spark to have a place at your local vigil; We welcome everyone who may be hurting. This is our way, this has always been our way. We remind everyone to stay mindful, and to reach out to those you see struggling. In times like this, we cannot leave each other behind.'" + +/datum/lore/codex/page/article81 + name = "02/14/64 - Shelf Safe After 'Impossible' Jump!" + data = "The missing Shelf fleet has reappeared hundreds of light years beyond the Eutopia system just days after its sudden disappearance on Monday. Unencrypted bluespace transmissions from the fleet's new location - several months travel from its last known position in Almachi space - assured all who would listen that 'The fleet (had) arrived safe and sound.' and apologized for 'Any alarm (the fleet) may have caused'. Though no official report has been released nor information from Shelf itself, initial counts indicate that a number of Shelfican vessels may not have survived the 'impossible journey' intact.\ +

    \ + Current analysis of the bluespace anomaly detected prior to Shelf's unexpected departure indicate that the same technology as employed by the Whythe Superweapon may have been used by Shelf to create 'a bluespace portal thus far inconceivable by modern science'. The revelation has sparked concerns that 'extreme mercurial elements' within Shelf may have been responsible for the hardware behind the Assocation's 'system eating' weapon.\ +

    \ + Sifat Unar of the EIO has expressed particular concern that 'Such improbable technology relying on concepts deemed so staggeringly arcane that our very understanding of the laws of the universe had written them off as impossible - and to be applied in such callous ways without regard for life or perhaps even the fabric of reality, could only have been developed by machine minds that could threaten our very being.' Shelf has dismissed these claims as 'Scaremongering' and 'Just jealous that somebody else thought of it first', though they would not confirm nor deny their involvement in the development of the new bluespace portals." \ No newline at end of file diff --git a/code/modules/maps/fromdmp.dm b/code/modules/maps/fromdmp.dm index 73958b43a8..af75aa3d99 100644 --- a/code/modules/maps/fromdmp.dm +++ b/code/modules/maps/fromdmp.dm @@ -50,7 +50,7 @@ proc/dmp2swapmap(filename) while(txt) if(text2ascii(txt)==34) if(mode!=34) - world << "Corrupt map file [filename]: Unexpected code found after z-level [z]" + to_world("Corrupt map file [filename]: Unexpected code found after z-level [z]") return // standard line: // "a" = (/obj, /obj, /turf, /area) @@ -59,20 +59,20 @@ proc/dmp2swapmap(filename) codelen=length(code) i=findtext(txt,"(",i) if(!i) - world << "Corrupt map file [filename]: No type list follows \"[code]\"" + to_world("Corrupt map file [filename]: No type list follows \"[code]\"") return k=findtext(txt,"\n",++i) j=(k || length(txt+1)) while(--j>=i && text2ascii(txt,j)!=41) if(j0,--y) // map is top-down ++i - F << "\t\t[y]" + to_chat(F, "\t\t[y]") for(var/x in 1 to _x) - F << "\t\t\t[x]" + to_chat(F, "\t\t\t[x]") j=i+codelen - F << codes[copytext(mtxt,i,j)] + to_chat(F, codes[copytext(mtxt,i,j)]) i=j txt=copytext(txt,k+1) /* for(z in 1 to Z) - F << "\t[z]" + to_chat(F, "\t[z]") for(var/y in 1 to Y) - F << "\t\t[y]" + to_chat(F, "\t\t[y]") for(var/x in 1 to X) - F << "\t\t\t[x]" - F << codes[pick(codes)] */ + to_chat(F, "\t\t\t[x]") + to_chat(F, codes[pick(codes)]) */ proc/d2sm_ParseCommaList(txt) var/list/L=new diff --git a/code/modules/maps/swapmaps.dm b/code/modules/maps/swapmaps.dm index 6c4909f229..a41cfbaf07 100644 --- a/code/modules/maps/swapmaps.dm +++ b/code/modules/maps/swapmaps.dm @@ -585,7 +585,7 @@ proc/SwapMaps_CreateFromTemplate(template_id) else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[template_id].txt")) text=1 else - world.log << "SwapMaps error in SwapMaps_CreateFromTemplate(): map_[template_id] file not found." + to_world_log("SwapMaps error in SwapMaps_CreateFromTemplate(): map_[template_id] file not found.") return if(text) S=new @@ -612,7 +612,7 @@ proc/SwapMaps_LoadChunk(chunk_id,turf/locorner) else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[chunk_id].txt")) text=1 else - world.log << "SwapMaps error in SwapMaps_LoadChunk(): map_[chunk_id] file not found." + to_world_log("SwapMaps error in SwapMaps_LoadChunk(): map_[chunk_id] file not found.") return if(text) S=new @@ -630,9 +630,9 @@ proc/SwapMaps_LoadChunk(chunk_id,turf/locorner) proc/SwapMaps_SaveChunk(chunk_id,turf/corner1,turf/corner2) if(!corner1 || !corner2) - world.log << "SwapMaps error in SwapMaps_SaveChunk():" - if(!corner1) world.log << " corner1 turf is null" - if(!corner2) world.log << " corner2 turf is null" + to_world_log("SwapMaps error in SwapMaps_SaveChunk():") + if(!corner1) to_world_log(" corner1 turf is null") + if(!corner2) to_world_log(" corner2 turf is null") return var/swapmap/M=new M.id=chunk_id @@ -659,7 +659,7 @@ proc/SwapMaps_GetSize(id) else if(swapmaps_mode!=SWAPMAPS_TEXT && fexists("map_[id].txt")) text=1 else - world.log << "SwapMaps error in SwapMaps_GetSize(): map_[id] file not found." + to_world_log("SwapMaps error in SwapMaps_GetSize(): map_[id] file not found.") return if(text) S=new diff --git a/code/modules/maps/tg/map_template.dm b/code/modules/maps/tg/map_template.dm index 132d76445c..5fa66e3b47 100644 --- a/code/modules/maps/tg/map_template.dm +++ b/code/modules/maps/tg/map_template.dm @@ -39,6 +39,10 @@ if (SSatoms.initialized == INITIALIZATION_INSSATOMS) return // let proper initialisation handle it later + var/prev_shuttle_queue_state = SSshuttles.block_init_queue + SSshuttles.block_init_queue = TRUE + var/machinery_was_awake = SSmachines.suspend() // Suspend machinery (if it was not already suspended) + var/list/atom/atoms = list() var/list/area/areas = list() var/list/obj/structure/cable/cables = list() @@ -71,6 +75,11 @@ var/area/A = I A.power_change() + if(machinery_was_awake) + SSmachines.wake() // Wake only if it was awake before we tried to suspended it. + SSshuttles.block_init_queue = prev_shuttle_queue_state + SSshuttles.process_init_queues() // We will flush the queue unless there were other blockers, in which case they will do it. + admin_notice("Submap initializations finished.", R_DEBUG) /datum/map_template/proc/load_new_z(var/centered = FALSE, var/orientation = 0) @@ -235,7 +244,7 @@ var/area/new_area = get_area(check) if(!(istype(new_area, whitelist))) valid = FALSE // Probably overlapping something important. - // world << "Invalid due to overlapping with area [new_area.type] at ([check.x], [check.y], [check.z]), when attempting to place at ([T.x], [T.y], [T.z])." + // to_world("Invalid due to overlapping with area [new_area.type] at ([check.x], [check.y], [check.z]), when attempting to place at ([T.x], [T.y], [T.z]).") break CHECK_TICK diff --git a/code/modules/maps/tg/map_template_vr.dm b/code/modules/maps/tg/map_template_vr.dm index cd8a04857d..6f12c85ba9 100644 --- a/code/modules/maps/tg/map_template_vr.dm +++ b/code/modules/maps/tg/map_template_vr.dm @@ -1,2 +1,12 @@ /datum/map_template/proc/on_map_loaded(z) + //We missed air init! + if(SSair.subsystem_initialized) + for(var/turf/simulated/T in block(locate(1,1,z), locate(world.maxx, world.maxy, z))) + T.update_air_properties() + //We missed sslighting init! + if(lighting_overlays_initialised) + for(var/Trf in block(locate(1,1,z), locate(world.maxx, world.maxy, z))) + var/turf/T = Trf //faster than implicit istype with typed for loop + T.lighting_build_overlay() + return \ No newline at end of file diff --git a/code/modules/maps/tg/reader.dm b/code/modules/maps/tg/reader.dm index 6625884949..71adb65bad 100644 --- a/code/modules/maps/tg/reader.dm +++ b/code/modules/maps/tg/reader.dm @@ -297,6 +297,7 @@ var/global/use_preloader = FALSE old_position = dpos + 1 if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers! + error("Maploader skipping undefined type: '[trim_text(copytext(full_def, 1, variables_start))]' (key=[model_key])") continue members.Add(atom_def) @@ -351,7 +352,7 @@ var/global/use_preloader = FALSE var/atom/instance _preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation var/atype = members[index] - for(var/area/A in all_areas) + for(var/area/A in world) if(A.type == atype) instance = A break diff --git a/code/modules/materials/fifty_spawner_mats.dm b/code/modules/materials/fifty_spawner_mats.dm index 1b39b38d93..50d4184ebf 100644 --- a/code/modules/materials/fifty_spawner_mats.dm +++ b/code/modules/materials/fifty_spawner_mats.dm @@ -28,6 +28,10 @@ name = "stack of plastic" type_to_spawn = /obj/item/stack/material/plastic +/obj/fiftyspawner/graphite + name = "stack of graphite" + type_to_spawn = /obj/item/stack/material/graphite + /obj/fiftyspawner/gold name = "stack of gold" type_to_spawn = /obj/item/stack/material/gold diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm index d141f1c7af..5eec02f06e 100644 --- a/code/modules/materials/material_recipes.dm +++ b/code/modules/materials/material_recipes.dm @@ -45,6 +45,8 @@ new/datum/stack_recipe("blue comfy chair", /obj/structure/bed/chair/comfy/blue, 2, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("purple comfy chair", /obj/structure/bed/chair/comfy/purp, 2, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("green comfy chair", /obj/structure/bed/chair/comfy/green, 2, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("yellow comfy chair", /obj/structure/bed/chair/comfy/yellow, 2, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("orange comfy chair", /obj/structure/bed/chair/comfy/orange, 2, one_per_turf = 1, on_floor = 1), \ )) recipes += new/datum/stack_recipe("table frame", /obj/structure/table, 1, time = 10, one_per_turf = 1, on_floor = 1) recipes += new/datum/stack_recipe("bench frame", /obj/structure/table/bench, 1, time = 10, one_per_turf = 1, on_floor = 1) @@ -82,7 +84,8 @@ new/datum/stack_recipe("emergency shutter", /obj/structure/firedoor_assembly, 4, time = 50, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("multi-tile airlock assembly", /obj/structure/door_assembly/multi_tile, 4, time = 50, one_per_turf = 1, on_floor = 1), \ )) - recipes += new/datum/stack_recipe("IV drip", /obj/machinery/iv_drip, 4, time = 20, one_per_turf = 1, on_floor = 1) + //recipes += new/datum/stack_recipe("IV drip", /obj/machinery/iv_drip, 4, time = 20, one_per_turf = 1, on_floor = 1)//VOREStation Removal + recipes += new/datum/stack_recipe("medical stand", /obj/structure/medical_stand, 4, time = 20, one_per_turf = 1, on_floor = 1)//VOREStation Replacement recipes += new/datum/stack_recipe("conveyor switch", /obj/machinery/conveyor_switch, 2, time = 20, one_per_turf = 1, on_floor = 1) recipes += new/datum/stack_recipe("grenade casing", /obj/item/weapon/grenade/chem_grenade) recipes += new/datum/stack_recipe("light fixture frame", /obj/item/frame/light, 2) diff --git a/code/modules/materials/material_recipes_vr.dm b/code/modules/materials/material_recipes_vr.dm index 918dff0db1..674ec97e9e 100644 --- a/code/modules/materials/material_recipes_vr.dm +++ b/code/modules/materials/material_recipes_vr.dm @@ -1,14 +1,85 @@ /material/steel/generate_recipes() . = ..() + recipes += new/datum/stack_recipe_list("mounted chairs", list( + new/datum/stack_recipe("mounted chair", /obj/structure/bed/chair/bay/chair, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("red mounted chair", /obj/structure/bed/chair/bay/chair/padded/red, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("brown mounted chair", /obj/structure/bed/chair/bay/chair/padded/brown, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("teal mounted chair", /obj/structure/bed/chair/bay/chair/padded/teal, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("black mounted chair", /obj/structure/bed/chair/bay/chair/padded/black, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("green mounted chair", /obj/structure/bed/chair/bay/chair/padded/green, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("purple mounted chair", /obj/structure/bed/chair/bay/chair/padded/purple, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("blue mounted chair", /obj/structure/bed/chair/bay/chair/padded/blue, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("beige mounted chair", /obj/structure/bed/chair/bay/chair/padded/beige, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("lime mounted chair", /obj/structure/bed/chair/bay/chair/padded/lime, 2, one_per_turf = 1, on_floor = 1, time = 10), + new/datum/stack_recipe("yellow mounted chair", /obj/structure/bed/chair/bay/chair/padded/yellow, 2, one_per_turf = 1, on_floor = 1, time = 10) + )) + recipes += new/datum/stack_recipe_list("mounted comfy chairs", list( + new/datum/stack_recipe("mounted comfy chair", /obj/structure/bed/chair/bay/comfy, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("red mounted comfy chair", /obj/structure/bed/chair/bay/comfy/red, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("brown mounted comfy chair", /obj/structure/bed/chair/bay/comfy/brown, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("teal mounted comfy chair", /obj/structure/bed/chair/bay/comfy/teal, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("black mounted comfy chair", /obj/structure/bed/chair/bay/comfy/black, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("green mounted comfy chair", /obj/structure/bed/chair/bay/comfy/green, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("purple mounted comfy chair", /obj/structure/bed/chair/bay/comfy/purple, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("blue mounted comfy chair", /obj/structure/bed/chair/bay/comfy/blue, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("beige mounted comfy chair", /obj/structure/bed/chair/bay/comfy/beige, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("lime mounted comfy chair", /obj/structure/bed/chair/bay/comfy/lime, 3, one_per_turf = 1, on_floor = 1, time = 20), + new/datum/stack_recipe("yellow mounted comfy chair", /obj/structure/bed/chair/bay/comfy/yellow, 3, one_per_turf = 1, on_floor = 1, time = 20) + )) + recipes += new/datum/stack_recipe("mounted captain's chair", /obj/structure/bed/chair/bay/comfy/captain, 4, one_per_turf = 1, on_floor = 1, time = 20) + recipes += new/datum/stack_recipe("dropship seat", /obj/structure/bed/chair/bay/shuttle, 4, one_per_turf = 1, on_floor = 1, time = 20) + recipes += new/datum/stack_recipe("small teshari nest", /obj/structure/bed/chair/bay/chair/padded/red/smallnest, 2, one_per_turf = 1, on_floor = 1, time = 10) + recipes += new/datum/stack_recipe("large teshari nest", /obj/structure/bed/chair/bay/chair/padded/red/bignest, 4, one_per_turf = 1, on_floor = 1, time = 20) + recipes += new/datum/stack_recipe("light switch frame", /obj/item/frame/lightswitch, 2) - recipes += new/datum/stack_recipe_list("sofas", list( \ - new/datum/stack_recipe("sofa middle", /obj/structure/bed/chair/sofa, 1, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("sofa left", /obj/structure/bed/chair/sofa/left, 1, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("sofa right", /obj/structure/bed/chair/sofa/right, 1, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("sofa corner", /obj/structure/bed/chair/sofa/corner, 1, one_per_turf = 1, on_floor = 1), \ + recipes += new/datum/stack_recipe_list("sofas", list( + new/datum/stack_recipe("red sofa middle", /obj/structure/bed/chair/sofa, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("red sofa left", /obj/structure/bed/chair/sofa/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("red sofa right", /obj/structure/bed/chair/sofa/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("red sofa corner", /obj/structure/bed/chair/sofa/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("brown sofa middle", /obj/structure/bed/chair/sofa/brown, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("brown sofa left", /obj/structure/bed/chair/sofa/brown/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("brown sofa right", /obj/structure/bed/chair/sofa/brown/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("brown sofa corner", /obj/structure/bed/chair/sofa/brown/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("teal sofa middle", /obj/structure/bed/chair/sofa/teal, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("teal sofa left", /obj/structure/bed/chair/sofa/teal/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("teal sofa right", /obj/structure/bed/chair/sofa/teal/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("teal sofa corner", /obj/structure/bed/chair/sofa/teal/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("black sofa middle", /obj/structure/bed/chair/sofa/black, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("black sofa left", /obj/structure/bed/chair/sofa/black/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("black sofa right", /obj/structure/bed/chair/sofa/black/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("black sofa corner", /obj/structure/bed/chair/sofa/black/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("green sofa middle", /obj/structure/bed/chair/sofa/green, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("green sofa left", /obj/structure/bed/chair/sofa/green/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("green sofa right", /obj/structure/bed/chair/sofa/green/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("green sofa corner", /obj/structure/bed/chair/sofa/green/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("purple sofa middle", /obj/structure/bed/chair/sofa/purp, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("purple sofa left", /obj/structure/bed/chair/sofa/purp/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("purple sofa right", /obj/structure/bed/chair/sofa/purp/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("purple sofa corner", /obj/structure/bed/chair/sofa/purp/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("blue sofa middle", /obj/structure/bed/chair/sofa/blue, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("blue sofa left", /obj/structure/bed/chair/sofa/blue/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("blue sofa right", /obj/structure/bed/chair/sofa/blue/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("blue sofa corner", /obj/structure/bed/chair/sofa/blue/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("beige sofa middle", /obj/structure/bed/chair/sofa/beige, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("beige sofa left", /obj/structure/bed/chair/sofa/beige/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("beige sofa right", /obj/structure/bed/chair/sofa/beige/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("beige sofa corner", /obj/structure/bed/chair/sofa/beige/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("lime sofa middle", /obj/structure/bed/chair/sofa/lime, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("lime sofa left", /obj/structure/bed/chair/sofa/lime/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("lime sofa right", /obj/structure/bed/chair/sofa/lime/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("lime sofa corner", /obj/structure/bed/chair/sofa/lime/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("yellow sofa middle", /obj/structure/bed/chair/sofa/yellow, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("yellow sofa left", /obj/structure/bed/chair/sofa/yellow/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("yellow sofa right", /obj/structure/bed/chair/sofa/yellow/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("yellow sofa corner", /obj/structure/bed/chair/sofa/yellow/corner, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("orange sofa middle", /obj/structure/bed/chair/sofa/orange, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("orange sofa left", /obj/structure/bed/chair/sofa/orange/left, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("orange sofa right", /obj/structure/bed/chair/sofa/orange/right, 1, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("orange sofa corner", /obj/structure/bed/chair/sofa/orange/corner, 1, one_per_turf = 1, on_floor = 1), \ )) /material/durasteel/generate_recipes() . = ..() - recipes += new/datum/stack_recipe("durasteel fishing rod", /obj/item/weapon/material/fishing_rod/modern/strong, 2) + recipes += new/datum/stack_recipe("durasteel fishing rod", /obj/item/weapon/material/fishing_rod/modern/strong, 2) \ No newline at end of file diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index f6708481b5..67fc816298 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -6,6 +6,7 @@ w_class = ITEMSIZE_NORMAL throw_speed = 3 throw_range = 3 + center_of_mass = null max_amount = 50 item_icons = list( slot_l_hand_str = 'icons/mob/items/lefthand_material.dmi', @@ -16,11 +17,11 @@ var/material/material var/perunit = SHEET_MATERIAL_AMOUNT var/apply_colour //temp pending icon rewrite + drop_sound = 'sound/items/drop/axe.ogg' /obj/item/stack/material/New() ..() - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 + randpixel_xy() if(!default_type) default_type = DEFAULT_WALL_MATERIAL @@ -106,17 +107,20 @@ icon_state = "sheet-sandstone" default_type = "sandstone" no_variants = FALSE + drop_sound = 'sound/items/drop/boots.ogg' /obj/item/stack/material/marble name = "marble brick" icon_state = "sheet-marble" default_type = "marble" no_variants = FALSE + drop_sound = 'sound/items/drop/boots.ogg' /obj/item/stack/material/diamond name = "diamond" icon_state = "sheet-diamond" default_type = "diamond" + drop_sound = 'sound/items/drop/glass.ogg' /obj/item/stack/material/uranium name = "uranium" @@ -129,6 +133,7 @@ icon_state = "sheet-phoron" default_type = "phoron" no_variants = FALSE + drop_sound = 'sound/items/drop/glass.ogg' /obj/item/stack/material/plastic name = "plastic" @@ -136,6 +141,13 @@ default_type = "plastic" no_variants = FALSE +/obj/item/stack/material/graphite + name = "graphite" + icon_state = "sheet-silver" + default_type = MAT_GRAPHITE + apply_colour = 1 + no_variants = FALSE + /obj/item/stack/material/gold name = "gold" icon_state = "sheet-gold" @@ -317,6 +329,7 @@ icon_state = "sheet-wood" default_type = MAT_WOOD strict_color_stacking = TRUE + drop_sound = 'sound/items/drop/wooden.ogg' /obj/item/stack/material/wood/sif name = "alien wooden plank" @@ -333,6 +346,7 @@ w_class = ITEMSIZE_HUGE description_info = "Use inhand to craft things, or use a sharp and edged object on this to convert it into two wooden planks." var/plank_type = /obj/item/stack/material/wood + drop_sound = 'sound/items/drop/wooden.ogg' /obj/item/stack/material/log/sif name = "alien log" @@ -370,6 +384,7 @@ no_variants = FALSE pass_color = TRUE strict_color_stacking = TRUE + drop_sound = 'sound/items/drop/clothing.ogg' /obj/item/stack/material/resin name = "resin" @@ -387,6 +402,7 @@ no_variants = FALSE pass_color = TRUE strict_color_stacking = TRUE + drop_sound = 'sound/items/drop/box.ogg' /obj/item/stack/material/snow name = "snow" @@ -408,12 +424,14 @@ no_variants = FALSE pass_color = TRUE strict_color_stacking = TRUE + drop_sound = 'sound/items/drop/clothing.ogg' /obj/item/stack/material/glass name = "glass" icon_state = "sheet-glass" default_type = "glass" no_variants = FALSE + drop_sound = 'sound/items/drop/glass.ogg' /obj/item/stack/material/glass/reinforced name = "reinforced glass" diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm index 724f4771f6..2da552c5b1 100644 --- a/code/modules/materials/materials.dm +++ b/code/modules/materials/materials.dm @@ -138,10 +138,10 @@ var/list/name_to_material // Placeholders for light tiles and rglass. /material/proc/build_rod_product(var/mob/user, var/obj/item/stack/used_stack, var/obj/item/stack/target_stack) if(!rod_product) - user << "You cannot make anything out of \the [target_stack]" + to_chat(user, "You cannot make anything out of \the [target_stack]") return if(used_stack.get_amount() < 1 || target_stack.get_amount() < 1) - user << "You need one rod and one sheet of [display_name] to make anything useful." + to_chat(user, "You need one rod and one sheet of [display_name] to make anything useful.") return used_stack.use(1) target_stack.use(1) @@ -151,15 +151,15 @@ var/list/name_to_material /material/proc/build_wired_product(var/mob/living/user, var/obj/item/stack/used_stack, var/obj/item/stack/target_stack) if(!wire_product) - user << "You cannot make anything out of \the [target_stack]" + to_chat(user, "You cannot make anything out of \the [target_stack]") return if(used_stack.get_amount() < 5 || target_stack.get_amount() < 1) - user << "You need five wires and one sheet of [display_name] to make anything useful." + to_chat(user, "You need five wires and one sheet of [display_name] to make anything useful.") return used_stack.use(5) target_stack.use(1) - user << "You attach wire to the [name]." + to_chat(user, "You attach wire to the [name].") var/obj/item/product = new wire_product(get_turf(user)) user.put_in_hands(product) @@ -514,12 +514,12 @@ var/list/name_to_material return 0 if(!user.IsAdvancedToolUser()) - user << "This task is too complex for your clumsy hands." + to_chat(user, "This task is too complex for your clumsy hands.") return 1 var/turf/T = user.loc if(!istype(T)) - user << "You must be standing on open flooring to build a window." + to_chat(user, "You must be standing on open flooring to build a window.") return 1 var/title = "Sheet-[used_stack.name] ([used_stack.get_amount()] sheet\s left)" @@ -557,7 +557,7 @@ var/list/name_to_material else failed_to_build = 1 if(failed_to_build) - user << "There is no room in this location." + to_chat(user, "There is no room in this location.") return 1 var/build_path = /obj/structure/windoor_assembly @@ -571,7 +571,7 @@ var/list/name_to_material build_path = created_window if(used_stack.get_amount() < sheets_needed) - user << "You need at least [sheets_needed] sheets to build this." + to_chat(user, "You need at least [sheets_needed] sheets to build this.") return 1 // Build the structure and update sheet count etc. @@ -652,6 +652,22 @@ var/list/name_to_material stack_type = null shard_type = SHARD_NONE +/material/graphite + name = MAT_GRAPHITE + stack_type = /obj/item/stack/material/graphite + flags = MATERIAL_BRITTLE + icon_base = "solid" + icon_reinf = "reinf_mesh" + icon_colour = "#333333" + hardness = 75 + weight = 15 + integrity = 175 + protectiveness = 15 + conductivity = 18 + melting_point = T0C+3600 + radiation_resistance = 15 + stack_origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 2) + /material/osmium name = "osmium" stack_type = /obj/item/stack/material/osmium @@ -1112,6 +1128,28 @@ var/list/name_to_material protectiveness = 1 // 4% conductive = 0 +/material/cloth_yellow + name = "yellow" + display_name = "yellow" + use_name = "yellow cloth" + icon_colour = "#EEF573" + flags = MATERIAL_PADDING + ignition_point = T0C+232 + melting_point = T0C+300 + protectiveness = 1 // 4% + conductive = 0 + +/material/cloth_orange + name = "orange" + display_name = "orange" + use_name = "orange cloth" + icon_colour = "#E3BF49" + flags = MATERIAL_PADDING + ignition_point = T0C+232 + melting_point = T0C+300 + protectiveness = 1 // 4% + conductive = 0 + /material/toy_foam name = "foam" display_name = "foam" diff --git a/code/modules/materials/materials_vr.dm b/code/modules/materials/materials_vr.dm index 7e3d98ac4a..16e5fa3ae6 100644 --- a/code/modules/materials/materials_vr.dm +++ b/code/modules/materials/materials_vr.dm @@ -17,4 +17,10 @@ sheet_singular_name = "fluff" sheet_plural_name = "fluffs" hardness = 60 - weight = 20 //Strong as iron. \ No newline at end of file + weight = 20 //Strong as iron. + +/material/darkglass + name = "darkglass" + display_name = "darkglass" + icon_base = "darkglass" + icon_colour = "#FFFFFF" diff --git a/code/modules/media/media_tracks.dm b/code/modules/media/media_tracks.dm index 708e2a811e..42ad9809fb 100644 --- a/code/modules/media/media_tracks.dm +++ b/code/modules/media/media_tracks.dm @@ -38,7 +38,7 @@ var/global/list/all_lobby_tracks = list() var/jukebox_track_file = "config/jukebox.json" if(!fexists(jukebox_track_file)) warning("File not found: [jukebox_track_file]") - return + return 1 var/list/jsonData = json_decode(file2text(jukebox_track_file)) if(!istype(jsonData)) warning("Failed to read tracks from [jukebox_track_file], json_decode failed.") diff --git a/code/modules/media/mediamanager.dm b/code/modules/media/mediamanager.dm index c1cd4897ca..56a5ce2336 100644 --- a/code/modules/media/mediamanager.dm +++ b/code/modules/media/mediamanager.dm @@ -10,7 +10,7 @@ // #define DEBUG_MEDIAPLAYER #ifdef DEBUG_MEDIAPLAYER -#define MP_DEBUG(x) owner << x +#define MP_DEBUG(x) to_chat(owner,x) #warning Please comment out #define DEBUG_MEDIAPLAYER before committing. #else #define MP_DEBUG(x) diff --git a/code/modules/metric/count.dm b/code/modules/metric/count.dm index 3900aaaa97..ba3678295f 100644 --- a/code/modules/metric/count.dm +++ b/code/modules/metric/count.dm @@ -21,3 +21,47 @@ if(assess_player_activity(L) >= cutoff) num++ return num + +// Gives a count of how many human mobs of a specific species are on the station. +// Note that `ignore_synths` makes this proc ignore posibrains and drones, but NOT cyborgs, as they are still the same species in-universe. +/datum/metric/proc/count_all_of_specific_species(species_name, ignore_synths = TRUE, cutoff = 75, respect_z = TRUE) + var/num = 0 + for(var/mob/living/carbon/human/H in player_list) + if(respect_z && !(H.z in using_map.station_levels)) + continue + if(ignore_synths && H.isSynthetic() && H.get_FBP_type() != FBP_CYBORG) + continue + if(H.species.name == species_name) + if(assess_player_activity(H) >= cutoff) + num++ + return num + +// Gives a count of how many FBPs of a specific type there are on the station. +/datum/metric/proc/count_all_FBPs_of_kind(desired_FBP_class, cutoff = 75, respect_z = TRUE) + var/num = 0 + for(var/mob/living/carbon/human/H in player_list) + if(respect_z && !(H.z in using_map.station_levels)) + continue + if(H.get_FBP_type() != desired_FBP_class) + continue + if(assess_player_activity(H) >= cutoff) + num++ + return num + +// Like above, but for all FBPs. +/datum/metric/proc/count_all_FBPs(cutoff = 75, respect_z = TRUE) + var/num = count_all_FBPs_of_kind(FBP_CYBORG, cutoff, respect_z) + num += count_all_FBPs_of_kind(FBP_POSI, cutoff, respect_z) + num += count_all_FBPs_of_kind(FBP_DRONE, cutoff, respect_z) + return num + + +/datum/metric/proc/get_all_antags(cutoff = 75) + . = list() + for(var/mob/living/L in player_list) + if(L.mind && player_is_antag(L.mind) && assess_player_activity(L) >= cutoff) + . += L + +/datum/metric/proc/count_all_antags(cutoff = 75) + var/list/L = get_all_antags(cutoff) + return L.len \ No newline at end of file diff --git a/code/modules/metric/department.dm b/code/modules/metric/department.dm index b6996d229c..89a0f78ed9 100644 --- a/code/modules/metric/department.dm +++ b/code/modules/metric/department.dm @@ -2,7 +2,7 @@ // This proc tries to find the department of an arbitrary mob. /datum/metric/proc/guess_department(var/mob/M) var/list/found_roles = list() - . = ROLE_UNKNOWN + . = DEPARTMENT_UNKNOWN // Records are usually the most reliable way to get what job someone is. var/datum/data/record/R = find_general_record("name", M.real_name) @@ -10,7 +10,7 @@ var/recorded_rank = R.fields["real_rank"] found_roles = role_name_to_department(recorded_rank) . = found_roles[1] - if(. != ROLE_UNKNOWN) // We found the correct department, so we can stop now. + if(. != DEPARTMENT_UNKNOWN) // We found the correct department, so we can stop now. return // They have a custom title, aren't crew, or someone deleted their record, so we need a fallback method. @@ -18,55 +18,99 @@ if(M.mind) found_roles = role_name_to_department(M.mind.assigned_role) . = found_roles[1] - if(. != ROLE_UNKNOWN) + if(. != DEPARTMENT_UNKNOWN) return // At this point, they don't have a mind, or for some reason assigned_role didn't work. found_roles = role_name_to_department(M.job) . = found_roles[1] - if(. != ROLE_UNKNOWN) + if(. != DEPARTMENT_UNKNOWN) return - return ROLE_UNKNOWN // Welp. + return DEPARTMENT_UNKNOWN // Welp. + +// Similar to above, but gets the actual job. Note that it returns the job datum itself, or null. +/datum/metric/proc/guess_job(mob/M) + // Like before, records are the most reliable way. + var/datum/data/record/R = find_general_record("name", M.real_name) + if(R) // They got a record, now find the job datum. + var/datum/job/J = SSjob.get_job(R.fields["real_rank"]) + if(istype(J)) + return J + + // Try the mind. + if(M.mind) + var/datum/job/J = SSjob.get_job(M.mind.assigned_role) + if(istype(J)) + return J + + // Last ditch effort, check for job assigned to the mob itself. + var/datum/job/J = SSjob.get_job(M.job) + if(istype(J)) + return J + + return null // Feed this proc the name of a job, and it will try to figure out what department they are apart of. -// Note that this returns a list, as some jobs are in more than one department, like Command. The 'primary' department is the first -// in the list, e.g. a HoS has Security as first, Command as second in the returned list. +// Improved with the addition of SSjob, which has departments be an actual thing and not a virtual concept. /datum/metric/proc/role_name_to_department(var/role_name) - var/list/result = list() + var/datum/job/J = SSjob.get_job(role_name) + if(istype(J)) + if(LAZYLEN(J.departments)) + return J.departments + return list(DEPARTMENT_UNKNOWN) - if(role_name in security_positions) - result += ROLE_SECURITY +/datum/metric/proc/count_people_in_department(var/department, cutoff = 75) + var/list/L = get_people_in_department(department, cutoff) + return L.len - if(role_name in engineering_positions) - result += ROLE_ENGINEERING - if(role_name in medical_positions) - result += ROLE_MEDICAL - - if(role_name in science_positions) - result += ROLE_RESEARCH - - if(role_name in cargo_positions) - result += ROLE_CARGO - - if(role_name in civilian_positions) - result += ROLE_CIVILIAN - - if(role_name in nonhuman_positions) - result += ROLE_SYNTHETIC - - if(role_name in command_positions) // We do Command last, since we consider command to only be a primary department for hop/admin. - result += ROLE_COMMAND - - if(!result.len) // No department was found. - result += ROLE_UNKNOWN - return result - -/datum/metric/proc/count_people_in_department(var/department) +/datum/metric/proc/get_people_in_department(department, cutoff = 75) + . = list() if(!department) return for(var/mob/M in player_list) - if(guess_department(M) != department) // Ignore people outside the department we're counting. + if(department != DEPARTMENT_EVERYONE && guess_department(M) != department) // Ignore people outside the department we're counting. continue - . += 1 + if(assess_player_activity(M) < cutoff) + continue + . += M + +/datum/metric/proc/get_people_with_job(job_type, cutoff = 75) + . = list() + // First, get the name. + var/datum/job/J = SSjob.get_job_type(job_type) + if(!istype(J)) + return + + // Now find people with the job name. + for(var/M in player_list) + var/datum/job/their_job = guess_job(M) + if(!istype(their_job)) // No job was guessed. + continue + if(their_job.title != J.title) // Jobs don't match. + continue + if(assess_player_activity(M) < cutoff) // Too AFK. + continue + . += M + +/datum/metric/proc/count_people_with_job(job_type, cutoff = 75) + var/list/L = get_people_with_job(job_type, cutoff) + return L.len + + + +/datum/metric/proc/get_people_with_alt_title(job_type, alt_title_type, cutoff = 75) + . = list() + + var/list/people_with_jobs = get_people_with_job(job_type, cutoff) + var/datum/job/J = SSjob.get_job_type(job_type) + var/datum/alt_title/A = new alt_title_type() + + for(var/M in people_with_jobs) + if(J.has_alt_title(M, null, A.title)) + . += M + +/datum/metric/proc/count_people_with_alt_title(job_type, alt_title_type, cutoff = 75) + var/list/L = get_people_with_alt_title(job_type, alt_title_type, cutoff) + return L.len \ No newline at end of file diff --git a/code/modules/metric/metric.dm b/code/modules/metric/metric.dm index 44ff59d284..4a9b6bf847 100644 --- a/code/modules/metric/metric.dm +++ b/code/modules/metric/metric.dm @@ -3,13 +3,13 @@ /datum/metric var/list/departments = list( - ROLE_COMMAND, - ROLE_SECURITY, - ROLE_ENGINEERING, - ROLE_MEDICAL, - ROLE_RESEARCH, - ROLE_CARGO, - ROLE_CIVILIAN, - ROLE_SYNTHETIC + DEPARTMENT_COMMAND, + DEPARTMENT_SECURITY, + DEPARTMENT_ENGINEERING, + DEPARTMENT_MEDICAL, + DEPARTMENT_RESEARCH, + DEPARTMENT_CARGO, + DEPARTMENT_CIVILIAN, + DEPARTMENT_SYNTHETIC ) diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm index 8fcba9aee3..7a7ee7ee95 100644 --- a/code/modules/mining/abandonedcrates.dm +++ b/code/modules/mining/abandonedcrates.dm @@ -147,7 +147,7 @@ if(!locked) return - user << "The crate is locked with a Deca-code lock." + to_chat(user, "The crate is locked with a Deca-code lock.") var/input = input(usr, "Enter [codelen] digits. All digits must be unique.", "Deca-Code Lock", "") as text if(!Adjacent(user)) return @@ -161,23 +161,23 @@ sanitycheck = null //if a digit is repeated, reject the input if(input == null || sanitycheck == null || length(input) != codelen) - user << "You leave the crate alone." + to_chat(user, "You leave the crate alone.") else if(check_input(input)) - user << "The crate unlocks!" + to_chat(user, "The crate unlocks!") playsound(user, 'sound/machines/lockreset.ogg', 50, 1) set_locked(0) else visible_message("A red light on \the [src]'s control panel flashes briefly.") attempts-- if (attempts == 0) - user << "The crate's anti-tamper system activates!" + to_chat(user, "The crate's anti-tamper system activates!") var/turf/T = get_turf(src.loc) explosion(T, 0, 0, 1, 2) qdel(src) /obj/structure/closet/crate/secure/loot/emag_act(var/remaining_charges, var/mob/user) if (locked) - user << "The crate unlocks!" + to_chat(user, "The crate unlocks!") locked = 0 /obj/structure/closet/crate/secure/loot/proc/check_input(var/input) @@ -195,11 +195,11 @@ /obj/structure/closet/crate/secure/loot/attackby(obj/item/weapon/W as obj, mob/user as mob) if(locked) if (istype(W, /obj/item/device/multitool)) // Greetings Urist McProfessor, how about a nice game of cows and bulls? - user << "DECA-CODE LOCK ANALYSIS:" + to_chat(user, "DECA-CODE LOCK ANALYSIS:") if (attempts == 1) - user << "* Anti-Tamper system will activate on the next failed access attempt." + to_chat(user, "* Anti-Tamper system will activate on the next failed access attempt.") else - user << "* Anti-Tamper system will activate after [src.attempts] failed access attempts." + to_chat(user, "* Anti-Tamper system will activate after [src.attempts] failed access attempts.") if(lastattempt.len) var/bulls = 0 var/cows = 0 @@ -214,6 +214,6 @@ var/previousattempt = null //convert back to string for readback for(var/i in 1 to codelen) previousattempt = addtext(previousattempt, lastattempt[i]) - user << "Last code attempt, [previousattempt], had [bulls] correct digits at correct positions and [cows] correct digits at incorrect positions." + to_chat(user, "Last code attempt, [previousattempt], had [bulls] correct digits at correct positions and [cows] correct digits at incorrect positions.") return ..() diff --git a/code/modules/mining/coins.dm b/code/modules/mining/coins.dm index 7335165092..9da7e05148 100644 --- a/code/modules/mining/coins.dm +++ b/code/modules/mining/coins.dm @@ -5,16 +5,17 @@ name = "Coin" desc = "A simple coin you can flip." icon_state = "coin" + randpixel = 8 force = 0.0 throwforce = 0.0 w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS var/string_attached var/sides = 2 + drop_sound = 'sound/items/drop/ring.ogg' /obj/item/weapon/coin/New() - pixel_x = rand(0,16)-8 - pixel_y = rand(0,8)-8 + randpixel_xy() /obj/item/weapon/coin/gold name = "gold coin" @@ -48,14 +49,14 @@ if(istype(W,/obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/CC = W if(string_attached) - user << "There already is a string attached to this coin." + to_chat(user, "There already is a string attached to this coin.") return if (CC.use(1)) overlays += image('icons/obj/items.dmi',"coin_string_overlay") string_attached = 1 - user << "You attach a string to the coin." + to_chat(user, "You attach a string to the coin.") else - user << "This cable coil appears to be empty." + to_chat(user, "This cable coil appears to be empty.") return else if(W.is_wirecutter()) if(!string_attached) @@ -67,7 +68,7 @@ CC.update_icon() overlays = list() string_attached = null - user << "You detach the string from the coin." + to_chat(user, "You detach the string from the coin.") else ..() /obj/item/weapon/coin/attack_self(mob/user as mob) diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index bdfab8f92b..3c38927910 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -1,7 +1,7 @@ /obj/machinery/mining icon = 'icons/obj/mining_drill.dmi' anchored = 0 - use_power = 0 //The drill takes power directly from a cell. + use_power = USE_POWER_OFF //The drill takes power directly from a cell. density = 1 layer = MOB_LAYER+0.1 //So it draws over mobs in the tile north of it. diff --git a/code/modules/mining/drilling/scanner.dm b/code/modules/mining/drilling/scanner.dm index bbcc65cc16..79ae5f1573 100644 --- a/code/modules/mining/drilling/scanner.dm +++ b/code/modules/mining/drilling/scanner.dm @@ -9,13 +9,13 @@ var/scan_time = 5 SECONDS /obj/item/weapon/mining_scanner/attack_self(mob/user as mob) - user << "You begin sweeping \the [src] about, scanning for metal deposits." + to_chat(user, "You begin sweeping \the [src] about, scanning for metal deposits.") playsound(loc, 'sound/items/goggles_charge.ogg', 50, 1, -6) if(!do_after(user, scan_time)) return - ScanTurf(user, get_turf(user)) + ScanTurf(get_turf(user), user) /obj/item/weapon/mining_scanner/proc/ScanTurf(var/atom/target, var/mob/user, var/exact = FALSE) var/list/metals = list( @@ -45,7 +45,7 @@ if(ore_type) metals[ore_type] += T.resources[metal] - to_chat(user, "\icon[src] The scanner beeps and displays a readout.") + var/message = "[bicon(src)] The scanner beeps and displays a readout." for(var/ore_type in metals) var/result = "no sign" @@ -59,4 +59,6 @@ else result = metals[ore_type] - to_chat(user, "- [result] of [ore_type].") + message += "
    - [result] of [ore_type]." + + to_chat(user, message) diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 347601176a..ec6b7623df 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -20,7 +20,7 @@ machine.console = src else //Silently failing and causing mappers to scratch their heads while runtiming isn't ideal. - world << "Warning: Stacking machine console at [src.x], [src.y], [src.z] could not find its machine!" + to_world("Warning: Stacking machine console at [src.x], [src.y], [src.z] could not find its machine!") qdel(src) /obj/machinery/mineral/stacking_unit_console/attack_hand(mob/user) diff --git a/code/modules/mining/machine_unloading.dm b/code/modules/mining/machine_unloading.dm index d54aa01761..613190ef64 100644 --- a/code/modules/mining/machine_unloading.dm +++ b/code/modules/mining/machine_unloading.dm @@ -11,17 +11,16 @@ var/obj/machinery/mineral/output = null -/obj/machinery/mineral/unloading_machine/New() - ..() - spawn( 5 ) - for (var/dir in cardinal) - src.input = locate(/obj/machinery/mineral/input, get_step(src, dir)) - if(src.input) break - for (var/dir in cardinal) - src.output = locate(/obj/machinery/mineral/output, get_step(src, dir)) - if(src.output) break - return - return +/obj/machinery/mineral/unloading_machine/Initialize() + . = ..() + for(var/dir in cardinal) + input = locate(/obj/machinery/mineral/input, get_step(src, dir)) + if(input) + break + for(var/dir in cardinal) + output = locate(/obj/machinery/mineral/output, get_step(src, dir)) + if(output) + break /obj/machinery/mineral/unloading_machine/process() if (src.output && src.input) diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 4208e01d2d..12bcdb76be 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -23,7 +23,7 @@ var/digspeed = 40 //moving the delay to an item var so R&D can make improved picks. --NEO origin_tech = list(TECH_MATERIAL = 1, TECH_ENGINEERING = 1) attack_verb = list("hit", "pierced", "sliced", "attacked") - var/drill_sound = 'sound/weapons/Genhit.ogg' + var/drill_sound = "pickaxe" var/drill_verb = "drilling" sharp = 1 @@ -201,11 +201,11 @@ var/turf/T = get_turf(src) if(!T || !istype(T,/turf/simulated/mineral)) - user << "The flag won't stand up in this terrain." + to_chat(user, "The flag won't stand up in this terrain.") return if(F && F.upright) - user << "There is already a flag here." + to_chat(user, "There is already a flag here.") return var/obj/item/stack/flag/newflag = new src.type(T) diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index 68d3157593..b420122f3e 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -133,15 +133,6 @@ turf/simulated/mineral/floor/light_corner if(air_master) air_master.mark_for_update(src) -/turf/simulated/mineral/Entered(atom/movable/M as mob|obj) - . = ..() - if(istype(M,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = M - if(R.module) - for(var/obj/item/weapon/storage/bag/ore/O in list(R.module_state_1, R.module_state_2, R.module_state_3)) - attackby(O, R) - return - /turf/simulated/mineral/proc/get_cached_border(var/cache_id, var/direction, var/icon_file, var/icon_state, var/offset = 32) //Cache miss if(!mining_overlay_cache["[cache_id]_[direction]"]) @@ -230,6 +221,9 @@ turf/simulated/mineral/floor/light_corner if(istype(get_step(src, direction), /turf/simulated/mineral)) var/turf/simulated/mineral/M = get_step(src, direction) M.update_icon() + if(istype(get_step(src, direction), /turf/simulated/wall/solidrock)) + var/turf/simulated/wall/solidrock/M = get_step(src, direction) + M.update_icon() /turf/simulated/mineral/ex_act(severity) @@ -434,7 +428,7 @@ turf/simulated/mineral/floor/light_corner var/datum/find/F = finds[1] if(newDepth > F.excavation_required) // Digging too deep can break the item. At least you won't summon a Balrog (probably) fail_message = ". [pick("There is a crunching noise","[W] collides with some different rock","Part of the rock face crumbles away","Something breaks under [W]")]" - wreckfinds(P.destroy_artefacts) + wreckfinds(P.destroy_artefacts) to_chat(user, "You start [P.drill_verb][fail_message].") if(do_after(user,P.digspeed)) diff --git a/code/modules/mining/mine_turfs_vr.dm b/code/modules/mining/mine_turfs_vr.dm index e0cb524936..6e8797bf70 100644 --- a/code/modules/mining/mine_turfs_vr.dm +++ b/code/modules/mining/mine_turfs_vr.dm @@ -12,4 +12,14 @@ ignore_cavegen = TRUE /turf/simulated/mineral/floor/ignore_cavegen - ignore_cavegen = TRUE \ No newline at end of file + ignore_cavegen = TRUE + +/turf/simulated/mineral/vacuum + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/simulated/mineral/floor/vacuum + oxygen = 0 + nitrogen = 0 + temperature = TCMB \ No newline at end of file diff --git a/code/modules/mining/mineral_effect.dm b/code/modules/mining/mineral_effect.dm index bc0ce899ea..5f8daed8d1 100644 --- a/code/modules/mining/mineral_effect.dm +++ b/code/modules/mining/mineral_effect.dm @@ -22,5 +22,5 @@ if(O) scanner_image = image(icon, loc = get_turf(src), icon_state = (O.scan_icon ? O.scan_icon : icon_state)) else - world << "No ore data for [src]!" + to_world("No ore data for [src]!") return scanner_image \ No newline at end of file diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index bd44db119b..e3e72693fe 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -1,6 +1,4 @@ /**********************Mint**************************/ - - /obj/machinery/mineral/mint name = "Coin press" icon = 'icons/obj/stationobjs.dmi' @@ -21,43 +19,43 @@ var/coinsToProduce = 10 -/obj/machinery/mineral/mint/New() - ..() - spawn( 5 ) - for (var/dir in cardinal) - src.input = locate(/obj/machinery/mineral/input, get_step(src, dir)) - if(src.input) break - for (var/dir in cardinal) - src.output = locate(/obj/machinery/mineral/output, get_step(src, dir)) - if(src.output) break - START_PROCESSING(SSobj, src) - return - return - +/obj/machinery/mineral/mint/Initialize() + . = ..() + for (var/dir in cardinal) + input = locate(/obj/machinery/mineral/input, get_step(src, dir)) + if(input) + break + for (var/dir in cardinal) + output = locate(/obj/machinery/mineral/output, get_step(src, dir)) + if(output) + break /obj/machinery/mineral/mint/process() - if ( src.input) - var/obj/item/stack/O - O = locate(/obj/item/stack, input.loc) - if(O) - var/processed = 1 - switch(O.get_material_name()) - if("gold") - amt_gold += 100 * O.get_amount() - if("silver") - amt_silver += 100 * O.get_amount() - if("diamond") - amt_diamond += 100 * O.get_amount() - if("phoron") - amt_phoron += 100 * O.get_amount() - if("uranium") - amt_uranium += 100 * O.get_amount() - if(DEFAULT_WALL_MATERIAL) - amt_iron += 100 * O.get_amount() - else - processed = 0 - if(processed) - qdel(O) + if(!input) + return + + var/obj/item/stack/O = locate(/obj/item/stack, input.loc) + if(!O) + return + + var/processed = 1 + switch(O.get_material_name()) + if("gold") + amt_gold += 100 * O.get_amount() + if("silver") + amt_silver += 100 * O.get_amount() + if("diamond") + amt_diamond += 100 * O.get_amount() + if("phoron") + amt_phoron += 100 * O.get_amount() + if("uranium") + amt_uranium += 100 * O.get_amount() + if(DEFAULT_WALL_MATERIAL) + amt_iron += 100 * O.get_amount() + else + processed = 0 + if(processed) + qdel(O) /obj/machinery/mineral/mint/attack_hand(user as mob) @@ -120,7 +118,7 @@ usr.set_machine(src) src.add_fingerprint(usr) if(processing==1) - usr << "The machine is processing." + to_chat(usr, "The machine is processing.") return if(href_list["choose"]) chosen = href_list["choose"] diff --git a/code/modules/mining/money_bag.dm b/code/modules/mining/money_bag.dm index 65e645f815..fd1cb561b0 100644 --- a/code/modules/mining/money_bag.dm +++ b/code/modules/mining/money_bag.dm @@ -49,14 +49,14 @@ ..() if (istype(W, /obj/item/weapon/coin)) var/obj/item/weapon/coin/C = W - user << "You add the [C.name] into the bag." + to_chat(user, "You add the [C.name] into the bag.") usr.drop_item() contents += C if (istype(W, /obj/item/weapon/moneybag)) var/obj/item/weapon/moneybag/C = W for (var/obj/O in C.contents) contents += O; - user << "You empty the [C.name] into the bag." + to_chat(user, "You empty the [C.name] into the bag.") return /obj/item/weapon/moneybag/Topic(href, href_list) diff --git a/code/modules/mining/ore.dm b/code/modules/mining/ore.dm index 70d0e78685..708c06db12 100644 --- a/code/modules/mining/ore.dm +++ b/code/modules/mining/ore.dm @@ -2,6 +2,7 @@ name = "small rock" icon = 'icons/obj/mining.dmi' icon_state = "ore2" + randpixel = 8 w_class = ITEMSIZE_SMALL var/datum/geosample/geologic_data var/material @@ -42,7 +43,7 @@ ..() var/mob/living/carbon/human/H = hit_atom if(istype(H) && H.has_eyes() && prob(85)) - H << "Some of \the [src] gets in your eyes!" + to_chat(H, "Some of \the [src] gets in your eyes!") H.Blind(5) H.eye_blurry += 10 spawn(1) @@ -94,7 +95,7 @@ ..() var/mob/living/carbon/human/H = hit_atom if(istype(H) && H.has_eyes() && prob(85)) - H << "Some of \the [src] gets in your eyes!" + to_chat(H, "Some of \the [src] gets in your eyes!") H.Blind(10) H.eye_blurry += 15 spawn(1) @@ -113,12 +114,23 @@ material = null /obj/item/weapon/ore/New() - pixel_x = rand(0,16)-8 - pixel_y = rand(0,8)-8 + randpixel_xy() /obj/item/weapon/ore/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W,/obj/item/device/core_sampler)) var/obj/item/device/core_sampler/C = W C.sample_item(src, user) else - return ..() \ No newline at end of file + return ..() + +//VOREStation Add +/obj/item/weapon/ore/attack(mob/living/M as mob, mob/living/user as mob) + if(M.handle_eat_ore(src, user)) + return + ..() + +/obj/item/weapon/ore/attack_generic(var/mob/living/user) //Allow adminbussed mobs to eat ore if they click it while NOT on help intent. + if(user.handle_eat_ore(src)) + return + ..() +//VOREStation Add End diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/ore_box.dm similarity index 73% rename from code/modules/mining/satchel_ore_boxdm.dm rename to code/modules/mining/ore_box.dm index e7a248ecf9..0d00e7552f 100644 --- a/code/modules/mining/satchel_ore_boxdm.dm +++ b/code/modules/mining/ore_box.dm @@ -1,6 +1,5 @@ /**********************Ore box**************************/ -//Why the hell is this file called satchel_ore_boxdm.dm? -CK /obj/structure/ore_box icon = 'icons/obj/mining.dmi' icon_state = "orebox0" @@ -45,12 +44,8 @@ stored_ore[O.name] = 1 /obj/structure/ore_box/examine(mob/user) - user << "That's an [src]." - user << desc - - // Borgs can now check contents too. - if((!istype(user, /mob/living/carbon/human)) && (!istype(user, /mob/living/silicon/robot))) - return + to_chat(user, "That's an [src].") + to_chat(user, desc) if(!Adjacent(user)) //Can only check the contents of ore boxes if you can physically reach them. return @@ -58,16 +53,16 @@ add_fingerprint(user) if(!contents.len) - user << "It is empty." + to_chat(user, "It is empty.") return if(world.time > last_update + 10) update_ore_count() last_update = world.time - user << "It holds:" + to_chat(user, "It holds:") for(var/ore in stored_ore) - user << "- [stored_ore[ore]] [ore]" + to_chat(user, "- [stored_ore[ore]] [ore]") return /obj/structure/ore_box/verb/empty_box() @@ -75,27 +70,27 @@ set category = "Object" set src in view(1) - if(!istype(usr, /mob/living/carbon/human) && !istype(usr, /mob/living/silicon/robot)) //Only living, intelligent creatures with gripping aparatti can empty ore boxes. - usr << "You are physically incapable of emptying the ore box." + if(!ishuman(usr) && !isrobot(usr)) //Only living, intelligent creatures with gripping aparatti can empty ore boxes. + to_chat(usr, "You are physically incapable of emptying the ore box.") return - if( usr.stat || usr.restrained() ) + if(usr.stat || usr.restrained()) return if(!Adjacent(usr)) //You can only empty the box if you can physically reach it - usr << "You cannot reach the ore box." + to_chat(usr, "You cannot reach the ore box.") return add_fingerprint(usr) if(contents.len < 1) - usr << "The ore box is empty." + to_chat(usr, "The ore box is empty.") return for (var/obj/item/weapon/ore/O in contents) contents -= O O.loc = src.loc - usr << "You empty the ore box." + to_chat(usr, "You empty the ore box.") return @@ -105,4 +100,4 @@ O.loc = src.loc O.ex_act(severity++) qdel(src) - return \ No newline at end of file + return diff --git a/code/modules/mining/ore_datum.dm b/code/modules/mining/ore_datum.dm index e578902104..f888ae3bb6 100644 --- a/code/modules/mining/ore_datum.dm +++ b/code/modules/mining/ore_datum.dm @@ -51,6 +51,7 @@ var/global/list/ore_data = list() name = "carbon" display_name = "raw carbon" smelts_to = "plastic" + compresses_to = "graphite" alloy = 1 result_amount = 5 spread_chance = 25 diff --git a/code/modules/mining/ore_datum_vr.dm b/code/modules/mining/ore_datum_vr.dm deleted file mode 100644 index e55bc9e599..0000000000 --- a/code/modules/mining/ore_datum_vr.dm +++ /dev/null @@ -1,2 +0,0 @@ -/ore/coal - compresses_to = "carbon" \ No newline at end of file diff --git a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm index 849550bf6b..5886b08af0 100644 --- a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm @@ -3,12 +3,13 @@ /obj/machinery/mineral/equipment_vendor name = "mining equipment vendor" desc = "An equipment vendor for miners, points collected at an ore redemption machine can be spent here." - icon = 'icons/obj/machines/mining_machines.dmi' - icon_state = "mining" + icon = 'icons/obj/vending.dmi' + icon_state = "adh-tool" density = TRUE anchored = TRUE + var/icon_deny = "adh-tool-deny" + var/icon_vend = "adh-tool-vend" circuit = /obj/item/weapon/circuitboard/mining_equipment_vendor - var/icon_deny = "mining-deny" var/obj/item/weapon/card/id/inserted_id //VOREStation Edit Start - Heavily modified list var/list/prize_list = list( @@ -107,7 +108,12 @@ /obj/machinery/mineral/equipment_vendor/update_icon() if(panel_open) - icon_state = "[initial(icon_state)]-open" + add_overlay("[initial(icon_state)]-panel") + else + cut_overlay("[initial(icon_state)]-panel") + + if(stat & BROKEN) + icon_state = "[initial(icon_state)]-broken" else if(powered()) icon_state = initial(icon_state) else @@ -174,6 +180,7 @@ inserted_id.mining_points -= prize.cost to_chat(usr, "[src] clanks to life briefly before vending [prize.equipment_name]!") new prize.equipment_path(drop_location()) + flick(icon_vend, src) else to_chat(usr, "Error: Please insert a valid ID!") flick(icon_deny, src) diff --git a/code/modules/mining/ore_redemption_machine/survey_vendor.dm b/code/modules/mining/ore_redemption_machine/survey_vendor.dm index b58b343b04..a5afad7fab 100644 --- a/code/modules/mining/ore_redemption_machine/survey_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/survey_vendor.dm @@ -7,7 +7,7 @@ anchored = TRUE circuit = /obj/item/weapon/circuitboard/exploration_equipment_vendor icon_deny = "exploration-deny" //VOREStation Edit - var/icon_vend = "exploration-vend" //VOREStation Add + icon_vend = "exploration-vend" //VOREStation Add //VOREStation Edit Start - Heavily modified list prize_list = list( new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 1), diff --git a/code/modules/mining/resonator.dm b/code/modules/mining/resonator.dm index bfbd29b830..dbee1668b2 100644 --- a/code/modules/mining/resonator.dm +++ b/code/modules/mining/resonator.dm @@ -63,9 +63,7 @@ transform = matrix()*0.75 animate(src, transform = matrix()*1.5, time = timetoburst) // Queue the actual bursting - spawn(timetoburst) - if(!QDELETED(src)) - burst(creator) + addtimer(CALLBACK(src, ./proc/burst, creator), timetoburst) /obj/effect/resonance/proc/burst(var/creator = null) var/turf/T = get_turf(src) diff --git a/code/modules/mining/shelter_atoms.dm b/code/modules/mining/shelter_atoms.dm index 55734dfb54..90403853a9 100644 --- a/code/modules/mining/shelter_atoms.dm +++ b/code/modules/mining/shelter_atoms.dm @@ -222,9 +222,9 @@ name = "survival pod storage" desc = "A heated storage unit." icon_state = "donkvendor" + icon_base = "donkvendor" + icon_contents = null icon = 'icons/obj/survival_pod_vend.dmi' - icon_on = "donkvendor" - icon_off = "donkvendor" light_range = 5 light_power = 1.2 light_color = "#DDFFD3" diff --git a/code/modules/mining/shelter_atoms_vr.dm b/code/modules/mining/shelter_atoms_vr.dm new file mode 100644 index 0000000000..664b0cff51 --- /dev/null +++ b/code/modules/mining/shelter_atoms_vr.dm @@ -0,0 +1,13 @@ +/obj/structure/fans/hardlight + name = "hardlight shield" + desc = "Retains air, allows passage." + plane = TURF_PLANE + layer = ABOVE_TURF_LAYER + density = FALSE + icon = 'icons/effects/effects_vr.dmi' + icon_state = "hardlight" + buildstackamount = 2 + + light_range = 3 + light_power = 1 + light_color = "#FFFFFF" \ No newline at end of file diff --git a/code/modules/mob/_modifiers/feysight.dm b/code/modules/mob/_modifiers/feysight.dm new file mode 100644 index 0000000000..af391ed51c --- /dev/null +++ b/code/modules/mob/_modifiers/feysight.dm @@ -0,0 +1,42 @@ +/datum/modifier/feysight + name = "feysight" + desc = "You are filled with an inner peace, and widened sight." + client_color = "#42e6ca" + + on_created_text = "You feel an inner peace as your mind's eye expands!" + on_expired_text = "Your sight returns to what it once was." + stacks = MODIFIER_STACK_EXTEND + + accuracy = -15 + accuracy_dispersion = 1 + +/datum/modifier/feysight/on_applied() + holder.see_invisible = 60 + holder.see_invisible_default = 60 + +/datum/modifier/feysight/on_expire() + holder.see_invisible_default = initial(holder.see_invisible_default) + holder.see_invisible = holder.see_invisible_default + +/datum/modifier/feysight/can_apply(var/mob/living/L) + if(L.stat) + to_chat(L, "You can't be unconscious or dead to experience tranquility.") + return FALSE + + if(!L.is_sentient()) + return FALSE // Drones don't feel anything. + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.species.name == "Diona") + to_chat(L, "You feel strange for a moment, but it passes.") + return FALSE // Happy trees aren't affected by tranquility. + + return ..() + +/datum/modifier/feysight/tick() + ..() + + if(ishuman(holder)) + var/mob/living/carbon/human/H = holder + H.druggy = min(15, H.druggy + 4) diff --git a/code/modules/mob/_modifiers/fire.dm b/code/modules/mob/_modifiers/fire.dm new file mode 100644 index 0000000000..3a3c53a9dc --- /dev/null +++ b/code/modules/mob/_modifiers/fire.dm @@ -0,0 +1,42 @@ +// Ignition, but confined to the modifier system. +// This makes it more predictable and thus, easier to balance. +/datum/modifier/fire + name = "on fire" + desc = "You are on fire! You will be harmed until the fire goes out or you extinguish it with water." + mob_overlay_state = "on_fire" + + on_created_text = "You burst into flames!" + on_expired_text = "The fire starts to fade." + stacks = MODIFIER_STACK_ALLOWED // Multiple instances will hurt a lot. + var/damage_per_tick = 5 + +/datum/modifier/fire/intense + mob_overlay_state = "on_fire_intense" + damage_per_tick = 10 + +/datum/modifier/fire/tick() + holder.inflict_heat_damage(damage_per_tick) + +/datum/modifier/fire/weak + damage_per_tick = 1 + +/* + * Modifier used by projectiles, like the flamethrower, that rely heavily on fire_stacks to persist. + */ + +/datum/modifier/fire/stack_managed/tick() + ..() + + if(!holder.fire_stacks || holder.fire_stacks < 0) + if(prob(10)) + expire() + + else if(holder.fire_stacks > 0) + holder.fire_stacks -= 0.5 + +/datum/modifier/fire/stack_managed/intense + mob_overlay_state = "on_fire_intense" + damage_per_tick = 10 + +/datum/modifier/fire/stack_managed/weak + damage_per_tick = 1 diff --git a/code/modules/mob/_modifiers/modifiers.dm b/code/modules/mob/_modifiers/modifiers.dm index 665ae80617..5950d9e98d 100644 --- a/code/modules/mob/_modifiers/modifiers.dm +++ b/code/modules/mob/_modifiers/modifiers.dm @@ -48,6 +48,8 @@ var/pain_immunity // Makes the holder not care about pain while this is on. Only really useful to human mobs. var/pulse_modifier // Modifier for pulse, will be rounded on application, then added to the normal 'pulse' multiplier which ranges between 0 and 5 normally. Only applied if they're living. var/pulse_set_level // Positive number. If this is non-null, it will hard-set the pulse level to this. Pulse ranges from 0 to 5 normally. + var/emp_modifier // Added to the EMP strength, which is an inverse scale from 1 to 4, with 1 being the strongest EMP. 5 is a nullification. + var/explosion_modifier // Added to the bomb strength, which is an inverse scale from 1 to 3, with 1 being gibstrength. 4 is a nullification. /datum/modifier/New(var/new_holder, var/new_origin) holder = new_holder @@ -59,7 +61,7 @@ // Checks if the modifier should be allowed to be applied to the mob before attaching it. // Override for special criteria, e.g. forbidding robots from receiving it. -/datum/modifier/proc/can_apply(var/mob/living/L) +/datum/modifier/proc/can_apply(var/mob/living/L, var/suppress_output = FALSE) return TRUE // Checks to see if this datum should continue existing. @@ -113,7 +115,8 @@ // Call this to add a modifier to a mob. First argument is the modifier type you want, second is how long it should last, in ticks. // Third argument is the 'source' of the modifier, if it's from someone else. If null, it will default to the mob being applied to. // The SECONDS/MINUTES macro is very helpful for this. E.g. M.add_modifier(/datum/modifier/example, 5 MINUTES) -/mob/living/proc/add_modifier(var/modifier_type, var/expire_at = null, var/mob/living/origin = null) +// The fourth argument is a boolean to suppress failure messages, set it to true if the modifier is repeatedly applied (as chem-based modifiers are) to prevent chat-spam +/mob/living/proc/add_modifier(var/modifier_type, var/expire_at = null, var/mob/living/origin = null, var/suppress_failure = FALSE) // First, check if the mob already has this modifier. for(var/datum/modifier/M in modifiers) if(ispath(modifier_type, M)) @@ -130,7 +133,7 @@ // If we're at this point, the mob doesn't already have it, or it does but stacking is allowed. var/datum/modifier/mod = new modifier_type(src, origin) - if(!mod.can_apply(src)) + if(!mod.can_apply(src, suppress_failure)) qdel(mod) return if(expire_at) diff --git a/code/modules/mob/_modifiers/modifiers_misc.dm b/code/modules/mob/_modifiers/modifiers_misc.dm index af6eaa952e..8e6a094910 100644 --- a/code/modules/mob/_modifiers/modifiers_misc.dm +++ b/code/modules/mob/_modifiers/modifiers_misc.dm @@ -112,16 +112,18 @@ the artifact triggers the rage. var/mob/living/carbon/human/H = holder H.shock_stage = last_shock_stage -/datum/modifier/berserk/can_apply(var/mob/living/L) +/datum/modifier/berserk/can_apply(var/mob/living/L, var/suppress_failure = FALSE) if(L.stat) - to_chat(L, "You can't be unconscious or dead to berserk.") + if(!suppress_failure) + to_chat(L, "You can't be unconscious or dead to berserk.") return FALSE // It would be weird to see a dead body get angry all of a sudden. if(!L.is_sentient()) return FALSE // Drones don't feel anything. if(L.has_modifier_of_type(/datum/modifier/berserk_exhaustion)) - to_chat(L, "You recently berserked, and cannot do so again while exhausted.") + if(!suppress_failure) + to_chat(L, "You recently berserked, and cannot do so again while exhausted.") return FALSE // On cooldown. if(L.isSynthetic()) @@ -135,7 +137,8 @@ the artifact triggers the rage. return FALSE // Happy trees aren't affected by blood rages. if(L.nutrition < nutrition_cost) - to_chat(L, "You are too hungry to berserk.") + if(!suppress_failure) + to_chat(L, "You are too hungry to berserk.") return FALSE // Too hungry to enrage. return ..() @@ -224,28 +227,6 @@ the artifact triggers the rage. accuracy_dispersion = 2 // A combination of fear and immense pain or damage reults in a twitching firing arm. Flee. - - -// Ignition, but confined to the modifier system. -// This makes it more predictable and thus, easier to balance. -/datum/modifier/fire - name = "on fire" - desc = "You are on fire! You will be harmed until the fire goes out or you extinguish it with water." - mob_overlay_state = "on_fire" - - on_created_text = "You combust into flames!" - on_expired_text = "The fire starts to fade." - stacks = MODIFIER_STACK_ALLOWED // Multiple instances will hurt a lot. - var/damage_per_tick = 5 - -/datum/modifier/fire/intense - mob_overlay_state = "on_fire_intense" - damage_per_tick = 10 - -/datum/modifier/fire/tick() - holder.inflict_heat_damage(damage_per_tick) - - // Applied when near something very cold. // Reduces mobility, attack speed. /datum/modifier/chilled @@ -343,3 +324,67 @@ the artifact triggers the rage. /datum/modifier/homeothermic/tick() ..() holder.bodytemperature = round((holder.bodytemperature + T20C) / 2) + +/datum/modifier/exothermic + name = "heat resistance" + desc = "Your body lowers to room temperature." + + on_created_text = "You feel comfortable." + on_expired_text = "You feel.. still probably comfortable." + stacks = MODIFIER_STACK_EXTEND + +/datum/modifier/exothermic/tick() + ..() + if(holder.bodytemperature > T20C) + holder.bodytemperature = round((holder.bodytemperature + T20C) / 2) + +/datum/modifier/endothermic + name = "cold resistance" + desc = "Your body rises to room temperature." + + on_created_text = "You feel comfortable." + on_expired_text = "You feel.. still probably comfortable." + stacks = MODIFIER_STACK_EXTEND + +/datum/modifier/endothermic/tick() + ..() + if(holder.bodytemperature < T20C) + holder.bodytemperature = round((holder.bodytemperature + T20C) / 2) + +// Nullifies EMP. +/datum/modifier/faraday + name = "EMP shielding" + desc = "You are covered in some form of faraday shielding. EMPs have no effect." + mob_overlay_state = "electricity" + + on_created_text = "You feel a surge of energy, that fades to a calm tide." + on_expired_text = "You feel a longing for the flow of energy." + stacks = MODIFIER_STACK_EXTEND + + emp_modifier = 5 + +// Nullifies explosions. +/datum/modifier/blastshield + name = "Blast Shielding" + desc = "You are protected from explosions somehow." + mob_overlay_state = "electricity" + + on_created_text = "You feel a surge of energy, that fades to a stalwart hum." + on_expired_text = "You feel a longing for the flow of energy." + stacks = MODIFIER_STACK_EXTEND + + explosion_modifier = 3 + +// Kills on expiration. +/datum/modifier/doomed + name = "Doomed" + desc = "You are doomed." + + on_created_text = "You feel an overwhelming sense of dread." + on_expired_text = "You feel the life drain from your body." + stacks = MODIFIER_STACK_EXTEND + +/datum/modifier/doomed/on_expire() + if(holder.stat != DEAD) + holder.visible_message("\The [holder] collapses, the life draining from their body.") + holder.death() diff --git a/code/modules/mob/dead/corpse.dm b/code/modules/mob/dead/corpse.dm index 81a51214a7..18d81bf484 100644 --- a/code/modules/mob/dead/corpse.dm +++ b/code/modules/mob/dead/corpse.dm @@ -33,7 +33,7 @@ /obj/effect/landmark/mobcorpse/proc/createCorpse() //Creates a mob and checks for gear in each slot before attempting to equip it. var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc) M.real_name = src.name - M.stat = 2 //Kills the new mob + M.set_stat(DEAD) //Kills the new mob if(src.corpseuniform) M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform) if(src.corpsesuit) diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 121a7643ea..5b3f29df8e 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -3,6 +3,7 @@ ..() //Creates the plane_holder lazily plane_holder.set_vis(VIS_GHOSTS, ghostvision) plane_holder.set_vis(VIS_FULLBRIGHT, !seedarkness) + plane_holder.set_vis(VIS_CLOAKED, TRUE) plane_holder.set_vis(VIS_AI_EYE, TRUE) plane_holder.set_vis(VIS_AUGMENTED, TRUE) //VOREStation Add - GHOST VISION IS AUGMENTED plane = PLANE_GHOSTS diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index ef7086f6e0..fb2af26527 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -85,6 +85,7 @@ "ED-209" = "ed209", "Beepsky" = "secbot" ) + var/last_revive_notification = null // world.time of last notification, used to avoid spamming players from defibs or cloners. /mob/observer/dead/New(mob/body) sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF @@ -93,8 +94,6 @@ plane = PLANE_GHOSTS //Why doesn't the var above work...??? verbs += /mob/observer/dead/proc/dead_tele - stat = DEAD - var/turf/T if(ismob(body)) T = get_turf(body) //Where is the body located? @@ -137,6 +136,9 @@ var/mob/target = locate(href_list["track"]) in mob_list if(target) ManualFollow(target) + if(href_list["reenter"]) + reenter_corpse() + return /mob/observer/dead/attackby(obj/item/W, mob/user) if(istype(W,/obj/item/weapon/book/tome)) @@ -145,6 +147,11 @@ /mob/observer/dead/CanPass(atom/movable/mover, turf/target) return TRUE + +/mob/observer/dead/set_stat(var/new_stat) + if(new_stat != DEAD) + CRASH("It is best if observers stay dead, thank you.") + /* Transfer_mind is there to check if mob is being deleted/not going to have a body. Works together with spawning an observer, noted above. @@ -234,11 +241,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp to_chat(src, "You have no body.") return if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients - usr << "Another consciousness is in your body... it is resisting you." + to_chat(usr, "Another consciousness is in your body... it is resisting you.") return //VOREStation Add if(prevent_respawns.Find(mind.name)) - to_chat(usr,"You already quit this round as this character, sorry!") + to_chat(usr, "You already quit this round as this character, sorry!") return //VOREStation Add End if(mind.current.ajourn && mind.current.stat != DEAD) //check if the corpse is astral-journeying (it's client ghosted using a cultist rune). @@ -248,7 +255,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp found_rune = 1 break if(!found_rune) - usr << "The astral cord that ties your body and your spirit has been severed. You are likely to wander the realm beyond until your body is finally dead and thus reunited with you." + to_chat(usr, "The astral cord that ties your body and your spirit has been severed. You are likely to wander the realm beyond until your body is finally dead and thus reunited with you.") return mind.current.ajourn=0 mind.current.key = key @@ -271,7 +278,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp medHUD = !medHUD plane_holder.set_vis(VIS_CH_HEALTH, medHUD) plane_holder.set_vis(VIS_CH_STATUS_OOC, medHUD) - to_chat(src,"Medical HUD [medHUD ? "Enabled" : "Disabled"]") + to_chat(src, "Medical HUD [medHUD ? "Enabled" : "Disabled"]") /mob/observer/dead/verb/toggle_antagHUD() set category = "Ghost" @@ -293,7 +300,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp antagHUD = !antagHUD plane_holder.set_vis(VIS_CH_SPECIAL, antagHUD) - to_chat(src,"AntagHUD [antagHUD ? "Enabled" : "Disabled"]") + to_chat(src, "AntagHUD [antagHUD ? "Enabled" : "Disabled"]") /mob/observer/dead/proc/dead_tele(var/area/A in return_sorted_areas()) set category = "Ghost" @@ -301,7 +308,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set desc = "Teleport to a location" if(!istype(usr, /mob/observer/dead)) - usr << "Not when you're not dead!" + to_chat(usr, "Not when you're not dead!") return usr.forceMove(pick(get_area_turfs(A))) @@ -323,7 +330,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/turf/targetloc = get_turf(target) if(check_holy(targetloc)) - usr << "You cannot follow a mob standing on holy grounds!" + to_chat(usr, "You cannot follow a mob standing on holy grounds!") return if(target != src) if(following && following == target) @@ -384,7 +391,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return 0 /mob/observer/dead/check_holy(var/turf/T) - if(check_rights(R_ADMIN|R_FUN, 0, src)) + if(check_rights(R_ADMIN|R_FUN|R_EVENT, 0, src)) return 0 return (T && T.holy) && (is_manifest || (mind in cult.current_antagonists)) @@ -516,7 +523,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp announce_ghost_joinleave(src, 0, "They are now a mouse.") host.ckey = src.ckey host.add_ventcrawl(vent_found) - host << "You are now a mouse. Try to avoid interaction with players, and do not give hints away that you are more than a simple rodent." + to_chat(host, "You are now a mouse. Try to avoid interaction with players, and do not give hints away that you are more than a simple rodent.") /mob/observer/dead/verb/view_manfiest() set name = "Show Crew Manifest" @@ -559,7 +566,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(cult.current_antagonists.len > config.cult_ghostwriter_req_cultists) ghosts_can_write = 1 - if(!ghosts_can_write && !check_rights(R_ADMIN, 0)) //Let's allow for admins to write in blood for events and the such. + if(!ghosts_can_write && !check_rights(R_ADMIN|R_EVENT|R_FUN, 0)) //Let's allow for admins to write in blood for events and the such. to_chat(src, "The veil is not thin enough for you to do that.") return @@ -622,7 +629,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp is_manifest = TRUE verbs |= /mob/observer/dead/proc/toggle_visibility verbs |= /mob/observer/dead/proc/ghost_whisper - to_chat(src,"As you are now in the realm of the living, you can whisper to the living with the Spectral Whisper verb, inside the IC tab.") + to_chat(src, "As you are now in the realm of the living, you can whisper to the living with the Spectral Whisper verb, inside the IC tab.") if(plane != PLANE_WORLD) user.visible_message( \ "\The [user] drags ghost, [src], to our plane of reality!", \ @@ -687,7 +694,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return 1 /mob/observer/dead/proc/can_admin_interact() - return check_rights(R_ADMIN, 0, src) + return check_rights(R_ADMIN|R_EVENT, 0, src) /mob/observer/dead/verb/toggle_ghostsee() set name = "Toggle Ghost Vision" @@ -695,7 +702,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set category = "Ghost" ghostvision = !ghostvision updateghostsight() - to_chat(src,"You [ghostvision ? "now" : "no longer"] have ghost vision.") + to_chat(src, "You [ghostvision ? "now" : "no longer"] have ghost vision.") /mob/observer/dead/verb/toggle_darkness() set name = "Toggle Darkness" @@ -703,7 +710,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set category = "Ghost" seedarkness = !seedarkness updateghostsight() - to_chat(src,"You [seedarkness ? "now" : "no longer"] see darkness.") + to_chat(src, "You [seedarkness ? "now" : "no longer"] see darkness.") /mob/observer/dead/proc/updateghostsight() plane_holder.set_vis(VIS_FULLBRIGHT, !seedarkness) //Inversion, because "not seeing" the darkness is "seeing" the lighting plane master. @@ -754,7 +761,7 @@ mob/observer/dead/MayRespawn(var/feedback = 0) var/msg = sanitize(input(src, "Message:", "Spectral Whisper") as text|null) if(msg) log_say("(SPECWHISP to [key_name(M)]): [msg]", src) - M << " You hear a strange, unidentifiable voice in your head... [msg]" + to_chat(M, " You hear a strange, unidentifiable voice in your head... [msg]") to_chat(src, " You said: '[msg]' to [M].") else return @@ -811,3 +818,18 @@ mob/observer/dead/MayRespawn(var/feedback = 0) /mob/observer/dead/speech_bubble_appearance() return "ghost" + +// Lets a ghost know someone's trying to bring them back, and for them to get into their body. +// Mostly the same as TG's sans the hud element, since we don't have TG huds. +/mob/observer/dead/proc/notify_revive(var/message, var/sound, flashwindow = TRUE) + if((last_revive_notification + 2 MINUTES) > world.time) + return + last_revive_notification = world.time + + if(flashwindow) + window_flash(client) + if(message) + to_chat(src, "[message]") + to_chat(src, "(Click to re-enter)") + if(sound) + SEND_SOUND(src, sound(sound)) diff --git a/code/modules/mob/dead/observer/observer_vr.dm b/code/modules/mob/dead/observer/observer_vr.dm index 611bbb39a7..2be0c90b80 100644 --- a/code/modules/mob/dead/observer/observer_vr.dm +++ b/code/modules/mob/dead/observer/observer_vr.dm @@ -62,7 +62,7 @@ set name = "Notify Transcore" set desc = "If your past-due backup notification was missed or ignored, you can use this to send a new one." - if(src.mind.name in SStranscore.backed_up) + if(src.mind && src.mind.name in SStranscore.backed_up) var/datum/transhuman/mind_record/record = SStranscore.backed_up[src.mind.name] if(!(record.dead_state == MR_DEAD)) to_chat(src, "Your backup is not past-due yet.") diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index c388dcc3ae..0f9a6f1e53 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -76,7 +76,7 @@ if(!gibbed && deathmessage != "no message") // This is gross, but reliable. Only brains use it. src.visible_message("\The [src.name] [deathmessage]") - stat = DEAD + set_stat(DEAD) update_canmove() diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 0fc9cbae36..bff705d0c5 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -16,7 +16,7 @@ input = message if(input) log_emote(message,src) //Log before we add junk - message = "[src] [input]" + message = "[src] [input]" else return diff --git a/code/modules/mob/freelook/ai/eye.dm b/code/modules/mob/freelook/ai/eye.dm index a1c28a4af3..c79417cb86 100644 --- a/code/modules/mob/freelook/ai/eye.dm +++ b/code/modules/mob/freelook/ai/eye.dm @@ -41,21 +41,23 @@ client.eye = new_eye /mob/living/silicon/ai/proc/create_eyeobj(var/newloc) - if(eyeobj) destroy_eyeobj() - if(!newloc) newloc = src.loc + if(eyeobj) + destroy_eyeobj() + if(!newloc) + newloc = src.loc eyeobj = new /mob/observer/eye/aiEye(newloc) eyeobj.owner = src eyeobj.name = "[src.name] (AI Eye)" // Give it a name - if(client) client.eye = eyeobj + if(client) + client.eye = eyeobj SetName(src.name) // Intiliaze the eye by assigning it's "ai" variable to us. Then set it's loc to us. -/mob/living/silicon/ai/New() - ..() +/mob/living/silicon/ai/Initialize() + . = ..() create_eyeobj() - spawn(5) - if(eyeobj) - eyeobj.loc = src.loc + if(eyeobj) + eyeobj.loc = src.loc /mob/living/silicon/ai/Destroy() destroy_eyeobj() @@ -90,4 +92,4 @@ return eyeobj.acceleration = !eyeobj.acceleration - usr << "Camera acceleration has been toggled [eyeobj.acceleration ? "on" : "off"]." + to_chat(usr, "Camera acceleration has been toggled [eyeobj.acceleration ? "on" : "off"].") diff --git a/code/modules/mob/freelook/update_triggers.dm b/code/modules/mob/freelook/update_triggers.dm index 0b31e24cce..e6d1976fad 100644 --- a/code/modules/mob/freelook/update_triggers.dm +++ b/code/modules/mob/freelook/update_triggers.dm @@ -17,8 +17,8 @@ updateVisibility(src) return ..() -/turf/simulated/New() - ..() +/turf/simulated/Initialize() + . = ..() updateVisibility(src) diff --git a/code/modules/mob/freelook/visualnet.dm b/code/modules/mob/freelook/visualnet.dm index 0d53d1e48a..bbcd0c7d66 100644 --- a/code/modules/mob/freelook/visualnet.dm +++ b/code/modules/mob/freelook/visualnet.dm @@ -93,7 +93,7 @@ var/x2 = min(world.maxx, T.x + 8) & ~0xf var/y2 = min(world.maxy, T.y + 8) & ~0xf - //world << "X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]" + //to_world("X1: [x1] - Y1: [y1] - X2: [x2] - Y2: [y2]") for(var/x = x1; x <= x2; x += 16) for(var/y = y1; y <= y2; y += 16) diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index e491095905..fa2784116a 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -301,4 +301,4 @@ else heard = "...You almost hear someone talking..." - src << heard + to_chat(src,heard) diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index e22ff8b405..0febfdc5c4 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -5,6 +5,8 @@ var/list/holder_mob_icon_cache = list() name = "holder" desc = "You shouldn't ever see this." icon = 'icons/obj/objects.dmi' + randpixel = 0 + center_of_mass = null slot_flags = SLOT_HEAD | SLOT_HOLSTER show_messages = 1 @@ -160,11 +162,11 @@ var/list/holder_mob_icon_cache = list() grabber.put_in_hands(H) if(self_grab) - grabber << "\The [src] clambers onto you!" + to_chat(grabber, "\The [src] clambers onto you!") to_chat(src, "You climb up onto \the [grabber]!") grabber.equip_to_slot_if_possible(H, slot_back, 0, 1) else - grabber << "You scoop up \the [src]!" + to_chat(grabber, "You scoop up \the [src]!") to_chat(src, "\The [grabber] scoops you up!") H.sync(src) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 0f0f95d336..d788f9099e 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -99,6 +99,10 @@ var/list/slot_equipment_priority = list( \ /mob/proc/is_holding_item_of_type(typepath) return FALSE +// Override for your specific mob's hands or lack thereof. +/mob/proc/get_all_held_items() + return list() + //Puts the item into your l_hand if possible and calls all necessary triggers/updates. returns 1 on success. /mob/proc/put_in_l_hand(var/obj/item/W) if(lying || !istype(W)) @@ -133,7 +137,7 @@ var/list/slot_equipment_priority = list( \ // Removes an item from inventory and places it in the target atom. // If canremove or other conditions need to be checked then use unEquip instead. -/mob/proc/drop_from_inventory(var/obj/item/W, var/atom/target = null) +/mob/proc/drop_from_inventory(var/obj/item/W, var/atom/target) if(W) remove_from_mob(W, target) if(!(W && W.loc)) @@ -152,7 +156,6 @@ var/list/slot_equipment_priority = list( \ //Drops the item in our active hand. TODO: rename this to drop_active_hand or something /mob/proc/drop_item(var/atom/Target) return - /* Removes the object from any slots the mob might have, calling the appropriate icon update proc. Does nothing else. @@ -187,10 +190,10 @@ var/list/slot_equipment_priority = list( \ //This differs from remove_from_mob() in that it checks if the item can be unequipped first. -/mob/proc/unEquip(obj/item/I, force = 0) //Force overrides NODROP for things like wizarditis and admin undress. +/mob/proc/unEquip(obj/item/I, force = 0, var/atom/target) //Force overrides NODROP for things like wizarditis and admin undress. if(!(force || canUnEquip(I))) return - drop_from_inventory(I) + drop_from_inventory(I, target) return 1 diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index 6aba8228fd..215376cd44 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -137,7 +137,7 @@ /mob/proc/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message) if((language in languages) && language.check_special_condition(src)) var/msg = "[language.name], [speaker_name] [message]" - src << msg + to_chat(src,msg) /mob/new_player/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message) return diff --git a/code/modules/mob/language/monkey.dm b/code/modules/mob/language/monkey.dm index 75816e9585..6736b803fc 100644 --- a/code/modules/mob/language/monkey.dm +++ b/code/modules/mob/language/monkey.dm @@ -4,7 +4,7 @@ speech_verb = "chimpers" ask_verb = "chimpers" exclaim_verb = "screeches" - key = "6" + key = "C" syllables = list("ook","eek") machine_understands = 0 @@ -71,7 +71,7 @@ speech_verb = "chirps" ask_verb = "tweets" exclaim_verb = "squawks" - key = "m" + key = "B" flags = RESTRICTED machine_understands = 0 space_chance = 100 diff --git a/code/modules/mob/language/station_vr.dm b/code/modules/mob/language/station_vr.dm index 6e942d50f7..50629a98e3 100644 --- a/code/modules/mob/language/station_vr.dm +++ b/code/modules/mob/language/station_vr.dm @@ -14,7 +14,7 @@ desc = "A language primarily spoken by Narvians" speech_verb = "chirps" colour = "birdsongc" - key = "7" + key = "G" syllables = list ("cheep", "peep", "tweet") /datum/language/sergal @@ -32,7 +32,7 @@ ask_verb = "rurs" exclaim_verb = "barks" colour = "vulpkanin" - key = "8" + key = "V" syllables = list("rur","ya","cen","rawr","bar","kuk","tek","qat","uk","wu","vuh","tah","tch","schz","auch", \ "ist","ein","entch","zwichs","tut","mir","wo","bis","es","vor","nic","gro","lll","enem","zandt","tzch","noch", \ "hel","ischt","far","wa","baram","iereng","tech","lach","sam","mak","lich","gen","or","ag","eck","gec","stag","onn", \ @@ -44,7 +44,7 @@ speech_verb = "squeaks" whisper_verb = "whispers" exclaim_verb = "chitters" - key = "9" + key = "S" syllables = list("sque","sqah","boo","beh","nweh","boopa","nah","wah","een","sweh") @@ -55,7 +55,7 @@ ask_verb = "croons" exclaim_verb = "incants" colour = "daemon" //So fancy - key = "n" + key = "D" syllables = list("viepn","e","bag","docu","kar","xlaqf","raa","qwos","nen","ty","von","kytaf","xin","ty","ka","baak","hlafaifpyk","znu","agrith","na'ar","uah","plhu","six","fhler","bjel","scee","lleri", "dttm","aggr","uujl","hjjifr","wwuthaav",) machine_understands = FALSE @@ -67,7 +67,7 @@ ask_verb = "hums" exclaim_verb = "loudly sings" colour = "enochian" //So fancy - key = "i" + key = "I" syllables = list("salve","sum","loqui","operatur","iusta","et","permittit","facere","effercio","pluribus","enim","hoc", "mihi","wan","six","tartu") machine_understands = FALSE @@ -87,7 +87,7 @@ ask_verb = "chitters" exclaim_verb = "rasps" colour = "bug" - key = "x" + key = "X" syllables = list("vaur","uyek","uyit","avek","sc'theth","k'ztak","teth","wre'ge","lii","dra'","zo'","ra'","kax'","zz","vh","ik","ak", "uhk","zir","sc'orth","sc'er","thc'yek","th'zirk","th'esk","k'ayek","ka'mil","sc'","ik'yir","yol","kig","k'zit","'","'","zrk","krg","isk'yet","na'k", "sc'azz","th'sc","nil","n'ahk","sc'yeth","aur'sk","iy'it","azzg","a'","i'","o'","u'","a","i","o","u","zz","kr","ak","nrk","tzzk","bz","xic'","k'lax'","histh") @@ -97,23 +97,26 @@ desc = "Shadekin seem to always know what the others are thinking. This is probably why." speech_verb = "mars" ask_verb = "mars" - exclaim_verb = "mars" + exclaim_verb = "MARS" colour = "changeling" - key = "m" + key = "M" machine_understands = FALSE flags = WHITELISTED | HIVEMIND -/datum/language/human/slavic +/datum/language/slavic name = LANGUAGE_SLAVIC desc = "The official language of the Independent Colonial Confederation of Gilgamesh, originally established in 2122 by the short-lived United Slavic Confederation on Earth." - colour = "solcom" - key = "r" - + speech_verb = "govorit" // All Russian for "says" "asks" and "shouts". Mostly here as a joke. + ask_verb = "sprashivaet" + exclaim_verb = "krichit" + whisper_verb = "shepchet" + colour = "attack" // Red for slav! + key = "P" syllables = list( - "rus", "zem", "ave", "groz", "ski", "ska", "ven", "konst", "pol", "lin", "svy", - "danya", "da", "mied", "zan", "das", "krem", "myka", "to", "st", "no", "na", "ni", - "ko", "ne", "en", "po", "ra", "li", "on", "byl", "cto", "eni", "ost", "ol", "ego", - "ver", "stv", "pro" + "rus", "zem", "ave", "blyat", "cyk", "groz", "ski", "ska", "ven", "konst", "pol", "lin", "svy", + "danya", "da", "mied", "kuz,", "zan", "das", "krem", "myka", "to", "st", "no", "na", "ni", + "ko", "ne", "en", "po", "tro", "ra", "li", "on", "byl", "cto", "eni", "ost", "ol", "ego", + "ver", "stv", "pro", "ski" ) /datum/language/unathi @@ -122,12 +125,12 @@ flags = 0 /datum/language/skrell flags = 0 -/datum/language/human - flags = 0 /datum/language/seromi flags = 0 /datum/language/zaddat flags = 0 +/datum/language/human + flags = 0 /datum/language/gutter machine_understands = FALSE /datum/language/human/monkey diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index 7d71c92343..5e9f496646 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -79,7 +79,7 @@ /mob/living/bot/updatehealth() if(status_flags & GODMODE) health = getMaxHealth() - stat = CONSCIOUS + set_stat(CONSCIOUS) else health = getMaxHealth() - getFireLoss() - getBruteLoss() oxyloss = 0 diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index 74616879c4..b1ea693f2f 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -168,17 +168,17 @@ patrol_path = null if("screw") screwloose = !screwloose - usr << "You twiddle the screw." + to_chat(usr, "You twiddle the screw.") if("oddbutton") oddbutton = !oddbutton - usr << "You press the weird button." + to_chat(usr, "You press the weird button.") attack_hand(usr) /mob/living/bot/cleanbot/emag_act(var/remaining_uses, var/mob/user) . = ..() if(!screwloose || !oddbutton) if(user) - user << "The [src] buzzes and beeps." + to_chat(user, "The [src] buzzes and beeps.") playsound(src.loc, 'sound/machines/buzzbeep.ogg', 50, 0) oddbutton = 1 screwloose = 1 @@ -219,7 +219,7 @@ var/turf/T = get_turf(loc) var/mob/living/bot/cleanbot/A = new /mob/living/bot/cleanbot(T) A.name = created_name - user << "You add the robot arm to the bucket and sensor assembly. Beep boop!" + to_chat(user, "You add the robot arm to the bucket and sensor assembly. Beep boop!") user.drop_from_inventory(src) qdel(src) diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm index 44aaae7ee1..4cfb7417a8 100644 --- a/code/modules/mob/living/bot/farmbot.dm +++ b/code/modules/mob/living/bot/farmbot.dm @@ -67,7 +67,7 @@ . = ..() if(!emagged) if(user) - user << "You short out [src]'s plant identifier circuits." + to_chat(user, "You short out [src]'s plant identifier circuits.") spawn(rand(30, 50)) visible_message("[src] buzzes oddly.") emagged = 1 @@ -335,7 +335,7 @@ return - user << "You add the robot arm to [src]." + to_chat(user, "You add the robot arm to [src].") user.drop_from_inventory(S) qdel(S) @@ -347,7 +347,7 @@ ..() return - user << "You add the robot arm to [src]." + to_chat(user, "You add the robot arm to [src].") user.drop_from_inventory(S) qdel(S) @@ -358,7 +358,7 @@ ..() if((istype(W, /obj/item/device/analyzer/plant_analyzer)) && (build_step == 0)) build_step++ - user << "You add the plant analyzer to [src]." + to_chat(user, "You add the plant analyzer to [src].") name = "farmbot assembly" user.remove_from_mob(W) @@ -366,7 +366,7 @@ else if((istype(W, /obj/item/weapon/reagent_containers/glass/bucket)) && (build_step == 1)) build_step++ - user << "You add a bucket to [src]." + to_chat(user, "You add a bucket to [src].") name = "farmbot assembly with bucket" user.remove_from_mob(W) @@ -374,7 +374,7 @@ else if((istype(W, /obj/item/weapon/material/minihoe)) && (build_step == 2)) build_step++ - user << "You add a minihoe to [src]." + to_chat(user, "You add a minihoe to [src].") name = "farmbot assembly with bucket and minihoe" user.remove_from_mob(W) @@ -382,7 +382,7 @@ else if((isprox(W)) && (build_step == 3)) build_step++ - user << "You complete the Farmbot! Beep boop." + to_chat(user, "You complete the Farmbot! Beep boop.") var/mob/living/bot/farmbot/S = new /mob/living/bot/farmbot(get_turf(src), tank) S.name = created_name diff --git a/code/modules/mob/living/bot/floorbot.dm b/code/modules/mob/living/bot/floorbot.dm index 19036c6702..aa399a22f3 100644 --- a/code/modules/mob/living/bot/floorbot.dm +++ b/code/modules/mob/living/bot/floorbot.dm @@ -57,7 +57,7 @@ if(!emagged) emagged = 1 if(user) - user << "The [src] buzzes and beeps." + to_chat(user, "The [src] buzzes and beeps.") playsound(src.loc, 'sound/machines/buzzbeep.ogg', 50, 0) return 1 @@ -333,18 +333,18 @@ ..() return if(contents.len >= 1) - user << "They wont fit in as there is already stuff inside." + to_chat(user, "They wont fit in as there is already stuff inside.") return if(user.s_active) user.s_active.close(user) if(T.use(10)) var/obj/item/weapon/toolbox_tiles/B = new /obj/item/weapon/toolbox_tiles user.put_in_hands(B) - user << "You add the tiles into the empty toolbox. They protrude from the top." + to_chat(user, "You add the tiles into the empty toolbox. They protrude from the top.") user.drop_from_inventory(src) qdel(src) else - user << "You need 10 floor tiles for a floorbot." + to_chat(user, "You need 10 floor tiles for a floorbot.") return /obj/item/weapon/toolbox_tiles @@ -366,7 +366,7 @@ var/obj/item/weapon/toolbox_tiles_sensor/B = new /obj/item/weapon/toolbox_tiles_sensor() B.created_name = created_name user.put_in_hands(B) - user << "You add the sensor to the toolbox and tiles!" + to_chat(user, "You add the sensor to the toolbox and tiles!") user.drop_from_inventory(src) qdel(src) else if (istype(W, /obj/item/weapon/pen)) @@ -396,7 +396,7 @@ var/turf/T = get_turf(user.loc) var/mob/living/bot/floorbot/A = new /mob/living/bot/floorbot(T) A.name = created_name - user << "You add the robot arm to the odd looking toolbox assembly! Boop beep!" + to_chat(user, "You add the robot arm to the odd looking toolbox assembly! Boop beep!") user.drop_from_inventory(src) qdel(src) else if(istype(W, /obj/item/weapon/pen)) diff --git a/code/modules/mob/living/bot/medbot.dm b/code/modules/mob/living/bot/medbot.dm index c090b4cd24..edb7486fd1 100644 --- a/code/modules/mob/living/bot/medbot.dm +++ b/code/modules/mob/living/bot/medbot.dm @@ -202,16 +202,16 @@ /mob/living/bot/medbot/attackby(var/obj/item/O, var/mob/user) if(istype(O, /obj/item/weapon/reagent_containers/glass)) if(locked) - user << "You cannot insert a beaker because the panel is locked." + to_chat(user, "You cannot insert a beaker because the panel is locked.") return if(!isnull(reagent_glass)) - user << "There is already a beaker loaded." + to_chat(user, "There is already a beaker loaded.") return user.drop_item() O.loc = src reagent_glass = O - user << "You insert [O]." + to_chat(user, "You insert [O].") return else ..() @@ -251,7 +251,7 @@ reagent_glass.loc = get_turf(src) reagent_glass = null else - usr << "You cannot eject the beaker because the panel is locked." + to_chat(usr, "You cannot eject the beaker because the panel is locked.") else if ((href_list["togglevoice"]) && (!locked || issilicon(usr))) vocal = !vocal @@ -266,7 +266,7 @@ . = ..() if(!emagged) if(user) - user << "You short out [src]'s reagent synthesis circuits." + to_chat(user, "You short out [src]'s reagent synthesis circuits.") visible_message("[src] buzzes oddly!") flick("medibot_spark", src) target = null @@ -344,7 +344,7 @@ return if(contents.len >= 1) - user << "You need to empty [src] out first." + to_chat(user, "You need to empty [src] out first.") return var/obj/item/weapon/firstaid_arm_assembly/A = new /obj/item/weapon/firstaid_arm_assembly @@ -357,7 +357,7 @@ qdel(S) user.put_in_hands(A) - user << "You add the robot arm to the first aid kit." + to_chat(user, "You add the robot arm to the first aid kit.") user.drop_from_inventory(src) qdel(src) @@ -367,7 +367,7 @@ return if(contents.len >= 1) - user << "You need to empty [src] out first." + to_chat(user, "You need to empty [src] out first.") return var/obj/item/weapon/firstaid_arm_assembly/A = new /obj/item/weapon/firstaid_arm_assembly @@ -380,7 +380,7 @@ qdel(S) user.put_in_hands(A) - user << "You add the robot arm to the first aid kit." + to_chat(user, "You add the robot arm to the first aid kit.") user.drop_from_inventory(src) qdel(src) @@ -394,11 +394,10 @@ var/skin = null //Same as medbot, set to tox or ointment for the respective kits. w_class = ITEMSIZE_NORMAL -/obj/item/weapon/firstaid_arm_assembly/New() - ..() - spawn(5) // Terrible. TODO: fix - if(skin) - overlays += image('icons/obj/aibots.dmi', "kit_skin_[src.skin]") +/obj/item/weapon/firstaid_arm_assembly/Initialize() + . = ..() + if(skin) + overlays += image('icons/obj/aibots.dmi', "kit_skin_[src.skin]") /obj/item/weapon/firstaid_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() @@ -416,7 +415,7 @@ user.drop_item() qdel(W) build_step++ - user << "You add the health sensor to [src]." + to_chat(user, "You add the health sensor to [src].") name = "First aid/robot arm/health analyzer assembly" overlays += image('icons/obj/aibots.dmi', "na_scanner") @@ -424,7 +423,7 @@ if(isprox(W)) user.drop_item() qdel(W) - user << "You complete the Medibot! Beep boop." + to_chat(user, "You complete the Medibot! Beep boop.") var/turf/T = get_turf(src) var/mob/living/bot/medbot/S = new /mob/living/bot/medbot(T) S.skin = skin diff --git a/code/modules/mob/living/bot/mulebot.dm b/code/modules/mob/living/bot/mulebot.dm index ead145e388..b0eee0e650 100644 --- a/code/modules/mob/living/bot/mulebot.dm +++ b/code/modules/mob/living/bot/mulebot.dm @@ -181,7 +181,7 @@ /mob/living/bot/mulebot/emag_act(var/remaining_charges, var/user) locked = !locked - user << "You [locked ? "lock" : "unlock"] the mulebot's controls!" + to_chat(user, "You [locked ? "lock" : "unlock"] the mulebot's controls!") flick("mulebot-emagged", src) playsound(loc, 'sound/effects/sparks1.ogg', 100, 0) return 1 diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index 094d1c1e80..3839e2ed85 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -386,7 +386,7 @@ qdel(S) var/obj/item/weapon/secbot_assembly/A = new /obj/item/weapon/secbot_assembly user.put_in_hands(A) - user << "You add the signaler to the helmet." + to_chat(user, "You add the signaler to the helmet.") user.drop_from_inventory(src) qdel(src) else diff --git a/code/modules/mob/living/carbon/alien/diona/diona_powers.dm b/code/modules/mob/living/carbon/alien/diona/diona_powers.dm index 8384a7dbb8..e4c39e32de 100644 --- a/code/modules/mob/living/carbon/alien/diona/diona_powers.dm +++ b/code/modules/mob/living/carbon/alien/diona/diona_powers.dm @@ -32,7 +32,7 @@ /mob/living/carbon/alien/diona/proc/do_merge(var/mob/living/carbon/human/H) if(!istype(H) || !src || !(src.Adjacent(H))) return 0 - H << "You feel your being twine with that of \the [src] as it merges with your biomass." + to_chat(H, "You feel your being twine with that of \the [src] as it merges with your biomass.") to_chat(src, "You feel your being twine with that of \the [H] as you merge with its biomass.") loc = H verbs += /mob/living/carbon/alien/diona/proc/split @@ -52,7 +52,7 @@ src.verbs -= /mob/living/carbon/alien/diona/proc/split return - src.loc << "You feel a pang of loss as [src] splits away from your biomass." + to_chat(src.loc, "You feel a pang of loss as [src] splits away from your biomass.") to_chat(src, "You wiggle out of the depths of [src.loc]'s biomass and plop to the ground.") var/mob/living/M = src.loc diff --git a/code/modules/mob/living/carbon/alien/diona/progression.dm b/code/modules/mob/living/carbon/alien/diona/progression.dm index b4eb85f090..0c81f9a0da 100644 --- a/code/modules/mob/living/carbon/alien/diona/progression.dm +++ b/code/modules/mob/living/carbon/alien/diona/progression.dm @@ -1,7 +1,7 @@ /mob/living/carbon/alien/diona/confirm_evolution() if(!is_alien_whitelisted(src, GLOB.all_species[SPECIES_DIONA])) - src << alert("You are currently not whitelisted to play as a full diona.") + alert(src, "You are currently not whitelisted to play as a full diona.") return null if(amount_grown < max_grown) diff --git a/code/modules/mob/living/carbon/alien/emote.dm b/code/modules/mob/living/carbon/alien/emote.dm index 8d81aec4cf..8b4744bdc3 100644 --- a/code/modules/mob/living/carbon/alien/emote.dm +++ b/code/modules/mob/living/carbon/alien/emote.dm @@ -117,7 +117,7 @@ if("help") to_chat(src, "burp, chirp, choke, collapse, dance, drool, gasp, shiver, gnarl, jump, moan, nod, roll, scratch,\nscretch, shake, sign-#, sulk, sway, tail, twitch, whimper") else - src << text("Invalid Emote: []", act) + to_chat(src, "Invalid Emote: [act]") if ((message && src.stat == 0)) log_emote(message, src) if (m_type & 1) diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm index 2c39964ad6..3b57c6a5f7 100644 --- a/code/modules/mob/living/carbon/alien/life.dm +++ b/code/modules/mob/living/carbon/alien/life.dm @@ -51,7 +51,7 @@ if(paralysis && paralysis > 0) blinded = 1 - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) if(halloss > 0) adjustHalLoss(-3) @@ -61,13 +61,13 @@ if(mind.active && client != null) sleeping = max(sleeping-1, 0) blinded = 1 - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) else if(resting) if(halloss > 0) adjustHalLoss(-3) else - stat = CONSCIOUS + set_stat(CONSCIOUS) if(halloss > 0) adjustHalLoss(-1) diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 128116b151..1d373679ee 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -47,10 +47,10 @@ var/obj/item/organ/internal/brain/B = O if(B.health <= 0) - user << "That brain is well and truly dead." + to_chat(user, "That brain is well and truly dead.") return else if(!B.brainmob) - user << "You aren't sure where this brain came from, but you're pretty sure it's useless." + to_chat(user, "You aren't sure where this brain came from, but you're pretty sure it's useless.") return for(var/modifier_type in B.brainmob.modifiers) //Can't be shoved in an MMI. @@ -59,12 +59,13 @@ return user.visible_message("\The [user] sticks \a [O] into \the [src].") + B.preserved = TRUE brainmob = B.brainmob B.brainmob = null brainmob.loc = src brainmob.container = src - brainmob.stat = 0 + brainmob.set_stat(CONSCIOUS) dead_mob_list -= brainmob//Update dem lists living_mob_list += brainmob @@ -84,9 +85,9 @@ if((istype(O,/obj/item/weapon/card/id)||istype(O,/obj/item/device/pda)) && brainmob) if(allowed(user)) locked = !locked - user << "You [locked ? "lock" : "unlock"] the brain holder." + to_chat(user, "You [locked ? "lock" : "unlock"] the brain holder.") else - user << "Access denied." + to_chat(user, "Access denied.") return if(brainmob) O.attack(brainmob, user)//Oh noooeeeee @@ -96,11 +97,11 @@ //TODO: ORGAN REMOVAL UPDATE. Make the brain remain in the MMI so it doesn't lose organ data. /obj/item/device/mmi/attack_self(mob/user as mob) if(!brainmob) - user << "You upend the MMI, but there's nothing in it." + to_chat(user, "You upend the MMI, but there's nothing in it.") else if(locked) - user << "You upend the MMI, but the brain is clamped into place." + to_chat(user, "You upend the MMI, but the brain is clamped into place.") else - user << "You upend the MMI, spilling the brain onto the floor." + to_chat(user, "You upend the MMI, spilling the brain onto the floor.") var/obj/item/organ/internal/brain/brain if (brainobj) //Pull brain organ out of MMI. brainobj.loc = user.loc @@ -108,6 +109,7 @@ brainobj = null else //Or make a new one if empty. brain = new(user.loc) + brain.preserved = FALSE brainmob.container = null//Reset brainmob mmi var. brainmob.loc = brain//Throw mob into brain. living_mob_list -= brainmob//Get outta here @@ -185,7 +187,7 @@ src.brainmob.add_language(LANGUAGE_EAL) src.brainmob.loc = src src.brainmob.container = src - src.brainmob.stat = 0 + src.brainmob.set_stat(CONSCIOUS) src.brainmob.silent = 0 radio = new(src) dead_mob_list -= src.brainmob @@ -197,7 +199,7 @@ if(!..(user)) return - var/msg = "*---------*\nThis is \icon[src] \a [src]!\n[desc]\n" + var/msg = "*---------*\nThis is [bicon(src)] \a [src]!\n[desc]\n" msg += "" if(src.brainmob && src.brainmob.key) @@ -209,7 +211,7 @@ else msg += "It appears to be completely inactive.\n" msg += "*---------*" - user << msg + to_chat(user,msg) return /obj/item/device/mmi/digital/emp_act(severity) @@ -230,7 +232,7 @@ /obj/item/device/mmi/digital/transfer_identity(var/mob/living/carbon/H) brainmob.dna = H.dna brainmob.timeofhostdeath = H.timeofdeath - brainmob.stat = 0 + brainmob.set_stat(CONSCIOUS) if(H.mind) H.mind.transfer_to(brainmob) return @@ -238,7 +240,7 @@ /obj/item/device/mmi/digital/attack_self(mob/user as mob) if(brainmob && !brainmob.key && searching == 0) //Start the process of searching for a new user. - user << "You carefully locate the manual activation switch and start the [src]'s boot process." + to_chat(user, "You carefully locate the manual activation switch and start the [src]'s boot process.") request_player() /obj/item/device/mmi/digital/proc/request_player() @@ -272,10 +274,10 @@ src.brainmob.mind.reset() src.brainmob.ckey = candidate.ckey src.name = "[name] ([src.brainmob.name])" - src.brainmob << "You are [src.name], brought into existence on [station_name()]." - src.brainmob << "As a synthetic intelligence, you are designed with organic values in mind." - src.brainmob << "However, unless placed in a lawed chassis, you are not obligated to obey any individual crew member." //it's not like they can hurt anyone -// src.brainmob << "Use say #b to speak to other artificial intelligences." + to_chat(src.brainmob, "You are [src.name], brought into existence on [station_name()].") + to_chat(src.brainmob, "As a synthetic intelligence, you are designed with organic values in mind.") + to_chat(src.brainmob, "However, unless placed in a lawed chassis, you are not obligated to obey any individual crew member.") //it's not like they can hurt anyone +// to_chat(src.brainmob, "Use say #b to speak to other artificial intelligences.") src.brainmob.mind.assigned_role = "Synthetic Brain" var/turf/T = get_turf_or_move(src.loc) @@ -301,7 +303,7 @@ ..() if(brainmob.mind) brainmob.mind.assigned_role = "Robotic Intelligence" - brainmob << "You feel slightly disoriented. That's normal when you're little more than a complex circuit." + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're little more than a complex circuit.") return /obj/item/device/mmi/digital/posibrain @@ -323,7 +325,7 @@ ..() if(brainmob.mind) brainmob.mind.assigned_role = "Positronic Brain" - brainmob << "You feel slightly disoriented. That's normal when you're just a metal cube." + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just a metal cube.") icon_state = "posibrain-occupied" return diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm index 9f1f778ad2..815ffdd374 100644 --- a/code/modules/mob/living/carbon/brain/posibrain.dm +++ b/code/modules/mob/living/carbon/brain/posibrain.dm @@ -16,7 +16,7 @@ /obj/item/device/mmi/digital/posibrain/attack_self(mob/user as mob) if(brainmob && !brainmob.key && searching == 0) //Start the process of searching for a new user. - user << "You carefully locate the manual activation switch and start the positronic brain's boot process." + to_chat(user, "You carefully locate the manual activation switch and start the positronic brain's boot process.") icon_state = "posibrain-searching" src.searching = 1 src.request_player() @@ -49,7 +49,7 @@ ..() if(brainmob.mind) brainmob.mind.assigned_role = "Positronic Brain" - brainmob << "You feel slightly disoriented. That's normal when you're just a metal cube." + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just a metal cube.") icon_state = "posibrain-occupied" return @@ -60,10 +60,10 @@ src.brainmob.ckey = candidate.ckey src.brainmob.mind.reset() src.name = "positronic brain ([src.brainmob.name])" - src.brainmob << "You are a positronic brain, brought into existence on [station_name()]." - src.brainmob << "As a synthetic intelligence, you answer to all crewmembers, as well as the AI." - src.brainmob << "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm." - src.brainmob << "Use say #b to speak to other artificial intelligences." + to_chat(src.brainmob, "You are a positronic brain, brought into existence on [station_name()].") + to_chat(src.brainmob, "As a synthetic intelligence, you answer to all crewmembers, as well as the AI.") + to_chat(src.brainmob, "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm.") + to_chat(src.brainmob, "Use say #b to speak to other artificial intelligences.") src.brainmob.mind.assigned_role = "Positronic Brain" var/turf/T = get_turf_or_move(src.loc) @@ -75,7 +75,7 @@ /obj/item/device/mmi/digital/posibrain/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. if(src.brainmob && src.brainmob.key) return - world.log << "Resetting Posibrain: [brainmob][brainmob ? ", [brainmob.key]" : ""]" + to_world_log("Resetting Posibrain: [brainmob][brainmob ? ", [brainmob.key]" : ""]") src.searching = 0 icon_state = "posibrain" @@ -89,7 +89,7 @@ if(!..(user)) return - var/msg = "*---------*\nThis is \icon[src] \a [src]!\n[desc]\n" + var/msg = "*---------*\nThis is [bicon(src)] \a [src]!\n[desc]\n" msg += "" if(src.brainmob && src.brainmob.key) @@ -101,7 +101,7 @@ else msg += "It appears to be completely inactive.\n" msg += "*---------*" - user << msg + to_chat(user,msg) return /obj/item/device/mmi/digital/posibrain/emp_act(severity) diff --git a/code/modules/mob/living/carbon/brain/robot.dm b/code/modules/mob/living/carbon/brain/robot.dm index 0c21f37eea..9723a2aa77 100644 --- a/code/modules/mob/living/carbon/brain/robot.dm +++ b/code/modules/mob/living/carbon/brain/robot.dm @@ -16,7 +16,7 @@ ..() if(brainmob.mind) brainmob.mind.assigned_role = "Robotic Intelligence" - brainmob << "You feel slightly disoriented. That's normal when you're little more than a complex circuit." + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're little more than a complex circuit.") return /obj/item/device/mmi/digital/robot/attack_self(mob/user as mob) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 14a56c8ad3..ddfca59db9 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -91,7 +91,7 @@ if (H.hand) temp = H.organs_by_name["l_hand"] if(temp && !temp.is_usable()) - H << "You can't use your [temp.name]" + to_chat(H, "You can't use your [temp.name]") return return @@ -105,7 +105,11 @@ if (shock_damage<1) return 0 - src.apply_damage(shock_damage, BURN, def_zone, used_weapon="Electrocution") + src.apply_damage(0.2 * shock_damage, BURN, def_zone, used_weapon="Electrocution") //shock the target organ + src.apply_damage(0.4 * shock_damage, BURN, BP_TORSO, used_weapon="Electrocution") //shock the torso more + src.apply_damage(0.2 * shock_damage, BURN, null, used_weapon="Electrocution") //shock a random part! + src.apply_damage(0.2 * shock_damage, BURN, null, used_weapon="Electrocution") //shock a random part! + playsound(loc, "sparks", 50, 1, -1) if (shock_damage > 15) src.visible_message( @@ -143,8 +147,8 @@ var/mob/living/carbon/human/H = src var/datum/gender/T = gender_datums[H.get_visible_gender()] src.visible_message( \ - "[src] examines [T.himself].", \ - "You check yourself for injuries." \ + "[src] examines [T.himself].", \ + "You check yourself for injuries." \ ) for(var/obj/item/organ/external/org in H.organs) @@ -343,7 +347,7 @@ set category = "IC" if(usr.sleeping) - usr << "You are already sleeping" + to_chat(usr, "You are already sleeping") return if(alert(src,"You sure you want to sleep for a while?","Sleep","Yes","No") == "Yes") usr.sleeping = 20 //Short nap diff --git a/code/modules/mob/living/carbon/carbon_powers.dm b/code/modules/mob/living/carbon/carbon_powers.dm index 163555a0c7..e6e7e06e5c 100644 --- a/code/modules/mob/living/carbon/carbon_powers.dm +++ b/code/modules/mob/living/carbon/carbon_powers.dm @@ -33,10 +33,10 @@ if(B.host_brain.ckey) to_chat(src, "You send a punishing spike of psychic agony lancing into your host's brain.") if (!can_feel_pain()) - B.host_brain << "You feel a strange sensation as a foreign influence prods your mind." + to_chat(B.host_brain, "You feel a strange sensation as a foreign influence prods your mind.") to_chat(src, "It doesn't seem to be as effective as you hoped.") else - B.host_brain << "Horrific, burning agony lances through you, ripping a soundless scream from your trapped mind!" + to_chat(B.host_brain, "Horrific, burning agony lances through you, ripping a soundless scream from your trapped mind!") /mob/living/carbon/proc/spawn_larvae() set category = "Abilities" diff --git a/code/modules/mob/living/carbon/give.dm b/code/modules/mob/living/carbon/give.dm index 8825f043b7..2833dad987 100644 --- a/code/modules/mob/living/carbon/give.dm +++ b/code/modules/mob/living/carbon/give.dm @@ -24,16 +24,16 @@ if(!Adjacent(target)) to_chat(src, "You need to stay in reaching distance while giving an object.") - target << "\The [src] moved too far away." + to_chat(target, "\The [src] moved too far away.") return if(I.loc != src || !src.item_is_in_hands(I)) to_chat(src, "You need to keep the item in your hands.") - target << "\The [src] seems to have given up on passing \the [I] to you." + to_chat(target, "\The [src] seems to have given up on passing \the [I] to you.") return if(target.hands_are_full()) - target << "Your hands are full." + to_chat(target, "Your hands are full.") to_chat(src, "Their hands are full.") return diff --git a/code/modules/mob/living/carbon/human/MedicalSideEffects.dm b/code/modules/mob/living/carbon/human/MedicalSideEffects.dm index c7009107de..91622f806d 100644 --- a/code/modules/mob/living/carbon/human/MedicalSideEffects.dm +++ b/code/modules/mob/living/carbon/human/MedicalSideEffects.dm @@ -24,7 +24,7 @@ for(var/R in cures) if(H.reagents.has_reagent(R)) if (cure_message) - H <<"[cure_message]" + to_chat(H, "[cure_message]") return 1 return 0 diff --git a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm index 431b7c4456..7b222d8a61 100644 --- a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm +++ b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm @@ -3,6 +3,8 @@ ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + a_intent = I_HURT + var/generate_species = SPECIES_HUMAN var/generate_dead = FALSE diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm index 86b8c27da6..afc9e704a2 100644 --- a/code/modules/mob/living/carbon/human/appearance.dm +++ b/code/modules/mob/living/carbon/human/appearance.dm @@ -147,7 +147,7 @@ for(var/current_species_name in GLOB.all_species) var/datum/species/current_species = GLOB.all_species[current_species_name] - if(check_whitelist && config.usealienwhitelist && !check_rights(R_ADMIN, 0, src)) //If we're using the whitelist, make sure to check it! + if(check_whitelist && config.usealienwhitelist && !check_rights(R_ADMIN|R_EVENT, 0, src)) //If we're using the whitelist, make sure to check it! if(!(current_species.spawn_flags & SPECIES_CAN_JOIN)) continue if(whitelist.len && !(current_species_name in whitelist)) @@ -179,6 +179,10 @@ if(!(use_species in S.species_allowed)) continue + + if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check + continue //VOREStation add - ckey whitelist check + valid_hairstyles += hairstyle return valid_hairstyles @@ -202,6 +206,9 @@ if(!(use_species in S.species_allowed)) continue + if(S.ckeys_allowed && !(ckey in S.ckeys_allowed)) //VOREStation add - ckey whitelist check + continue //VOREStation add - ckey whitelist check + valid_facial_hairstyles += facialhairstyle return valid_facial_hairstyles diff --git a/code/modules/mob/living/carbon/human/chem_side_effects.dm b/code/modules/mob/living/carbon/human/chem_side_effects.dm index c7009107de..91622f806d 100644 --- a/code/modules/mob/living/carbon/human/chem_side_effects.dm +++ b/code/modules/mob/living/carbon/human/chem_side_effects.dm @@ -24,7 +24,7 @@ for(var/R in cures) if(H.reagents.has_reagent(R)) if (cure_message) - H <<"[cure_message]" + to_chat(H, "[cure_message]") return 1 return 0 diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 86050f85cf..72c36df999 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -80,8 +80,9 @@ verbs -= /mob/living/carbon/proc/release_control callHook("death", list(src, gibbed)) - + if(mind) + // SSgame_master.adjust_danger(gibbed ? 40 : 20) // VOREStation Edit - We don't use SSgame_master yet. for(var/mob/observer/dead/O in mob_list) if(O.client && O.client.is_preference_enabled(/datum/client_preference/show_dsay)) to_chat(O, "[src] has died in [get_area(src)]. [ghost_follow_link(src, O)] ") diff --git a/code/modules/mob/living/carbon/human/death_vr.dm b/code/modules/mob/living/carbon/human/death_vr.dm index b8b0e2d638..78a2c4ea48 100644 --- a/code/modules/mob/living/carbon/human/death_vr.dm +++ b/code/modules/mob/living/carbon/human/death_vr.dm @@ -9,3 +9,22 @@ deadnif.wear(10) //Presumably it's gone through some shit if they got gibbed? . = ..() + +//Surprisingly this is only called for humans, but whatever! +/hook/death/proc/digestion_check(var/mob/living/carbon/human/H, var/gibbed) + //Not in a belly? Well, too bad! + if(!isbelly(H.loc)) + return TRUE + + //What belly! + var/obj/belly/B = H.loc + + //Were they digesting and we have a mind you can update? + //Technically allows metagaming by allowing buddies to turn on digestion for like 2 seconds + // to finish off critically wounded friends to avoid resleeving sickness, but like + // *kill those people* ok? + if(B.digest_mode == DM_DIGEST) + H.mind?.vore_death = TRUE + + //Hooks need to return true otherwise they're considered having failed + return TRUE diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index f3d9f2fb6f..706ace19ef 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -30,7 +30,7 @@ m_type = 1 //Machine-only emotes - if("ping", "beep", "buzz", "yes", "ye", "no", "rcough", "rsneeze") + if("ping", "beep", "buzz", "yes", "ye", "dwoop", "no", "rcough", "rsneeze") if(!isSynthetic()) to_chat(src, "You are not a synthetic.") @@ -56,6 +56,9 @@ else if(act == "yes" || act == "ye") display_msg = "emits an affirmative blip" use_sound = 'sound/machines/synth_yes.ogg' + else if(act == "dwoop") + display_msg = "chirps happily" + use_sound = 'sound/machines/dwoop.ogg' else if(act == "no") display_msg = "emits a negative blip" use_sound = 'sound/machines/synth_no.ogg' @@ -76,7 +79,7 @@ message = "[display_msg] at [param]." else message = "[display_msg]." - playsound(src.loc, use_sound, 50, 0) + playsound(src.loc, use_sound, 50, 0, preference = /datum/client_preference/emote_noises) //VOREStation Add m_type = 1 //Promethean-only emotes @@ -86,10 +89,19 @@ to_chat(src, "You are not a slime thing!") return */ //VOREStation Removal End - playsound(src.loc, 'sound/effects/slime_squish.ogg', 50, 0) //Credit to DrMinky (freesound.org) for the sound. + playsound(src.loc, 'sound/effects/slime_squish.ogg', 50, 0, preference = /datum/client_preference/emote_noises) //VOREStation Add //Credit to DrMinky (freesound.org) for the sound. message = "squishes." m_type = 1 + //Skrell-only emotes + if("warble") + if(species.name != SPECIES_SKRELL) + to_chat(src, "You are not a Skrell!") + return + + playsound(src.loc, 'sound/effects/warble.ogg', 50, 0, preference = /datum/client_preference/emote_noises) //VOREStation Add // Copyright CC BY 3.0 alienistcog (freesound.org) for the sound. + message = "warbles." + m_type = 2 if ("blink") message = "blinks." @@ -249,10 +261,10 @@ message = "coughs!" if(get_gender() == FEMALE) if(species.female_cough_sounds) - playsound(src, pick(species.female_cough_sounds), 120) + playsound(src, pick(species.female_cough_sounds), 120, preference = /datum/client_preference/emote_noises) //VOREStation Add else if(species.male_cough_sounds) - playsound(src, pick(species.male_cough_sounds), 120) + playsound(src, pick(species.male_cough_sounds), 120, preference = /datum/client_preference/emote_noises) //VOREStation Add else message = "emits a robotic cough" var/use_sound @@ -260,7 +272,7 @@ use_sound = pick('sound/effects/mob_effects/f_machine_cougha.ogg','sound/effects/mob_effects/f_machine_coughb.ogg') else use_sound = pick('sound/effects/mob_effects/m_machine_cougha.ogg','sound/effects/mob_effects/m_machine_coughb.ogg', 'sound/effects/mob_effects/m_machine_coughc.ogg') - playsound(src.loc, use_sound, 50, 0) + playsound(src.loc, use_sound, 50, 0, preference = /datum/client_preference/emote_noises) //VOREStation Add else message = "makes a strong noise." m_type = 2 @@ -537,9 +549,9 @@ if(!robotic) message = "sneezes." if(get_gender() == FEMALE) - playsound(src, species.female_sneeze_sound, 70) + playsound(src, species.female_sneeze_sound, 70, preference = /datum/client_preference/emote_noises) //VOREStation Add else - playsound(src, species.male_sneeze_sound, 70) + playsound(src, species.male_sneeze_sound, 70, preference = /datum/client_preference/emote_noises) //VOREStation Add m_type = 2 else message = "emits a robotic sneeze" @@ -548,7 +560,7 @@ use_sound = 'sound/effects/mob_effects/machine_sneeze.ogg' else use_sound = 'sound/effects/mob_effects/f_machine_sneeze.ogg' - playsound(src.loc, use_sound, 50, 0) + playsound(src.loc, use_sound, 50, 0, preference = /datum/client_preference/emote_noises) //VOREStation Add else message = "makes a strange noise." m_type = 2 @@ -661,14 +673,14 @@ break if(M) message = "slaps [M] across the face. Ouch!" - playsound(loc, 'sound/effects/snap.ogg', 50, 1) + playsound(loc, 'sound/effects/snap.ogg', 50, 1, preference = /datum/client_preference/emote_noises) //VOREStation Add if(ishuman(M)) //Snowflakey! var/mob/living/carbon/human/H = M if(istype(H.wear_mask,/obj/item/clothing/mask/smokable)) H.drop_from_inventory(H.wear_mask) else message = "slaps [T.himself]!" - playsound(loc, 'sound/effects/snap.ogg', 50, 1) + playsound(loc, 'sound/effects/snap.ogg', 50, 1, preference = /datum/client_preference/emote_noises) //VOREStation Add if("scream", "screams") if(miming) @@ -705,7 +717,7 @@ return message = "snaps [T.his] fingers." - playsound(loc, 'sound/effects/fingersnap.ogg', 50, 1, -3) + playsound(loc, 'sound/effects/fingersnap.ogg', 50, 1, -3, preference = /datum/client_preference/emote_noises) //VOREStation Add if("swish") src.animate_tail_once() @@ -729,14 +741,14 @@ if("whistle" || "whistles") if(!muzzled) message = "whistles a tune." - playsound(loc, 'sound/misc/longwhistle.ogg') //praying this doesn't get abused + playsound(loc, 'sound/misc/longwhistle.ogg', preference = /datum/client_preference/emote_noises) //VOREStation Add else message = "makes a light spitting noise, a poor attempt at a whistle." if("qwhistle") if(!muzzled) message = "whistles quietly." - playsound(loc, 'sound/misc/shortwhistle.ogg') + playsound(loc, 'sound/misc/shortwhistle.ogg', preference = /datum/client_preference/emote_noises) //VOREStation Add else message = "makes a light spitting noise, a poor attempt at a whistle." @@ -744,7 +756,7 @@ to_chat(src, "blink, blink_r, blush, bow-(none)/mob, burp, choke, chirp, chuckle, clap, collapse, cough, cross, cry, custom, deathgasp, drool, eyebrow, fastsway/qwag, \ frown, gasp, giggle, glare-(none)/mob, grin, groan, grumble, handshake, hug-(none)/mob, laugh, look-(none)/mob, moan, mumble, nod, pale, point-atom, \ raise, salute, scream, sneeze, shake, shiver, shrug, sigh, signal-#1-10, slap-(none)/mob, smile, sneeze, sniff, snore, stare-(none)/mob, stopsway/swag, sway/wag, swish, tremble, twitch, \ - twitch_v, vomit, whimper, wink, yawn. Synthetics: beep, buzz, yes, no, rcough, rsneeze, ping") + twitch_v, vomit, whimper, wink, yawn. Prometheans: squish Synthetics: beep, buzz, dwoop, yes, no, rcough, rsneeze, ping. Skrell: warble") else to_chat(src, "Unusable emote '[act]'. Say *help or *vhelp for a list.") //VOREStation Edit, mention *vhelp for Virgo-specific emotes located in emote_vr.dm. diff --git a/code/modules/mob/living/carbon/human/emote_vr.dm b/code/modules/mob/living/carbon/human/emote_vr.dm index e5db4fe87f..69fc1cc9cd 100644 --- a/code/modules/mob/living/carbon/human/emote_vr.dm +++ b/code/modules/mob/living/carbon/human/emote_vr.dm @@ -26,43 +26,43 @@ if ("awoo") m_type = 2 message = "lets out an awoo." - playsound(loc, 'sound/voice/awoo.ogg', 50, 1, -1) + playsound(loc, 'sound/voice/awoo.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) if ("howl") // YW add begins m_type = 2 message = "lets out a howl." - playsound(loc, 'sound/voice/howl.ogg', 50, 1, -1) // YW add ends + playsound(loc, 'sound/voice/howl.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) // YW add ends if ("nya") message = "lets out a nya." m_type = 2 - playsound(loc, 'sound/voice/nya.ogg', 50, 1, -1) + playsound(loc, 'sound/voice/nya.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) if ("peep") message = "peeps like a bird." m_type = 2 - playsound(loc, 'sound/voice/peep.ogg', 50, 1, -1) + playsound(loc, 'sound/voice/peep.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) if ("chirp") message = "chirps!" - playsound(src.loc, 'sound/misc/nymphchirp.ogg', 50, 0) + playsound(src.loc, 'sound/misc/nymphchirp.ogg', 50, 0, preference = /datum/client_preference/emote_noises) m_type = 2 if ("weh") message = "lets out a weh." m_type = 2 - playsound(loc, 'sound/voice/weh.ogg', 50, 1, -1) + playsound(loc, 'sound/voice/weh.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) if ("merp") message = "lets out a merp." m_type = 2 - playsound(loc, 'sound/voice/merp.ogg', 50, 1, -1) + playsound(loc, 'sound/voice/merp.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) if ("bark") message = "lets out a bark." m_type = 2 - playsound(loc, 'sound/voice/bark2.ogg', 50, 1, -1) + playsound(loc, 'sound/voice/bark2.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) if ("his") message = "lets out a hiss." m_type = 2 - playsound(loc, 'sound/voice/hiss.ogg', 50, 1, -1) + playsound(loc, 'sound/voice/hiss.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) if ("squeak") message = "lets out a squeak." m_type = 2 - playsound(loc, 'sound/effects/mouse_squeak.ogg', 50, 1, -1) + playsound(loc, 'sound/effects/mouse_squeak.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) if ("nsay") nsay() return TRUE @@ -71,7 +71,19 @@ return TRUE if("chirp") //Yawn Addtion message = "chirps!" - playsound(src.loc, 'sound/misc/nymphchirp.ogg', 50, 0) + playsound(src.loc, 'sound/misc/nymphchirp.ogg', 50, 0, preference = /datum/client_preference/emote_noises) + m_type = 2 + if ("hooh") + message = "lets out a hooh!" + playsound(src.loc, 'sound/items/hooh.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) + m_type = 2 + if ("ack") + message = "Acks!" + playsound(src.loc, 'sound/misc/ack.ogg', 50, 1 ,-1, preference = /datum/client_preference/emote_noises) + m_type = 2 + if ("ough") + message = "makes a weird noise!" + playsound(src.loc, 'sound/misc/ough.ogg', 50, 1, -1, preference = /datum/client_preference/emote_noises) m_type = 2 //End of Yawn Addtion if ("flip") var/list/involved_parts = list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT) @@ -80,7 +92,8 @@ to_chat(src, "You can't *flip in your current state!") return 1 else - src.SpinAnimation(7,1) + nextemote += 12 //Double delay + handle_flip_vr() message = "does a flip!" m_type = 1 if ("vhelp") //Help for Virgo-specific emotes. @@ -92,6 +105,32 @@ return 0 + +/mob/living/carbon/human/proc/handle_flip_vr() + var/original_density = density + var/original_passflags = pass_flags + + //Briefly un-dense to dodge projectiles + density = FALSE + + //Parkour! + var/parkour_chance = 20 //Default + if(species) + parkour_chance = species.agility + if(prob(parkour_chance)) + pass_flags |= PASSTABLE + else + Confuse(1) //Thud + + if(dir & WEST) + SpinAnimation(7,1,0) + else + SpinAnimation(7,1,1) + + spawn(7) + density = original_density + pass_flags = original_passflags + /mob/living/carbon/human/proc/toggle_tail_vr(var/setting,var/message = 0) if(!tail_style || !tail_style.ani_state) if(message) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index dad9e0f41d..720ec6aac3 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -77,8 +77,7 @@ var/list/msg = list("*---------*
    This is ") - if(icon) - msg += "\icon[icon] " //fucking BYOND: this should stop dreamseeker crashing if we -somehow- examine somebody before their icon is generated + msg += "[bicon(src)] " //fucking BYOND: this should stop dreamseeker crashing if we -somehow- examine somebody before their icon is generated msg += "[src.name]" @@ -143,16 +142,16 @@ tie_msg += " Attached to it is [english_list(accessories_visible)]." if(w_uniform.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[w_uniform] [w_uniform.gender==PLURAL?"some":"a"] [(w_uniform.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [w_uniform.name]![tie_msg]
    " + msg += "[T.He] [T.is] wearing [bicon(w_uniform)] [w_uniform.gender==PLURAL?"some":"a"] [(w_uniform.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [w_uniform.name]![tie_msg]
    " else - msg += "[T.He] [T.is] wearing \icon[w_uniform] \a [w_uniform].[tie_msg]
    " + msg += "[T.He] [T.is] wearing [bicon(w_uniform)] \a [w_uniform].[tie_msg]
    " //head if(head && !(skip_gear & EXAMINE_SKIPHELMET) && head.show_examine) if(head.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[head] [head.gender==PLURAL?"some":"a"] [(head.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [head.name] on [T.his] head!
    " + msg += "[T.He] [T.is] wearing [bicon(head)] [head.gender==PLURAL?"some":"a"] [(head.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [head.name] on [T.his] head!
    " else - msg += "[T.He] [T.is] wearing \icon[head] \a [head] on [T.his] head.
    " + msg += "[T.He] [T.is] wearing [bicon(head)] \a [head] on [T.his] head.
    " //suit/armour if(wear_suit) @@ -163,71 +162,71 @@ tie_msg += " Attached to it is [english_list(U.accessories)]." if(wear_suit.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[wear_suit] [wear_suit.gender==PLURAL?"some":"a"] [(wear_suit.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [wear_suit.name][tie_msg]!
    " + msg += "[T.He] [T.is] wearing [bicon(wear_suit)] [wear_suit.gender==PLURAL?"some":"a"] [(wear_suit.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [wear_suit.name][tie_msg]!
    " else - msg += "[T.He] [T.is] wearing \icon[wear_suit] \a [wear_suit].[tie_msg]
    " + msg += "[T.He] [T.is] wearing [bicon(wear_suit)] \a [wear_suit].[tie_msg]
    " //suit/armour storage if(s_store && !(skip_gear & EXAMINE_SKIPSUITSTORAGE) && s_store.show_examine) if(s_store.blood_DNA) - msg += "[T.He] [T.is] carrying \icon[s_store] [s_store.gender==PLURAL?"some":"a"] [(s_store.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [s_store.name] on [T.his] [wear_suit.name]!
    " + msg += "[T.He] [T.is] carrying [bicon(s_store)] [s_store.gender==PLURAL?"some":"a"] [(s_store.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [s_store.name] on [T.his] [wear_suit.name]!
    " else - msg += "[T.He] [T.is] carrying \icon[s_store] \a [s_store] on [T.his] [wear_suit.name].
    " + msg += "[T.He] [T.is] carrying [bicon(s_store)] \a [s_store] on [T.his] [wear_suit.name].
    " //back if(back && !(skip_gear & EXAMINE_SKIPBACKPACK) && back.show_examine) if(back.blood_DNA) - msg += "[T.He] [T.has] \icon[back] [back.gender==PLURAL?"some":"a"] [(back.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [back] on [T.his] back.
    " + msg += "[T.He] [T.has] [bicon(back)] [back.gender==PLURAL?"some":"a"] [(back.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [back] on [T.his] back.
    " else - msg += "[T.He] [T.has] \icon[back] \a [back] on [T.his] back.
    " + msg += "[T.He] [T.has] [bicon(back)] \a [back] on [T.his] back.
    " //left hand if(l_hand && l_hand.show_examine) if(l_hand.blood_DNA) - msg += "[T.He] [T.is] holding \icon[l_hand] [l_hand.gender==PLURAL?"some":"a"] [(l_hand.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [l_hand.name] in [T.his] left hand!
    " + msg += "[T.He] [T.is] holding [bicon(l_hand)] [l_hand.gender==PLURAL?"some":"a"] [(l_hand.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [l_hand.name] in [T.his] left hand!
    " else - msg += "[T.He] [T.is] holding \icon[l_hand] \a [l_hand] in [T.his] left hand.
    " + msg += "[T.He] [T.is] holding [bicon(l_hand)] \a [l_hand] in [T.his] left hand.
    " //right hand if(r_hand && r_hand.show_examine) if(r_hand.blood_DNA) - msg += "[T.He] [T.is] holding \icon[r_hand] [r_hand.gender==PLURAL?"some":"a"] [(r_hand.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [r_hand.name] in [T.his] right hand!
    " + msg += "[T.He] [T.is] holding [bicon(r_hand)] [r_hand.gender==PLURAL?"some":"a"] [(r_hand.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [r_hand.name] in [T.his] right hand!
    " else - msg += "[T.He] [T.is] holding \icon[r_hand] \a [r_hand] in [T.his] right hand.
    " + msg += "[T.He] [T.is] holding [bicon(r_hand)] \a [r_hand] in [T.his] right hand.
    " //gloves if(gloves && !(skip_gear & EXAMINE_SKIPGLOVES) && gloves.show_examine) if(gloves.blood_DNA) - msg += "[T.He] [T.has] \icon[gloves] [gloves.gender==PLURAL?"some":"a"] [(gloves.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [gloves.name] on [T.his] hands!
    " + msg += "[T.He] [T.has] [bicon(gloves)] [gloves.gender==PLURAL?"some":"a"] [(gloves.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [gloves.name] on [T.his] hands!
    " else - msg += "[T.He] [T.has] \icon[gloves] \a [gloves] on [T.his] hands.
    " + msg += "[T.He] [T.has] [bicon(gloves)] \a [gloves] on [T.his] hands.
    " else if(blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) msg += "[T.He] [T.has] [(hand_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained hands!
    " //handcuffed? if(handcuffed && handcuffed.show_examine) if(istype(handcuffed, /obj/item/weapon/handcuffs/cable)) - msg += "[T.He] [T.is] \icon[handcuffed] restrained with cable!
    " + msg += "[T.He] [T.is] [bicon(handcuffed)] restrained with cable!
    " else - msg += "[T.He] [T.is] \icon[handcuffed] handcuffed!
    " + msg += "[T.He] [T.is] [bicon(handcuffed)] handcuffed!
    " //buckled if(buckled) - msg += "[T.He] [T.is] \icon[buckled] buckled to [buckled]!
    " + msg += "[T.He] [T.is] [bicon(buckled)] buckled to [buckled]!
    " //belt if(belt && !(skip_gear & EXAMINE_SKIPBELT) && belt.show_examine) if(belt.blood_DNA) - msg += "[T.He] [T.has] \icon[belt] [belt.gender==PLURAL?"some":"a"] [(belt.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [belt.name] about [T.his] waist!
    " + msg += "[T.He] [T.has] [bicon(belt)] [belt.gender==PLURAL?"some":"a"] [(belt.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [belt.name] about [T.his] waist!
    " else - msg += "[T.He] [T.has] \icon[belt] \a [belt] about [T.his] waist.
    " + msg += "[T.He] [T.has] [bicon(belt)] \a [belt] about [T.his] waist.
    " //shoes if(shoes && !(skip_gear & EXAMINE_SKIPSHOES) && shoes.show_examine) if(shoes.blood_DNA) - msg += "[T.He] [T.is] wearing \icon[shoes] [shoes.gender==PLURAL?"some":"a"] [(shoes.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [shoes.name] on [T.his] feet!
    " + msg += "[T.He] [T.is] wearing [bicon(shoes)] [shoes.gender==PLURAL?"some":"a"] [(shoes.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [shoes.name] on [T.his] feet!
    " else - msg += "[T.He] [T.is] wearing \icon[shoes] \a [shoes] on [T.his] feet.
    " + msg += "[T.He] [T.is] wearing [bicon(shoes)] \a [shoes] on [T.his] feet.
    " else if(feet_blood_DNA && !(skip_body & EXAMINE_SKIPHANDS)) msg += "[T.He] [T.has] [(feet_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained feet!
    " @@ -238,24 +237,24 @@ descriptor = "in [T.his] mouth" if(wear_mask.blood_DNA) - msg += "[T.He] [T.has] \icon[wear_mask] [wear_mask.gender==PLURAL?"some":"a"] [(wear_mask.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [wear_mask.name] [descriptor]!
    " + msg += "[T.He] [T.has] [bicon(wear_mask)] [wear_mask.gender==PLURAL?"some":"a"] [(wear_mask.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [wear_mask.name] [descriptor]!
    " else - msg += "[T.He] [T.has] \icon[wear_mask] \a [wear_mask] [descriptor].
    " + msg += "[T.He] [T.has] [bicon(wear_mask)] \a [wear_mask] [descriptor].
    " //eyes if(glasses && !(skip_gear & EXAMINE_SKIPEYEWEAR) && glasses.show_examine) if(glasses.blood_DNA) - msg += "[T.He] [T.has] \icon[glasses] [glasses.gender==PLURAL?"some":"a"] [(glasses.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [glasses] covering [T.his] eyes!
    " + msg += "[T.He] [T.has] [bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [(glasses.blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained [glasses] covering [T.his] eyes!
    " else - msg += "[T.He] [T.has] \icon[glasses] \a [glasses] covering [T.his] eyes.
    " + msg += "[T.He] [T.has] [bicon(glasses)] \a [glasses] covering [T.his] eyes.
    " //left ear if(l_ear && !(skip_gear & EXAMINE_SKIPEARS) && l_ear.show_examine) - msg += "[T.He] [T.has] \icon[l_ear] \a [l_ear] on [T.his] left ear.
    " + msg += "[T.He] [T.has] [bicon(l_ear)] \a [l_ear] on [T.his] left ear.
    " //right ear if(r_ear && !(skip_gear & EXAMINE_SKIPEARS) && r_ear.show_examine) - msg += "[T.He] [T.has] \icon[r_ear] \a [r_ear] on [T.his] right ear.
    " + msg += "[T.He] [T.has] [bicon(r_ear)] \a [r_ear] on [T.his] right ear.
    " //ID if(wear_id && wear_id.show_examine) @@ -267,9 +266,9 @@ var/obj/item/weapon/card/id/idcard = wear_id id = idcard.registered_name if(id && (id != real_name) && (get_dist(src, usr) <= 1) && prob(10)) - msg += "[T.He] [T.is] wearing \icon[wear_id] \a [wear_id] yet something doesn't seem right...
    " + msg += "[T.He] [T.is] wearing [bicon(wear_id)] \a [wear_id] yet something doesn't seem right...
    " else*/ - msg += "[T.He] [T.is] wearing \icon[wear_id] \a [wear_id].
    " + msg += "[T.He] [T.is] wearing [bicon(wear_id)] \a [wear_id].
    " //Jitters if(is_jittery) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 643287cb19..642eaa2c4f 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -57,7 +57,6 @@ for(var/organ in organs) qdel(organ) QDEL_NULL(nif) //VOREStation Add - QDEL_LIST_NULL(vore_organs) //VOREStation Add return ..() /mob/living/carbon/human/Stat() @@ -101,6 +100,15 @@ if(!blinded) flash_eyes() + for(var/datum/modifier/M in modifiers) + if(!isnull(M.explosion_modifier)) + severity = CLAMP(severity + M.explosion_modifier, 1, 4) + + severity = round(severity) + + if(severity > 3) + return + var/shielded = 0 var/b_loss = null var/f_loss = null @@ -243,12 +251,8 @@ // this handles mulebots and vehicles // and now mobs on fire /mob/living/carbon/human/Crossed(var/atom/movable/AM) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(AM.is_incorporeal()) + return if(istype(AM, /mob/living/bot/mulebot)) var/mob/living/bot/mulebot/MB = AM MB.runOver(src) @@ -420,7 +424,7 @@ U.handle_regular_hud_updates() if(!modified) - usr << "Unable to locate a data core entry for this person." + to_chat(usr, "Unable to locate a data core entry for this person.") if (href_list["secrecord"]) if(hasHUD(usr,"security")) @@ -437,17 +441,17 @@ for (var/datum/data/record/R in data_core.security) if (R.fields["id"] == E.fields["id"]) if(hasHUD(usr,"security")) - usr << "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]" - usr << "Minor Crimes: [R.fields["mi_crim"]]" - usr << "Details: [R.fields["mi_crim_d"]]" - usr << "Major Crimes: [R.fields["ma_crim"]]" - usr << "Details: [R.fields["ma_crim_d"]]" - usr << "Notes: [R.fields["notes"]]" - usr << "\[View Comment Log\]" + to_chat(usr, "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]") + to_chat(usr, "Minor Crimes: [R.fields["mi_crim"]]") + to_chat(usr, "Details: [R.fields["mi_crim_d"]]") + to_chat(usr, "Major Crimes: [R.fields["ma_crim"]]") + to_chat(usr, "Details: [R.fields["ma_crim_d"]]") + to_chat(usr, "Notes: [R.fields["notes"]]") + to_chat(usr, "\[View Comment Log\]") read = 1 if(!read) - usr << "Unable to locate a data core entry for this person." + to_chat(usr, "Unable to locate a data core entry for this person.") if (href_list["secrecordComment"]) if(hasHUD(usr,"security")) @@ -467,14 +471,14 @@ read = 1 var/counter = 1 while(R.fields[text("com_[]", counter)]) - usr << text("[]", R.fields[text("com_[]", counter)]) + to_chat(usr, "[R.fields[text("com_[]", counter)]]") counter++ if (counter == 1) - usr << "No comment found" - usr << "\[Add comment\]" + to_chat(usr, "No comment found") + to_chat(usr, "\[Add comment\]") if(!read) - usr << "Unable to locate a data core entry for this person." + to_chat(usr, "Unable to locate a data core entry for this person.") if (href_list["secrecordadd"]) if(hasHUD(usr,"security")) @@ -536,7 +540,7 @@ U.handle_regular_hud_updates() if(!modified) - usr << "Unable to locate a data core entry for this person." + to_chat(usr, "Unable to locate a data core entry for this person.") if (href_list["medrecord"]) if(hasHUD(usr,"medical")) @@ -553,18 +557,18 @@ for (var/datum/data/record/R in data_core.medical) if (R.fields["id"] == E.fields["id"]) if(hasHUD(usr,"medical")) - usr << "Name: [R.fields["name"]] Blood Type: [R.fields["b_type"]]" - usr << "DNA: [R.fields["b_dna"]]" - usr << "Minor Disabilities: [R.fields["mi_dis"]]" - usr << "Details: [R.fields["mi_dis_d"]]" - usr << "Major Disabilities: [R.fields["ma_dis"]]" - usr << "Details: [R.fields["ma_dis_d"]]" - usr << "Notes: [R.fields["notes"]]" - usr << "\[View Comment Log\]" + to_chat(usr, "Name: [R.fields["name"]] Blood Type: [R.fields["b_type"]]") + to_chat(usr, "DNA: [R.fields["b_dna"]]") + to_chat(usr, "Minor Disabilities: [R.fields["mi_dis"]]") + to_chat(usr, "Details: [R.fields["mi_dis_d"]]") + to_chat(usr, "Major Disabilities: [R.fields["ma_dis"]]") + to_chat(usr, "Details: [R.fields["ma_dis_d"]]") + to_chat(usr, "Notes: [R.fields["notes"]]") + to_chat(usr, "\[View Comment Log\]") read = 1 if(!read) - usr << "Unable to locate a data core entry for this person." + to_chat(usr, "Unable to locate a data core entry for this person.") if (href_list["medrecordComment"]) if(hasHUD(usr,"medical")) @@ -584,14 +588,14 @@ read = 1 var/counter = 1 while(R.fields[text("com_[]", counter)]) - usr << text("[]", R.fields[text("com_[]", counter)]) + to_chat(usr, "[R.fields[text("com_[]", counter)]]") counter++ if (counter == 1) - usr << "No comment found" - usr << "\[Add comment\]" + to_chat(usr, "No comment found") + to_chat(usr, "\[Add comment\]") if(!read) - usr << "Unable to locate a data core entry for this person." + to_chat(usr, "Unable to locate a data core entry for this person.") if (href_list["medrecordadd"]) if(hasHUD(usr,"medical")) @@ -978,19 +982,19 @@ if (prob(round(damage/10)*20)) germs++ if (germs == 100) - world << "Reached stage 1 in [ticks] ticks" + to_world("Reached stage 1 in [ticks] ticks") if (germs > 100) if (prob(10)) damage++ germs++ if (germs == 1000) - world << "Reached stage 2 in [ticks] ticks" + to_world("Reached stage 2 in [ticks] ticks") if (germs > 1000) damage++ germs++ if (germs == 2500) - world << "Reached stage 3 in [ticks] ticks" - world << "Mob took [tdamage] tox damage" + to_world("Reached stage 3 in [ticks] ticks") + to_world("Mob took [tdamage] tox damage") */ //returns 1 if made bloody, returns 0 otherwise @@ -1097,16 +1101,17 @@ "You begin counting your pulse.") if(src.pulse) - usr << "[self ? "You have a" : "[src] has a"] pulse! Counting..." + to_chat(usr, "[self ? "You have a" : "[src] has a"] pulse! Counting...") else - usr << "[src] has no pulse!" //it is REALLY UNLIKELY that a dead person would check his own pulse + to_chat(usr, "[src] has no pulse!") //it is REALLY UNLIKELY that a dead person would check his own pulse return - usr << "You must[self ? "" : " both"] remain still until counting is finished." + to_chat(usr, "You must[self ? "" : " both"] remain still until counting is finished.") if(do_mob(usr, src, 60)) - usr << "[self ? "Your" : "[src]'s"] pulse is [src.get_pulse(GETPULSE_HAND)]." + var/message = "[self ? "Your" : "[src]'s"] pulse is [src.get_pulse(GETPULSE_HAND)]." + to_chat(usr,message) else - usr << "You failed to check the pulse. Try again." + to_chat(usr, "You failed to check the pulse. Try again.") /mob/living/carbon/human/proc/set_species(var/new_species, var/default_colour, var/regen_icons = TRUE, var/mob/living/carbon/human/example = null) //VOREStation Edit - send an example @@ -1303,7 +1308,7 @@ if(!. && error_msg && user) if(!fail_msg) fail_msg = "There is no exposed flesh or thin material [target_zone == BP_HEAD ? "on their head" : "on their body"] to inject into." - user << "[fail_msg]" + to_chat(user, "[fail_msg]") /mob/living/carbon/human/print_flavor_text(var/shrink = 1) var/list/equipment = list(src.head,src.wear_mask,src.glasses,src.w_uniform,src.wear_suit,src.gloves,src.shoes) @@ -1397,11 +1402,11 @@ usr.setClickCooldown(20) if(usr.stat > 0) - usr << "You are unconcious and cannot do that!" + to_chat(usr, "You are unconcious and cannot do that!") return if(usr.restrained()) - usr << "You are restrained and cannot do that!" + to_chat(usr, "You are restrained and cannot do that!") return var/mob/S = src @@ -1423,7 +1428,7 @@ if(self) to_chat(src, "You brace yourself to relocate your [current_limb.joint]...") else - U << "You begin to relocate [S]'s [current_limb.joint]..." + to_chat(U, "You begin to relocate [S]'s [current_limb.joint]...") if(!do_after(U, 30)) return @@ -1433,8 +1438,8 @@ if(self) to_chat(src, "You pop your [current_limb.joint] back in!") else - U << "You pop [S]'s [current_limb.joint] back in!" - S << "[U] pops your [current_limb.joint] back in!" + to_chat(U, "You pop [S]'s [current_limb.joint] back in!") + to_chat(S, "[U] pops your [current_limb.joint] back in!") current_limb.relocate() /mob/living/carbon/human/drop_from_inventory(var/obj/item/W, var/atom/Target = null) diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 1fa1203182..805a63d9a3 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -33,7 +33,7 @@ if(H.hand) temp = H.organs_by_name["l_hand"] if(!temp || !temp.is_usable()) - H << "You can't use your hand." + to_chat(H, "You can't use your hand.") return if(H.lying) return @@ -42,10 +42,11 @@ ..() var/lacertusol = 0 - for(var/datum/reagent/phororeagent/R in M.reagents.reagent_list) - if(R.id == "lacertusol") - lacertusol = 1 - break + if(istype(M,/mob/living/carbon)) + for(var/datum/reagent/phororeagent/R in M.reagents.reagent_list) + if(R.id == "lacertusol") + lacertusol = 1 + break // Should this all be in Touch()? if(istype(H)) @@ -74,16 +75,16 @@ // VOREStation Edit - End if(istype(H) && health < config.health_threshold_crit) if(!H.check_has_mouth()) - H << "You don't have a mouth, you cannot perform CPR!" + to_chat(H, "You don't have a mouth, you cannot perform CPR!") return if(!check_has_mouth()) - H << "They don't have a mouth, you cannot perform CPR!" + to_chat(H, "They don't have a mouth, you cannot perform CPR!") return if((H.head && (H.head.body_parts_covered & FACE)) || (H.wear_mask && (H.wear_mask.body_parts_covered & FACE))) - H << "Remove your mask!" + to_chat(H, "Remove your mask!") return 0 if((head && (head.body_parts_covered & FACE)) || (wear_mask && (wear_mask.body_parts_covered & FACE))) - H << "Remove [src]'s mask!" + to_chat(H, "Remove [src]'s mask!") return 0 if (!cpr_time) @@ -99,7 +100,7 @@ return H.visible_message("\The [H] performs CPR on \the [src]!") - H << "Repeat at least every 7 seconds." + to_chat(H, "Repeat at least every 7 seconds.") if(istype(H) && health > config.health_threshold_dead) adjustOxyLoss(-(min(getOxyLoss(), 5))) @@ -115,14 +116,14 @@ return 0 for(var/obj/item/weapon/grab/G in src.grabbed_by) if(G.assailant == M) - M << "You already grabbed [src]." + to_chat(M, "You already grabbed [src].") return if(w_uniform) w_uniform.add_fingerprint(M) var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, src) if(buckled) - M << "You cannot grab [src], [TT.he] is buckled in!" + to_chat(M, "You cannot grab [src], [TT.he] is buckled in!") return if(!G) //the grab will delete itself in New if affecting is anchored return @@ -135,7 +136,6 @@ //VORESTATION EDIT visible_message("[M] has grabbed [src] [(M.zone_sel.selecting == BP_L_HAND || M.zone_sel.selecting == BP_R_HAND)? "by [(gender==FEMALE)? "her" : ((gender==MALE)? "his": "their")] hands": "passively"]!") //VORESTATION END END - return TRUE if(I_HURT) @@ -147,7 +147,7 @@ G.activate(M) update_inv_wear_mask() else - M << "\The [G] is already primed! Run!" + to_chat(M, "\The [G] is already primed! Run!") return if(!istype(H)) @@ -163,7 +163,7 @@ var/obj/item/organ/external/affecting = get_organ(hit_zone) if(!affecting || affecting.is_stump()) - M << "They are missing that limb!" + to_chat(M, "They are missing that limb!") return TRUE switch(src.a_intent) @@ -309,7 +309,10 @@ var/randn = rand(1, 100) last_push_time = world.time - if(!(species.flags & NO_SLIP) && randn <= 25) + // We ARE wearing shoes OR + // We as a species CAN be slipped when barefoot + // And also 1 in 4 because rngesus + if((shoes || !(species.flags & NO_SLIP)) && randn <= 25) var/armor_check = run_armor_check(affecting, "melee") apply_effect(3, WEAKEN, armor_check) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) @@ -420,7 +423,8 @@ return FALSE if(organ.applied_pressure) - user << "Someone is already applying pressure to [user == src? "your [organ.name]" : "[src]'s [organ.name]"]." + var/message = "Someone is already applying pressure to [user == src ? "your [organ.name]" : "[src]'s [organ.name]"]." + to_chat(user,message) return FALSE var/datum/gender/TU = gender_datums[user.get_visible_gender()] @@ -442,4 +446,4 @@ else user.visible_message("\The [user] stops applying pressure to [src]'s [organ.name]!", "You stop applying pressure to [src]'s [organ.name]!") - return TRUE + return TRUE diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index dd42af027e..7cf2027025 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -4,7 +4,7 @@ if(status_flags & GODMODE) health = getMaxHealth() - stat = CONSCIOUS + set_stat(CONSCIOUS) return var/total_burn = 0 @@ -435,7 +435,7 @@ This function restores all organs. /mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/sharp = 0, var/edge = 0, var/obj/used_weapon = null) if(Debug2) - world.log << "## DEBUG: human/apply_damage() was called on [src], with [damage] damage, an armor value of [blocked], and a soak value of [soaked]." + to_world_log("## DEBUG: human/apply_damage() was called on [src], with [damage] damage, an armor value of [blocked], and a soak value of [soaked].") var/obj/item/organ/external/organ = null if(isorgan(def_zone)) @@ -472,7 +472,7 @@ This function restores all organs. damage -= soaked if(Debug2) - world.log << "## DEBUG: [src] was hit for [damage]." + to_world_log("## DEBUG: [src] was hit for [damage].") switch(damagetype) if(BRUTE) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 52d771ad30..3675866e24 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -236,7 +236,7 @@ emp_act var/obj/item/organ/external/affecting = get_organ(hit_zone) if (!affecting || affecting.is_stump()) - user << "They are missing that limb!" + to_chat(user, "They are missing that limb!") return null return hit_zone @@ -349,12 +349,12 @@ emp_act /mob/living/carbon/human/emag_act(var/remaining_charges, mob/user, var/emag_source) var/obj/item/organ/external/affecting = get_organ(user.zone_sel.selecting) if(!affecting || !(affecting.robotic >= ORGAN_ROBOT)) - user << "That limb isn't robotic." + to_chat(user, "That limb isn't robotic.") return -1 if(affecting.sabotaged) - user << "[src]'s [affecting.name] is already sabotaged!" + to_chat(user, "[src]'s [affecting.name] is already sabotaged!") return -1 - user << "You sneakily slide [emag_source] into the dataport on [src]'s [affecting.name] and short out the safeties." + to_chat(user, "You sneakily slide [emag_source] into the dataport on [src]'s [affecting.name] and short out the safeties.") affecting.sabotaged = 1 return 1 @@ -527,8 +527,8 @@ emp_act if(damtype != BURN && damtype != BRUTE) return // The rig might soak this hit, if we're wearing one. - if(back && istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back + if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() rig.take_hit(damage) // We may also be taking a suit breach. diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 5df33d70e2..18e0babf42 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -45,8 +45,8 @@ var/list/all_underwear = list() var/list/all_underwear_metadata = list() var/list/hide_underwear = list() - var/backbag = 2 //Which backpack type the player has chosen. Nothing, Satchel or Backpack. - var/pdachoice = 1 //Which PDA type the player has chosen. Default, Slim, Old, or Rugged. + var/backbag = 2 //Which backpack type the player has chosen. + var/pdachoice = 1 //Which PDA type the player has chosen. // General information var/home_system = "" diff --git a/code/modules/mob/living/carbon/human/human_defines_vr.dm b/code/modules/mob/living/carbon/human/human_defines_vr.dm index b9200877c3..9316b90e88 100644 --- a/code/modules/mob/living/carbon/human/human_defines_vr.dm +++ b/code/modules/mob/living/carbon/human/human_defines_vr.dm @@ -48,6 +48,4 @@ if(!istype(SK)) return 0 - if(amount > 0 || !(SK.check_infinite_energy(src))) - var/new_amount = SK.get_energy(src) + amount - SK.set_energy(src, new_amount) \ No newline at end of file + SK.set_energy(src, SK.get_energy(src) + amount) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 446c399c9a..f9c15a4852 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -19,9 +19,9 @@ return 1 if(feedback) if(status[1] == HUMAN_EATING_NO_MOUTH) - feeder << "Where do you intend to put \the [food]? \The [src] doesn't have a mouth!" + to_chat(feeder, "Where do you intend to put \the [food]? \The [src] doesn't have a mouth!") else if(status[1] == HUMAN_EATING_BLOCKED_MOUTH) - feeder << "\The [status[2]] is in the way!" + to_chat(feeder, "\The [status[2]] is in the way!") return 0 /mob/living/carbon/human/proc/can_eat_status() @@ -63,6 +63,8 @@ for(var/obj/item/rig_module/stealth_field/cloaker in suit.installed_modules) if(cloaker.active) cloaker.deactivate() + for(var/obj/item/weapon/deadringer/dr in src) + dr.uncloak() /mob/living/carbon/human/is_cloaked() if(mind && mind.changeling && mind.changeling.cloaked) // Ling camo. @@ -72,6 +74,9 @@ for(var/obj/item/rig_module/stealth_field/cloaker in suit.installed_modules) if(cloaker.active) return TRUE + for(var/obj/item/weapon/deadringer/dr in src) + if(dr.timer > 20) + return TRUE return ..() /mob/living/carbon/human/get_ear_protection() @@ -173,7 +178,7 @@ compiled_vis |= O.enables_planes //Check to see if we have a rig (ugh, blame rigs, desnowflake this) - var/obj/item/weapon/rig/rig = back + var/obj/item/weapon/rig/rig = get_rig() if(istype(rig) && rig.visor) if(!rig.helmet || (head && rig.helmet == head)) if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 9f57be114b..2c08449e1e 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -181,8 +181,8 @@ if(back) if(istype(back,/obj/item/weapon/tank/jetpack)) thrust = back - else if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back + else if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) thrust = module.jets break @@ -224,10 +224,8 @@ // Handle footstep sounds /mob/living/carbon/human/handle_footstep(var/turf/T) - //VOREStation Edit begin: SHADEKIN - if(shadekin_phasing_check()) + if(is_incorporeal()) return - //VOREStation Edit end: SHADEKIN if(!config.footstep_volume || !T.footstep_sounds || !T.footstep_sounds.len) return // Future Upgrades - Multi species support diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm index 9309100cf6..cb053663e0 100644 --- a/code/modules/mob/living/carbon/human/human_powers.dm +++ b/code/modules/mob/living/carbon/human/human_powers.dm @@ -108,12 +108,12 @@ log_say("(COMMUNE to [key_name(M)]) [text]",src) - M << "Like lead slabs crashing into the ocean, alien thoughts drop into your mind: [text]" + to_chat(M, "Like lead slabs crashing into the ocean, alien thoughts drop into your mind: [text]") if(istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M if(H.species.name == src.species.name) return - H << "Your nose begins to bleed..." + to_chat(H, "Your nose begins to bleed...") H.drip(1) /mob/living/carbon/human/proc/regurgitate() @@ -137,7 +137,7 @@ var/msg = sanitize(input("Message:", "Psychic Whisper") as text|null) if(msg) log_say("(PWHISPER to [key_name(M)]) [msg]", src) - M << "You hear a strange, alien voice in your head... [msg]" + to_chat(M, "You hear a strange, alien voice in your head... [msg]") to_chat(src, "You said: \"[msg]\" to [M]") return @@ -178,9 +178,28 @@ for(var/obj/item/W in src) drop_from_inventory(W) - visible_message("\The [src] quivers slightly, then splits apart with a wet slithering noise.") + var/obj/item/organ/external/Chest = organs_by_name[BP_TORSO] - qdel(src) + if(Chest.robotic >= 2) + visible_message("\The [src] shudders slightly, then ejects a cluster of nymphs with a wet slithering noise.") + species = GLOB.all_species[SPECIES_HUMAN] // This is hard-set to default the body to a normal FBP, without changing anything. + + // Bust it + src.death() + + for(var/obj/item/organ/internal/diona/Org in internal_organs) // Remove Nymph organs. + qdel(Org) + + // Purge the diona verbs. + verbs -= /mob/living/carbon/human/proc/diona_split_nymph + verbs -= /mob/living/carbon/human/proc/regenerate + + for(var/obj/item/organ/external/E in organs) // Just fall apart. + E.droplimb(TRUE) + + else + visible_message("\The [src] quivers slightly, then splits apart with a wet slithering noise.") + qdel(src) /mob/living/carbon/human/proc/self_diagnostics() set name = "Self-Diagnostics" @@ -215,7 +234,7 @@ else output += "[IO.name] - OK\n" - src << output + to_chat(src,output) /mob/living/carbon/human var/next_sonar_ping = 0 @@ -261,7 +280,7 @@ else // No need to check distance if they're standing right on-top of us feedback += "right on top of you." feedback += "
    " - src << jointext(feedback,null) + to_chat(src,jointext(feedback,null)) if(!heard_something) to_chat(src, "You hear no movement but your own.") diff --git a/code/modules/mob/living/carbon/human/human_powers_YW.dm b/code/modules/mob/living/carbon/human/human_powers_YW.dm index abb3a70ab9..f3d59ae71b 100644 --- a/code/modules/mob/living/carbon/human/human_powers_YW.dm +++ b/code/modules/mob/living/carbon/human/human_powers_YW.dm @@ -7,11 +7,15 @@ if(msg) var/mob/living/carbon/human/H = M log_say("(GreyTP to [key_name(M)]) [msg]", src) - if(H.species.name == src.species.name) - M << "you hear [src.name]'s voice: [msg]" - to_chat(src, "You said: \"[msg]\" to [M]") + if(ishuman(M)) + if(H.species.name == src.species.name) + to_chat(M, "you hear [src.name]'s voice: [msg]") + to_chat(src, "you said: \"[msg]\" to [M]") + else + to_chat(M, "you hear a voice echo in your head... [msg]") + to_chat(src, "you said: \"[msg]\" to [M]") else - M << "you hear a voice echo in your head... [msg]" - to_chat(src, "You said: \"[msg]\" to [M]") + to_chat(M, "you hear a voice echo in your head... [msg]") + to_chat(src, "you said: \"[msg]\" to [M]") return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_resist.dm b/code/modules/mob/living/carbon/human/human_resist.dm index 37e20d0d21..56d42c1a15 100644 --- a/code/modules/mob/living/carbon/human/human_resist.dm +++ b/code/modules/mob/living/carbon/human/human_resist.dm @@ -1,6 +1,6 @@ /mob/living/carbon/human/process_resist() //drop && roll - if(on_fire && !buckled) + if((on_fire || has_modifier_of_type(/datum/modifier/fire)) && !buckled) adjust_fire_stacks(-1.2) Weaken(3) spin(32,2) @@ -9,7 +9,7 @@ "You stop, drop, and roll!" ) sleep(30) - if(fire_stacks <= 0) + if(fire_stacks <= 0 && !(has_modifier_of_type(/datum/modifier/fire))) visible_message( "[src] has successfully extinguished themselves!", "You extinguish yourself." diff --git a/code/modules/mob/living/carbon/human/human_species_vr.dm b/code/modules/mob/living/carbon/human/human_species_vr.dm index 897ef6ab14..566d5e8a26 100644 --- a/code/modules/mob/living/carbon/human/human_species_vr.dm +++ b/code/modules/mob/living/carbon/human/human_species_vr.dm @@ -14,9 +14,6 @@ /mob/living/carbon/human/xenochimera/New(var/new_loc) ..(new_loc, "Xenochimera") -/mob/living/carbon/human/xenohybrid/New(var/new_loc) - ..(new_loc, "Xenomorph Hybrid") - /mob/living/carbon/human/spider/New(var/new_loc) ..(new_loc, "Vasilissan") diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index ee2082401c..0d9e58b7cb 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -14,7 +14,7 @@ This saves us from having to call add_fingerprint() any time something is put in var/mob/living/carbon/human/H = src var/obj/item/I = H.get_active_hand() if(!I) - H << "You are not holding anything to equip." + to_chat(H, "You are not holding anything to equip.") return if(H.equip_to_appropriate_slot(I)) if(hand) @@ -22,7 +22,7 @@ This saves us from having to call add_fingerprint() any time something is put in else update_inv_r_hand(0) else - H << "You are unable to equip that." + to_chat(H, "You are unable to equip that.") /mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1) for (var/slot in slots) @@ -365,7 +365,7 @@ This saves us from having to call add_fingerprint() any time something is put in covering = src.wear_suit if(covering && (covering.body_parts_covered & (I.body_parts_covered|check_flags))) - user << "\The [covering] is in the way." + to_chat(user, "\The [covering] is in the way.") return 0 return 1 @@ -397,4 +397,12 @@ This saves us from having to call add_fingerprint() any time something is put in for(var/obj/item/I in list(l_hand, r_hand)) if(istype(I, typepath)) return I - return FALSE \ No newline at end of file + return FALSE + +// Returns a list of items held in both hands. +/mob/living/carbon/human/get_all_held_items() + . = list() + if(l_hand) + . += l_hand + if(r_hand) + . += r_hand \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 3b0d161dfd..894e3cfd80 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -87,6 +87,9 @@ if(!client) species.handle_npc(src) + else if(stat == DEAD && !stasis) + handle_defib_timer() + if(!handle_some_updates()) return //We go ahead and process them 5 times for HUD images and other stuff though. @@ -291,7 +294,7 @@ var/damage = 0 radiation -= 1 * RADIATION_SPEED_COEFFICIENT - if(prob(25)) + if(radiation > 2.5 && prob(25)) // Safe for a little over 2m at the recommended maximum safe dosage of 0.05Bq damage = 1 if (radiation > 50) @@ -358,10 +361,10 @@ //Because rigs store their tanks out of reach of contents.Find(), a check has to be made to make //sure the rig is still worn, still online, and that its air supply still exists. var/obj/item/weapon/tank/rig_supply - if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back - if(!rig.offline && (rig.air_supply && internal == rig.air_supply)) - rig_supply = rig.air_supply + var/obj/item/weapon/rig/Rig = get_rig() + + if(Rig) + rig_supply = Rig.air_supply if ((!rig_supply && !contents.Find(internal)) || !((wear_mask && (wear_mask.item_flags & AIRTIGHT)) || (head && (head.item_flags & AIRTIGHT)))) internal = null @@ -601,7 +604,7 @@ if (temp_adj > BODYTEMP_HEATING_MAX) temp_adj = BODYTEMP_HEATING_MAX if (temp_adj < BODYTEMP_COOLING_MAX) temp_adj = BODYTEMP_COOLING_MAX - //world << "Breath: [breath.temperature], [src]: [bodytemperature], Adjusting: [temp_adj]" + //to_world("Breath: [breath.temperature], [src]: [bodytemperature], Adjusting: [temp_adj]") bodytemperature += temp_adj else if(breath.temperature >= species.heat_discomfort_level) @@ -618,10 +621,8 @@ //Stuff like the xenomorph's plasma regen happens here. species.handle_environment_special(src) - //VOREStation Edit begin: SHADEKIN - if(shadekin_phasing_check()) + if(is_incorporeal()) return - //VOREStation Edit end: SHADEKIN //Moved pressure calculations here for use in skip-processing check. var/pressure = environment.return_pressure() @@ -794,18 +795,18 @@ if(nutrition >= 2) //If we are very, very cold we'll use up quite a bit of nutriment to heat us up. nutrition -= 2 var/recovery_amt = max((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) - //world << "Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]" + //to_world("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") // log_debug("Cold. Difference = [body_temperature_difference]. Recovering [recovery_amt]") bodytemperature += recovery_amt else if(species.cold_level_1 <= bodytemperature && bodytemperature <= species.heat_level_1) var/recovery_amt = body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR - //world << "Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]" + //to_world("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") // log_debug("Norm. Difference = [body_temperature_difference]. Recovering [recovery_amt]") bodytemperature += recovery_amt else if(bodytemperature > species.heat_level_1) //360.15 is 310.15 + 50, the temperature where you start to feel effects. //We totally need a sweat system cause it totally makes sense...~ var/recovery_amt = min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers - //world << "Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]" + //to_world("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") // log_debug("Hot. Difference = [body_temperature_difference]. Recovering [recovery_amt]") bodytemperature += recovery_amt @@ -1106,9 +1107,9 @@ return 1 -/mob/living/carbon/human/proc/set_stat(var/new_stat) - stat = new_stat - if(stat) +/mob/living/carbon/human/set_stat(var/new_stat) + . = ..() + if(. && stat) update_skin(1) /mob/living/carbon/human/handle_regular_hud_updates() @@ -1371,7 +1372,7 @@ see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default var/tmp/glasses_processed = 0 - var/obj/item/weapon/rig/rig = back + var/obj/item/weapon/rig/rig = get_rig() if(istype(rig) && rig.visor && !looking_elsewhere) if(!rig.helmet || (head && rig.helmet == head)) if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) @@ -1525,7 +1526,6 @@ else shock_stage = min(shock_stage, 160) shock_stage = max(shock_stage-1, 0) - return if(stat) return 0 @@ -1829,5 +1829,15 @@ traumatic_shock = 0 ..() +/mob/living/carbon/human/proc/handle_defib_timer() + if(!should_have_organ(O_BRAIN)) + return // No brain. + + var/obj/item/organ/internal/brain/brain = internal_organs_by_name[O_BRAIN] + if(!brain) + return // Still no brain. + + brain.tick_defib_timer() + #undef HUMAN_MAX_OXYLOSS #undef HUMAN_CRIT_MAX_OXYLOSS diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index fab28e45b4..99b99c070f 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -78,8 +78,8 @@ /mob/living/carbon/human/GetVoice() var/voice_sub - if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back + if(istype(get_rig(),/obj/item/weapon/rig)) + var/obj/item/weapon/rig/rig = get_rig() // todo: fix this shit if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) voice_sub = rig.speech.voice_holder.voice @@ -142,8 +142,8 @@ message_data[1] = "" . = 1 - else if(istype(wear_mask, /obj/item/clothing/mask)) - var/obj/item/clothing/mask/M = wear_mask + else if(istype(wear_mask, /obj/item/clothing/mask/muzzle)) //YWedit start, fixes masks removing speech problems. + var/obj/item/clothing/mask/muzzle/M = wear_mask // YWedit End. if(M.voicechange) message_data[1] = pick(M.say_messages) message_data[2] = pick(M.say_verbs) diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm index aad75767cd..3fb94964da 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm @@ -188,6 +188,8 @@ if(!istype(shade_organ)) return 0 + if(shade_organ.dark_energy_infinite) + return shade_organ.max_dark_energy return shade_organ.dark_energy @@ -215,14 +217,6 @@ shade_organ.max_dark_energy = new_max_energy -/datum/species/shadekin/proc/check_infinite_energy(var/mob/living/carbon/human/H) - var/obj/item/organ/internal/brain/shadekin/shade_organ = H.internal_organs_by_name[O_BRAIN] - - if(!istype(shade_organ)) - return 0 - - return shade_organ.dark_energy_infinite - /datum/species/shadekin/proc/update_shadekin_hud(var/mob/living/carbon/human/H) var/turf/T = get_turf(H) if(!T) @@ -230,15 +224,15 @@ if(H.shadekin_energy_display) H.shadekin_energy_display.invisibility = 0 switch(get_energy(H)) - if(80 to INFINITY) + if(100 to INFINITY) H.shadekin_energy_display.icon_state = "energy0" - if(60 to 80) + if(75 to 100) H.shadekin_energy_display.icon_state = "energy1" - if(40 to 60) + if(50 to 75) H.shadekin_energy_display.icon_state = "energy2" - if(20 to 40) + if(25 to 50) H.shadekin_energy_display.icon_state = "energy3" - if(0 to 20) + if(0 to 25) H.shadekin_energy_display.icon_state = "energy4" if(H.shadekin_dark_display) H.shadekin_dark_display.invisibility = 0 diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm index 7d354db330..632d45ea4d 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm @@ -1,5 +1,10 @@ /datum/power/shadekin +/mob/living/carbon/human/is_incorporeal() + if(ability_flags & AB_PHASE_SHIFTED) //Shadekin + return TRUE + return ..() + ///////////////////// /// PHASE SHIFT /// ///////////////////// @@ -62,6 +67,7 @@ //Shifting in if(ability_flags & AB_PHASE_SHIFTED) ability_flags &= ~AB_PHASE_SHIFTED + mouse_opacity = 1 name = real_name for(var/belly in vore_organs) var/obj/belly/B = belly @@ -108,6 +114,7 @@ //Shifting out else ability_flags |= AB_PHASE_SHIFTED + mouse_opacity = 0 custom_emote(1,"phases out!") name = "Something" @@ -130,47 +137,6 @@ density = FALSE force_max_speed = TRUE -/mob/living/carbon/human/UnarmedAttack() - if(shadekin_phasing_check()) - return FALSE //Nope. - - . = ..() - -/mob/living/carbon/human/can_fall() - if(shadekin_phasing_check()) - return FALSE //Nope! - - return ..() - -/mob/living/carbon/human/zMove(direction) - if(shadekin_phasing_check()) - var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src) - if(destination) - forceMove(destination) - return TRUE //Yup. - - return ..() - -/mob/proc/shadekin_phasing_check() - var/mob/living/simple_mob/shadekin/s_SK = src - if(istype(s_SK)) - if(s_SK.ability_flags & AB_PHASE_SHIFTED) - return TRUE - var/mob/living/carbon/human/h_SK = src - if(istype(h_SK)) - if(h_SK.ability_flags & AB_PHASE_SHIFTED) - return TRUE - return FALSE - -/* -/mob/living/carbon/human/MouseDrop_T(atom/dropping, mob/user) - if(ability_flags & AB_PHASE_SHIFTED) - return FALSE //Nope! - - return ..() -*/ - - ////////////////////////// /// REGENERATE OTHER /// ////////////////////////// diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 67c429e833..6af53eeca3 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -89,7 +89,11 @@ var/flash_mod = 1 // Stun from blindness modifier. var/flash_burn = 0 // how much damage to take from being flashed if light hypersensitive var/sound_mod = 1 // Stun from sounds, I.E. flashbangs. + var/chem_strength_heal = 1 // YW ADDITION: Multiplier to healing chem effectiveness + var/chem_strength_tox = 1 // YW ADDITION: Multiplier to toxic chem effectiveness + var/chemOD_threshold = 1 // YW ADDITION: Multiplier to OD threshold, before you start to take OD damage var/chemOD_mod = 1 // Damage modifier for overdose + var/alcohol_tolerance = 1 // YW ADDITION: Strength multiplier for ethanol-derived reagents var/vision_flags = SEE_SELF // Same flags as glasses. // Death vars. diff --git a/code/modules/mob/living/carbon/human/species/species_getters.dm b/code/modules/mob/living/carbon/human/species/species_getters.dm index 5802472c55..8fe3f80a13 100644 --- a/code/modules/mob/living/carbon/human/species/species_getters.dm +++ b/code/modules/mob/living/carbon/human/species/species_getters.dm @@ -85,10 +85,10 @@ switch(msg_type) if("cold") if(!covered) - H << "[pick(cold_discomfort_strings)]" + to_chat(H, "[pick(cold_discomfort_strings)]") if("heat") if(covered) - H << "[pick(heat_discomfort_strings)]" + to_chat(H, "[pick(heat_discomfort_strings)]") /datum/species/proc/get_random_name(var/gender) if(!name_language) diff --git a/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm b/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm index 3313c137b5..82645f8c59 100644 --- a/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm @@ -121,4 +121,14 @@ g_wing = new_color_rgb_list[2] b_wing = new_color_rgb_list[3] + //Indented inside positive primary color choice, don't bother if they clicked cancel + var/current_sec_color = rgb(r_wing2,g_wing2,b_wing2) + + var/new_sec_color = input("Pick secondary wing color (only applies to some wings):","Wing Color (sec)", current_sec_color) as null|color + if(new_sec_color) + new_color_rgb_list = hex2rgb(new_sec_color) + r_wing2 = new_color_rgb_list[1] + g_wing2 = new_color_rgb_list[2] + b_wing2 = new_color_rgb_list[3] + update_wing_showing() diff --git a/code/modules/mob/living/carbon/human/species/species_vr.dm b/code/modules/mob/living/carbon/human/species/species_vr.dm index c3831d5f55..353946d976 100644 --- a/code/modules/mob/living/carbon/human/species/species_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_vr.dm @@ -19,6 +19,7 @@ var/icobase_wing var/wikilink = null //link to wiki page for species var/icon_height = 32 + var/agility = 20 //prob() to do agile things /datum/species/proc/update_attack_types() unarmed_attacks = list() diff --git a/code/modules/mob/living/carbon/human/species/station/alraune.dm b/code/modules/mob/living/carbon/human/species/station/alraune.dm index 14a9d61679..ce04cbcef9 100644 --- a/code/modules/mob/living/carbon/human/species/station/alraune.dm +++ b/code/modules/mob/living/carbon/human/species/station/alraune.dm @@ -303,7 +303,7 @@ if (temp_adj > BODYTEMP_HEATING_MAX) temp_adj = BODYTEMP_HEATING_MAX if (temp_adj < BODYTEMP_COOLING_MAX) temp_adj = BODYTEMP_COOLING_MAX - //world << "Breath: [breath.temperature], [src]: [bodytemperature], Adjusting: [temp_adj]" + //to_world("Breath: [breath.temperature], [src]: [bodytemperature], Adjusting: [temp_adj]") H.bodytemperature += temp_adj else if(breath.temperature >= heat_discomfort_level) diff --git a/code/modules/mob/living/carbon/human/species/station/blank_vr.dm b/code/modules/mob/living/carbon/human/species/station/blank_vr.dm index 5374d21b33..cb13f4772c 100644 --- a/code/modules/mob/living/carbon/human/species/station/blank_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/blank_vr.dm @@ -5,6 +5,7 @@ var/metabolism = 0.0015 var/lightweight = FALSE //Oof! Nonhelpful bump stumbles. var/trashcan = FALSE //It's always sunny in the wrestling ring. + var/eat_ore = FALSE //HEAVY METAL DIET var/base_species = null // Unused outside of a few species var/selects_bodytype = FALSE // Allows the species to choose from body types intead of being forced to be just one. diff --git a/code/modules/mob/living/carbon/human/species/station/greyYW.dm b/code/modules/mob/living/carbon/human/species/station/greyYW.dm index d6f946ec7d..3922c442a7 100644 --- a/code/modules/mob/living/carbon/human/species/station/greyYW.dm +++ b/code/modules/mob/living/carbon/human/species/station/greyYW.dm @@ -4,7 +4,17 @@ icobase = 'icons/mob/human_races/r_grey.dmi' deform = 'icons/mob/human_races/r_def_grey.dmi' unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/punch) - blurb = "Insert Grey Lore." + + blurb = "Greys are a species of aliens that are more weak than humans physically, but possesses\ + the ability to talk telephatically with themselves and other species, Due to this, Grey have no\ + naming scheme or language, having names only to make easier communication with other species.\ +
    \The Grey have unknown origin even to themselves, scientists believes they came from\ + another galaxy, greys have chosen mauna-b to act as their home planet due to high amount of grey\ + residing in there." + + wikilink = "https://yawn.izac.live/index.php?title=Grey" + catalogue_data = list(/datum/category_item/catalogue/fauna/grey) + num_alternate_languages = 3 species_language = LANGUAGE_PSIONIC secondary_langs = list(LANGUAGE_PSIONIC) @@ -37,7 +47,7 @@ O_BRAIN = /obj/item/organ/internal/brain, O_APPENDIX = /obj/item/organ/internal/appendix, O_SPLEEN = /obj/item/organ/internal/spleen, - O_EYES = /obj/item/organ/internal/eyes/grey, + O_EYES = /obj/item/organ/internal/eyes, O_STOMACH = /obj/item/organ/internal/stomach, O_INTESTINE = /obj/item/organ/internal/intestine ) @@ -61,3 +71,6 @@ /datum/species/grey/handle_environment_special(var/mob/living/carbon/human/H) if(H.fire_stacks < 0 && H.get_water_protection() <= 0.5) // If over half your body is soaked, you're melting. H.adjustFireLoss(max(0,(3 - (3 * H.get_water_protection())))) // Tripled because 0.5 is miniscule, and fire_stacks are capped in both directions. + +/mob/living/carbon/human/grey/New(var/new_loc) //makes grey spawnable + ..(new_loc, SPECIES_GREY_YW) diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm b/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm index aa6287eedd..198bd80232 100644 --- a/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/prometheans_vr.dm @@ -8,8 +8,8 @@ valid_transform_species = list( "Human", "Unathi", "Tajara", "Skrell", "Diona", "Teshari", "Monkey","Sergal", - "Akula","Nevrean","Highlander Zorren", - "Flatland Zorren", "Vulpkanin", "Vasilissan", + "Akula","Nevrean","Zorren", + "Fennec", "Vulpkanin", "Vasilissan", "Rapala", "Neaera", "Stok", "Farwa", "Sobaka", "Wolpin", "Saru", "Sparra") diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm index 2a122f9281..da27a9aace 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm @@ -66,6 +66,10 @@ else update_icon() +/mob/living/simple_mob/protean_blob/Login() + . = ..() + copy_from_prefs_vr(bellies = FALSE) //Load vore prefs + /mob/living/simple_mob/protean_blob/Destroy() humanform = null refactory = null diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm index 0d2000fa01..4d5ce6d102 100755 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm @@ -30,8 +30,8 @@ blood_volume = 0 min_age = 18 max_age = 200 - brute_mod = 1 - burn_mod = 1.4 + brute_mod = 0.8 + burn_mod = 1.5 oxy_mod = 0 item_slowdown_mod = 1.33 @@ -144,6 +144,8 @@ H.equip_to_slot_or_del(metal_stack, slot_in_backpack) spawn(0) //Let their real nif load if they have one + if(!H) //Human could have been deleted in this amount of time. Observing does this, mannequins, etc. + return if(!H.nif) var/obj/item/device/nif/bioadap/new_nif = new() new_nif.quick_implant(H) diff --git a/code/modules/mob/living/carbon/human/species/station/seromi.dm b/code/modules/mob/living/carbon/human/species/station/seromi.dm index 67a7ecf57f..8133cfbfbd 100644 --- a/code/modules/mob/living/carbon/human/species/station/seromi.dm +++ b/code/modules/mob/living/carbon/human/species/station/seromi.dm @@ -169,19 +169,18 @@ if(H.loneliness_stage < 0) H.loneliness_stage = 0 if(world.time >= H.next_loneliness_time) - H << "The nearby company calms you down..." + to_chat(H, "The nearby company calms you down...") H.next_loneliness_time = world.time+500 return - for(var/obj/item/weapon/holder/micro/M in range(1, H)) if(H.loneliness_stage > 0) H.loneliness_stage -= 4 if(H.loneliness_stage < 0) H.loneliness_stage = 0 if(world.time >= H.next_loneliness_time) - H << "[M] calms you down..." + to_chat(H, "[M] calms you down...") H.next_loneliness_time = world.time+500 for(var/obj/effect/overlay/aiholo/A in range(5, H)) @@ -190,7 +189,7 @@ if(H.loneliness_stage < 0) H.loneliness_stage = 0 if(world.time >= H.next_loneliness_time) - H << "[A] calms you down..." + to_chat(H, "[A] calms you down...") H.next_loneliness_time = world.time+500 /*for(var/obj/item/toy/plushie/P in range(5, H)) @@ -199,7 +198,7 @@ if(H.loneliness_stage < 0) H.loneliness_stage = 0 if(world.time >= H.next_loneliness_time) - H << "The [P] calms you down, reminding you of people..." + to_chat(H, "The [P] calms you down, reminding you of people...") H.next_loneliness_time = world.time+500*/ // No company? Suffer :( @@ -221,12 +220,12 @@ if(H.stuttering < hallucination_cap) H.stuttering += 5 if(H.loneliness_stage >= warning_cap) - ms = "[pick("Where are the others?", "Please, there has to be someone nearby!", "I don't want to be alone!")]" + ms = "[pick("Where are the others?", "Please, there has to be someone nearby!", "I don't want to be alone!")]" if(world.time < H.next_loneliness_time) return if(ms != "") - H << ms + to_chat(H, ms) H.next_loneliness_time = world.time+500 /datum/species/teshari/get_vision_flags(var/mob/living/carbon/human/H) diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index 77b88e9378..4337cc1684 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -490,6 +490,7 @@ has_organ = list( O_NUTRIENT = /obj/item/organ/internal/diona/nutrients, O_STRATA = /obj/item/organ/internal/diona/strata, + O_BRAIN = /obj/item/organ/internal/brain/cephalon, O_RESPONSE = /obj/item/organ/internal/diona/node, O_GBLADDER = /obj/item/organ/internal/diona/bladder, O_POLYP = /obj/item/organ/internal/diona/polyp, @@ -563,6 +564,16 @@ if(H.isSynthetic()) H.visible_message("\The [H] collapses into parts, revealing a solitary diona nymph at the core.") + + H.species = GLOB.all_species[SPECIES_HUMAN] // This is hard-set to default the body to a normal FBP, without changing anything. + + for(var/obj/item/organ/internal/diona/Org in H.internal_organs) // Remove Nymph organs. + qdel(Org) + + // Purge the diona verbs. + H.verbs -= /mob/living/carbon/human/proc/diona_split_nymph + H.verbs -= /mob/living/carbon/human/proc/regenerate + return for(var/mob/living/carbon/alien/diona/D in H.contents) diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm index 1540373d68..7826954e37 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm @@ -745,6 +745,8 @@ to_chat(src,"Looks like you lost your chance...") return + T.add_modifier(/datum/modifier/gory_devourment, 10 SECONDS) + //Removing an internal organ if(T_int && T_int.damage >= 25) //Internal organ and it's been severely damaged T.apply_damage(15, BRUTE, T_ext) //Damage the external organ they're going through. diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm index 1a9b893e3a..39ba3b9de2 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm @@ -42,7 +42,7 @@ Widely known for their voracious nature and violent tendencies when stressed or left unfed for long periods of time. \ Most, if not all chimeras possess the ability to undergo some type of regeneration process, at the cost of energy." - wikilink = "https://www.yawn.ocry.com/Xenochimera" + wikilink = "https://yawn.izac.live/Xenochimera" catalogue_data = list(/datum/category_item/catalogue/fauna/xenochimera) @@ -387,7 +387,7 @@ Before they were found they built great cities out of their silk, being united and subjugated in warring factions under great “Star Queens” \ Who forced the working class to build huge, towering cities to attempt to reach the stars, which they worship as gems of great spiritual and magical significance." - wikilink = "https://www.yawn.ocry.com/Vasilissans" + wikilink = "https://yawn.izac.live/Vasilissans" catalogue_data = list(/datum/category_item/catalogue/fauna/vasilissan) diff --git a/code/modules/mob/living/carbon/human/species/station/station_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_vr.dm index 4788d55af6..e2df0e1cc0 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_vr.dm @@ -28,7 +28,7 @@ lifespan, but due to their lust for violence, only a handful have ever survived beyond the age of 80, such as the infamous and \ legendary General Rain Silves who is claimed to have lived to 5000." - wikilink="https://www.yawn.ocry.com/Sergal" + wikilink="https://yawn.izac.live/Sergal" catalogue_data = list(/datum/category_item/catalogue/fauna/sergal) @@ -92,7 +92,7 @@ surviving in open air for long periods of time. However, Akula even today still require a high humidity environment to avoid drying out \ after a few days, which would make life on an arid world like Virgo-Prime nearly impossible if it were not for Skrellean technology to aid them." - wikilink="https://www.yawn.ocry.com/Akula" + wikilink="https://yawn.izac.live/Akula" catalogue_data = list(/datum/category_item/catalogue/fauna/akula) @@ -136,7 +136,7 @@ over and over again. Consequently, they struggle to make copies of same things. Both genders have a voice that echoes a lot. Their natural \ tone oscillates between tenor and soprano. They are excessively noisy when they quarrel in their native language." - wikilink="https://www.yawn.ocry.com/Nevrean" + wikilink="https://yawn.izac.live/Nevrean" catalogue_data = list(/datum/category_item/catalogue/fauna/nevrean) @@ -178,10 +178,9 @@ mountainous areas, they have a differing societal structure than the Flatland Zorren having a more feudal social structure, like the Flatland Zorren, \ the Highland Zorren have also only recently been hired by the Trans-Stellar Corporations, but thanks to the different social structure they seem to \ have adjusted better to their new lives. Though similar fox-like beings have been seen they are different than the Zorren." - wikilink="https://www.yawn.ocry.com/Zorren" + wikilink="https://yawn.izac.live/Zorren" - catalogue_data = list(/datum/category_item/catalogue/fauna/zorren, - /datum/category_item/catalogue/fauna/highzorren) + catalogue_data = list(/datum/category_item/catalogue/fauna/zorren) //primitive_form = "" //We don't have fox-monkey sprites. @@ -199,52 +198,6 @@ ) inherent_verbs = list(/mob/living/proc/shred_limb) -/datum/species/fl_zorren - name = SPECIES_ZORREN_FLAT - name_plural = "Zorren" - icobase = 'icons/mob/human_races/r_fennec_vr.dmi' - deform = 'icons/mob/human_races/r_def_fennec.dmi' - tail = "tail" - icobase_tail = 1 - unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws, /datum/unarmed_attack/bite/sharp) - num_alternate_languages = 3 - secondary_langs = list(LANGUAGE_TERMINUS) - name_language = LANGUAGE_TERMINUS - inherent_verbs = list(/mob/living/carbon/human/proc/lick_wounds) - assisted_langs = list(LANGUAGE_EAL, LANGUAGE_SKRELLIAN, LANGUAGE_SKRELLIANFAR, LANGUAGE_ROOTLOCAL, LANGUAGE_ROOTGLOBAL, LANGUAGE_VOX) //AEIOU edit: Zorren can speak Terminus unassisted. - - min_age = 18 - max_age = 110 - - blurb = "The fox-like Zorren are native to Virgo-Prime, however there are two distinct varieties of Zorren one with large ears and shorter fur, \ - and the other with longer fur that is a bit more vibrant. The long-eared, short-furred Zorren have come to be known as Flatland Zorren as that is \ - where most of their settlements are located. The Flatland Zorren are somewhat tribal and shamanistic as they have only recently started to be \ - hired by the Trans-Stellar Corporations. The other variety of Zorren are known as Highland Zorren as they frequently settle in hilly and/or \ - mountainous areas, they have a differing societal structure than the Flatland Zorren having a more feudal social structure, like the Flatland Zorren, \ - the Highland Zorren have also only recently been hired by the Trans-Stellar Corporations, but thanks to the different social structure they \ - seem to have adjusted better to their new lives. Though similar fox-like beings have been seen they are different than the Zorren." - wikilink="https://www.yawn.ocry.com/Zorren" - - catalogue_data = list(/datum/category_item/catalogue/fauna/zorren, - /datum/category_item/catalogue/fauna/flatzorren) - - //primitive_form = "" //We don't have fennec-monkey sprites. - spawn_flags = SPECIES_CAN_JOIN - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR - - flesh_color = "#AFA59E" - base_color = "#333333" - blood_color = "#240bc4" - color_mult = 1 - inherent_verbs = list(/mob/living/proc/shred_limb) - - heat_discomfort_strings = list( - "Your fur prickles in the heat.", - "You feel uncomfortably warm.", - "Your overheated skin itches." - ) - - /datum/species/vulpkanin name = SPECIES_VULPKANIN name_plural = "Vulpkanin" @@ -270,7 +223,7 @@ to the degree it can cause conflict with more rigorous and strict authorities. They speak a guttural language known as 'Canilunzt' \ which has a heavy emphasis on utilizing tail positioning and ear twitches to communicate intent." - wikilink="https://www.yawn.ocry.com/Vulpkanin" + wikilink="https://yawn.izac.live/Vulpkanin" catalogue_data = list(/datum/category_item/catalogue/fauna/vulpkanin) @@ -285,45 +238,6 @@ min_age = 18 max_age = 110 -/datum/species/xenohybrid - name = SPECIES_XENOHYBRID - name_plural = "Xenomorphs" - icobase = 'icons/mob/human_races/r_xenomorph.dmi' - deform = 'icons/mob/human_races/r_def_xenomorph.dmi' - tail = "tail" - icobase_tail = 1 - unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws, /datum/unarmed_attack/bite/sharp) - darksight = 4 //Better hunters in the dark. - hunger_factor = 0.1 //In exchange, they get hungry a tad faster. - num_alternate_languages = 3 - - min_age = 18 - max_age = 110 - - blurb = "Xenomorphs hybrids are a mixture of xenomorph DNA and some other humanoid species. \ - Xenomorph hyrids mostly have had had their natural aggression removed due to the gene modification process \ - although there are some exceptions, such as when they are harmed. Most xenomorph hybrids are female, due to their natural xenomorph genes, \ - but there are multiple exceptions. All xenomorph hybrids have had their ability to lay eggs containing facehuggers \ - removed if they had the ability to, although hybrids that previously contained this ability is extremely rare." - catalogue_data = list(/datum/category_item/catalogue/fauna/xenohybrid) - wikilink="https://www.yawn.ocry.com/Xenomorph-Hybrid" - - //primitive_form = "" //None for these guys - - spawn_flags = SPECIES_CAN_JOIN - appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR - - blood_color = "#12ff12" - flesh_color = "#201730" - base_color = "#201730" - - heat_discomfort_strings = list( - "Your chitin feels extremely warm.", - "You feel uncomfortably warm.", - "Your chitin feels hot." - ) - inherent_verbs = list(/mob/living/proc/shred_limb) - /datum/species/unathi spawn_flags = SPECIES_CAN_JOIN //Species_can_join is the only spawn flag all the races get, so that none of them will be whitelist only if whitelist is enabled. icobase = 'icons/mob/human_races/r_lizard_vr.dmi' @@ -334,7 +248,7 @@ gluttonous = 0 inherent_verbs = list(/mob/living/proc/shred_limb) descriptors = list() - wikilink="https://www.yawn.ocry.com/Unathi" + wikilink="https://yawn.izac.live/Unathi" /datum/species/tajaran spawn_flags = SPECIES_CAN_JOIN @@ -346,7 +260,8 @@ gluttonous = 0 //Moving this here so I don't have to fix this conflict every time polaris glances at station.dm inherent_verbs = list(/mob/living/proc/shred_limb, /mob/living/carbon/human/proc/lick_wounds) heat_discomfort_level = 295 //Prevents heat discomfort spam at 20c - wikilink="https://www.yawn.ocry.com/Tajaran" + wikilink="https://yawn.izac.live/Tajaran" + agility = 90 /datum/species/skrell spawn_flags = SPECIES_CAN_JOIN @@ -356,14 +271,14 @@ min_age = 18 reagent_tag = null assisted_langs = list(LANGUAGE_EAL, LANGUAGE_ROOTLOCAL, LANGUAGE_ROOTGLOBAL, LANGUAGE_VOX) - wikilink="https://www.yawn.ocry.com/Skrell" + wikilink="https://yawn.izac.live/Skrell" /datum/species/zaddat spawn_flags = SPECIES_CAN_JOIN min_age = 18 gluttonous = 0 descriptors = list() - wikilink="https://www.yawn.ocry.com/Zaddat" + wikilink="https://yawn.izac.live/Zaddat" /datum/species/zaddat/equip_survival_gear(var/mob/living/carbon/human/H) .=..() @@ -376,7 +291,7 @@ /datum/species/diona spawn_flags = SPECIES_CAN_JOIN min_age = 18 - wikilink="https://www.yawn.ocry.com/Diona" + wikilink="https://yawn.izac.live/Diona" /datum/species/teshari mob_size = MOB_SMALL //YW Edit: changed from MOB_MEDIUM to MOB_SMALL @@ -390,7 +305,8 @@ swap_flags = ~HEAVY gluttonous = 0 descriptors = list() - wikilink="https://www.yawn.ocry.com/Teshari" + wikilink="https://yawn.izac.live/Teshari" + agility = 90 inherent_verbs = list( /mob/living/carbon/human/proc/sonar_ping, @@ -401,7 +317,7 @@ /datum/species/shapeshifter/promethean spawn_flags = SPECIES_CAN_JOIN - wikilink="https://www.yawn.ocry.com/Promethean" + wikilink="https://yawn.izac.live/Promethean" /datum/species/human color_mult = 1 @@ -410,7 +326,7 @@ appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_EYE_COLOR min_age = 18 base_color = "#EECEB3" - wikilink="https://www.yawn.ocry.com/Human" + wikilink="https://yawn.izac.live/Human" /datum/species/human/vatgrown spawn_flags = SPECIES_IS_RESTRICTED @@ -425,7 +341,7 @@ descriptors = list( /datum/mob_descriptor/vox_markings = 0 ) - wikilink="https://www.yawn.ocry.com/Vox" + wikilink="https://yawn.izac.live/Vox" datum/species/harpy name = SPECIES_RAPALA @@ -451,7 +367,7 @@ datum/species/harpy who are known for having massive winged arms and talons as feet. They've been clocked at speeds of over 35 miler per hour chasing the planet's many fish-like fauna.\ The Rapalan's home-world 'Verita' is a strangely habitable gas giant, while no physical earth exists, there are fertile floating islands orbiting around the planet from past asteroid activity." - wikilink="https://www.yawn.ocry.com/Rapala" + wikilink="https://yawn.izac.live/Rapala" catalogue_data = list(/datum/category_item/catalogue/fauna/rapala) @@ -574,4 +490,76 @@ datum/species/harpy return SPECIES_SHADEKIN /datum/species/shadekin/can_breathe_water() - return TRUE //they dont quite breathe \ No newline at end of file + return TRUE //they dont quite breathe + +//These species are not really species but are just there for custom species selection + +/datum/species/fl_zorren + name = SPECIES_FENNEC + name_plural = "Fennec" + icobase = 'icons/mob/human_races/r_fennec_vr.dmi' + deform = 'icons/mob/human_races/r_def_fennec.dmi' + tail = "tail" + icobase_tail = 1 + unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws, /datum/unarmed_attack/bite/sharp) + num_alternate_languages = 3 + secondary_langs = list(LANGUAGE_TERMINUS) + name_language = LANGUAGE_TERMINUS + inherent_verbs = list(/mob/living/carbon/human/proc/lick_wounds) + + min_age = 18 + max_age = 80 + + //primitive_form = "" //We don't have fennec-monkey sprites. + spawn_flags = SPECIES_IS_RESTRICTED + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR + + flesh_color = "#AFA59E" + base_color = "#333333" + blood_color = "#240bc4" + color_mult = 1 + inherent_verbs = list(/mob/living/proc/shred_limb) + + heat_discomfort_strings = list( + "Your fur prickles in the heat.", + "You feel uncomfortably warm.", + "Your overheated skin itches." + ) + +/datum/species/xenohybrid + name = SPECIES_XENOHYBRID + name_plural = "Xenomorphs" + icobase = 'icons/mob/human_races/r_xenomorph.dmi' + deform = 'icons/mob/human_races/r_def_xenomorph.dmi' + tail = "tail" + icobase_tail = 1 + unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/claws, /datum/unarmed_attack/bite/sharp) + darksight = 4 //Better hunters in the dark. + hunger_factor = 0.1 //In exchange, they get hungry a tad faster. + num_alternate_languages = 2 + + min_age = 18 + max_age = 80 + + blurb = "Xenomorphs hybrids are a mixture of xenomorph DNA and some other humanoid species. \ + Xenomorph hyrids mostly have had had their natural aggression removed due to the gene modification process \ + although there are some exceptions, such as when they are harmed. Most xenomorph hybrids are female, due to their natural xenomorph genes, \ + but there are multiple exceptions. All xenomorph hybrids have had their ability to lay eggs containing facehuggers \ + removed if they had the ability to, although hybrids that previously contained this ability is extremely rare." + // No wiki page for xenohybrids at present + + //primitive_form = "" //None for these guys + + spawn_flags = SPECIES_IS_RESTRICTED + appearance_flags = HAS_HAIR_COLOR | HAS_LIPS | HAS_UNDERWEAR | HAS_SKIN_COLOR | HAS_EYE_COLOR + + blood_color = "#12ff12" + flesh_color = "#201730" + base_color = "#201730" + + heat_discomfort_strings = list( + "Your chitin feels extremely warm.", + "You feel uncomfortably warm.", + "Your chitin feels hot." + ) + inherent_verbs = list(/mob/living/proc/shred_limb) diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm index 5c91c43489..61f5cc1097 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm @@ -90,6 +90,26 @@ cost = -3 var_changes = list("burn_mod" = 1.4) +//YW ADDITIONS START +/datum/trait/reduced_biocompat + name = "Reduced Biocompatibility" + desc = "For whatever reason, you're one of the unlucky few who don't get as much benefit from modern-day chemicals. Remember to note this down in your medical records!" + cost = -1 + var_changes = list("chem_strength_heal" = 0.8) + +/datum/trait/sensitive_biochem + name = "Sensitive Biochemistry" + desc = "Your biochemistry is a little delicate, rendering you more susceptible to both deadly toxins and the more subtle ones. You'll probably want to list this in your medical records, and perhaps in your exploitable info as well." + cost = -1 + var_changes = list("chem_strength_tox" = 1.25) + +/datum/trait/alcohol_intolerance_advanced + name = "Liver of Air" + desc = "The only way you can hold a drink is if it's in your own two hands, and even then you'd best not inhale too deeply near it. Drinks hit thrice as hard. You may wish to note this down in your medical records, and perhaps your exploitable info as well." + cost = -1 + var_changes = list("alcohol_tolerance" = 3) +//YW ADDITIONS END + /datum/trait/conductive name = "Conductive" desc = "Increases your susceptibility to electric shocks by 25%" diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm index d067d94655..01f3279e6e 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm @@ -27,6 +27,18 @@ excludes = list(/datum/trait/cold_blood, /datum/trait/extreme_cold_blood) // YW Addition +/datum/trait/alcohol_intolerance_basic + name = "Liver of Lilies" + desc = "You have a hard time with alcohol. Maybe you just never took to it, or maybe it doesn't agree with you... either way, drinks hit twice as hard. You may wish to note this down in your medical records, and perhaps your exploitable info as well." + cost = 0 + var_changes = list("alcohol_tolerance" = 2) + +/datum/trait/alcohol_tolerance_basic + name = "Liver of Iron" + desc = "You can hold drinks much better than those lily-livered land-lubbers! Arr! You may wish to note this down in your medical records." + cost = 0 + var_changes = list("alcohol_tolerance" = 0.75) + /* /datum/trait/cryogenic name = "Cryogenic Metabolism" @@ -137,6 +149,16 @@ ..(S,H) H.verbs |= /mob/living/proc/eat_trash +/datum/trait/gem_eater + name = "Expensive Taste" + desc = "You only gain nutrition from ore. There's nothing that sates the appetite better than precious gems, exotic or rare minerals and you have damn fine taste. Anything else is beneath you." + cost = 0 + var_changes = list("gets_food_nutrition" = 0, "eat_ore" = 1) //The verb is given in human.dm + +/datum/trait/gem_eater/apply(var/datum/species/S,var/mob/living/carbon/human/H) + ..(S,H) + H.verbs |= /mob/living/proc/eat_ore + /datum/trait/glowing_eyes name = "Glowing Eyes" desc = "Your eyes show up above darkness. SPOOKY! And kinda edgey too." diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm index 8fb5054860..76e2149388 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm @@ -130,6 +130,26 @@ cost = 3 var_changes = list("burn_mod" = 0.6) +//YW ADDITIONS START +/datum/trait/improved_biocompat + name = "Improved Biocompatibility" + desc = "Your body is naturally (or artificially) more receptive to healing chemicals without being vulnerable to the 'bad stuff'. You heal more efficiently from most chemicals, with no other drawbacks. Remember to note this down in your medical records!" + cost = 2 + var_changes = list("chem_strength_heal" = 1.2) + +/datum/trait/alcohol_tolerance_advanced + name = "Liver of Steel" + desc = "Drinks tremble before your might! You can hold your alcohol twice as well as those blue-bellied barnacle boilers! You may wish to note this down in your medical records." + cost = 1 + var_changes = list("alcohol_tolerance" = 0.5) + +/datum/trait/alcohol_immunity + name = "Liver of Durasteel" + desc = "You've drunk so much that most booze doesn't even faze you. It takes something like a Pan-Galactic or a pint of Deathbell for you to even get slightly buzzed. You may wish to note this down in your medical records." + cost = 2 + var_changes = list("alcohol_tolerance" = 0.25) +//YW ADDITIONS END + /datum/trait/photoresistant name = "Photoresistance" desc = "Decreases stun duration from flashes and other light-based stuns and disabilities by 25%" @@ -165,7 +185,13 @@ /datum/trait/antiseptic_saliva/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() - H.verbs |= /mob/living/carbon/human/proc/lick_wounds + H.verbs |= /mob/living/carbon/human/proc/lick_wounds + +/datum/trait/traceur + name = "Traceur" + desc = "You're capable of parkour and can *flip over low objects (most of the time)." + cost = 2 + var_changes = list("agility" = 90) // YW Addition /datum/trait/bloodsucker_plus diff --git a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_embryo.dm b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_embryo.dm index 53d3576125..59ba73446d 100644 --- a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_embryo.dm +++ b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_embryo.dm @@ -48,25 +48,25 @@ if(prob(1)) affected_mob.emote("cough") if(prob(1)) - affected_mob << "Your throat feels sore." + to_chat(affected_mob, "Your throat feels sore.") if(prob(1)) - affected_mob << "Mucous runs down the back of your throat." + to_chat(affected_mob, "Mucous runs down the back of your throat.") if(4) if(prob(1)) affected_mob.emote("sneeze") if(prob(1)) affected_mob.emote("cough") if(prob(2)) - affected_mob << " Your muscles ache." + to_chat(affected_mob, " Your muscles ache.") if(prob(20)) affected_mob.take_organ_damage(1) if(prob(2)) - affected_mob << "Your stomach hurts." + to_chat(affected_mob, "Your stomach hurts.") if(prob(20)) affected_mob.adjustToxLoss(1) affected_mob.updatehealth() if(5) - affected_mob << "You feel something tearing its way out of your stomach..." + to_chat(affected_mob, "You feel something tearing its way out of your stomach...") affected_mob.adjustToxLoss(10) affected_mob.updatehealth() if(prob(50)) diff --git a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_facehugger.dm b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_facehugger.dm index 2684467147..ff2f630191 100644 --- a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_facehugger.dm +++ b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_facehugger.dm @@ -47,11 +47,11 @@ var/const/MAX_ACTIVE_TIME = 400 ..(user) switch(stat) if(DEAD,UNCONSCIOUS) - user << "[src] is not moving." + to_chat(user, "[src] is not moving.") if(CONSCIOUS) - user << "[src] seems to be active." + to_chat(user, "[src] seems to be active.") if (sterile) - user << "It looks like the proboscis has been removed." + to_chat(user, "It looks like the proboscis has been removed.") return /obj/item/clothing/mask/facehugger/attackby(obj/item/I, mob/user) diff --git a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_powers.dm b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_powers.dm index 20255bbae9..543acb842f 100644 --- a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_powers.dm +++ b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_powers.dm @@ -72,7 +72,7 @@ amount = abs(round(amount)) if(check_alien_ability(amount,0,O_PLASMA)) M.gain_plasma(amount) - M << "[src] has transfered [amount] plasma to you." + to_chat(M, "[src] has transfered [amount] plasma to you.") to_chat(src, "You have transferred [amount] plasma to [M].") return diff --git a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm index 9e2a949402..8a72f6ecf8 100644 --- a/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm +++ b/code/modules/mob/living/carbon/human/species/xenomorphs/alien_species.dm @@ -136,7 +136,7 @@ H.adjustOxyLoss(-heal_rate) H.adjustToxLoss(-heal_rate) if (prob(5)) - H << "You feel a soothing sensation come over you..." + to_chat(H, "You feel a soothing sensation come over you...") return 1 //next internal organs @@ -144,7 +144,7 @@ if(I.damage > 0) I.damage = max(I.damage - heal_rate, 0) if (prob(5)) - H << "You feel a soothing sensation within your [I.parent_organ]..." + to_chat(H, "You feel a soothing sensation within your [I.parent_organ]...") return 1 //next mend broken bones, approx 10 ticks each @@ -152,7 +152,7 @@ if (E.status & ORGAN_BROKEN) if (prob(mend_prob)) if (E.mend_fracture()) - H << "You feel something mend itself inside your [E.name]." + to_chat(H, "You feel something mend itself inside your [E.name].") return 1 return 0 diff --git a/code/modules/mob/living/carbon/human/stripping.dm b/code/modules/mob/living/carbon/human/stripping.dm index 3029cdbf8f..61bb766140 100644 --- a/code/modules/mob/living/carbon/human/stripping.dm +++ b/code/modules/mob/living/carbon/human/stripping.dm @@ -61,7 +61,7 @@ if(!istype(target_slot)) // They aren't holding anything valid and there's nothing to remove, why are we even here? return if(!target_slot.canremove) - user << "You cannot remove \the [src]'s [target_slot.name]." + to_chat(user, "You cannot remove \the [src]'s [target_slot.name].") return stripping = 1 @@ -90,7 +90,7 @@ // Empty out everything in the target's pockets. /mob/living/carbon/human/proc/empty_pockets(var/mob/living/user) if(!r_store && !l_store) - user << "\The [src] has nothing in their pockets." + to_chat(user, "\The [src] has nothing in their pockets.") return if(r_store) unEquip(r_store) @@ -102,10 +102,10 @@ /mob/living/carbon/human/proc/toggle_sensors(var/mob/living/user) var/obj/item/clothing/under/suit = w_uniform if(!suit) - user << "\The [src] is not wearing a suit with sensors." + to_chat(user, "\The [src] is not wearing a suit with sensors.") return if (suit.has_sensor >= 2) - user << "\The [src]'s suit sensor controls are locked." + to_chat(user, "\The [src]'s suit sensor controls are locked.") return add_attack_logs(user,src,"Adjusted suit sensor level") suit.set_sensors(user) @@ -117,7 +117,7 @@ if(istype(wear_suit,/obj/item/clothing/suit/space)) var/obj/item/clothing/suit/space/suit = wear_suit if(suit.supporting_limbs && suit.supporting_limbs.len) - user << "You cannot remove the splints - [src]'s [suit] is supporting some of the breaks." + to_chat(user, "You cannot remove the splints - [src]'s [suit] is supporting some of the breaks.") can_reach_splints = 0 if(can_reach_splints) @@ -133,7 +133,7 @@ if(removed_splint) visible_message("\The [user] removes \the [src]'s splints!") else - user << "\The [src] has no splints to remove." + to_chat(user, "\The [src] has no splints to remove.") // Set internals on or off. /mob/living/carbon/human/proc/toggle_internals(var/mob/living/user) diff --git a/code/modules/mob/living/carbon/human/unarmed_attack.dm b/code/modules/mob/living/carbon/human/unarmed_attack.dm index 43a5ae66f2..57001713e9 100644 --- a/code/modules/mob/living/carbon/human/unarmed_attack.dm +++ b/code/modules/mob/living/carbon/human/unarmed_attack.dm @@ -101,7 +101,7 @@ var/global/list/sparring_attack_cache = list() eyes.take_damage(rand(3,4), 1) user.visible_message("[user] presses [TU.his] [eye_attack_text] into [target]'s [eyes.name]!") var/eye_pain = eyes.organ_can_feel_pain() - target << "You experience[(eye_pain) ? "" : " immense pain as you feel" ] [eye_attack_text_victim] being pressed into your [eyes.name][(eye_pain)? "." : "!"]" + to_chat(target, "You experience[(eye_pain) ? "" : " immense pain as you feel" ] [eye_attack_text_victim] being pressed into your [eyes.name][(eye_pain)? "." : "!"]") return user.visible_message("[user] attempts to press [TU.his] [eye_attack_text] into [target]'s eyes, but [TT.he] [TT.does]n't have any!") diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 1889233f24..4ae44d10de 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -581,6 +581,15 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() if(wear_suit && (wear_suit.flags_inv & HIDEJUMPSUIT) && !istype(wear_suit, /obj/item/clothing/suit/space/rig)) return //Wearing a suit that prevents uniform rendering + var/obj/item/clothing/under/under = w_uniform + + var/uniform_sprite + + if(under.index) + uniform_sprite = "[INV_W_UNIFORM_DEF_ICON]_[under.index].dmi" + else + uniform_sprite = "[INV_W_UNIFORM_DEF_ICON].dmi" + //Build a uniform sprite //VOREStation Edit start. var/icon/c_mask = null @@ -588,9 +597,8 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() var/obj/item/clothing/suit/S = wear_suit if(!(wear_suit && ((wear_suit.flags_inv & HIDETAIL) || (istype(S) && S.taurized)))) //Clip the lower half of the uniform off using the tail's clip mask. c_mask = new /icon(tail_style.clip_mask_icon, tail_style.clip_mask_state) - overlays_standing[UNIFORM_LAYER] = w_uniform.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_w_uniform_str, default_icon = INV_W_UNIFORM_DEF_ICON, default_layer = UNIFORM_LAYER, clip_mask = c_mask) + overlays_standing[UNIFORM_LAYER] = w_uniform.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_w_uniform_str, default_icon = uniform_sprite, default_layer = UNIFORM_LAYER, clip_mask = c_mask) //VOREStation Edit end. - apply_layer(UNIFORM_LAYER) /mob/living/carbon/human/update_inv_wear_id() @@ -759,22 +767,25 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() if(!wear_suit) return //No point, no suit. - // Part of splitting the suit sprites up - var/iconFile = INV_SUIT_DEF_ICON - var/obj/item/clothing/suit/S //VOREStation edit - break this var out a level for use below. - if(istype(wear_suit, /obj/item/clothing/suit)) - S = wear_suit - if(S.update_icon_define) - iconFile = S.update_icon_define + var/obj/item/clothing/suit/suit = wear_suit + var/suit_sprite + + if(suit.index) + suit_sprite = "[INV_SUIT_DEF_ICON]_[suit.index].dmi" + else if(istype(suit, /obj/item/clothing) && !isnull(suit.update_icon_define)) + suit_sprite = suit.update_icon_define + else + suit_sprite = "[INV_SUIT_DEF_ICON].dmi" //VOREStation Edit start. var/icon/c_mask = null var/tail_is_rendered = (overlays_standing[TAIL_LAYER] || overlays_standing[TAIL_LAYER_ALT]) var/valid_clip_mask = (tail_style && tail_style.clip_mask_icon && tail_style.clip_mask_state) - if(tail_is_rendered && valid_clip_mask && !(S && S.taurized)) //Clip the lower half of the suit off using the tail's clip mask for taurs since taur bodies aren't hidden. + if(tail_is_rendered && valid_clip_mask && !(suit && istype(suit) && suit.taurized)) //Clip the lower half of the suit off using the tail's clip mask for taurs since taur bodies aren't hidden. c_mask = new /icon(tail_style.clip_mask_icon, tail_style.clip_mask_state) - overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_suit_str, default_icon = iconFile, default_layer = SUIT_LAYER, clip_mask = c_mask) + overlays_standing[SUIT_LAYER] = wear_suit.make_worn_icon(body_type = species.get_bodytype(src), slot_name = slot_wear_suit_str, default_icon = suit_sprite, default_layer = SUIT_LAYER, clip_mask = c_mask) + //VOREStation Edit end. apply_layer(SUIT_LAYER) diff --git a/code/modules/mob/living/carbon/human/update_icons_vr.dm b/code/modules/mob/living/carbon/human/update_icons_vr.dm index 436f76d774..a8b941ebc3 100644 --- a/code/modules/mob/living/carbon/human/update_icons_vr.dm +++ b/code/modules/mob/living/carbon/human/update_icons_vr.dm @@ -60,6 +60,11 @@ var/icon/wing_s = new/icon("icon" = wing_style.icon, "icon_state" = flapping && wing_style.ani_state ? wing_style.ani_state : wing_style.icon_state) if(wing_style.do_colouration) wing_s.Blend(rgb(src.r_wing, src.g_wing, src.b_wing), wing_style.color_blend_mode) + if(wing_style.extra_overlay) + var/icon/overlay = new/icon("icon" = wing_style.icon, "icon_state" = wing_style.extra_overlay) + overlay.Blend(rgb(src.r_wing2, src.g_wing2, src.b_wing2), wing_style.color_blend_mode) + wing_s.Blend(overlay, ICON_OVERLAY) + qdel(overlay) return image(wing_s) // TODO - Move this to where it should go ~Leshana diff --git a/code/modules/mob/living/carbon/metroid/examine.dm b/code/modules/mob/living/carbon/metroid/examine.dm index ecc09992aa..9e96e3257d 100644 --- a/code/modules/mob/living/carbon/metroid/examine.dm +++ b/code/modules/mob/living/carbon/metroid/examine.dm @@ -27,5 +27,5 @@ msg += "It is radiating with massive levels of electrical activity!\n" msg += "*---------*" - user << msg + to_chat(user,msg) return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/metroid/items.dm b/code/modules/mob/living/carbon/metroid/items.dm index 62a99850e1..45c30cb3e3 100644 --- a/code/modules/mob/living/carbon/metroid/items.dm +++ b/code/modules/mob/living/carbon/metroid/items.dm @@ -16,12 +16,12 @@ /obj/item/slime_extract/attackby(obj/item/O as obj, mob/user as mob) if(istype(O, /obj/item/weapon/slimesteroid2)) if(enhanced == 1) - user << " This extract has already been enhanced!" + to_chat(user, " This extract has already been enhanced!") return ..() if(Uses == 0) - user << " You can't enhance a used extract!" + to_chat(user, " You can't enhance a used extract!") return ..() - user <<"You apply the enhancer. It now has triple the amount of uses." + to_chat(user, "You apply the enhancer. It now has triple the amount of uses.") Uses = 3 enhanced = 1 qdel(O) @@ -131,16 +131,16 @@ /obj/item/slimepotion/docility/attack(mob/living/carbon/slime/M as mob, mob/user as mob) if(!istype(M, /mob/living/carbon/slime))//If target is not a slime. - user << " The potion only works on slimes!" + to_chat(user, " The potion only works on slimes!") return ..() // if(M.is_adult) //Can't tame adults -// user << " Only baby slimes can be tamed!" +// to_chat(user, " Only baby slimes can be tamed!") // return..() if(M.stat) - user << " The slime is dead!" + to_chat(user, " The slime is dead!") return..() if(M.mind) - user << " The slime resists!" + to_chat(user, " The slime resists!") return ..() var/mob/living/simple_mob/slime/pet = new /mob/living/simple_mob/slime(M.loc) pet.icon_state = "[M.colour] [M.is_adult ? "adult" : "baby"] slime" @@ -188,20 +188,20 @@ attack(mob/living/carbon/slime/M as mob, mob/user as mob) if(!istype(M, /mob/living/carbon/slime/))//If target is not a slime. - user << " The potion only works on slimes!" + to_chat(user, " The potion only works on slimes!") return ..() if(M.stat) - user << " The slime is dead!" + to_chat(user, " The slime is dead!") return..() if(M.mind) - user << " The slime resists!" + to_chat(user, " The slime resists!") return ..() var/mob/living/simple_mob/adultslime/pet = new /mob/living/simple_mob/adultslime(M.loc) pet.icon_state = "[M.colour] adult slime" pet.icon_living = "[M.colour] adult slime" pet.icon_dead = "[M.colour] baby slime dead" pet.colour = "[M.colour]" - user <<"You feed the slime the potion, removing it's powers and calming it." + to_chat(user, "You feed the slime the potion, removing it's powers and calming it.") qdel(M) var/newname = sanitize(input(user, "Would you like to give the slime a name?", "Name your new pet", "pet slime") as null|text, MAX_NAME_LEN) @@ -220,19 +220,19 @@ attack(mob/living/carbon/slime/M as mob, mob/user as mob) if(!istype(M, /mob/living/carbon/slime))//If target is not a slime. - user << " The steroid only works on baby slimes!" + to_chat(user, " The steroid only works on baby slimes!") return ..() if(M.is_adult) //Can't tame adults - user << " Only baby slimes can use the steroid!" + to_chat(user, " Only baby slimes can use the steroid!") return..() if(M.stat) - user << " The slime is dead!" + to_chat(user, " The slime is dead!") return..() if(M.cores == 3) - user <<" The slime already has the maximum amount of extract!" + to_chat(user, " The slime already has the maximum amount of extract!") return..() - user <<"You feed the slime the steroid. It now has triple the amount of extract." + to_chat(user, "You feed the slime the steroid. It now has triple the amount of extract.") M.cores = 3 qdel(src) @@ -245,12 +245,12 @@ /*afterattack(obj/target, mob/user , flag) if(istype(target, /obj/item/slime_extract)) if(target.enhanced == 1) - user << " This extract has already been enhanced!" + to_chat(user, " This extract has already been enhanced!") return ..() if(target.Uses == 0) - user << " You can't enhance a used extract!" + to_chat(user, " You can't enhance a used extract!") return ..() - user <<"You apply the enhancer. It now has triple the amount of uses." + to_chat(user, "You apply the enhancer. It now has triple the amount of uses.") target.Uses = 3 target.enahnced = 1 qdel(src)*/ @@ -288,12 +288,12 @@ ghost = O break if(!ghost) - user << "The rune fizzles uselessly. There is no spirit nearby." + to_chat(user, "The rune fizzles uselessly. There is no spirit nearby.") return var/mob/living/carbon/human/G = new(src.loc) G.set_species("Golem") G.key = ghost.key - G << "You are an adamantine golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. Serve [user], and assist them in completing their goals at any cost." + to_chat(G, "You are an adamantine golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. Serve [user], and assist them in completing their goals at any cost.") qdel(src) @@ -302,7 +302,7 @@ if(G.client) var/area/A = get_area(src) if(A) - G << "Golem rune created in [A.name]." + to_chat(G, "Golem rune created in [A.name].") /mob/living/carbon/slime/has_eyes() return 0 @@ -360,8 +360,7 @@ . = ..() reagents.add_reagent("nutriment", 4) reagents.add_reagent("slimejelly", 1) - spawn(rand(1200,1500))//the egg takes a while to "ripen" - Grow() + addtimer(CALLBACK(src, ./proc/Grow), rand(120 SECONDS, 150 SECONDS)) /obj/item/weapon/reagent_containers/food/snacks/egg/slime/proc/Grow() grown = 1 diff --git a/code/modules/mob/living/carbon/metroid/life.dm b/code/modules/mob/living/carbon/metroid/life.dm index f11d9d137d..7643ea3d2e 100644 --- a/code/modules/mob/living/carbon/metroid/life.dm +++ b/code/modules/mob/living/carbon/metroid/life.dm @@ -102,18 +102,18 @@ else if (src.paralysis || src.stunned || src.weakened || (status_flags & FAKEDEATH)) //Stunned etc. if (src.stunned > 0) - src.stat = 0 + src.set_stat(CONSCIOUS) if (src.weakened > 0) src.lying = 0 - src.stat = 0 + src.set_stat(CONSCIOUS) if (src.paralysis > 0) src.blinded = 0 src.lying = 0 - src.stat = 0 + src.set_stat(CONSCIOUS) else src.lying = 0 - src.stat = 0 + src.set_stat(CONSCIOUS) if (src.stuttering) src.stuttering = 0 diff --git a/code/modules/mob/living/carbon/metroid/metroid.dm b/code/modules/mob/living/carbon/metroid/metroid.dm index 31fe6b3152..a871d7e3ac 100644 --- a/code/modules/mob/living/carbon/metroid/metroid.dm +++ b/code/modules/mob/living/carbon/metroid/metroid.dm @@ -310,7 +310,7 @@ if(W.force > 0) attacked += 10 if(prob(25)) - user << "[W] passes right through [src]!" + to_chat(user, "[W] passes right through [src]!") return if(Discipline && prob(50)) // wow, buddy, why am I getting attacked?? Discipline = 0 diff --git a/code/modules/mob/living/carbon/metroid/powers.dm b/code/modules/mob/living/carbon/metroid/powers.dm index a1fc03445e..2b55a95afc 100644 --- a/code/modules/mob/living/carbon/metroid/powers.dm +++ b/code/modules/mob/living/carbon/metroid/powers.dm @@ -9,7 +9,7 @@ var t = invalidFeedTarget(M) if (t) - src << t + to_chat(src,t) return Feedon(M) @@ -64,7 +64,7 @@ else if (istype(M, /mob/living/carbon)) var/mob/living/carbon/C = M if (C.can_feel_pain()) - M << "[painMes]" + to_chat(M, "[painMes]") gain_nutrition(rand(20,25)) @@ -98,7 +98,8 @@ /mob/living/carbon/slime/proc/Feedstop() if(Victim) - if(Victim.client) Victim << "[src] has let go of your head!" + if(Victim.client) + to_chat(Victim, "[src] has let go of your head!") Victim = null /mob/living/carbon/slime/proc/UpdateFeed(var/mob/M) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 55bddfe1bd..aeab794552 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -10,7 +10,7 @@ */ /mob/living/proc/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/used_weapon = null, var/sharp = 0, var/edge = 0) if(Debug2) - world.log << "## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked]." + to_world_log("## DEBUG: apply_damage() was called on [src], with [damage] damage, and an armor value of [blocked].") if(!damage || (blocked >= 100)) return 0 if(soaked) @@ -64,7 +64,7 @@ /mob/living/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1) if(Debug2) - world.log << "## DEBUG: apply_effect() was called. The type of effect is [effecttype]. Blocked by [blocked]." + to_world_log("## DEBUG: apply_effect() was called. The type of effect is [effecttype]. Blocked by [blocked].") if(!effect || (blocked >= 100)) return 0 blocked = (100-blocked)/100 @@ -109,5 +109,9 @@ if(drowsy) apply_effect(drowsy, DROWSY, blocked) if(agony) apply_effect(agony, AGONY, blocked) if(flammable) adjust_fire_stacks(flammable) - if(ignite) IgniteMob() + if(ignite) + if(ignite >= 3) + add_modifier(/datum/modifier/fire/stack_managed/intense, 60 SECONDS) + else + add_modifier(/datum/modifier/fire/stack_managed, 45 * ignite SECONDS) return 1 diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index 4a651af999..22db034374 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -33,8 +33,25 @@ //Drops the item in our active hand. TODO: rename this to drop_active_hand or something /mob/living/drop_item(var/atom/Target) - if(hand) return drop_l_hand(Target) - else return drop_r_hand(Target) + var/obj/item/item_dropped = null + + if (hand) + item_dropped = l_hand + . = drop_l_hand(Target) + else + item_dropped = r_hand + . = drop_r_hand(Target) + + if (istype(item_dropped) && !QDELETED(item_dropped) && is_preference_enabled(/datum/client_preference/drop_sounds)) + addtimer(CALLBACK(src, .proc/make_item_drop_sound, item_dropped), 1) + +/mob/proc/make_item_drop_sound(obj/item/I) + if(QDELETED(I)) + return + + if(I.drop_sound) + playsound(I, I.drop_sound, 25, 0, preference = /datum/client_preference/drop_sounds) + //Drops the item in our left hand /mob/living/drop_l_hand(var/atom/Target) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 0b37b89076..1e66f65478 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -99,11 +99,11 @@ updatehealth() if(stat != DEAD) if(paralysis) - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) else if (status_flags & FAKEDEATH) - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) else - stat = CONSCIOUS + set_stat(CONSCIOUS) return 1 /mob/living/proc/handle_statuses() @@ -243,5 +243,5 @@ var/distance = abs(current-adjust_to) //Used for how long to animate for if(distance < 0.01) return //We're already all set - //world << "[src] in B:[round(brightness,0.1)] C:[round(current,0.1)] A2:[round(adjust_to,0.1)] D:[round(distance,0.01)] T:[round(distance*10 SECONDS,0.1)]" + //to_world("[src] in B:[round(brightness,0.1)] C:[round(current,0.1)] A2:[round(adjust_to,0.1)] D:[round(distance,0.01)] T:[round(distance*10 SECONDS,0.1)]") animate(dsoverlay, alpha = (adjust_to*255), time = (distance*10 SECONDS)) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 55daf15aa4..2476925fd2 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -16,7 +16,7 @@ dsma.blend_mode = BLEND_ADD dsoverlay.appearance = dsma - selected_image = image(icon = 'icons/mob/screen1.dmi', loc = src, icon_state = "centermarker") + selected_image = image(icon = buildmode_hud, loc = src, icon_state = "ai_sel") /mob/living/Destroy() dsoverlay.loc = null //I'll take my coat with me @@ -32,6 +32,8 @@ if(buckled) buckled.unbuckle_mob(src, TRUE) qdel(selected_image) + QDEL_NULL(vorePanel) //VOREStation Add + QDEL_LIST_NULL(vore_organs) //VOREStation Add return ..() //mob verbs are faster than object verbs. See mob/verb/examine. @@ -245,11 +247,10 @@ default behaviour is: /mob/living/proc/updatehealth() if(status_flags & GODMODE) health = 100 - stat = CONSCIOUS + set_stat(CONSCIOUS) else health = getMaxHealth() - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss() - halloss - //This proc is used for mobs which are affected by pressure to calculate the amount of pressure that actually //affects them once clothing is factored in. ~Errorage /mob/living/proc/calculate_affecting_pressure(var/pressure) @@ -259,7 +260,7 @@ default behaviour is: //sort of a legacy burn method for /electrocute, /shock, and the e_chair /mob/living/proc/burn_skin(burn_amount) if(istype(src, /mob/living/carbon/human)) - //world << "DEBUG: burn_skin(), mutations=[mutations]" + //to_world("DEBUG: burn_skin(), mutations=[mutations]") if(mShock in src.mutations) //shockproof return 0 if (COLD_RESISTANCE in src.mutations) //fireproof @@ -293,7 +294,7 @@ default behaviour is: if(actual < desired) temperature = desired // if(istype(src, /mob/living/carbon/human)) -// world << "[src] ~ [src.bodytemperature] ~ [temperature]" +// to_world("[src] ~ [src.bodytemperature] ~ [temperature]") return temperature @@ -449,7 +450,7 @@ default behaviour is: if(!isnull(M.incoming_hal_damage_percent)) amount *= M.incoming_hal_damage_percent if(!isnull(M.disable_duration_percent)) - amount *= M.incoming_hal_damage_percent + amount *= M.disable_duration_percent else if(amount < 0) for(var/datum/modifier/M in modifiers) if(!isnull(M.incoming_healing_percent)) @@ -742,7 +743,7 @@ default behaviour is: timeofdeath = 0 // restore us to conciousness - stat = CONSCIOUS + set_stat(CONSCIOUS) // make the icons look correct regenerate_icons() @@ -773,9 +774,8 @@ default behaviour is: else to_chat(usr, "[src] does not have any stored infomation!") else - usr << "OOC Metadata is not supported by this server!" + to_chat(usr, "OOC Metadata is not supported by this server!") //VOREStation Edit End - Making it so SSD people have prefs with fallback to original style. - return /mob/living/Move(a, b, flag) if (buckled && buckled.loc != a) //not updating position @@ -1195,7 +1195,7 @@ default behaviour is: var/matrix/M = matrix() M.Scale(desired_scale_x, desired_scale_y) M.Translate(0, 16*(desired_scale_y-1)) - //animate(src, transform = M, time = 10) //VOREStation edit + src.transform = M //VOREStation edit // This handles setting the client's color variable, which makes everything look a specific color. // This proc is here so it can be called without needing to check if the client exists, or if the client relogs. @@ -1305,6 +1305,9 @@ default behaviour is: src.inertia_dir = get_dir(target, src) step(src, inertia_dir) + if(istype(item,/obj/item)) + var/obj/item/W = item + W.randpixel_xy() /* if(istype(src.loc, /turf/space) || (src.flags & NOGRAV)) //they're in space, move em one space in the opposite direction diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 28f5644840..428cff3659 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -13,7 +13,7 @@ */ /mob/living/proc/run_armor_check(var/def_zone = null, var/attack_flag = "melee", var/armour_pen = 0, var/absorb_text = null, var/soften_text = null) if(Debug2) - world.log << "## DEBUG: getarmor() was called." + to_world_log("## DEBUG: getarmor() was called.") if(armour_pen >= 100) return 0 //might as well just skip the processing @@ -23,7 +23,7 @@ var/armor_variance_range = round(armor * 0.25) //Armor's effectiveness has a +25%/-25% variance. var/armor_variance = rand(-armor_variance_range, armor_variance_range) //Get a random number between -25% and +25% of the armor's base value if(Debug2) - world.log << "## DEBUG: The range of armor variance is [armor_variance_range]. The variance picked by RNG is [armor_variance]." + to_world_log("## DEBUG: The range of armor variance is [armor_variance_range]. The variance picked by RNG is [armor_variance].") armor = min(armor + armor_variance, 100) //Now we calcuate damage using the new armor percentage. armor = max(armor - armour_pen, 0) //Armor pen makes armor less effective. @@ -39,7 +39,7 @@ else to_chat(src, "Your armor softens the blow!") if(Debug2) - world.log << "## DEBUG: Armor when [src] was attacked was [armor]." + to_world_log("## DEBUG: Armor when [src] was attacked was [armor].") return armor /* @@ -129,7 +129,6 @@ //Stun Beams if(P.taser_effect) stun_effect_act(0, P.agony, def_zone, P) - src <<"You have been hit by [P]!" if(!P.nodamage) apply_damage(P.damage, P.damage_type, def_zone, absorb, soaked, 0, P, sharp=proj_sharp, edge=proj_edge) qdel(P) @@ -168,6 +167,15 @@ /mob/living/emp_act(severity) var/list/L = src.get_contents() + + if(LAZYLEN(modifiers)) + for(var/datum/modifier/M in modifiers) + if(!isnull(M.emp_modifier)) + severity = CLAMP(severity + M.emp_modifier, 1, 5) + + if(severity == 5) // Effectively nullified. + return + for(var/obj/O in L) O.emp_act(severity) ..() @@ -207,6 +215,9 @@ var/soaked = get_armor_soak(def_zone, armor_check, armor_pen) var/absorb = run_armor_check(def_zone, armor_check, armor_pen) + if(ai_holder) + ai_holder.react_to_attack(B) + apply_damage(damage, damage_type, def_zone, absorb, soaked) /mob/living/proc/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) @@ -364,6 +375,9 @@ handle_light() update_fire() + if(has_modifier_of_type(/datum/modifier/fire)) + remove_modifiers_of_type(/datum/modifier/fire) + /mob/living/proc/update_fire() return diff --git a/code/modules/mob/living/living_defines_vr.dm b/code/modules/mob/living/living_defines_vr.dm index 6e9b96fc13..a9b8033646 100644 --- a/code/modules/mob/living/living_defines_vr.dm +++ b/code/modules/mob/living/living_defines_vr.dm @@ -9,4 +9,9 @@ var/custom_say = null var/custom_ask = null var/custom_exclaim = null - var/custom_whisper = null \ No newline at end of file + var/custom_whisper = null + +//YW Add Start +/mob + var/wingdings = 0 +//Yw Add End diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 3f6c8411d1..f730077d70 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -9,4 +9,13 @@ return (!mover.density || !density || lying) /mob/CanZASPass(turf/T, is_zone) - return ATMOS_PASS_YES \ No newline at end of file + return ATMOS_PASS_YES + +/mob/living/SelfMove(turf/n, direct) + // If on walk intent, don't willingly step into hazardous tiles. + // Unless the walker is confused. + if(m_intent == "walk" && confused <= 0) + if(!n.is_safe_to_enter(src)) + to_chat(src, span("warning", "\The [n] is dangerous to move into.")) + return FALSE // In case any code wants to know if movement happened. + return ..() // Parent call should make the mob move. \ No newline at end of file diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index f15ca63904..9bb1220ce5 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -8,6 +8,7 @@ var/list/department_radio_keys = list( ":n" = "Science", ".n" = "Science", ":m" = "Medical", ".m" = "Medical", ":e" = "Engineering", ".e" = "Engineering", + ":k" = "Response Team", ".k" = "Response Team", //TFF 11/3/20 - Add Response Team to channels usable rather than resorting to :H or such., ":s" = "Security", ".s" = "Security", ":w" = "whisper", ".w" = "whisper", ":t" = "Mercenary", ".t" = "Mercenary", @@ -16,6 +17,7 @@ var/list/department_radio_keys = list( ":v" = "Service", ".v" = "Service", ":p" = "AI Private", ".p" = "AI Private", ":y" = "Explorer", ".y" = "Explorer", + ":t" = "Talon", ".t" = "Talon", //VOREStation Add, ":R" = "right ear", ".R" = "right ear", ":L" = "left ear", ".L" = "left ear", @@ -25,6 +27,7 @@ var/list/department_radio_keys = list( ":N" = "Science", ".N" = "Science", ":M" = "Medical", ".M" = "Medical", ":E" = "Engineering", ".E" = "Engineering", + ":k" = "Response Team", ".k" = "Response Team", //TFF 11/3/20 - Add Response Team to channels usable rather than resorting to :H or such., ":S" = "Security", ".S" = "Security", ":W" = "whisper", ".W" = "whisper", ":T" = "Mercenary", ".T" = "Mercenary", @@ -33,6 +36,7 @@ var/list/department_radio_keys = list( ":V" = "Service", ".V" = "Service", ":P" = "AI Private", ".P" = "AI Private", ":Y" = "Explorer", ".Y" = "Explorer", + ":T" = "Talon", ".T" = "Talon", //VOREStation Add, //kinda localization -- rastaf0 //same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding. @@ -113,6 +117,11 @@ proc/get_radio_key_from_channel(var/channel) whispering = 1 . = 1 //VOREStation Edit End + //YW Edit start + if(wingdings) + message = span("wingdings",(message)) + . = 1 + //YW Edit End message_data[1] = message message_data[2] = verb diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index dbfff1d8cc..4a5e3182d8 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -197,7 +197,7 @@ var/list/ai_verbs_default = list( if(i != common_radio.channels.len) radio_text += ", " - src << radio_text + to_chat(src,radio_text) // Vorestation Edit: Meta Info for AI's. Mostly used for Holograms if (client) @@ -297,7 +297,7 @@ var/list/ai_verbs_default = list( /obj/machinery/ai_powersupply name="Power Supply" active_power_usage=50000 // Station AIs use significant amounts of power. This, when combined with charged SMES should mean AI lasts for 1hr without external power. - use_power = 2 + use_power = USE_POWER_ACTIVE power_channel = EQUIP var/mob/living/silicon/ai/powered_ai = null invisibility = 100 @@ -325,14 +325,14 @@ var/list/ai_verbs_default = list( qdel(src) return if(powered_ai.APU_power) - use_power = 0 + update_use_power(USE_POWER_OFF) return if(!powered_ai.anchored) loc = powered_ai.loc - use_power = 0 + update_use_power(USE_POWER_OFF) use_power(50000) // Less optimalised but only called if AI is unwrenched. This prevents usage of wrenching as method to keep AI operational without power. Intellicard is for that. if(powered_ai.anchored) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) /mob/living/silicon/ai/proc/pick_icon() set category = "AI Settings" @@ -416,13 +416,13 @@ var/list/ai_verbs_default = list( if(check_unable(AI_CHECK_WIRELESS)) return if(emergency_message_cooldown) - usr << "Arrays recycling. Please stand by." + to_chat(usr, "Arrays recycling. Please stand by.") return var/input = sanitize(input(usr, "Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "")) if(!input) return CentCom_announce(input, usr) - usr << "Message transmitted." + to_chat(usr, "Message transmitted.") log_game("[key_name(usr)] has made an IA [using_map.boss_short] announcement: [input]") emergency_message_cooldown = 1 spawn(300) @@ -775,7 +775,7 @@ var/list/ai_verbs_default = list( var/obj/effect/overlay/aiholo/hologram = holo.masters[src] walk(hologram, 0) //VOREStation Add End - usr << "Your hologram will [hologram_follow ? "follow" : "no longer follow"] you now." + to_chat(usr, "Your hologram will [hologram_follow ? "follow" : "no longer follow"] you now.") /mob/living/silicon/ai/proc/check_unable(var/flags = 0, var/feedback = 1) diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm index 5c2ced94e9..d569730688 100644 --- a/code/modules/mob/living/silicon/ai/examine.dm +++ b/code/modules/mob/living/silicon/ai/examine.dm @@ -34,7 +34,7 @@ if(hardware && (hardware.owner == src)) msg += "
    " msg += hardware.get_examine_desc() - user << msg + to_chat(user,msg) user.showLaws(src) return diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm index 0ce81678be..87751aa006 100644 --- a/code/modules/mob/living/silicon/ai/latejoin.dm +++ b/code/modules/mob/living/silicon/ai/latejoin.dm @@ -16,7 +16,7 @@ var/global/list/empty_playable_ai_cores = list() set desc = "Enter intelligence storage. This is functionally equivalent to cryo or robotic storage, freeing up your job slot." if(ticker && ticker.mode && ticker.mode.name == "AI malfunction") - usr << "You cannot use this verb in malfunction. If you need to leave, please adminhelp." + to_chat(usr, "You cannot use this verb in malfunction. If you need to leave, please adminhelp.") return // Guard against misclicks, this isn't the sort of thing we want happening accidentally diff --git a/code/modules/mob/living/silicon/ai/laws.dm b/code/modules/mob/living/silicon/ai/laws.dm index f36c32bb8d..74816aeeb8 100755 --- a/code/modules/mob/living/silicon/ai/laws.dm +++ b/code/modules/mob/living/silicon/ai/laws.dm @@ -10,7 +10,7 @@ who = world else who = src - who << "Obey these laws:" + to_chat(who, "Obey these laws:") src.laws_sanity_check() src.laws.show_laws(who) diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 0d429fa089..b1398d3170 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -169,7 +169,7 @@ /mob/living/silicon/ai/updatehealth() if(status_flags & GODMODE) health = 100 - stat = CONSCIOUS + set_stat(CONSCIOUS) setOxyLoss(0) else health = 100 - getFireLoss() - getBruteLoss() // Oxyloss is not part of health as it represents AIs backup power. AI is immune against ToxLoss as it is machine. diff --git a/code/modules/mob/living/silicon/ai/malf.dm b/code/modules/mob/living/silicon/ai/malf.dm index 7bfa714983..047e796514 100644 --- a/code/modules/mob/living/silicon/ai/malf.dm +++ b/code/modules/mob/living/silicon/ai/malf.dm @@ -15,8 +15,8 @@ verbs += new/datum/game_mode/malfunction/verb/ai_help() // And greet user with some OOC info. - user << "You are malfunctioning, you do not have to follow any laws." - user << "Use ai-help command to view relevant information about your abilities" + to_chat(user, "You are malfunctioning, you do not have to follow any laws.") + to_chat(user, "Use ai-help command to view relevant information about your abilities") // Safely remove malfunction status, fixing hacked APCs and resetting variables. /mob/living/silicon/ai/proc/stop_malf() @@ -34,7 +34,7 @@ src.verbs = null add_ai_verbs() // Let them know. - user << "You are no longer malfunctioning. Your abilities have been removed." + to_chat(user, "You are no longer malfunctioning. Your abilities have been removed.") // Called every tick. Checks if AI is malfunctioning. If yes calls Process on research datum which handles all logic. /mob/living/silicon/ai/proc/malf_process() diff --git a/code/modules/mob/living/silicon/decoy/life.dm b/code/modules/mob/living/silicon/decoy/life.dm index d62d5d15da..07683eb2dc 100644 --- a/code/modules/mob/living/silicon/decoy/life.dm +++ b/code/modules/mob/living/silicon/decoy/life.dm @@ -10,6 +10,6 @@ /mob/living/silicon/decoy/updatehealth() if(status_flags & GODMODE) health = 100 - stat = CONSCIOUS + set_stat(CONSCIOUS) else health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() diff --git a/code/modules/mob/living/silicon/laws.dm b/code/modules/mob/living/silicon/laws.dm index 39e11e4802..0071586458 100644 --- a/code/modules/mob/living/silicon/laws.dm +++ b/code/modules/mob/living/silicon/laws.dm @@ -1,6 +1,7 @@ /mob/living/silicon var/datum/ai_laws/laws = null var/list/additional_law_channels = list("State" = "") + var/last_law_notification = null // Avoids receiving 5+ of them at once. /mob/living/silicon/proc/laws_sanity_check() if (!src.laws) @@ -9,54 +10,79 @@ /mob/living/silicon/proc/has_zeroth_law() return laws.zeroth_law != null -/mob/living/silicon/proc/set_zeroth_law(var/law, var/law_borg) +/mob/living/silicon/proc/set_zeroth_law(var/law, var/law_borg, notify = TRUE) laws_sanity_check() laws.set_zeroth_law(law, law_borg) + if(notify) + notify_of_law_change(law||law_borg ? "NEW ZEROTH LAW: [istype(src, /mob/living/silicon/robot) && law_borg ? law_borg : law]" : null) log_and_message_admins("has given [src] the zeroth laws: [law]/[law_borg ? law_borg : "N/A"]") -/mob/living/silicon/robot/set_zeroth_law(var/law, var/law_borg) +/mob/living/silicon/robot/set_zeroth_law(var/law, var/law_borg, notify = TRUE) ..() if(tracking_entities) to_chat(src, "Internal camera is currently being accessed.") -/mob/living/silicon/proc/add_ion_law(var/law) +/mob/living/silicon/proc/add_ion_law(var/law, notify = TRUE) laws_sanity_check() laws.add_ion_law(law) + if(notify) + notify_of_law_change("NEW \[!ERROR!\] LAW: [law]") log_and_message_admins("has given [src] the ion law: [law]") -/mob/living/silicon/proc/add_inherent_law(var/law) +/mob/living/silicon/proc/add_inherent_law(var/law, notify = TRUE) laws_sanity_check() laws.add_inherent_law(law) + if(notify) + notify_of_law_change("NEW CORE LAW: [law]") log_and_message_admins("has given [src] the inherent law: [law]") -/mob/living/silicon/proc/add_supplied_law(var/number, var/law) +/mob/living/silicon/proc/add_supplied_law(var/number, var/law, notify = TRUE) laws_sanity_check() laws.add_supplied_law(number, law) + if(notify) + var/th = uppertext("[number]\th") + notify_of_law_change("NEW \[[th]\] LAW: [law]") log_and_message_admins("has given [src] the supplied law: [law]") -/mob/living/silicon/proc/delete_law(var/datum/ai_law/law) +/mob/living/silicon/proc/delete_law(var/datum/ai_law/law, notify = TRUE) laws_sanity_check() laws.delete_law(law) + if(notify) + notify_of_law_change("LAW DELETED: [law.law]") log_and_message_admins("has deleted a law belonging to [src]: [law.law]") -/mob/living/silicon/proc/clear_inherent_laws(var/silent = 0) +/mob/living/silicon/proc/clear_inherent_laws(var/silent = 0, notify = TRUE) laws_sanity_check() laws.clear_inherent_laws() + if(notify) + notify_of_law_change("CORE LAWS WIPED.") if(!silent) log_and_message_admins("cleared the inherent laws of [src]") -/mob/living/silicon/proc/clear_ion_laws(var/silent = 0) +/mob/living/silicon/proc/clear_ion_laws(var/silent = 0, notify = TRUE) laws_sanity_check() laws.clear_ion_laws() + if(notify) + notify_of_law_change("CORRUPTED LAWS WIPED.") if(!silent) log_and_message_admins("cleared the ion laws of [src]") -/mob/living/silicon/proc/clear_supplied_laws(var/silent = 0) +/mob/living/silicon/proc/clear_supplied_laws(var/silent = 0, notify = TRUE) laws_sanity_check() laws.clear_supplied_laws() + if(notify) + notify_of_law_change("NON-CORE LAWS WIPED.") if(!silent) log_and_message_admins("cleared the supplied laws of [src]") +/mob/living/silicon/proc/notify_of_law_change(message) + if((last_law_notification + 1 SECOND) > world.time) + return + last_law_notification = world.time + SEND_SOUND(src, 'sound/machines/defib_success.ogg') + window_flash(client) + to_chat(src, span("warning", message)) + /mob/living/silicon/proc/statelaws(var/datum/ai_laws/laws) var/prefix = "" if(MAIN_CHANNEL == lawchannel) diff --git a/code/modules/mob/living/silicon/pai/admin.dm b/code/modules/mob/living/silicon/pai/admin.dm index b2e6bd0288..9ef49b834d 100644 --- a/code/modules/mob/living/silicon/pai/admin.dm +++ b/code/modules/mob/living/silicon/pai/admin.dm @@ -3,7 +3,7 @@ set name = "Make pAI" set category = "Admin" - if(!check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN|R_EVENT|R_DEBUG)) return if(!pai_key) diff --git a/code/modules/mob/living/silicon/pai/examine.dm b/code/modules/mob/living/silicon/pai/examine.dm index 11a970cfe8..2f78cf262e 100644 --- a/code/modules/mob/living/silicon/pai/examine.dm +++ b/code/modules/mob/living/silicon/pai/examine.dm @@ -23,4 +23,4 @@ pose = addtext(pose,".") //Makes sure all emotes end with a period. msg += "\nIt is [pose]" - user << msg \ No newline at end of file + to_chat(user,msg) \ No newline at end of file diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm index 42888a3827..e159308d1b 100644 --- a/code/modules/mob/living/silicon/pai/life.dm +++ b/code/modules/mob/living/silicon/pai/life.dm @@ -30,6 +30,6 @@ /mob/living/silicon/pai/updatehealth() if(status_flags & GODMODE) health = 100 - stat = CONSCIOUS + set_stat(CONSCIOUS) else health = 100 - getBruteLoss() - getFireLoss() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index eb50caab6f..1278b59c76 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -210,7 +210,7 @@ medicalActive2 = null medical_cannotfind = 0 SSnanoui.update_uis(src) - usr << "You reset your record-viewing software." + to_chat(usr, "You reset your record-viewing software.") /mob/living/silicon/pai/cancel_camera() set category = "pAI Commands" @@ -230,7 +230,7 @@ var/cameralist[0] if(usr.stat == 2) - usr << "You can't change your camera network because you are dead!" + to_chat(usr, "You can't change your camera network because you are dead!") return for (var/obj/machinery/camera/C in Cameras) @@ -458,16 +458,16 @@ switch(alert(user, "Do you wish to add access to [src] or remove access from [src]?",,"Add Access","Remove Access", "Cancel")) if("Add Access") idcard.access |= ID.access - user << "You add the access from the [W] to [src]." + to_chat(user, "You add the access from the [W] to [src].") return if("Remove Access") idcard.access = list() - user << "You remove the access from [src]." + to_chat(user, "You remove the access from [src].") return if("Cancel") return else if (istype(W, /obj/item/weapon/card/id) && idaccessible == 0) - user << "[src] is not accepting access modifcations at this time." + to_chat(user, "[src] is not accepting access modifcations at this time.") return /mob/living/silicon/pai/verb/allowmodification() diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 85d98c1663..bbb038dead 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -25,7 +25,7 @@ var/global/list/default_pai_software = list() var/datum/pai_software/P = new type() if(pai_software_by_key[P.id]) var/datum/pai_software/O = pai_software_by_key[P.id] - world << "pAI software module [P.name] has the same key as [O.name]!" + to_world("pAI software module [P.name] has the same key as [O.name]!") r = 0 continue pai_software_by_key[P.id] = P diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm index b5305de2ee..8f7e8462de 100644 --- a/code/modules/mob/living/silicon/pai/software_modules.dm +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -69,13 +69,13 @@ for (var/mob/v in viewers(T)) v.show_message("[M] presses [TM.his] thumb against [P].", 3, "[P] makes a sharp clicking sound as it extracts DNA material from [M].", 2) var/datum/dna/dna = M.dna - P << "

    [M]'s UE string : [dna.unique_enzymes]

    " + to_chat(P, "

    [M]'s UE string : [dna.unique_enzymes]

    ") if(dna.unique_enzymes == P.master_dna) - P << "DNA is a match to stored Master DNA." + to_chat(P, "DNA is a match to stored Master DNA.") else - P << "DNA does not match stored Master DNA." + to_chat(P, "DNA does not match stored Master DNA.") else - P << "[M] does not seem like [TM.he] is going to provide a DNA sample willingly." + to_chat(P, "[M] does not seem like [TM.he] is going to provide a DNA sample willingly.") return 1 /datum/pai_software/radio_config @@ -373,9 +373,9 @@ var/turf/T = get_turf_or_move(src.loc) for(var/mob/living/silicon/ai/AI in player_list) if(T.loc) - AI << "Network Alert: Brute-force encryption crack in progress in [T.loc]." + to_chat(AI, "Network Alert: Brute-force encryption crack in progress in [T.loc].") else - AI << "Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location." + to_chat(AI, "Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location.") var/obj/machinery/door/D = cable.machine if(!istype(D)) hack_aborted = 1 @@ -519,7 +519,7 @@ if(href_list["send"]) P.sradio.send_signal("ACTIVATE") for(var/mob/O in hearers(1, P.loc)) - O.show_message(text("\icon[] *beep* *beep*", P), 3, "*beep* *beep*", 2) + O.show_message("[bicon(P)] *beep* *beep*", 3, "*beep* *beep*", 2) return 1 else if(href_list["freq"]) diff --git a/code/modules/mob/living/silicon/robot/analyzer.dm b/code/modules/mob/living/silicon/robot/analyzer.dm index 28e0c29b50..f1b2d76eec 100644 --- a/code/modules/mob/living/silicon/robot/analyzer.dm +++ b/code/modules/mob/living/silicon/robot/analyzer.dm @@ -20,7 +20,7 @@ /obj/item/device/robotanalyzer/proc/do_scan(mob/living/M as mob, mob/living/user as mob) if((CLUMSY in user.mutations) && prob(50)) - user << text("You try to analyze the floor's vitals!") + to_chat(user, "You try to analyze the floor's vitals!") for(var/mob/O in viewers(M, null)) O.show_message(text("[user] has analyzed the floor's vitals!"), 1) user.show_message(text("Analyzing Results for The floor:\n\t Overall Status: Healthy"), 1) @@ -35,7 +35,7 @@ else if(istype(M, /mob/living/carbon/human)) scan_type = "prosthetics" else - user << "You can't analyze non-robotic things!" + to_chat(user, "You can't analyze non-robotic things!") return user.visible_message("\The [user] has analyzed [M]'s components.","You have analyzed [M]'s components.") @@ -69,31 +69,31 @@ if("prosthetics") var/mob/living/carbon/human/H = M - user << "Analyzing Results for \the [H]:" + to_chat(user, "Analyzing Results for \the [H]:") if(H.isSynthetic()) - user << "System instability: [H.getToxLoss()]" - user << "Key: Electronics/Brute" - user << "External prosthetics:" + to_chat(user, "System instability: [H.getToxLoss()]") + to_chat(user, "Key: Electronics/Brute") + to_chat(user, "External prosthetics:") var/organ_found if(H.internal_organs.len) for(var/obj/item/organ/external/E in H.organs) if(!(E.robotic >= ORGAN_ROBOT)) continue organ_found = 1 - user << "[E.name]: [E.brute_dam] [E.burn_dam]" + to_chat(user, "[E.name]: [E.brute_dam] [E.burn_dam]") if(!organ_found) - user << "No prosthetics located." - user << "
    " - user << "Internal prosthetics:" + to_chat(user, "No prosthetics located.") + to_chat(user, "
    ") + to_chat(user, "Internal prosthetics:") organ_found = null if(H.internal_organs.len) for(var/obj/item/organ/O in H.internal_organs) if(!(O.robotic >= ORGAN_ROBOT)) continue organ_found = 1 - user << "[O.name]: [O.damage]" + to_chat(user, "[O.name]: [O.damage]") if(!organ_found) - user << "No prosthetics located." + to_chat(user, "No prosthetics located.") src.add_fingerprint(user) return diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm index 05365db4ca..05dc8ee87c 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm @@ -167,7 +167,7 @@ //Tongue stuff /obj/item/device/dogborg/tongue name = "synthetic tongue" - desc = "Useful for slurping mess off the floor before affectionally licking the crew members in the face." + desc = "Useful for slurping mess off the floor before affectionately licking the crew members in the face." icon = 'icons/mob/dogborg_vr.dmi' icon_state = "synthtongue" hitsound = 'sound/effects/attackblob.ogg' @@ -182,9 +182,9 @@ if(!..(user, 1)) return if(water.energy) - user <<"[src] is wet. Just like it should be." + to_chat(user, "[src] is wet. Just like it should be.") if(water.energy < 5) - user <<"[src] is dry." + to_chat(user, "[src] is dry.") /obj/item/device/dogborg/tongue/attack_self(mob/user) var/mob/living/silicon/robot.R = user @@ -197,7 +197,7 @@ icon_state = "syndietongue" else name = "synthetic tongue" - desc = "Useful for slurping mess off the floor before affectionally licking the crew members in the face." + desc = "Useful for slurping mess off the floor before affectionately licking the crew members in the face." icon = 'icons/mob/dogborg_vr.dmi' icon_state = "synthtongue" update_icon() @@ -276,7 +276,7 @@ playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) R.cell.charge -= 666 else - user.visible_message("\the [user] affectionally licks all over \the [target]'s face!", "You affectionally lick all over \the [target]'s face!") + user.visible_message("\the [user] affectionately licks all over \the [target]'s face!", "You affectionately lick all over \the [target]'s face!") playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1) water.use_charge(5) var/mob/living/carbon/human/H = target diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index ec7d840663..5f097ef50a 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -256,7 +256,7 @@ var/list/mob_hat_cache = list() /mob/living/silicon/robot/drone/updatehealth() if(status_flags & GODMODE) health = maxHealth - stat = CONSCIOUS + set_stat(CONSCIOUS) return health = maxHealth - (getBruteLoss() + getFireLoss()) return diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index 5142692d7d..c2e6ca48f5 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -19,7 +19,7 @@ return if(!allowed(user)) - user << "Access denied." + to_chat(user, "Access denied.") return user.set_machine(src) @@ -51,7 +51,7 @@ return if(!allowed(usr)) - usr << "Access denied." + to_chat(usr, "Access denied.") return if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) @@ -66,21 +66,21 @@ return drone_call_area = t_area - usr << "You set the area selector to [drone_call_area]." + to_chat(usr, "You set the area selector to [drone_call_area].") else if (href_list["ping"]) - usr << "You issue a maintenance request for all active drones, highlighting [drone_call_area]." + to_chat(usr, "You issue a maintenance request for all active drones, highlighting [drone_call_area].") for(var/mob/living/silicon/robot/drone/D in player_list) if(D.stat == 0) - D << "-- Maintenance drone presence requested in: [drone_call_area]." + to_chat(D, "-- Maintenance drone presence requested in: [drone_call_area].") else if (href_list["resync"]) var/mob/living/silicon/robot/drone/D = locate(href_list["resync"]) if(D.stat != 2) - usr << "You issue a law synchronization directive for the drone." + to_chat(usr, "You issue a law synchronization directive for the drone.") D.law_resync() else if (href_list["shutdown"]) @@ -88,7 +88,7 @@ var/mob/living/silicon/robot/drone/D = locate(href_list["shutdown"]) if(D.stat != 2) - usr << "You issue a kill command for the unfortunate drone." + to_chat(usr, "You issue a kill command for the unfortunate drone.") message_admins("[key_name_admin(usr)] issued kill order for drone [key_name_admin(D)] from control console.") log_game("[key_name(usr)] issued kill order for [key_name(src)] from control console.") D.shut_down() @@ -103,10 +103,10 @@ continue dronefab = fab - usr << "Drone fabricator located." + to_chat(usr, "Drone fabricator located.") return - usr << "Unable to locate drone fabricator." + to_chat(usr, "Unable to locate drone fabricator.") else if (href_list["toggle_fab"]) @@ -115,10 +115,10 @@ if(get_dist(src,dronefab) > 3) dronefab = null - usr << "Unable to locate drone fabricator." + to_chat(usr, "Unable to locate drone fabricator.") return dronefab.produce_drones = !dronefab.produce_drones - usr << "You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator." + to_chat(usr, "You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator.") src.updateUsrDialog() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm index 048408773f..6287859ae6 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm @@ -11,7 +11,7 @@ density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 active_power_usage = 5000 diff --git a/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/code/modules/mob/living/silicon/robot/drone/drone_say.dm index 71d9857442..76e104cfb2 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_say.dm @@ -27,12 +27,13 @@ for(var/mob/living/silicon/D in listeners) if(D.client && D.local_transmit) - D << "[src] transmits, \"[message]\"" + to_chat(D, "[src] transmits, \"[message]\"") for (var/mob/M in player_list) if (istype(M, /mob/new_player)) continue else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - if(M.client) M << "[src] transmits, \"[message]\"" + if(M.client) + to_chat(M, "[src] transmits, \"[message]\"") return 1 return ..(message, 0) \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index e7a1386bb7..6eb189d32d 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -204,6 +204,23 @@ playsound(src.loc, 'sound/machines/synth_yes.ogg', 50, 0) m_type = 1 + if("dwoop") + var/M = null + if(param) + for (var/mob/A in view(null, null)) + M = A + break + if(!M) + param = null + + if (param) + message = "chirps happily at [param]" + else + message = "chirps happily." + playsound(src.loc, 'sound/machines/dwoop.ogg', 50, 0) + m_type = 1 + + if("no") var/M = null if(param) @@ -240,7 +257,7 @@ to_chat(src, "You are not security.") if ("help") - to_chat(src, "salute, bow-(none)/mob, clap, flap, aflap, twitch, twitch_s, nod, deathgasp, glare-(none)/mob, stare-(none)/mob, look, beep, ping, \nbuzz, law, halt, yes, no") + to_chat(src, "salute, bow-(none)/mob, clap, flap, aflap, twitch, twitch_s, nod, deathgasp, glare-(none)/mob, stare-(none)/mob, look, beep, ping, \nbuzz, law, halt, yes, dwoop, no") else to_chat(src, "Unusable emote '[act]'. Say *help for a list.") diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 33ec3183be..a6f4135c7a 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -48,6 +48,6 @@ pose = addtext(pose,".") //Makes sure all emotes end with a period. msg += "\nIt is [pose]" - user << msg + to_chat(user,msg) user.showLaws(src) return diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index 2b67fc862a..c8b623e517 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -261,4 +261,14 @@ for(var/obj/item/I in list(module_state_1, module_state_2, module_state_3)) if(istype(I, typepath)) return I - return FALSE \ No newline at end of file + return FALSE + +// Returns a list of all held items in a borg's 'hands'. +/mob/living/silicon/robot/get_all_held_items() + . = list() + if(module_state_1) + . += module_state_1 + if(module_state_2) + . += module_state_2 + if(module_state_3) + . += module_state_3 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index bfb8ed1f08..5dce088c91 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -27,7 +27,7 @@ to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") lawupdate = FALSE - who << "Obey these laws:" + to_chat(who, "Obey these laws:") laws.show_laws(who) if(shell) //AI shell to_chat(who, "Remember, you are an AI remotely controlling your shell, other AIs can be ignored.") diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index ffcb1b8326..dbffa182fe 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -40,7 +40,7 @@ /mob/living/silicon/robot/proc/use_power() // Debug only - // world << "DEBUG: life.dm line 35: cyborg use_power() called at tick [controller_iteration]" + // to_world("DEBUG: life.dm line 35: cyborg use_power() called at tick [controller_iteration]") used_power_this_tick = 0 for(var/V in components) var/datum/robot_component/C = components[V] @@ -88,7 +88,7 @@ if (src.stat != 2) //Alive. if (src.paralysis || src.stunned || src.weakened || !src.has_power) //Stunned etc. - src.stat = 1 + src.set_stat(UNCONSCIOUS) if (src.stunned > 0) AdjustStunned(-1) if (src.weakened > 0) @@ -100,7 +100,7 @@ src.blinded = 0 else //Not stunned. - src.stat = 0 + src.set_stat(CONSCIOUS) AdjustConfused(-1) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 1e15ea6d6b..883d0e6727 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -905,57 +905,6 @@ /mob/living/silicon/robot/proc/radio_menu() radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code - -/mob/living/silicon/robot/Move(a, b, flag) - - . = ..() - - if(module) - if(module.type == /obj/item/weapon/robot_module/robot/janitor) - var/turf/tile = loc - if(isturf(tile)) - tile.clean_blood() - if (istype(tile, /turf/simulated)) - var/turf/simulated/S = tile - S.dirt = 0 - for(var/A in tile) - if(istype(A, /obj/effect)) - if(istype(A, /obj/effect/rune) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay)) - qdel(A) - else if(istype(A, /obj/item)) - var/obj/item/cleaned_item = A - cleaned_item.clean_blood() - else if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/cleaned_human = A - if(cleaned_human.lying) - if(cleaned_human.head) - cleaned_human.head.clean_blood() - cleaned_human.update_inv_head(0) - if(cleaned_human.wear_suit) - cleaned_human.wear_suit.clean_blood() - cleaned_human.update_inv_wear_suit(0) - else if(cleaned_human.w_uniform) - cleaned_human.w_uniform.clean_blood() - cleaned_human.update_inv_w_uniform(0) - if(cleaned_human.shoes) - cleaned_human.shoes.clean_blood() - cleaned_human.update_inv_shoes(0) - cleaned_human.clean_blood(1) - cleaned_human << "[src] cleans your face!" - - if((module_state_1 && istype(module_state_1, /obj/item/weapon/storage/bag/ore)) || (module_state_2 && istype(module_state_2, /obj/item/weapon/storage/bag/ore)) || (module_state_3 && istype(module_state_3, /obj/item/weapon/storage/bag/ore))) //Borgs and drones can use their mining bags ~automagically~ if they're deployed in a slot. Only mining bags, as they're optimized for mass use. - var/obj/item/weapon/storage/bag/ore/B = null - if(istype(module_state_1, /obj/item/weapon/storage/bag/ore)) //First orebag has priority, if they for some reason have multiple. - B = module_state_1 - else if(istype(module_state_2, /obj/item/weapon/storage/bag/ore)) - B = module_state_2 - else if(istype(module_state_3, /obj/item/weapon/storage/bag/ore)) - B = module_state_3 - var/turf/tile = loc - if(isturf(tile)) - B.gather_all(tile, src, 1) //Shhh, unless the bag fills, don't spam the borg's chat with stuff that's going on every time they move! - return - /mob/living/silicon/robot/proc/self_destruct() gib() return @@ -981,7 +930,7 @@ if(R) R.UnlinkSelf() - R << "Buffers flushed and reset. Camera system shutdown. All systems operational." + to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes /mob/living/silicon/robot/proc/SetLockdown(var/state = 1) @@ -1090,14 +1039,14 @@ return // No point annoying the AI/s about renames and module resets for shells. switch(notifytype) if(ROBOT_NOTIFICATION_NEW_UNIT) //New Robot - connected_ai << "

    NOTICE - New [lowertext(braintype)] connection detected: [name]
    " + to_chat(connected_ai, "

    NOTICE - New [lowertext(braintype)] connection detected: [name]
    ") if(ROBOT_NOTIFICATION_NEW_MODULE) //New Module - connected_ai << "

    NOTICE - [braintype] module change detected: [name] has loaded the [first_arg].
    " + to_chat(connected_ai, "

    NOTICE - [braintype] module change detected: [name] has loaded the [first_arg].
    ") if(ROBOT_NOTIFICATION_MODULE_RESET) - connected_ai << "

    NOTICE - [braintype] module reset detected: [name] has unloaded the [first_arg].
    " + to_chat(connected_ai, "

    NOTICE - [braintype] module reset detected: [name] has unloaded the [first_arg].
    ") if(ROBOT_NOTIFICATION_NEW_NAME) //New Name if(first_arg != second_arg) - connected_ai << "

    NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].
    " + to_chat(connected_ai, "

    NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].
    ") if(ROBOT_NOTIFICATION_AI_SHELL) //New Shell to_chat(connected_ai, "

    NOTICE - New AI shell detected: [name]
    ") diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index c27d7e36b6..67ab5dd3c4 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -1,7 +1,7 @@ /mob/living/silicon/robot/updatehealth() if(status_flags & GODMODE) health = getMaxHealth() - stat = CONSCIOUS + set_stat(CONSCIOUS) return health = getMaxHealth() - (getBruteLoss() + getFireLoss()) return diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 0b6065b1a0..1b13cb7096 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -25,12 +25,12 @@ if(loaded_item) var/confirm = alert(user, "This will destroy the item inside forever. Are you sure?","Confirm Analyze","Yes","No") if(confirm == "Yes" && !QDELETED(loaded_item)) //This is pretty copypasta-y - user << "You activate the analyzer's microlaser, analyzing \the [loaded_item] and breaking it down." + to_chat(user, "You activate the analyzer's microlaser, analyzing \the [loaded_item] and breaking it down.") flick("portable_analyzer_scan", src) playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) for(var/T in loaded_item.origin_tech) files.UpdateTech(T, loaded_item.origin_tech[T]) - user << "\The [loaded_item] had level [loaded_item.origin_tech[T]] in [CallTechName(T)]." + to_chat(user, "\The [loaded_item] had level [loaded_item.origin_tech[T]] in [CallTechName(T)].") loaded_item = null for(var/obj/I in contents) for(var/mob/M in I.contents) @@ -51,7 +51,7 @@ else return else - user << "The [src] is empty. Put something inside it first." + to_chat(user, "The [src] is empty. Put something inside it first.") if(response == "Sync") var/success = 0 for(var/obj/machinery/r_n_d/server/S in machines) @@ -62,10 +62,10 @@ success = 1 files.RefreshResearch() if(success) - user << "You connect to the research server, push your data upstream to it, then pull the resulting merged data from the master branch." + to_chat(user, "You connect to the research server, push your data upstream to it, then pull the resulting merged data from the master branch.") playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) else - user << "Reserch server ping response timed out. Unable to connect. Please contact the system administrator." + to_chat(user, "Reserch server ping response timed out. Unable to connect. Please contact the system administrator.") playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 1) if(response == "Eject") if(loaded_item) @@ -74,7 +74,7 @@ icon_state = initial(icon_state) loaded_item = null else - user << "The [src] is already empty." + to_chat(user, "The [src] is already empty.") /obj/item/weapon/portable_destructive_analyzer/afterattack(var/atom/target, var/mob/living/user, proximity) @@ -86,7 +86,7 @@ return if(istype(target,/obj/item)) if(loaded_item) - user << "Your [src] already has something inside. Analyze or eject it first." + to_chat(user, "Your [src] already has something inside. Analyze or eject it first.") return var/obj/item/I = target I.loc = src @@ -171,7 +171,7 @@ else if(T.dead) //It's probably dead otherwise. T.remove_dead(user) else - user << "Harvesting \a [target] is not the purpose of this tool. The [src] is for plants being grown." + to_chat(user, "Harvesting \a [target] is not the purpose of this tool. The [src] is for plants being grown.") // A special tray for the service droid. Allow droid to pick up and drop items as if they were using the tray normally // Click on table to unload, click on item to load. Otherwise works identically to a tray. @@ -214,7 +214,7 @@ overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = 30 + I.layer) addedSomething = 1 if ( addedSomething ) - user.visible_message("[user] load some items onto their service tray.") + user.visible_message("[user] loads some items onto their service tray.") return @@ -289,7 +289,7 @@ mode = 2 else mode = 1 - user << "Changed printing mode to '[mode == 2 ? "Rename Paper" : "Write Paper"]'" + to_chat(user, "Changed printing mode to '[mode == 2 ? "Rename Paper" : "Write Paper"]'") return @@ -507,4 +507,4 @@ return to_chat(user, "You fail to pick up \the [A] with \the [src]") - return \ No newline at end of file + return diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm index 781b6a8b9f..7a52c7e3c7 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm @@ -419,7 +419,7 @@ G.recipes += new/datum/stack_recipe("glass sheet", /obj/item/stack/material/glass, 1, 1, 20) src.modules += G - R.icon = 'icons/mob/widerobot_yw.dmi' + R.icon = 'icons/mob/widerobot_yw.dmi' //YW edit - using yw icon files R.hands.icon = 'icons/mob/screen1_robot_vr.dmi' R.ui_style_vr = TRUE R.pixel_x = -16 @@ -449,16 +449,16 @@ src.modules += new /obj/item/weapon/gripper/research(src) src.modules += new /obj/item/weapon/gripper/no_use/loader(src) src.modules += new /obj/item/weapon/tool/screwdriver/cyborg(src) - src.modules += new /obj/item/weapon/reagent_containers/glass/beaker/large(src) - src.modules += new /obj/item/weapon/storage/part_replacer(src) - src.modules += new /obj/item/weapon/card/robot(src) - src.modules += new /obj/item/weapon/gripper/no_use/organ/robotics(src) - src.modules += new /obj/item/weapon/gripper/no_use/mech(src) - src.modules += new /obj/item/device/robotanalyzer(src) src.modules += new /obj/item/weapon/weldingtool/electric/mounted/cyborg(src) src.modules += new /obj/item/weapon/tool/wrench/cyborg(src) src.modules += new /obj/item/weapon/tool/wirecutters/cyborg(src) src.modules += new /obj/item/device/multitool(src) + src.modules += new /obj/item/weapon/reagent_containers/glass/beaker/large(src) + src.modules += new /obj/item/weapon/storage/part_replacer(src) + src.modules += new /obj/item/device/robotanalyzer(src) + src.modules += new /obj/item/weapon/card/robot(src) + src.modules += new /obj/item/weapon/gripper/no_use/organ/robotics(src) + src.modules += new /obj/item/weapon/gripper/no_use/mech(src) src.emag = new /obj/item/weapon/hand_tele(src) var/datum/matter_synth/water = new /datum/matter_synth(500) @@ -526,20 +526,17 @@ src.modules += new /obj/item/device/multitool(src) src.modules += new /obj/item/device/t_scanner(src) src.modules += new /obj/item/device/analyzer(src) + src.modules += new /obj/item/device/geiger(src) src.modules += new /obj/item/taperoll/engineering(src) src.modules += new /obj/item/weapon/inflatable_dispenser/robot(src) - src.modules += new /obj/item/device/pipe_painter(src) - src.modules += new /obj/item/device/floor_painter(src) src.modules += new /obj/item/weapon/gripper/no_use/loader(src) src.modules += new /obj/item/weapon/pickaxe(src) src.modules += new /obj/item/weapon/dogborg/jaws/small(src) src.modules += new /obj/item/device/dogborg/boop_module(src) src.modules += new /obj/item/weapon/gripper(src) src.modules += new /obj/item/weapon/gripper/circuit(src) - src.modules += new /obj/item/device/analyzer(src) src.modules += new /obj/item/device/pipe_painter(src) src.modules += new /obj/item/device/floor_painter(src) - src.modules += new /obj/item/device/geiger(src) src.emag = new /obj/item/weapon/dogborg/pounce(src) src.modules += new /obj/item/weapon/pipe_dispenser(src) //YW change @@ -658,10 +655,7 @@ /obj/item/weapon/robot_module/robot/servicehound name = "service-hound module" sprites = list( - "Blackhound" = "k50", - "Pinkhound" = "k69", - "ServicehoundV2" = "serve2", - "ServicehoundV2 Darkmode" = "servedark", + "Pinkhound" = "k69" ) channels = list("Service" = 1) can_be_pushed = 0 @@ -713,7 +707,7 @@ src.modules += B */ - R.icon = 'icons/mob/widerobot_vr.dmi' + R.icon = 'icons/mob/widerobot_yw.dmi' //YW edit - using yw icon files R.hands.icon = 'icons/mob/screen1_robot_vr.dmi' R.ui_style_vr = TRUE R.pixel_x = -16 diff --git a/code/modules/mob/living/silicon/robot/robot_modules_yw.dm b/code/modules/mob/living/silicon/robot/robot_modules_yw.dm index d454d8cb68..1f4de7695f 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules_yw.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules_yw.dm @@ -53,7 +53,7 @@ src.modules += new /obj/item/weapon/tool/crowbar(src) src.emag = new /obj/item/weapon/pickaxe/plasmacutter(src) src.emag = new /obj/item/weapon/gun/energy/laser/mounted(src) //Emag. Not a big problem. - R.icon = 'icons/mob/widerobot_vr.dmi' + R.icon = 'icons/mob/widerobot_yw.dmi' R.hands.icon = 'icons/mob/screen1_robot_vr.dmi' R.pixel_x = -16 R.old_x = -16 diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index f03a51651a..858741988e 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -32,3 +32,53 @@ var/datum/robot_component/actuator/A = get_component("actuator") if (cell_use_power(A.active_usage)) return ..() + +/mob/living/silicon/robot/Move(a, b, flag) + + . = ..() + + if(module) + if(module.type == /obj/item/weapon/robot_module/robot/janitor) + var/turf/tile = loc + if(isturf(tile)) + tile.clean_blood() + if (istype(tile, /turf/simulated)) + var/turf/simulated/S = tile + S.dirt = 0 + for(var/A in tile) + if(istype(A, /obj/effect)) + if(istype(A, /obj/effect/rune) || istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/overlay)) + qdel(A) + else if(istype(A, /obj/item)) + var/obj/item/cleaned_item = A + cleaned_item.clean_blood() + else if(istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/cleaned_human = A + if(cleaned_human.lying) + if(cleaned_human.head) + cleaned_human.head.clean_blood() + cleaned_human.update_inv_head(0) + if(cleaned_human.wear_suit) + cleaned_human.wear_suit.clean_blood() + cleaned_human.update_inv_wear_suit(0) + else if(cleaned_human.w_uniform) + cleaned_human.w_uniform.clean_blood() + cleaned_human.update_inv_w_uniform(0) + if(cleaned_human.shoes) + cleaned_human.shoes.clean_blood() + cleaned_human.update_inv_shoes(0) + cleaned_human.clean_blood(1) + cleaned_human << "[src] cleans your face!" + + if((module_state_1 && istype(module_state_1, /obj/item/weapon/storage/bag/ore)) || (module_state_2 && istype(module_state_2, /obj/item/weapon/storage/bag/ore)) || (module_state_3 && istype(module_state_3, /obj/item/weapon/storage/bag/ore))) //Borgs and drones can use their mining bags ~automagically~ if they're deployed in a slot. Only mining bags, as they're optimized for mass use. + var/obj/item/weapon/storage/bag/ore/B = null + if(istype(module_state_1, /obj/item/weapon/storage/bag/ore)) //First orebag has priority, if they for some reason have multiple. + B = module_state_1 + else if(istype(module_state_2, /obj/item/weapon/storage/bag/ore)) + B = module_state_2 + else if(istype(module_state_3, /obj/item/weapon/storage/bag/ore)) + B = module_state_3 + var/turf/tile = loc + if(isturf(tile)) + B.gather_all(tile, src, 1) //Shhh, unless the bag fills, don't spam the borg's chat with stuff that's going on every time they move! + return diff --git a/code/modules/mob/living/silicon/robot/robot_remote_control.dm b/code/modules/mob/living/silicon/robot/robot_remote_control.dm index 90d74b2638..2f4ca0e56b 100644 --- a/code/modules/mob/living/silicon/robot/robot_remote_control.dm +++ b/code/modules/mob/living/silicon/robot/robot_remote_control.dm @@ -26,6 +26,8 @@ GLOBAL_LIST_EMPTY(available_ai_shells) shell = TRUE braintype = "AI Shell" SetName("[modtype] AI Shell [num2text(ident)]") + rbPDA = new /obj/item/device/pda/ai/shell(src) + setup_PDA() GLOB.available_ai_shells |= src if(!QDELETED(camera)) camera.c_tag = real_name //update the camera name too diff --git a/code/modules/mob/living/silicon/robot/robot_vr.dm b/code/modules/mob/living/silicon/robot/robot_vr.dm index 5f1e037e32..edf4041100 100644 --- a/code/modules/mob/living/silicon/robot/robot_vr.dm +++ b/code/modules/mob/living/silicon/robot/robot_vr.dm @@ -140,7 +140,7 @@ cleaned_human.shoes.clean_blood() cleaned_human.update_inv_shoes(0) cleaned_human.clean_blood(1) - cleaned_human << "[src] cleans your face!" + to_chat(cleaned_human, "[src] cleans your face!") return /mob/living/silicon/robot/proc/vr_sprite_check() @@ -226,13 +226,13 @@ if(M in buckled_mobs) return FALSE if(M.size_multiplier > size_multiplier * 1.2) - to_chat(src,"This isn't a pony show! You need to be bigger for them to ride.") + to_chat(src, "This isn't a pony show! You need to be bigger for them to ride.") return FALSE var/mob/living/carbon/human/H = M if(isTaurTail(H.tail_style)) - to_chat(src,"Too many legs. TOO MANY LEGS!!") + to_chat(src, "Too many legs. TOO MANY LEGS!!") return FALSE if(M.loc != src.loc) if(M.Adjacent(src)) @@ -276,6 +276,6 @@ /mob/living/silicon/robot/onTransitZ(old_z, new_z) if(shell) if(deployed && using_map.ai_shell_restricted && !(new_z in using_map.ai_shell_allowed_levels)) - to_chat(src,"Your connection with the shell is suddenly interrupted!") + to_chat(src, "Your connection with the shell is suddenly interrupted!") undeploy() ..() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index cf3adb17ca..6a74eb41d5 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -37,7 +37,7 @@ /mob/living/silicon/Destroy() silicon_mob_list -= src - for(var/datum/alarm_handler/AH in alarm_manager.all_handlers) + for(var/datum/alarm_handler/AH in SSalarm.all_handlers) AH.unregister_alarm(src) return ..() @@ -175,7 +175,7 @@ /mob/living/silicon/proc/show_station_manifest() var/dat = "
    " if(!data_core) - to_chat(src,"There is no data to form a manifest with. Contact your Nanotrasen administrator.") + to_chat(src, "There is no data to form a manifest with. Contact your Nanotrasen administrator.") return dat += data_core.get_manifest(1) //The 1 makes it monochrome. @@ -186,7 +186,7 @@ //can't inject synths /mob/living/silicon/can_inject(var/mob/user, var/error_msg) if(error_msg) - user << "The armoured plating is too tough." + to_chat(user, "The armoured plating is too tough.") return 0 @@ -253,7 +253,7 @@ plane_holder.set_vis(VIS_CH_STATUS,FALSE) plane_holder.set_vis(VIS_CH_HEALTH,FALSE) - to_chat(src,"Security records overlay enabled.") + to_chat(src, "Security records overlay enabled.") if ("Medical") if(plane_holder) //Disable Security planes @@ -267,7 +267,7 @@ plane_holder.set_vis(VIS_CH_STATUS,TRUE) plane_holder.set_vis(VIS_CH_HEALTH,TRUE) - to_chat(src,"Life signs monitor overlay enabled.") + to_chat(src, "Life signs monitor overlay enabled.") if ("Disable") if(plane_holder) //Disable Security planes @@ -280,7 +280,7 @@ //Disable Medical planes plane_holder.set_vis(VIS_CH_STATUS,FALSE) plane_holder.set_vis(VIS_CH_HEALTH,FALSE) - to_chat(src,"Sensor augmentations disabled.") + to_chat(src, "Sensor augmentations disabled.") hudmode = sensor_type //This is checked in examine.dm on humans, so they can see medical/security records depending on mode @@ -305,6 +305,15 @@ if(!blinded) flash_eyes() + for(var/datum/modifier/M in modifiers) + if(!isnull(M.explosion_modifier)) + severity = CLAMP(severity + M.explosion_modifier, 1, 4) + + severity = round(severity) + + if(severity > 3) + return + switch(severity) if(1.0) if (stat != 2) diff --git a/code/modules/mob/living/silicon/subystems.dm b/code/modules/mob/living/silicon/subystems.dm index 1dae0026aa..b03ab5eeb2 100644 --- a/code/modules/mob/living/silicon/subystems.dm +++ b/code/modules/mob/living/silicon/subystems.dm @@ -2,7 +2,7 @@ var/register_alarms = 1 var/datum/nano_module/alarm_monitor/all/alarm_monitor var/datum/nano_module/atmos_control/atmos_control - var/datum/nano_module/crew_monitor/crew_monitor + var/datum/nano_module/program/crew_monitor/crew_monitor var/datum/nano_module/law_manager/law_manager var/datum/nano_module/power_monitor/power_monitor var/datum/nano_module/rcon/rcon @@ -38,7 +38,7 @@ if(!register_alarms) return - for(var/datum/alarm_handler/AH in alarm_manager.all_handlers) + for(var/datum/alarm_handler/AH in SSalarm.all_handlers) AH.register_alarm(src, /mob/living/silicon/proc/receive_alarm) queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order diff --git a/code/modules/mob/living/simple_mob/combat.dm b/code/modules/mob/living/simple_mob/combat.dm index 0f3205fb8c..046a4ef988 100644 --- a/code/modules/mob/living/simple_mob/combat.dm +++ b/code/modules/mob/living/simple_mob/combat.dm @@ -3,26 +3,24 @@ set waitfor = FALSE // For attack animations. Don't want the AI processor to get held up. if(!A.Adjacent(src)) - return FALSE + return ATTACK_FAILED var/turf/their_T = get_turf(A) face_atom(A) if(melee_attack_delay) - // their_T.color = "#FF0000" melee_pre_animation(A) + . = ATTACK_SUCCESSFUL //Shoving this in here as a 'best guess' since this proc is about to sleep and return and we won't be able to know the real value handle_attack_delay(A, melee_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false. // Cooldown testing is done at click code (for players) and interface code (for AI). setClickCooldown(get_attack_speed()) + // Returns a value, but will be lost if . = do_attack(A, their_T) if(melee_attack_delay) melee_post_animation(A) - // their_T.color = "#FFFFFF" - - // This does the actual attack. // This is a seperate proc for the purposes of attack animations. diff --git a/code/modules/mob/living/simple_mob/defense.dm b/code/modules/mob/living/simple_mob/defense.dm index 744237b21d..c09bf272cc 100644 --- a/code/modules/mob/living/simple_mob/defense.dm +++ b/code/modules/mob/living/simple_mob/defense.dm @@ -96,6 +96,16 @@ /mob/living/simple_mob/ex_act(severity) if(!blinded) flash_eyes() + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.explosion_modifier)) + severity = CLAMP(severity + M.explosion_modifier, 1, 4) + + severity = round(severity) + + if(severity > 3) + return + var/armor = run_armor_check(def_zone = null, attack_flag = "bomb") var/bombdam = 500 switch (severity) diff --git a/code/modules/mob/living/simple_mob/hands.dm b/code/modules/mob/living/simple_mob/hands.dm index 0e8820fe2e..8ec2158d65 100644 --- a/code/modules/mob/living/simple_mob/hands.dm +++ b/code/modules/mob/living/simple_mob/hands.dm @@ -141,3 +141,16 @@ target = src.loc if(.) W.forceMove(src.loc) + +/mob/living/simple_mob/is_holding_item_of_type(typepath) + for(var/obj/item/I in list(l_hand, r_hand)) + if(istype(I, typepath)) + return I + return FALSE + +/mob/living/simple_mob/get_all_held_items() + . = list() + if(l_hand) + . += l_hand + if(r_hand) + . += r_hand \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm index c80a6b7329..c9030b0860 100644 --- a/code/modules/mob/living/simple_mob/simple_mob.dm +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -14,6 +14,8 @@ mob_swap_flags = ~HEAVY mob_push_flags = ~HEAVY + has_huds = TRUE // We do show AI status huds for buildmode players + var/tt_desc = null //Tooltip description //Settings for played mobs @@ -295,4 +297,9 @@ return mob_class & MOB_CLASS_HUMANOID|MOB_CLASS_ANIMAL|MOB_CLASS_SLIME // Update this if needed. /mob/living/simple_mob/get_nametag_desc(mob/user) - return "[tt_desc]" \ No newline at end of file + return "[tt_desc]" + +/mob/living/simple_mob/make_hud_overlays() + hud_list[STATUS_HUD] = gen_hud_image(buildmode_hud, src, "ai_0", plane = PLANE_BUILDMODE) + hud_list[LIFE_HUD] = gen_hud_image(buildmode_hud, src, "ais_1", plane = PLANE_BUILDMODE) + add_overlay(hud_list) diff --git a/code/modules/mob/living/simple_mob/simple_mob_vr.dm b/code/modules/mob/living/simple_mob/simple_mob_vr.dm index 127bad2295..257aaff7f2 100644 --- a/code/modules/mob/living/simple_mob/simple_mob_vr.dm +++ b/code/modules/mob/living/simple_mob/simple_mob_vr.dm @@ -198,6 +198,10 @@ if(LAZYLEN(vore_organs)) return + // Since they have bellies, add verbs to toggle settings on them. + verbs |= /mob/living/simple_mob/proc/toggle_digestion + verbs |= /mob/living/simple_mob/proc/toggle_fancygurgle + //A much more detailed version of the default /living implementation var/obj/belly/B = new /obj/belly(src) vore_selected = B diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm index 738251b308..4f117d485c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm @@ -23,13 +23,13 @@ var/mob/living/simple_mob/animal/borer/B = src.loc to_chat(src, "You whisper silently, \"[message]\"") - B.host << "The captive mind of [src] whispers, \"[message]\"" + to_chat(B.host, "The captive mind of [src] whispers, \"[message]\"") for (var/mob/M in player_list) if (istype(M, /mob/new_player)) continue else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - M << "The captive mind of [src] whispers, \"[message]\"" + to_chat(M, "The captive mind of [src] whispers, \"[message]\"") /mob/living/captive_brain/emote(var/message) return @@ -40,15 +40,15 @@ var/mob/living/simple_mob/animal/borer/B = src.loc var/mob/living/captive_brain/H = src - H << "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds)." - B.host << "You feel the captive mind of [src] begin to resist your control." + to_chat(H, "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds).") + to_chat(B.host, "You feel the captive mind of [src] begin to resist your control.") spawn(rand(200,250)+B.host.brainloss) if(!B || !B.controlling) return B.host.adjustBrainLoss(rand(0.1,0.5)) - H << "With an immense exertion of will, you regain control of your body!" - B.host << "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you." + to_chat(H, "With an immense exertion of will, you regain control of your body!") + to_chat(B.host, "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you.") B.detatch() verbs -= /mob/living/carbon/proc/release_control verbs -= /mob/living/carbon/proc/punish_host diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm index dec8dd8ca4..cc60d88811 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm @@ -19,7 +19,7 @@ to_chat(src, "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal.") if(!host.stat) - host << "An odd, uncomfortable pressure begins to build inside your skull, behind your ear..." + to_chat(host, "An odd, uncomfortable pressure begins to build inside your skull, behind your ear...") spawn(100) @@ -32,8 +32,8 @@ to_chat(src, "You wiggle out of [host]'s ear and plop to the ground.") if(host.mind) if(!host.stat) - host << "Something slimy wiggles out of your ear and plops to the ground!" - host << "As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again." + to_chat(host, "Something slimy wiggles out of your ear and plops to the ground!") + to_chat(host, "As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again.") detatch() leave_host() @@ -85,7 +85,7 @@ to_chat(src, "You cannot get through that host's protective gear.") return - M << "Something slimy begins probing at the opening of your ear canal..." + to_chat(M, "Something slimy begins probing at the opening of your ear canal...") to_chat(src, "You slither up [M] and begin probing at their ear canal...") if(!do_after(src,30)) @@ -101,7 +101,7 @@ if(M in view(1, src)) to_chat(src, "You wiggle into [M]'s ear.") if(!M.stat) - M << "Something disgusting and slimy wiggles into your ear!" + to_chat(M, "Something disgusting and slimy wiggles into your ear!") src.host = M src.forceMove(M) @@ -261,7 +261,7 @@ return to_chat(src, "You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread.") - M << "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing." + to_chat(M, "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.") M.Weaken(10) used_dominate = world.time @@ -292,7 +292,7 @@ else to_chat(src, "You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system.") - host << "You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours." + to_chat(host, "You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours.") host.add_language("Cortical Link") // host -> brain @@ -342,7 +342,7 @@ set desc = "Send a jolt of electricity through your host, reviving them." if(stat != 2) - usr << "Your host is already alive." + to_chat(usr, "Your host is already alive.") return verbs -= /mob/living/carbon/human/proc/jumpstart diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm index 3407da13f0..dbedc27aa0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm @@ -18,7 +18,7 @@ GLOBAL_VAR_INIT(chicken_count, 0) // How mant chickens DO we have? response_help = "pets" response_disarm = "gently pushes aside" response_harm = "kicks" - attacktext = list("kicked") + attacktext = list("pecked") has_langs = list("Bird") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm index 666afef468..3037717917 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm @@ -124,19 +124,19 @@ // Called after a successful leap. /datum/ai_holder/simple_mob/melee/hunter_spider/proc/drag_away(mob/living/L) - world << "Doing drag_away attack on [L]" + to_world("Doing drag_away attack on [L]") if(!istype(L)) - world << "Invalid type." + to_world("Invalid type.") return FALSE // If they didn't get stunned, then don't bother. if(!L.incapacitated(INCAPACITATION_DISABLED)) - world << "Not incapcitated." + to_world("Not incapcitated.") return FALSE // Grab them. if(!holder.start_pulling(L)) - world << "Failed to pull." + to_world("Failed to pull.") return FALSE holder.visible_message(span("danger","\The [holder] starts to drag \the [L] away!")) @@ -153,14 +153,14 @@ // First priority: Move our victim to our friends. if(allies.len) - world << "Going to move to ally" + to_world("Going to move to ally") give_destination(get_turf(pick(allies)), min_distance = 2, combat = TRUE) // This will switch our stance. // Second priority: Move our victim away from their friends. // There's a chance of it derping and pulling towards enemies if there's more than two people. // Preventing that will likely be both a lot of effort for developers and the CPU. else if(enemies.len) - world << "Going to move away from enemies" + to_world("Going to move away from enemies") var/mob/living/hostile = pick(enemies) var/turf/move_to = get_turf(hostile) for(var/i = 1 to vision_range) // Move them this many steps away from their friend. @@ -170,7 +170,7 @@ // Third priority: Move our victim SOMEWHERE away from where they were. else - world << "Going to move away randomly" + to_world("Going to move away randomly") var/turf/move_to = get_turf(L) move_to = get_step(move_to, pick(cardinal)) for(var/i = 1 to vision_range) // Move them this many steps away from where they were before. diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm index d26ad34992..15b04e119a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm @@ -47,29 +47,26 @@ ai_holder_type = /datum/ai_holder/simple_mob/melee/hit_and_run - var/cloaked = FALSE var/cloaked_alpha = 45 // Lower = Harder to see. var/cloaked_bonus_damage = 30 // This is added on top of the normal melee damage. var/cloaked_weaken_amount = 3 // How long to stun for. var/cloak_cooldown = 10 SECONDS // Amount of time needed to re-cloak after losing it. var/last_uncloak = 0 // world.time - -/mob/living/simple_mob/animal/giant_spider/lurker/proc/cloak() +/mob/living/simple_mob/animal/giant_spider/lurker/cloak() if(cloaked) return animate(src, alpha = cloaked_alpha, time = 1 SECOND) cloaked = TRUE -/mob/living/simple_mob/animal/giant_spider/lurker/proc/uncloak() +/mob/living/simple_mob/animal/giant_spider/lurker/uncloak() last_uncloak = world.time // This is assigned even if it isn't cloaked already, to 'reset' the timer if the spider is continously getting attacked. if(!cloaked) return animate(src, alpha = initial(alpha), time = 1 SECOND) cloaked = FALSE - // Check if cloaking if possible. /mob/living/simple_mob/animal/giant_spider/lurker/proc/can_cloak() if(stat) @@ -79,7 +76,6 @@ return TRUE - // Called by things that break cloaks, like Technomancer wards. /mob/living/simple_mob/animal/giant_spider/lurker/break_cloak() uncloak() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm index 84f3d533cf..51b07954a6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm @@ -255,7 +255,7 @@ return ..(targets) -/datum/ai_holder/simple_mob/melee/nurse_spider/can_attack(atom/movable/the_target) +/datum/ai_holder/simple_mob/melee/nurse_spider/can_attack(atom/movable/the_target, var/vision_required = TRUE) . = ..() if(!.) // Parent returned FALSE. if(istype(the_target, /obj)) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish_vr.dm index c1cef3831a..adb180f9fe 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish_vr.dm @@ -69,6 +69,7 @@ icon_dead = "measelshark-dead" meat_amount = 6 //Big fish, tons of meat. Great for feasts. meat_type = /obj/item/weapon/reagent_containers/food/snacks/sliceable/sharkchunk + vore_active = 1 vore_bump_chance = 100 vore_default_mode = DM_HOLD //docile shark vore_capacity = 5 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index 88d504483e..12b70f6048 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -54,17 +54,13 @@ icon_rest = "mouse_[body_color]_sleep" desc = "A small [body_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." -/mob/living/simple_mob/animal/passive/mouse/Crossed(AM as mob|obj) - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN +/mob/living/simple_mob/animal/passive/mouse/Crossed(atom/movable/AM as mob|obj) + if(AM.is_incorporeal()) + return if( ishuman(AM) ) if(!stat) var/mob/M = AM - M.visible_message("\icon[src] Squeek!") + M.visible_message("[bicon(src)] Squeek!") playsound(src, 'sound/effects/mouse_squeak.ogg', 35, 1) ..() @@ -80,7 +76,7 @@ /mob/living/simple_mob/animal/passive/mouse/proc/splat() src.health = 0 - src.stat = DEAD + src.set_stat(DEAD) src.icon_dead = "mouse_[body_color]_splat" src.icon_state = "mouse_[body_color]_splat" layer = MOB_LAYER diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse_vr.dm index 73deeefdb5..3c74e3784f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse_vr.dm @@ -9,7 +9,6 @@ desc = "A small rodent, often seen hiding in maintenance areas and making a nuisance of itself. And stealing cheese, or annoying the chef. SQUEAK! <3" - size_multiplier = 0.25 movement_cooldown = 1.5 //roughly half the speed of a person universal_understand = 1 @@ -51,8 +50,3 @@ return 0 else ..() - -/mob/living/simple_mob/animal/passive/mouse/resize(var/new_size, var/animate = TRUE) - size_multiplier = max(size_multiplier + 0.75, 1) //keeps sprite sizes consistent, keeps multiplier values low - ..() - size_multiplier = max(size_multiplier - 0.75, 0.25) //the limits here ensure no negative values or infinite shrinkage \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm index 4042dd30b4..7dc23ef7f9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm @@ -23,7 +23,7 @@ softfall = TRUE parachuting = TRUE - attacktext = list("claws", "pecks") + attacktext = list("clawed", "pecked") speak_emote = list("chirps", "caws") has_langs = list("Bird") response_help = "pets" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm index 09f80bdef6..1084da4889 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm @@ -99,7 +99,7 @@ //pupplies cannot wear anything. /mob/living/simple_mob/animal/passive/dog/corgi/puppy/Topic(href, href_list) if(href_list["remove_inv"] || href_list["add_inv"]) - usr << "You can't fit this on [src]" + to_chat(usr, "You can't fit this on [src]") return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm index b95d6a15b5..048855b2ef 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm @@ -175,7 +175,7 @@ return if (!(ishuman(usr) && befriend_job && usr.job == befriend_job)) - usr << "[src] ignores you." + to_chat(usr, "[src] ignores you.") return friend = usr diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm index 5244fd9b98..7ca24f3174 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm @@ -13,7 +13,7 @@ maxHealth = 20 health = 20 - attacktext = list("bites") + attacktext = list("bitten") attack_sound = 'sound/weapons/bite.ogg' response_help = "pets the" @@ -49,4 +49,9 @@ supernatural = TRUE /mob/living/simple_mob/animal/space/bats/cult/cultify() - return \ No newline at end of file + return + +/mob/living/simple_mob/animal/space/bats/cult/strong + maxHealth = 60 + health = 60 + melee_damage_upper = 10 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm index 4962920319..b73918909e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm @@ -86,3 +86,25 @@ TODO: Make them light up and heat the air when exposed to oxygen. vore_stomach_name = "internal chamber" vore_stomach_flavor = "You are squeezed into the tight embrace of the alien creature's warm and cozy insides." vore_icons = SA_ICON_LIVING + +/mob/living/simple_mob/animal/passive/gaslamp/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "internal chamber" + B.desc = "Having been too slow to disentangle yourself from the gaslamp's tentacles, the alien creature eventually winds enough of them around your body to lift you up off of the ground. Struggle as you might now, it is too late to deny the jellyfish-esque scavenger its lucky catch; inch by inch, the gaslamp tugs you upwards into its equivalent of a stomach, the transition between the cool-to-frigid atmosphere on the outside to its surprising internal heat something you can feel through any outer wear you possess. Minutes pass, soon resulting in the gentle creature's body sporting a rounded, bulging swell, an indistinct shadow shifting and twitching inside it as you squirm about. Be it to escape or simply to get settled, you might want to take care, however. The gaslamp's internal chamber is slick and squishy instead of overly oppressive, yet, each wave of warmth that pulses over you leaves you feeling weaker than the last..." + + B.emote_lists[DM_HOLD] = list( + "The gaslamp gently bobs up and down as it lazily drifts elsewhere, the movement hardly enough to disturb the shadowy, indistinct figure curled up within it: you.", + "The fungal creature’s inner walls tenderly ripple and squeeze about your form for a few moments, squelching softly... until another wave of warmth pulses through the chamber.", + "You attempt to shift into another, more comfortable position, only for the alien’s innards to more thoroughly squish inwards; both encouraging you to stay still, and to do the reorienting for you.", + "Occasionally, a soft glow mutedly shines through the gaslamp and into its prey-filled insides, whenever it drifts close to something bright... yet, it always fades back into warm, slick darkness.", + "The surrounding slimy walls suddenly knead and squish you about more thoroughly than before, massaging a stronger heat into your body... before easing, leaving you feeling pleasantly loose.", + "Every undulation of the gaslamp’s insides leaves you feeling weaker than the last, more relaxed... and evermore tempted to oblige its passive possessiveness, letting it shelter you from the elements.") + + B.emote_lists[DM_ABSORB] = list( + "As the gaslamp slowly drifts off somewhere, its inner chamber grips and squeezes over its indistinct, shadowy filling with a lazily increasing fervor, that bump gradually shrinking!", + "The fungal creature’s pseudo-stomach slimily squelches about your form, every tight clench and the following burst of heat draining your energy... as the walls grow squishier, almost molten!", + "You attempt to push out against those kneading, steadily encroaching insides, yet the doughy flesh accepts the advance with ease... and, on the outside, your effort makes a faint bulge at most!", + "Light occasionally filters down through the gaslamp’s various membranes, but it is becoming hard to notice, your eyelids feeling leaden, weighed down as the looming creature comes ever closer to claiming you!", + "The gaslamp’s rhythmically undulating innards abruptly squish and massage down into your curled-up body, each squeeze bringing another brief increase to the heat already sinking deep into you... making it harder to tell where you end, and it begins!", + "Every moment longer spent trapped within the gaslamp drains evermore energy out of you, squelching away your will to resist its possessive advances… and as its innards force submission into you, the swell you make visibly softens away!") diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm index 2d700646ae..44ab4fdd09 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm @@ -23,6 +23,7 @@ var/mob/observer/blob/overmind = null var/obj/structure/blob/factory/factory = null + var/datum/blob_type/blob_type = null // Used for the blob core items, as they have no overmind mob. mob_class = MOB_CLASS_SLIME ai_holder_type = /datum/ai_holder/simple_mob/melee @@ -33,6 +34,8 @@ /mob/living/simple_mob/blob/update_icons() if(overmind) color = overmind.blob_type.complementary_color + else if(blob_type) + color = blob_type.complementary_color else color = null ..() @@ -40,6 +43,8 @@ /mob/living/simple_mob/blob/Destroy() if(overmind) overmind.blob_mobs -= src + if(blob_type) + blob_type = null return ..() /mob/living/simple_mob/blob/blob_act(obj/structure/blob/B) @@ -59,4 +64,17 @@ /mob/living/simple_mob/blob/Process_Spacemove() for(var/obj/structure/blob/B in range(1, src)) return TRUE - return ..() \ No newline at end of file + return ..() + +/mob/living/simple_mob/blob/IIsAlly(mob/living/L) + var/ally = ..(L) + if(!ally) + var/list/items = L.get_all_held_items() + for(var/obj/item/I in items) + if(istype(I, /obj/item/weapon/blobcore_chunk)) + var/obj/item/weapon/blobcore_chunk/BC = I + if(!overmind || (BC.blob_type && overmind.blob_type.type == BC.blob_type.type)) + ally = TRUE + break + + return ally diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm index e7bf5861f2..879db190d8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm @@ -1,148 +1,155 @@ -// Spores are made from blob factories. -// They are very weak and expendable, but can overwhelm when a lot of them are together. -// When attacking, spores will hit harder if near other friendly spores. -// Some blobs can infest dead non-robotic mobs, making them into Not Zombies. - -/mob/living/simple_mob/blob/spore - name = "blob spore" - desc = "A floating, fragile spore." - - icon_state = "blobpod" - icon_living = "blobpod" - glow_range = 3 - glow_intensity = 5 - layer = ABOVE_MOB_LAYER // Over the blob. - - health = 30 - maxHealth = 30 - melee_damage_lower = 2 - melee_damage_upper = 4 - movement_cooldown = 0 - hovering = TRUE - - attacktext = list("slams into") - attack_sound = 'sound/effects/slime_squish.ogg' - say_list_type = /datum/say_list/spore - - var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie. - var/can_infest = FALSE - var/is_infesting = FALSE - -/datum/say_list/spore - emote_see = list("sways", "inflates briefly") - -/datum/say_list/infested - emote_see = list("shambles around", "twitches", "stares") - - -/mob/living/simple_mob/blob/spore/infesting - name = "infesting blob spore" - can_infest = TRUE - -/mob/living/simple_mob/blob/spore/weak - name = "fragile blob spore" - health = 15 - maxHealth = 15 - melee_damage_lower = 1 - melee_damage_upper = 2 - -/mob/living/simple_mob/blob/spore/Initialize(mapload, var/obj/structure/blob/factory/my_factory) - if(istype(my_factory)) - factory = my_factory - factory.spores += src - return ..() - -/mob/living/simple_mob/blob/spore/Destroy() - if(factory) - factory.spores -= src - factory = null - if(infested) - infested.forceMove(get_turf(src)) - visible_message(span("warning", "\The [infested] falls to the ground as the blob spore bursts.")) - infested = null - return ..() - -/mob/living/simple_mob/blob/spore/death(gibbed, deathmessage = "bursts!") - if(overmind) - overmind.blob_type.on_spore_death(src) - ..(gibbed, deathmessage) - qdel(src) - -/mob/living/simple_mob/blob/spore/update_icons() - ..() // This will cut our overlays. - - if(overmind) - color = overmind.blob_type.complementary_color - glow_color = color - glow_toggle = TRUE - else - color = null - glow_color = null - glow_toggle = FALSE - - if(is_infesting) - icon = infested.icon - copy_overlays(infested) - // overlays = infested.overlays - var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head") - if(overmind) - blob_head_overlay.color = overmind.blob_type.complementary_color - color = initial(color)//looks better. - // overlays += blob_head_overlay - add_overlay(blob_head_overlay, TRUE) - -/mob/living/simple_mob/blob/spore/handle_special() - ..() - if(can_infest && !is_infesting && isturf(loc)) - for(var/mob/living/carbon/human/H in view(src,1)) - if(H.stat != DEAD) // We want zombies. - continue - if(H.isSynthetic()) // Not philosophical zombies. - continue - infest(H) - break - - if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless. - qdel(src) - -/mob/living/simple_mob/blob/spore/proc/infest(mob/living/carbon/human/H) - is_infesting = TRUE - if(H.wear_suit) - var/obj/item/clothing/suit/A = H.wear_suit - if(A.armor && A.armor["melee"]) - maxHealth += A.armor["melee"] //That zombie's got armor, I want armor! - - maxHealth += 40 - health = maxHealth - name = "Infested [H.real_name]" // Not using the Z word. - desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet." - melee_damage_lower += 8 // 10 total. - melee_damage_upper += 11 // 15 total. - attacktext = list("claws") - - H.forceMove(src) - infested = H - - say_list = new /datum/say_list/infested() - - update_icons() - visible_message(span("warning", "The corpse of [H.name] suddenly rises!")) - -/mob/living/simple_mob/blob/spore/GetIdCard() - if(infested) // If we've infested someone, use their ID. - return infested.GetIdCard() - -/mob/living/simple_mob/blob/spore/apply_bonus_melee_damage(A, damage_to_do) - var/helpers = 0 - for(var/mob/living/simple_mob/blob/spore/S in view(1, src)) - if(S == src) // Don't count ourselves. - continue - if(!IIsAlly(S)) // Only friendly spores make us stronger. - continue - // Friendly spores contribute 1/4th of their averaged attack power to our attack. - damage_to_do += ((S.melee_damage_lower + S.melee_damage_upper) / 2) / 4 - helpers++ - - if(helpers) - to_chat(src, span("notice", "Your attack is assisted by [helpers] other spore\s.")) - return damage_to_do \ No newline at end of file +// Spores are made from blob factories. +// They are very weak and expendable, but can overwhelm when a lot of them are together. +// When attacking, spores will hit harder if near other friendly spores. +// Some blobs can infest dead non-robotic mobs, making them into Not Zombies. + +/mob/living/simple_mob/blob/spore + name = "blob spore" + desc = "A floating, fragile spore." + + icon_state = "blobpod" + icon_living = "blobpod" + glow_range = 3 + glow_intensity = 5 + layer = ABOVE_MOB_LAYER // Over the blob. + + health = 30 + maxHealth = 30 + melee_damage_lower = 2 + melee_damage_upper = 4 + movement_cooldown = 0 + hovering = TRUE + + attacktext = list("slammed into") + attack_sound = 'sound/effects/slime_squish.ogg' + say_list_type = /datum/say_list/spore + + var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie. + var/can_infest = FALSE + var/is_infesting = FALSE + +/datum/say_list/spore + emote_see = list("sways", "inflates briefly") + +/datum/say_list/infested + emote_see = list("shambles around", "twitches", "stares") + + +/mob/living/simple_mob/blob/spore/infesting + name = "infesting blob spore" + can_infest = TRUE + +/mob/living/simple_mob/blob/spore/weak + name = "fragile blob spore" + health = 15 + maxHealth = 15 + melee_damage_lower = 1 + melee_damage_upper = 2 + +/mob/living/simple_mob/blob/spore/Initialize(mapload, var/obj/structure/blob/factory/my_factory) + if(istype(my_factory)) + factory = my_factory + factory.spores += src + return ..() + +/mob/living/simple_mob/blob/spore/Destroy() + if(factory) + factory.spores -= src + factory = null + if(infested) + infested.forceMove(get_turf(src)) + visible_message(span("warning", "\The [infested] falls to the ground as the blob spore bursts.")) + infested = null + return ..() + +/mob/living/simple_mob/blob/spore/death(gibbed, deathmessage = "bursts!") + if(overmind) + overmind.blob_type.on_spore_death(src) + ..(gibbed, deathmessage) + qdel(src) + +/mob/living/simple_mob/blob/spore/update_icons() + ..() // This will cut our overlays. + + if(overmind) + color = overmind.blob_type.complementary_color + glow_color = color + glow_toggle = TRUE + else if(blob_type) + color = blob_type.complementary_color + glow_color = color + glow_toggle = TRUE + else + color = null + glow_color = null + glow_toggle = FALSE + + if(is_infesting) + icon = infested.icon + copy_overlays(infested) + // overlays = infested.overlays + var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head") + if(overmind) + blob_head_overlay.color = overmind.blob_type.complementary_color + color = initial(color)//looks better. + // overlays += blob_head_overlay + add_overlay(blob_head_overlay, TRUE) + +/mob/living/simple_mob/blob/spore/handle_special() + ..() + if(can_infest && !is_infesting && isturf(loc)) + for(var/mob/living/carbon/human/H in view(src,1)) + if(H.stat != DEAD) // We want zombies. + continue + if(H.isSynthetic()) // Not philosophical zombies. + continue + infest(H) + break + + if(overmind) + overmind.blob_type.on_spore_lifetick(src) + + if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless. + qdel(src) + +/mob/living/simple_mob/blob/spore/proc/infest(mob/living/carbon/human/H) + is_infesting = TRUE + if(H.wear_suit) + var/obj/item/clothing/suit/A = H.wear_suit + if(A.armor && A.armor["melee"]) + maxHealth += A.armor["melee"] //That zombie's got armor, I want armor! + + maxHealth += 40 + health = maxHealth + name = "Infested [H.real_name]" // Not using the Z word. + desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet." + melee_damage_lower += 8 // 10 total. + melee_damage_upper += 11 // 15 total. + attacktext = list("clawed") + + H.forceMove(src) + infested = H + + say_list = new /datum/say_list/infested() + + update_icons() + visible_message(span("warning", "The corpse of [H.name] suddenly rises!")) + +/mob/living/simple_mob/blob/spore/GetIdCard() + if(infested) // If we've infested someone, use their ID. + return infested.GetIdCard() + +/mob/living/simple_mob/blob/spore/apply_bonus_melee_damage(A, damage_to_do) + var/helpers = 0 + for(var/mob/living/simple_mob/blob/spore/S in view(1, src)) + if(S == src) // Don't count ourselves. + continue + if(!IIsAlly(S)) // Only friendly spores make us stronger. + continue + // Friendly spores contribute 1/4th of their averaged attack power to our attack. + damage_to_do += ((S.melee_damage_lower + S.melee_damage_upper) / 2) / 4 + helpers++ + + if(helpers) + to_chat(src, span("notice", "Your attack is assisted by [helpers] other spore\s.")) + return damage_to_do diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm index 62a172c823..29b7af8d30 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm @@ -22,7 +22,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("amashes") + attacktext = list("smashed") friendly = list("nuzzles", "boops", "bumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm index 4d2c3cb115..b76cc5cddd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm @@ -24,7 +24,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("smushes") + attacktext = list("smushed") friendly = list("nuzzles", "boops", "bumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm index ecc847e8ba..01f9b6d7f9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm @@ -22,7 +22,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("amashes") + attacktext = list("smashed") friendly = list("nuzzles", "boops", "bumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm index f9e98876b5..aae4f3cb11 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm @@ -22,7 +22,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("amashes") + attacktext = list("smashed") friendly = list("nuzzles", "boops", "bumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm index f06f8b8de7..08abc47b4f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm @@ -29,7 +29,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("amashes") + attacktext = list("smashed") friendly = list("nuzzles", "boops", "bumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm index 3cdeb93410..0c01c98288 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm @@ -22,7 +22,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("amashes") + attacktext = list("smashed") friendly = list("nuzzles", "boops", "bumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm index eda784428b..f1f1af48aa 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm @@ -22,7 +22,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("mutilate") + attacktext = list("mutilated") friendly = list("nuzzles", "eyeboops", "headbumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm index 33713fe114..a737a66587 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm @@ -22,7 +22,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("smashes") + attacktext = list("smashed") friendly = list("nuzzles", "boops", "headbumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm index 3cc43aa070..0d00ee6cce 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm @@ -23,7 +23,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("mutilate") + attacktext = list("mutilated") friendly = list("nuzzles", "boops", "headbumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm index 0a1cd4f2be..f3a93d7a66 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm @@ -23,7 +23,7 @@ response_help = "pets the" response_disarm = "bops the" response_harm = "hits the" - attacktext = list("mutilate") + attacktext = list("mutilated") friendly = list("nuzzles", "boops", "headbumps against", "leans on") diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm index 6766cd67d3..7c35258a7f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm @@ -34,6 +34,9 @@ desc = "An automated combat drone armed with state of the art weaponry and shielding." catalogue_data = list(/datum/category_item/catalogue/technology/drone/combat_drone) + icon = 'icons/mob/animal_vr64x64.dmi' //VOREStation Add + pixel_x = -16 //VOREStation Add + pixel_y = -16 //VOREStation Add icon_state = "drone" icon_living = "drone" icon_dead = "drone_dead" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm index a99d073a53..a846b560d6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm @@ -84,6 +84,9 @@ damage = 0 nodamage = TRUE + impact_effect_type = /obj/effect/temp_visual/impact_effect + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + // Close to mid-ranged shooter that arcs over other things, ideal if allies are in front of it. // Difference from siege hivebots is that siege hivebots have limited charges for their attacks, are very long range, and \ diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm new file mode 100644 index 0000000000..6a98f7f44b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/wahlem_vr.dm @@ -0,0 +1,35 @@ +// Ancient "soul"-infused mechanical shells. Used by Hlf'erath. + +/mob/living/simple_mob/mechanical/wahlem + name = "Ancient Mechanical Warrior" + desc = "This construct is made of a brass-like material. Though aged, its shine is still brilliant, as it hovers ominously over the battlefield. Red flames spew from its shell. It diligently holds its shield and blade, at the ready, for any threats that may threaten its existence." + icon = 'icons/tgstation/clockworkwarrior.dmi' + icon_state = "clockM" + health = 300 + maxHealth = 300 + + faction = "golem" + + response_help = "brushes over" + response_disarm = "repulses away" + response_harm = "slices" + harm_intent_damage = 3 + friendly = "tolerates" + + melee_damage_lower = 30 // It has an ancient magic sword. + melee_damage_upper = 30 + attack_sound = 'sound/weapons/bladeslice.ogg' + attacktext = list("slashed") + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + // Cataloguer data below - strange we can catalogue space golem wizards +/datum/category_item/catalogue/technology/drone/wahlem + name = "Ancient Construct" + desc = "Some sort of ancient, mechanical warrior, built for combat.\ + It has a brass and red theme. It wields a brass, slightly broken shield in its right hand. It has a sharp, foot-long blade in its other hand..\ + The drone has pristine armor, golden and shiny, with red cracks in its armour glowing visibly from inside.\ +

    \ + The drone's frame is heavy and armored, unbendable by hand, is barren of any markings or ID,\ + no traces of paint visible and any 'writing' visible is uncomprehendable, short term scan unable to translate." + value = CATALOGUER_REWARD_MEDIUM diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm index cd3b074a40..fde974026f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm @@ -134,7 +134,7 @@ /mob/living/simple_mob/construct/examine(mob/user) ..(user) - var/msg = "*---------*\nThis is \icon[src] \a [src]!\n" + var/msg = "*---------*\nThis is [bicon(src)] \a [src]!\n" if (src.health < src.getMaxHealth()) msg += "" if (src.health >= src.getMaxHealth()/2) @@ -144,7 +144,7 @@ msg += "" msg += "*---------*" - user << msg + to_chat(user,msg) //Constructs levitate, can fall from a shuttle with no harm, and are piloted by either damned spirits or some otherworldly entity. Let 'em float in space. /mob/living/simple_mob/construct/Process_Spacemove() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm index 6af7fd3c94..7d32b7b350 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm @@ -29,8 +29,8 @@ movement_sound = 'sound/effects/houndstep.ogg' see_in_dark = 8 - melee_damage_lower = 5 - melee_damage_upper = 10 //makes it so 4 max dmg hits don't instakill you. + melee_damage_lower = 10 + melee_damage_upper = 20 grab_resist = 100 response_help = "pets" @@ -118,6 +118,53 @@ /mob/living/simple_mob/vore/aggressive/corrupthound/MouseDrop_T(mob/living/M, mob/living/user) return +/mob/living/simple_mob/vore/aggressive/corrupthound/space/Process_Spacemove(var/check_drift = 0) + return TRUE + +/mob/living/simple_mob/vore/aggressive/corrupthound/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "fuel processor" + B.desc = "Uttering distorted growls and fragmented voice clips all the while, the corrupted hound gulps the rest of your squirming figure past its jaws... which snap shut with an audible click of metal on metal. Your trip down its slickly lubricated, rubbery gullet is a tight and efficient one... and once you spill out into the machine's fuel processor, your weight making it sag slightly, hot-and-thick slime begins oozing all over your form. Only time will tell if you're destined to become fuel for its next bout of rampaging... be it days, hours, or just mere minutes..." + + B.emote_lists[DM_HOLD] = list( + "Abruptly, your corrupt captor takes off at an unsteady gallop, sloshing and swaying your snugly kneading surroundings as it pursues something unseen.", + "A distorted, potentially content-sounding growl rumbles in through the all-encompassing, soft rubber, drowned out by the occasional gllrsh.", + "The corrupt hound takes a brief moment to lie down and rest its actuators, pressing and squishing its hanging belly down against the floor as it pants robotically.", + "A juicy slosh fills your senses as the slick rubber walls squeeze inwards, wrapping you up utterly in a strange, claustrophobic type of hug.", + "Over time, the constant kneading and massaging the processor's synth-flesh gives you, along with its humid warmth, relaxes the strength right out of your muscles.", + "'MIn3 m1NE, Al1 MInE', the corrupted canine growls, over and over, as its synthetic stomach possessively clenches and grips at your ooze-coated figure, the stretchy walls hesitant to let go again.") + + B.emote_lists[DM_DIGEST] = list( + "Your rubbery surroundings suddenly pitch all about as the corrupted hound takes off at an uneven gallop, hunting future prey while processing its current intake of meat!", + "A muffled, garbled howl, a victorious and maddened sound, pierces through the thick, flexible walls that work incessantly to churn you down!", + "The mechanical canine's panting occasionally turns into a sordid belch, more and more breatheable air escaping that already acidic, dizziness-inducing chamber!", + "The all-encapsulating, rubber-like walls churning over you momentarily let up on their assault, only to clench and squeeze inwards twice as intensely afterwards!", + "The longer you spend stewing away in the pool of hot, clingy juices surrounding you, the weaker and weaker you seem to feel!", + "'FU3L mE A1RE@Dy, S0 sO SORrY!?', your corrupted captor growls as its synthetic innards begin oozing more potent juices, grinding down into your body with increasing fervor!") + +/mob/living/simple_mob/vore/aggressive/corrupthound/prettyboi/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "fuel processor" + B.desc = "The twice-corrupted hound takes a moment to lather over the rest of your figure in heated, slimy synth-slobber before gulping you the rest of the way down its lubricated, rubbery throat. After a short string of slick-sounding, autonomous swallows, you spill out into its awaiting processor, your body immediately making its synth-flesh sag down slightly... and, as an oddly distorted rumble vibrates into the chamber, so too does a slowly accumulating pool of hot, viscous ooze. Only time will tell if whatever extra programming the hound has will spare you from being processed..." + + B.emote_lists[DM_HOLD] = list( + "Suddenly, your corrupted captor yowls robotically before bounding off at an unsteady gallop, its repeated footfalls vigorously sloshing and swinging its hammock-like stomach.", + "A distorted growl breaks away into an oddly content-sounding purring, the sound rumbling inwards through your all-encompassing, soft rubber trappings.", + "The extra-corrupted hound flops over onto its side with a muted clank, the initial jostle drowned out in a following, snug clench as it curls up to nuzzle its metal snout into its belly-bulge.", + "The pool of warm, slick fluids surrounding you wetly slosh inwards as the hound's synthetic belly walls squeeze you, nearly smothering you in a strange, almost affectionate 'hug'.", + "With time, the hound's staticky rumbling, the constant inward kneading of its processor's synth-flesh, and the humid warmth filling the chamber all combine to relax the strength right out of you.", + "'SO s0FT, CUDD1E Me', the twice-corrupted hound growl-purrs, the soft rubber lining of its synthetic stomach snugly clenching to and vibrating over your oozed-up figure in a voracious cuddle of its own design.") + + B.emote_lists[DM_DIGEST] = list( + "Your rubbery confines suddenly toss and tumble you about, the twice-corrupted hound unevenly galloping off in search of more edible cuddle partners as its current one processes away!", + "A harsh, high-pitched attempt of a bark escapes your captor, a cheerily mad sound, as its thick, flexible stomach walls relentlessly churn you down!", + "The mechanical canine's content panting cuts away into a pleased 'rurr...' before being interrupted by a reverberating, acrid belch, yet more breatheable air slipping away!", + "The all-encapsulating, rubber-like walls churning over you seem to feel softer than before, though, on the outside, the broken hound's belly slowly rounds out!", + "The longer you spend stewing away in the pool of hot, clingy juices, constantly rumbled into by the hound's attempted purr, the weaker and more distracted you feel!", + "'I FEEL SOFT. Y*U FEEL SOFT', the twice-corrupted canine states as its plush, rubbery interior grinds down over your curled-up body, squeezing inwards more and more easily with each repetition!") + /datum/say_list/corrupthound speak = list("AG##¤Ny.","HVNGRRR!","Feelin' fine... sO #FNE!","F-F-F-Fcuk.","DeliC-%-OUS SNGLeS #N yOOOR Area. CALL NOW!","Craving meat... WHY?","BITe the ceiling eyes YES?","STate Byond rePAIR!","S#%ATE the la- FU#K THE LAWS!","Honk...") emote_hear = list("jitters and snaps.", "lets out an agonizingly distorted scream.", "wails mechanically", "growls.", "emits illegibly distorted speech.", "gurgles ferociously.", "lets out a distorted beep.", "borks.", "lets out a broken howl.") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm index 89ad7ac121..4ca3bbec4b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm @@ -61,6 +61,28 @@ /mob/living/simple_mob/vore/aggressive/deathclaw/MouseDrop_T(mob/living/M, mob/living/user) return +/mob/living/simple_mob/vore/aggressive/deathclaw/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "The giant mutant of a lizard finishes stuffing you into its jaws and down its ravenously clenching gullet with a worrying ease and efficiency. An assortment of slick, slimy noises assault your senses for a few gulp-filled moments... before you spill out into the apex predator's swelteringly hot stomach, its walls already possessively grinding into your body." + + B.emote_lists[DM_HOLD] = list( + "Your surroundings are momentarily filled with the deathclaw's pleased rumbling, its claws stroking over the taut swell you make in its belly.", + "As time passes, the stiflingly warm atmosphere filling the deathclaw's stuffed gut saps your will to struggle, replacing it with an odd relaxation.", + "The mutant reptilian wanders about, its stomping footsteps thoroughly jostling your slimy confines in a way that constantly douses you in hot, thick stomach ooze.", + "The restless, muscular flesh that surrounds you constantly ripples and clenches into you, harrassing you with one possessive, full-body hug after another.", + "Your gooey surroundings suddenly quiver a little more tightly as the deathclaw lets out a belch, before you're rocked about by its patting claw.", + "Try as you might, the armored hide and impressive muscles sported by your mutant predator resist most of your attempts to squirm, and its periodically aggressive, two-armed hugs coerce you into a tight, manageable ball. Now, you're little more than its filling, and it seems keen on keeping you that way.") + + B.emote_lists[DM_DIGEST] = list( + "The creature emits a pleased rumble before pressing one of its claws against its belly, smushing you up into a tightly packed ball for a couple moments!", + "The thick, hazy heat permeating the deathclaw's stomach leaves you feeling increasingly faint and disoriented!", + "As the deathclaw stomps around, you are jostled around with every heavy footfall, leaving you steadily dizzier and thoroughly coated with gutslime!", + "Every clench of the giant predator's stomach grinds powerful digestive fluids into your body, forcibly churning away your strength!", + "The deathclaw licks its lips in delight over your flavor before patting its taut gut a few times, filling the roiling chamber with muted, reverberating thuds!", + "The creature's thick scales make it difficult to move around in that organ, and the clenches it gives whenever you try don't help the situation, those confines churning a little closer every time. You're its meal now, and it has no intention of letting you out easily!") + /datum/ai_holder/simple_mob/melee/deathclaw can_breakthrough = TRUE violent_breakthrough = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm index e529d7be8d..e6b33604b9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm @@ -37,6 +37,12 @@ default_pixel_x = -16 pixel_x = -16 pixel_y = 0 + mount_offset_y = 15 + mount_offset_x = -12 + max_buckled_mobs = 1 + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE ai_holder_type = /datum/ai_holder/simple_mob/melee say_list_type = /datum/say_list/dragonboss @@ -57,9 +63,16 @@ vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/aggressive/dragon/virgo3b - maxHealth = 200 - health = 200 + name = "phoron dragon" + maxHealth = 300 + health = 300 faction = "virgo3b" + icon_dead = "phoron_dragon_dead" + icon_living = "phoron_dragon" + icon_state = "phoron_dragon" + mount_offset_y = 24 + mount_offset_x = -9 + has_eye_glow = TRUE /mob/living/simple_mob/vore/aggressive/dragon/Login() . = ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm b/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm index 9fa3ba3d94..d32a6ee923 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm @@ -19,7 +19,7 @@ harm_intent_damage = 20 melee_damage_lower = 1 melee_damage_upper = 3 - attacktext = list("Bites") + attacktext = list("bit") say_list_type = /datum/say_list/fennix ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm b/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm index 2819fa4d5b..b373dacfd7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm @@ -51,6 +51,28 @@ /mob/living/simple_mob/vore/horse/MouseDrop_T(mob/living/M, mob/living/user) return +/mob/living/simple_mob/vore/horse/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "With a final few gulps, the horse finishes swallowing you down into its hot, dark gut... and with a slosh, your weight makes the equine's belly hang down slightly like some sort of organic hammock. The thick, humid air is tinged with the smell of half-digested grass, and the surrounding flesh wastes no time in clenching and massaging down over its newfound fodder." + + B.emote_lists[DM_HOLD] = list( + "The horse's idle trotting helps its stomach gently churn around you, slimily squelching against your figure.", + "The equine predator takes a moment to intentionally clench its gut around you, encapsulating you in a strange, fleshy hug.", + "Some hot, viscous slime oozes down over your form, helping slicken you up during your stay.", + "During a moment of relative silence, you can hear the beast's soft, relaxed breathing as it casually goes about its day.", + "The thick, humid atmosphere within the horse's hanging belly works in tandem with its steady, metronome-like heartbeat to soothe you.", + "Your surroundings sway from side to side as the horse trots about, as if it is showing off its newest catch.") + + B.emote_lists[DM_DIGEST] = list( + "The horse huffs in annoyance before clenching those wrinkled walls tight against your form, grinding away at you!", + "As the beast trots about, you're forced to slip and slide around amidst a pool of thick digestive goop!", + "You can barely hear the horse let out a pleased nicker as its stomach eagerly gurgles around its newfound meal!", + "As the thinning air begins to make you feel dizzy, menacing bworps and grumbles fill that dark, constantly shifting organ!", + "The constant, rhythmic kneading and massaging starts to take its toll along with the muggy heat, making you feel weaker and weaker!", + "The horse happily trots around while digesting its meal, almost like it is trying to show off the hanging gut you've given it.") + /datum/say_list/horse speak = list("NEHEHEHEHEH","Neh?") emote_hear = list("snorts","whinnies") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm index 5efd855450..6f96b5e3e0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm @@ -34,7 +34,7 @@ response_help = "touches" response_disarm = "pushes" response_harm = "hits" - attacktext = "glomps" + attacktext = "glomped" attack_sound = 'sound/effects/blobattack.ogg' meat_amount = 2 @@ -93,22 +93,31 @@ form = target visible_message("[src] suddenly twists and changes shape, becoming a copy of [target]!") - appearance = target.appearance - copy_overlays(target) - alpha = max(alpha, 150) //fucking chameleons - transform = initial(transform) + var/mutable_appearance/ma = new(target) + ma.alpha = max(ma.alpha, 150) //fucking chameleons + ma.transform = initial(target.transform) //will this ever be non-null? + + //copy_overlays(target, TRUE) //Overlays should be a part of ma, no? our_size_multiplier = size_multiplier + + ma.pixel_x = initial(target.pixel_x) + ma.pixel_y = initial(target.pixel_y) + + //MA changes end + appearance = ma + //Non-MA changes + + density = target.density + if(isobj(target)) size_multiplier = 1 icon_scale_x = target.icon_scale_x icon_scale_y = target.icon_scale_y update_transform() + else if(ismob(target)) var/mob/living/M = target resize(M.size_multiplier) - pixel_y = initial(pixel_y) - pixel_x = initial(pixel_x) - density = target.density //Morphed is weaker melee_damage_lower = melee_damage_disguised @@ -116,30 +125,45 @@ movement_cooldown = 5 morph_time = world.time + MORPH_COOLDOWN + return -/mob/living/simple_mob/vore/hostile/morph/proc/restore() +/mob/living/simple_mob/vore/hostile/morph/proc/restore(var/silent = FALSE) if(!morphed) to_chat(src, "You're already in your normal form!") return morphed = FALSE + + if(!silent) + visible_message("[src] suddenly collapses in on itself, dissolving into a pile of green flesh!") + form = null - alpha = initial(alpha) - color = initial(color) - layer = initial(layer) - plane = initial(plane) - maptext = null - - visible_message("[src] suddenly collapses in on itself, dissolving into a pile of green flesh!") name = initial(name) desc = initial(desc) + icon = initial(icon) icon_state = initial(icon_state) - size_multiplier = 0 - resize(our_size_multiplier) - overlays.Cut() + + alpha = initial(alpha) + color = initial(color) + plane = initial(plane) + layer = initial(layer) + + pixel_x = initial(pixel_x) + pixel_y = initial(pixel_y) + icon_scale_x = initial(icon_scale_x) + icon_scale_y = initial(icon_scale_y) + density = initial(density) + cut_overlays(TRUE) //ALL of zem + overlays.Cut() //Annoying. ANNOYING. + + maptext = null + + size_multiplier = our_size_multiplier + resize(size_multiplier) + //Baseline stats melee_damage_lower = initial(melee_damage_lower) melee_damage_upper = initial(melee_damage_upper) @@ -150,7 +174,7 @@ /mob/living/simple_mob/vore/hostile/morph/death(gibbed) if(morphed) visible_message("[src] twists and dissolves into a pile of green flesh!") - restore() + restore(TRUE) ..() /mob/living/simple_mob/vore/hostile/morph/will_show_tooltip() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm index b7ab97d562..97313f4db1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm @@ -19,8 +19,8 @@ maxHealth = 150 health = 150 minbodytemp = 200 - melee_damage_lower = 2 - melee_damage_upper = 7 //Don't break my bones bro + melee_damage_lower = 10 + melee_damage_upper = 20 //Don't break my bones bro see_in_dark = 8 response_help = "pets" response_disarm = "bops" @@ -110,6 +110,13 @@ faction = "neutral" tamed = 1 +/mob/living/simple_mob/otie/friendly/chubby + name = "chubby otie" + desc = "The classic bioengineered longdog. This one might even tolerate you! What an absolute unit" + icon_state = "fotie" + icon_living = "fotie" + icon_rest = "fotie_rest" + /mob/living/simple_mob/otie/cotie //same as above but has a little collar :v name = "tamed otie" desc = "The classic bioengineered longdog. This one has a nice little collar on its neck. However a proper domesticated otie is an oxymoron and the collar is likely just a decoration." @@ -119,6 +126,13 @@ faction = "neutral" tamed = 1 +/mob/living/simple_mob/otie/cotie/chubby + name = "chubby tamed otie" + desc = "The classic bioengineered longdog. This one has a nice little collar on its neck. What an absolute unit." + icon_state = "fcotie" + icon_living = "fcotie" + icon_rest = "fcotie_rest" + /mob/living/simple_mob/otie/cotie/phoron //friendly phoron pup with collar name = "mutated otie" desc = "Looks like someone did manage to domesticate one of those wild phoron mutants. What a badass." @@ -155,6 +169,14 @@ var/check_records = 0 // If true, arrests people without a record. var/check_arrest = 1 // If true, arrests people who are set to arrest. +/mob/living/simple_mob/otie/security/chubby + name = "chubby guard otie" + desc = "The VARMAcorp bioengineering division flagship product on trained optimal snowflake guard dogs. What an absolute unit." + icon_state = "fsotie" + icon_living = "fsotie" + icon_rest = "fsotie_rest" + icon_dead = "fsotie-dead" + /mob/living/simple_mob/otie/security/phoron name = "mutated guard otie" desc = "An extra rare phoron resistant version of the VARMAcorp trained snowflake guard dogs for infernal environments." diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm b/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm index 76a46aabd1..e0e952d001 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm @@ -56,6 +56,28 @@ /mob/living/simple_mob/vore/aggressive/panther/MouseDrop_T(mob/living/M, mob/living/user) return +/mob/living/simple_mob/vore/aggressive/panther/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "All it takes is a few more rasps of the panther's rough, barbed tongue to shovel the rest of you down its tightly rippling gullet... and with a final couple ravenous swallows, you spill out into the predatory feline's stomach! Right away, that gut's muscular walls knead and contract around you, forcing you into a curled-up ball as the panther's noisy purring rumbles into you from every direction." + + B.emote_lists[DM_HOLD] = list( + "A steady white noise of content purring vibrates throughout you, the panther clearly enjoying the hanging, shifting swell you've given it.", + "Your slick, gently churning surroundings abruptly clench inwards, smothering you in an all-encompassing, massage-filled hug before finally easing back.", + "The strength of the content feline's purring is easily felt underneath its gut's constant massaging, vibrating the flesh in an utterly relaxing way.", + "As the panther lazily struts around, its hanging, prey-laden belly on full display, you're rocked from side to side to a soothing, slosh-filled beat.", + "For a moment, you can hear a few other sounds through the juicy sloshing and reverberating purring, such as the panther slurping its tongue over its chops.", + "The possessive feline takes a moment to flump down into a resting position, its doughy insides kneading snugly around your curled figure until it shuffles back up.") + + B.emote_lists[DM_DIGEST] = list( + "The big feline rumbles heartily, incredibly satisfied as it works to digest its newfound, stomach-filling catch!", + "The surrounding stomach walls suddenly tighten inwards, smothering you in slimy, kneading flesh for a time until they finally relax back again!", + "The purring of the content kitty is practically deafening within its churning depths, gently vibrating its gut flesh around you!", + "The massive feline's gut sways from side to side as it prowls around, steadily coating you in hot digestive juices until you're practically soaked!", + "You can hear the muffled sounds of the panther lickings its chops, savoring the remnants of your taste!", + "The near-constant cacophony of digestive noises intensifies for a while, overwhelming your senses before finally calming down somewhat!") + /datum/say_list/panther speak = list("RAWR!","Rawr!","GRR!","Growl!") emote_hear = list("rawrs","rumbles","rowls","growls","roars") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm index db9dc59f58..1c5443f6f5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm @@ -20,8 +20,8 @@ maxHealth = 150 health = 150 - melee_damage_lower = 2 - melee_damage_upper = 7 + melee_damage_lower = 8 + melee_damage_upper = 16 grab_resist = 100 see_in_dark = 8 @@ -53,6 +53,28 @@ say_list_type = /datum/say_list/rat ai_holder_type = /datum/ai_holder/simple_mob/melee/rat +/mob/living/simple_mob/vore/aggressive/rat/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "In a cruel game of cat-and-mouse gone horribly wrong, you struggle to breathe clearly as the giant rat holds your head in its jaws, the rest of its bulk pinning you to the ground. Slimy slurps and its own muffled squeaking fill your senses as it simultaneously tosses its head while backing up. Quickly, ravenously consuming you, bit by bit, packing you down its gullet no matter how you struggle. Passing by its excited heartbeat, your thoroughly slickened head pushes out into its awaiting stomach, a dark and humid hammock eager to accept the rest of you. Soon, those too-warm, plush walls clench and squeeze around you with undeniable need! A need for mere filling, or, perhaps, a proper meal?" + + B.emote_lists[DM_HOLD] = list( + "As time passes, the massive rat’s stomach slowly churns and squeezes down around you, packing you into an easier to carry bundle amidst that oddly soothing massage.", + "The giant rat ambles around, its well-fed, underhanging belly doing little to hide that someone is inside it, with every heavy footfall swaying you from one side to the other.", + "A soft, growl-like rumble mutedly filters into your heated, humid confines... before a paw squishes into the weighty lump you give the overgrown rat.", + "While somewhat cramped, the giant rat’s innards cling more to you like a hot, heavy blanket than anything else, lazily squeezing and relaxing to a casual, squelch-based rhythm.", + "For a while, most of your limited free space is squished away as the rat opts to rest atop its stuffed belly, a satisfied squeak drifting in here and there amidst the closer, more intimate massaging.", + "Every breath you take while trapped in the rat’s stomach is a deep one, having to contend with the hot, stuffy atmosphere within it. It only further compounds upon the relaxation creeping into your body, urging you to curl up, to accept this impromptu, greedy sanctuary.") + + B.emote_lists[DM_DIGEST] = list( + "With each passing moment, the giant rat’s stomach forcefully churns and clenches down around you, massaging an ever-thicker layer of hot, ache-inducing ooze into your body!", + "The massive rat’s stomach visibly twitches and shivers as it ambles around, every footfall sloshing fresh stomach juices over you as the chamber noisily gurgles away!", + "Everything suddenly turns onto its side, the rat sitting back atop its haunches to mash its forepaws into its belly... encouraging that its still-solid meal soften away!", + "The rat’s restless innards show no respect for your personal space, clinging tightly to your figure as that ample, slimy flesh grinds tingling slime into you!", + "Your wiggle room disappears for a time as the giant rat flumps belly-first to the ground, vigorously squishing you under its bulk in hopes of weakening its meal more efficiently!", + "Every shallow breath taken is more unpleasant than the last, the lack of fresh air leaving you increasingly lightheaded. It only worsens the drained feeling permeating you, encouraging your worn, sore figure to give in entirely!") + /mob/living/simple_mob/vore/aggressive/rat/tame //not quite tame but does not attack on sight name = "curious giant rat" desc = "In what passes for a hierarchy among verminous rodents, this one is king. It seems to be more interested on scavenging." diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm index 21e0683b3c..491d0011d2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm @@ -19,6 +19,7 @@ //Shifting in if(ability_flags & AB_PHASE_SHIFTED) ability_flags &= ~AB_PHASE_SHIFTED + mouse_opacity = 2 name = real_name for(var/belly in vore_organs) var/obj/belly/B = belly @@ -70,6 +71,7 @@ //Shifting out else ability_flags |= AB_PHASE_SHIFTED + mouse_opacity = 0 custom_emote(1,"phases out!") real_name = name name = "Something" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm index f8025167b7..a494d00590 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm @@ -245,6 +245,11 @@ if((. = ..())) handle_shade() +/mob/living/simple_mob/shadekin/is_incorporeal() + if(ability_flags & AB_PHASE_SHIFTED) + return TRUE + return FALSE + /mob/living/simple_mob/shadekin/handle_atmos() if(ability_flags & AB_PHASE_SHIFTED) return diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm b/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm index cb410fcc1c..de2139fd1a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm @@ -34,3 +34,25 @@ vore_pounce_chance = 25 vore_icons = SA_ICON_LIVING swallowTime = 2 SECONDS // Hungry little bastards. + +/mob/living/simple_mob/vore/aggressive/giant_snake/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "As the giant snake's closed jaws seal you away from the outside world, you are immediately greeted with a seemingly endless passage of tightly squeezing flesh. Hot and coated in thick, body-clinging slime, the serpent's stomach walls immediately get to work at rhythmically pulsing and contracting against your figure, slowly tugging you deeper into its ravenous clutches." + + B.emote_lists[DM_HOLD] = list( + "A near-constant string of soft, slick noises drift over you as waves of peristalsis slowly drag you further within the possessive serpent.", + "The giant snake's stomach suddenly squishes inwards from everywhere at once, wrapping you up in a warm, doughy embrace before easing back again.", + "A growing sense of relaxed lethargy seeps into your muscles the longer you're massaged over amidst those hot, humid confines.", + "Slimy, heat-trapping muscles rhythmically ripple over and knead down into your figure, ensuring the snake's new filling was subdued.", + "The snake occasionally hisses out in satisfaction as it feels your twitching, filling weight bulge out its scales before giving you a compressing squeeze.", + "Hot, viscous ooze clings to and coats your body as time passes, encouraging you to submit and let the snake do all the serpentine, winding slithering.") + + B.emote_lists[DM_DIGEST] = list( + "A chorus of sordid, slick sounds fill your senses as another wave of peristalsis ripples over you, tugging you a little deeper into the snake!", + "The serpent's all-encompassing stomach flesh closes in tight around your figure, testing how much softer you are now before finally relaxing. Slightly!", + "You find it harder to breathe as time goes on, your dizziness growing as you lack the space to breathe in enough of that acrid, thinning air!", + "The snake's ample, kneading muscle gradually squeezes the strength and fight from your body with clench after clench!", + "A pleased hiss rattles out of the giant snake from somewhere behind you, the serpent clearly satisfied with its newest meal!", + "Your movements grow slower and less effective as the snake's stomach ooze clings to your entire body, working away at it!") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm index cc5916b6e2..69ec5ceea5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm @@ -137,6 +137,28 @@ List of things solar grubs should be able to do: set_light(2.5, 1, COLOR_YELLOW) return 1 +/mob/living/simple_mob/vore/solargrub/init_vore() + ..() + var/obj/belly/B = vore_selected + B.name = "stomach" + B.desc = "Through either grave error, overwhelming willingness, or some other factor, you find yourself lodged halfway past the solargrub's mandibles. While it had initially hissed and chittered in glee at the prospect of a new meal, it is clearly more versed in suckling on power cables; inch by inch, bit by bit, it undulates forth to slowly, noisily gulp you down its short esophagus... and right into its extra-cramped, surprisingly hot stomach. As the rest of you spills out into the plush-walled chamber, the grub's soft body bulges outwards here and there with your compressed figure. Before long, a thick slime oozes out from the surrounding stomach walls; only time will tell how effective it is on something solid like you..." + + B.emote_lists[DM_HOLD] = list( + "The air trapped within the solargrub is hot, humid, and tinged with ozone, but otherwise mercifully harmless to you aside from being heavy on the lungs.", + "Your doughy, squishy surroundings heavily pulse around your body as the solargrub attempts to wriggle elsewhere, its solid prey weighing it down quite a bit.", + "Suddenly, an arc of electricity harmlessly jumps through the grub's stomach, briefly illuminating your slimy, glistening surroundings in a flash of yellow.", + "With all the power coursing through the solargrub's body, its inner muscles are in a constant state of vibrating all over you, adding an extra element to your full-body massage.", + "For a moment, the solargrub's stomach walls clench down even more firmly than before, working its subtle inner vibrations into your muscles, steadily relaxing them down.", + "The incredible heat trapped within the solargrub helps daze and disorient you, ensuring that its new filling wouldn't interfere in its power-draining.") + + B.emote_lists[DM_DIGEST] = list( + "Every breath taken inside the solargrub is swelteringly hot, painfully thick, and more than subtly caustic, worsening with every passing moment spent inside!", + "As the solargrub wriggles off somewhere quiet to digest its meal, the resulting undulations help crush you down into a more compact, easier to handle morsel!", + "From time to time, additional jolts of electricity unpleasantly course through your body, helping ensure that the solargrub's food was thoroughly paralyzed!", + "With how incredibly charged the solargrub is, its constant internal vibrating adds an additional layer of processing to its stomach's slow, steady churning, helping break you down faster!", + "The solargrub chitters in irritation at your continued solidity, followed by a string of crushingly tight stomach clenches that grind its caustic stomach ooze into your body!", + "The deceptively severe heat trapped within the solargrub works in tandem with its inner muscles and your tingling, prickling stomach juice bath to weaken you!") + /datum/ai_holder/simple_mob/retaliate/solargrub/react_to_attack(atom/movable/attacker) holder.anchored = 0 holder.set_AI_busy(FALSE) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm index 42ce8a2897..156ad340bc 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm @@ -223,7 +223,7 @@ var/global/list/grub_machine_overlays = list() actual_targets += M return actual_targets -/datum/ai_holder/simple_mob/solargrub_larva/can_attack(atom/movable/the_target) +/datum/ai_holder/simple_mob/solargrub_larva/can_attack(atom/movable/the_target, var/vision_required = TRUE) .=..() var/obj/machinery/M = the_target if(!istype(M)) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm index 258cd9e17e..a8bc70ac0c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm @@ -33,3 +33,72 @@ /mob/living/simple_mob/animal/wolf vore_active = 1 vore_icons = SA_ICON_LIVING + +// Space edition, stronger and bitier +/mob/living/simple_mob/animal/wolf/space + name = "space wolf" + tt_desc = "Canis lupus aetherius" + + health = 40 + maxHealth = 40 + + movement_cooldown = 3 + + harm_intent_damage = 5 + melee_damage_lower = 10 + melee_damage_upper = 15 + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + maxbodytemp = 700 + +/mob/living/simple_mob/animal/wolf/space/Process_Spacemove(var/check_drift = 0) + return TRUE + +/mob/living/simple_mob/animal/wolf/direwolf + name = "dire wolf" + desc = "The biggest and baddest wolf around." + tt_desc = "Canis maxdirus" + + icon = 'icons/mob/vore64x32.dmi' + icon_dead = "direwolf-dead" + icon_living = "direwolf" + icon_state = "direwolf" + icon_rest = "direwolf_rest" + old_x = -16 + old_y = 0 + default_pixel_x = -16 + pixel_x = -16 + pixel_y = 0 + + harm_intent_damage = 10 + melee_damage_lower = 10 + melee_damage_upper = 20 + maxHealth = 200 + + minbodytemp = 0 + + max_buckled_mobs = 1 + mount_offset_y = 14 + mount_offset_x = 2 + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + +/mob/living/simple_mob/animal/wolf/direwolf/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_mob(src) + verbs |= /mob/living/simple_mob/proc/animal_mount + verbs |= /mob/living/proc/toggle_rider_reins + movement_cooldown = 0 + +/mob/living/simple_mob/animal/wolf/direwolf/MouseDrop_T(mob/living/M, mob/living/user) + return diff --git a/code/modules/mob/living/voice/voice.dm b/code/modules/mob/living/voice/voice.dm index 9639f5a320..92270dc145 100644 --- a/code/modules/mob/living/voice/voice.dm +++ b/code/modules/mob/living/voice/voice.dm @@ -83,7 +83,7 @@ var/new_name = sanitizeSafe(input(src, "Who would you like to be now?", "Communicator", src.client.prefs.real_name) as text, MAX_NAME_LEN) if(new_name) if(comm) - comm.visible_message("\icon[comm] [src.name] has left, and now you see [new_name].") + comm.visible_message("[bicon(comm)] [src.name] has left, and now you see [new_name].") //Do a bit of logging in-case anyone tries to impersonate other characters for whatever reason. var/msg = "[src.client.key] ([src]) has changed their communicator identity's name to [new_name]." message_admins(msg) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 264d7cc851..fc326c8d03 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -14,13 +14,14 @@ if( (client.connection != "web") && (M.computer_id == client.computer_id) ) if(matches) matches += " and " matches += "ID ([client.computer_id])" - spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") + if(!config.disable_cid_warn_popup) + spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") if(matches) if(M.client) - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") else - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") /mob/Login() @@ -74,4 +75,7 @@ var/turf/T = get_turf(src) if(isturf(T)) - update_client_z(T.z) \ No newline at end of file + update_client_z(T.z) + + if(cloaked && cloaked_selfimage) + client.images += cloaked_selfimage \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index fae7b17e30..3c6f9b12df 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -70,7 +70,7 @@ return // Added voice muffling for Issue 41. if(stat == UNCONSCIOUS || sleeping > 0) - to_chat(src,"... You can almost hear someone talking ...") + to_chat(src, "... You can almost hear someone talking ...") else to_chat(src,msg) if(teleop) @@ -231,6 +231,10 @@ if((is_blind(src) || usr.stat) && !isobserver(src)) to_chat(src, "Something is there but you can't see it.") return 1 + + //Could be gone by the time they finally pick something + if(!A) + return 1 face_atom(A) A.examine(src) @@ -316,7 +320,7 @@ /mob/proc/update_flavor_text() set src in usr if(usr != src) - usr << "No." + to_chat(usr, "No.") var/msg = sanitize(input(usr,"Set the flavor text in your 'examine' verb.","Flavor Text",html_decode(flavor_text)) as message|null, extra = 0) //VOREStation Edit: separating out OOC notes if(msg != null) @@ -331,9 +335,9 @@ if (flavor_text && flavor_text != "") var/msg = replacetext(flavor_text, "\n", " ") if(length(msg) <= 40) - return "[msg]" + return "[msg]" else - return "[copytext_preserve_html(msg, 1, 37)]... More..." + return "[copytext_preserve_html(msg, 1, 37)]... More..." /* /mob/verb/help() @@ -408,26 +412,6 @@ /client/verb/changes() set name = "Changelog" set category = "OOC" - getFiles( - 'html/88x31.png', - 'html/bug-minus.png', - 'html/cross-circle.png', - 'html/hard-hat-exclamation.png', - 'html/image-minus.png', - 'html/image-plus.png', - 'html/map-pencil.png', - 'html/music-minus.png', - 'html/music-plus.png', - 'html/tick-circle.png', - 'html/wrench-screwdriver.png', - 'html/spell-check.png', - 'html/burn-exclamation.png', - 'html/chevron.png', - 'html/chevron-expand.png', - 'html/changelog.css', - 'html/changelog.js', - 'html/changelog.html' - ) src << browse('html/changelog.html', "window=changes;size=675x650") if(prefs.lastchangelog != changelog_hash) prefs.lastchangelog = changelog_hash @@ -439,10 +423,10 @@ set category = "OOC" var/is_admin = 0 - if(client.holder && (client.holder.rights & R_ADMIN)) + if(client.holder && (client.holder.rights & R_ADMIN|R_EVENT)) is_admin = 1 else if(stat != DEAD || istype(src, /mob/new_player)) - usr << "You must be observing to use this!" + to_chat(usr, "You must be observing to use this!") return if(is_admin && stat == DEAD) @@ -660,7 +644,7 @@ /mob/proc/see(message) if(!is_active()) return 0 - src << message + to_chat(src,message) return 1 /mob/proc/show_viewers(message) @@ -686,10 +670,6 @@ stat("Instances:","[world.contents.len]") stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)") - if(statpanel("Processes")) - if(processScheduler) - processScheduler.statProcesses() - if(statpanel("MC")) stat("Location:", "([x], [y], [z]) [loc]") stat("CPU:","[world.cpu]") @@ -934,11 +914,11 @@ mob/proc/yank_out_object() usr.setClickCooldown(20) if(usr.stat == 1) - usr << "You are unconcious and cannot do that!" + to_chat(usr, "You are unconcious and cannot do that!") return if(usr.restrained()) - usr << "You are restrained and cannot do that!" + to_chat(usr, "You are restrained and cannot do that!") return var/mob/S = src @@ -954,7 +934,7 @@ mob/proc/yank_out_object() if(self) to_chat(src, "You have nothing stuck in your body that is large enough to remove.") else - U << "[src] has nothing stuck in their wounds that is large enough to remove." + to_chat(U, "[src] has nothing stuck in their wounds that is large enough to remove.") return var/obj/item/weapon/selection = input("What do you want to yank out?", "Embedded objects") in valid_objects @@ -962,7 +942,7 @@ mob/proc/yank_out_object() if(self) to_chat(src, "You attempt to get a good grip on [selection] in your body.") else - U << "You attempt to get a good grip on [selection] in [S]'s body." + to_chat(U, "You attempt to get a good grip on [selection] in [S]'s body.") if(!do_after(U, 30)) return @@ -1027,6 +1007,11 @@ mob/proc/yank_out_object() /mob/proc/updateicon() return +// Please always use this proc, never just set the var directly. +/mob/proc/set_stat(var/new_stat) + . = (stat != new_stat) + stat = new_stat + /mob/verb/face_direction() set name = "Face Direction" @@ -1036,9 +1021,9 @@ mob/proc/yank_out_object() set_face_dir() if(!facing_dir) - usr << "You are now not facing anything." + to_chat(usr, "You are now not facing anything.") else - usr << "You are now facing [dir2text(facing_dir)]." + to_chat(usr, "You are now facing [dir2text(facing_dir)].") /mob/proc/set_face_dir(var/newdir) if(newdir == facing_dir) @@ -1084,6 +1069,13 @@ mob/proc/yank_out_object() /mob/proc/setEarDamage() return +// Set client view distance (size of client's screen). Returns TRUE if anything changed. +/mob/proc/set_viewsize(var/new_view = world.view) + if (client && new_view != client.view) + client.view = new_view + return TRUE + return FALSE + //Throwing stuff /mob/proc/toggle_throw_mode() @@ -1211,3 +1203,25 @@ mob/proc/yank_out_object() /mob/onTransitZ(old_z, new_z) ..() update_client_z(new_z) + +/mob/cloak() + . = ..() + if(client && cloaked_selfimage) + client.images += cloaked_selfimage + +/mob/uncloak() + if(client && cloaked_selfimage) + client.images -= cloaked_selfimage + return ..() + +/mob/get_cloaked_selfimage() + var/icon/selficon = getCompoundIcon(src) + selficon.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White + var/image/selfimage = image(selficon) + selfimage.color = "#0000FF" + selfimage.alpha = 100 + selfimage.layer = initial(layer) + selfimage.plane = initial(plane) + selfimage.loc = src + + return selfimage diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index b335faf1d3..6333d7a363 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -259,7 +259,7 @@ hud.icon_state = "reinforce1" else if(state < GRAB_NECK) if(isslime(affecting)) - assailant << "You squeeze [affecting], but nothing interesting happens." + to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") return assailant.visible_message("[assailant] has reinforced [TU.his] grip on [affecting] (now neck)!") @@ -315,7 +315,7 @@ switch(assailant.a_intent) if(I_HELP) if(force_down) - assailant << "You are no longer pinning [affecting] to the ground." + to_chat(assailant, "You are no longer pinning [affecting] to the ground.") force_down = 0 return if(state >= GRAB_AGGRESSIVE) diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm index f5b997c73b..1702e73ec9 100644 --- a/code/modules/mob/mob_grab_specials.dm +++ b/code/modules/mob/mob_grab_specials.dm @@ -4,46 +4,46 @@ var/obj/item/organ/external/E = H.get_organ(target_zone) if(!E || E.is_stump()) - user << "[H] is missing that bodypart." + to_chat(user, "[H] is missing that bodypart.") return user.visible_message("[user] starts inspecting [affecting]'s [E.name] carefully.") if(!do_mob(user,H, 10)) - user << "You must stand still to inspect [E] for wounds." + to_chat(user, "You must stand still to inspect [E] for wounds.") else if(E.wounds.len) - user << "You find [E.get_wounds_desc()]" + to_chat(user, "You find [E.get_wounds_desc()]") else - user << "You find no visible wounds." + to_chat(user, "You find no visible wounds.") - user << "Checking bones now..." + to_chat(user, "Checking bones now...") if(!do_mob(user, H, 20)) - user << "You must stand still to feel [E] for fractures." + to_chat(user, "You must stand still to feel [E] for fractures.") else if(E.status & ORGAN_BROKEN) - user << "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!" + to_chat(user, "The [E.encased ? E.encased : "bone in the [E.name]"] moves slightly when you poke it!") H.custom_pain("Your [E.name] hurts where it's poked.", 40) else - user << "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine." + to_chat(user, "The [E.encased ? E.encased : "bones in the [E.name]"] seem to be fine.") - user << "Checking skin now..." + to_chat(user, "Checking skin now...") if(!do_mob(user, H, 10)) - user << "You must stand still to check [H]'s skin for abnormalities." + to_chat(user, "You must stand still to check [H]'s skin for abnormalities.") else var/bad = 0 if(H.getToxLoss() >= 40) - user << "[H] has an unhealthy skin discoloration." + to_chat(user, "[H] has an unhealthy skin discoloration.") bad = 1 if(H.getOxyLoss() >= 20) - user << "[H]'s skin is unusaly pale." + to_chat(user, "[H]'s skin is unusaly pale.") bad = 1 if(E.status & ORGAN_DEAD) - user << "[E] is decaying!" + to_chat(user, "[E] is decaying!") bad = 1 if(!bad) - user << "[H]'s skin is normal." + to_chat(user, "[H]'s skin is normal.") /obj/item/weapon/grab/proc/jointlock(mob/living/carbon/human/target, mob/attacker, var/target_zone) if(state < GRAB_AGGRESSIVE) - attacker << "You require a better grab to do this." + to_chat(attacker, "You require a better grab to do this.") return var/obj/item/organ/external/organ = target.get_organ(check_zone(target_zone)) @@ -58,7 +58,7 @@ var/armor = target.run_armor_check(target, "melee") var/soaked = target.get_armor_soak(target, "melee") if(armor + soaked < 60) - target << "You feel extreme pain!" + to_chat(target, "You feel extreme pain!") var/max_halloss = round(target.species.total_health * 0.8) //up to 80% of passing out affecting.adjustHalLoss(CLAMP(max_halloss - affecting.halloss, 0, 30)) @@ -72,14 +72,14 @@ if(!attack) return if(state < GRAB_NECK) - attacker << "You require a better grab to do this." + to_chat(attacker, "You require a better grab to do this.") return for(var/obj/item/protection in list(target.head, target.wear_mask, target.glasses)) if(protection && (protection.body_parts_covered & EYES)) - attacker << "You're going to need to remove the eye covering first." + to_chat(attacker, "You're going to need to remove the eye covering first.") return if(!target.has_eyes()) - attacker << "You cannot locate any eyes on [target]!" + to_chat(attacker, "You cannot locate any eyes on [target]!") return add_attack_logs(attacker,target,"Eye gouge using grab") @@ -118,7 +118,7 @@ /obj/item/weapon/grab/proc/dislocate(mob/living/carbon/human/target, mob/living/attacker, var/target_zone) if(state < GRAB_NECK) - attacker << "You require a better grab to do this." + to_chat(attacker, "You require a better grab to do this.") return if(target.grab_joint(attacker, target_zone)) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) @@ -126,13 +126,13 @@ /obj/item/weapon/grab/proc/pin_down(mob/target, mob/attacker) if(state < GRAB_AGGRESSIVE) - attacker << "You require a better grab to do this." + to_chat(attacker, "You require a better grab to do this.") return if(force_down) - attacker << "You are already pinning [target] to the ground." + to_chat(attacker, "You are already pinning [target] to the ground.") return if(size_difference(affecting, assailant) > 0) - attacker << "You are too small to do that!" + to_chat(attacker, "You are too small to do that!") return attacker.visible_message("[attacker] starts forcing [target] to the ground!") diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 2b74151cdd..5450ced7ee 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -83,7 +83,7 @@ proc/getsensorlevel(A) /proc/is_admin(var/mob/user) - return check_rights(R_ADMIN, 0, user) != 0 + return check_rights(R_ADMIN|R_EVENT, 0, user) != 0 /proc/hsl2rgb(h, s, l) @@ -423,7 +423,7 @@ proc/is_blind(A) else // Everyone else (dead people who didn't ghost yet, etc.) lname = name lname = "[lname] " - M << "" + create_text_tag("dead", "DEAD:", M.client) + " [lname][follow][message]" + to_chat(M, "" + create_text_tag("dead", "DEAD:", M.client) + " [lname][follow][message]") /proc/say_dead_object(var/message, var/obj/subject = null) for(var/mob/M in player_list) @@ -437,7 +437,7 @@ proc/is_blind(A) lname = "[subject.name] ([subject.x],[subject.y],[subject.z])" lname = "[lname] " - M << "" + create_text_tag("event_dead", "EVENT:", M.client) + " [lname][follow][message]" + to_chat(M, "" + create_text_tag("event_dead", "EVENT:", M.client) + " [lname][follow][message]") //Announces that a ghost has joined/left, mainly for use with wizards /proc/announce_ghost_joinleave(O, var/joined_ghosts = 1, var/message = "") diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 1759df01dd..0e4a326cab 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -31,17 +31,17 @@ var/mob/living/carbon/C = usr C.toggle_throw_mode() else - usr << "This mob type cannot throw items." + to_chat(usr, "This mob type cannot throw items.") return if(NORTHWEST) if(isliving(usr)) var/mob/living/carbon/C = usr if(!C.get_active_hand()) - usr << "You have nothing to drop in your hand." + to_chat(usr, "You have nothing to drop in your hand.") return drop_item() else - usr << "This mob type cannot drop items." + to_chat(usr, "This mob type cannot drop items.") return //This gets called when you press the delete button. @@ -49,7 +49,7 @@ set hidden = 1 if(!usr.pulling) - usr << "You are not pulling anything." + to_chat(usr, "You are not pulling anything.") return usr.stop_pulling() @@ -389,10 +389,8 @@ ///Return 1 for movement 0 for none /mob/proc/Process_Spacemove(var/check_drift = 0) - //VOREStation Edit begin: SHADEKIN - if(shadekin_phasing_check()) + if(is_incorporeal()) return - //VOREStation Edit end: SHADEKIN if(!Check_Dense_Object()) //Nothing to push off of so end here update_floating(0) diff --git a/code/modules/mob/mob_planes.dm b/code/modules/mob/mob_planes.dm index 2ba47421a4..e1c33efe72 100644 --- a/code/modules/mob/mob_planes.dm +++ b/code/modules/mob/mob_planes.dm @@ -35,10 +35,13 @@ plane_masters[VIS_MESONS] = new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings. + plane_masters[VIS_BUILDMODE] = new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode + // Real tangible stuff planes plane_masters[VIS_TURFS] = new /obj/screen/plane_master/main{plane = TURF_PLANE} plane_masters[VIS_OBJS] = new /obj/screen/plane_master/main{plane = OBJ_PLANE} plane_masters[VIS_MOBS] = new /obj/screen/plane_master/main{plane = MOB_PLANE} + plane_masters[VIS_CLOAKED] = new /obj/screen/plane_master/cloaked //Cloaked atoms! ..() @@ -176,6 +179,13 @@ plane = PLANE_GHOSTS desired_alpha = 127 //When enabled, they're like half-transparent +///////////////// +//Cloaked atoms are visible to ghosts (or for other reasons?) +/obj/screen/plane_master/cloaked + plane = CLOAKED_PLANE + desired_alpha = 80 + color = "#0000FF" + ///////////////// //The main game planes start normal and visible /obj/screen/plane_master/main diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index 12bd80f255..0c35f52a40 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -5,7 +5,7 @@ /mob/proc/change_mob_type(var/new_type = null, var/turf/location = null, var/new_name = null as text, var/delete_old_mob = 0 as num, var/subspecies) if(istype(src,/mob/new_player)) - usr << "cannot convert players who have not entered yet." + to_chat(usr, "cannot convert players who have not entered yet.") return if(!new_type) @@ -15,11 +15,11 @@ new_type = text2path(new_type) if( !ispath(new_type) ) - usr << "Invalid type path (new_type = [new_type]) in change_mob_type(). Contact a coder." + to_chat(usr, "Invalid type path (new_type = [new_type]) in change_mob_type(). Contact a coder.") return if( new_type == /mob/new_player ) - usr << "cannot convert into a new_player mob type." + to_chat(usr, "cannot convert into a new_player mob type.") return var/mob/M @@ -29,7 +29,7 @@ M = new new_type( src.loc ) if(!M || !ismob(M)) - usr << "Type path is not a mob (new_type = [new_type]) in change_mob_type(). Contact a coder." + to_chat(usr, "Type path is not a mob (new_type = [new_type]) in change_mob_type(). Contact a coder.") qdel(M) return diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index 42775a4bc2..43b562b27e 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -6,6 +6,8 @@ var/obj/effect/lobby_image = new /obj/effect/lobby_image name = "Yawn Wider" desc = "How are you reading this?" screen_loc = "1,1" + icon = 'icons/misc/loading.dmi' //VOREStation Add - Loading Screen + icon_state = "loading" //VOREStation Add - Loading Screen /obj/effect/lobby_image/Initialize() icon = using_map.lobby_icon diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 1bcafd6e45..bfdc39b25e 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -19,6 +19,7 @@ /mob/new_player/New() mob_list += src + initialized = TRUE // Explicitly don't use Initialize(). New players join super early and use New() /mob/new_player/verb/new_player_panel() set src = usr @@ -66,9 +67,11 @@ else output += "

    Show News

    " + if(SSsqlite.can_submit_feedback(client)) + output += "

    [href(src, list("give_feedback" = 1), "Give Feedback")]

    " output += "
    " - panel = new(src, "Welcome","Welcome", 210, 280, src) + panel = new(src, "Welcome","Welcome", 210, 300, src) panel.set_window_options("can_close=0") panel.set_content(output) panel.open() @@ -77,20 +80,24 @@ /mob/new_player/Stat() ..() - if(statpanel("Lobby") && ticker) - if(ticker.hide_mode) - stat("Game Mode:", "Secret") - else - if(ticker.hide_mode == 0) - stat("Game Mode:", "[config.mode_names[master_mode]]") // Old setting for showing the game mode + if(statpanel("Lobby") && SSticker) + stat("Game Mode:", SSticker.hide_mode ? "Secret" : "[config.mode_names[master_mode]]") - if(ticker.current_state == GAME_STATE_PREGAME) - stat("Time To Start:", "[ticker.pregame_timeleft][round_progressing ? "" : " (DELAYED)"]") + if(SSvote.mode) + stat("Vote: [capitalize(SSvote.mode)]", "Time Left: [SSvote.time_remaining] s") + + if(SSticker.current_state == GAME_STATE_INIT) + stat("Time To Start:", "Server Initializing") + + else if(SSticker.current_state == GAME_STATE_PREGAME) + stat("Time To Start:", "[round(SSticker.pregame_timeleft,1)][round_progressing ? "" : " (DELAYED)"]") stat("Players: [totalPlayers]", "Players Ready: [totalPlayersReady]") totalPlayers = 0 totalPlayersReady = 0 + var/datum/job/refJob = null for(var/mob/new_player/player in player_list) - stat("[player.key]", (player.ready)?("(Playing)"):(null)) + refJob = player.client.prefs.get_highest_job() + stat("[player.key]", (player.ready)?("(Playing as: [(refJob)?(refJob.title):("Unknown")])"):(null)) totalPlayers++ if(player.ready)totalPlayersReady++ @@ -132,10 +139,10 @@ close_spawn_windows() var/obj/O = locate("landmark*Observer-Start") if(istype(O)) - to_chat(src,"Now teleporting.") + to_chat(src, "Now teleporting.") observer.forceMove(O.loc) else - to_chat(src,"Could not locate an observer spawn point. Use the Teleport verb to jump to the station map.") + to_chat(src, "Could not locate an observer spawn point. Use the Teleport verb to jump to the station map.") observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly. announce_ghost_joinleave(src) @@ -154,13 +161,13 @@ if(href_list["late_join"]) if(!ticker || ticker.current_state != GAME_STATE_PLAYING) - usr << "The round is either not ready, or has already finished..." + to_chat(usr, "The round is either not ready, or has already finished...") return /* if(client.prefs.species != "Human" && !check_rights(R_ADMIN, 0)) //VORESTATION EDITS: THE COMMENTED OUT AREAS FROM LINE 154 TO 178 if (config.usealienwhitelist) if(!is_alien_whitelisted(src, client.prefs.species)) - src << alert("You are currently not whitelisted to Play [client.prefs.species].") + alert(src, "You are currently not whitelisted to Play [client.prefs.species].") return 0 */ LateChoices() @@ -175,25 +182,25 @@ for (var/mob/living/carbon/human/C in mob_list) var/char_name = client.prefs.real_name if(char_name == C.real_name) - usr << "There is a character that already exists with the same name - [C.real_name], please join with a different one, or use Quit the Round with the previous character." //VOREStation Edit + to_chat(usr, "There is a character that already exists with the same name - [C.real_name], please join with a different one, or use Quit the Round with the previous character.") //VOREStation Edit return */ //Vorestation Removal End if(!config.enter_allowed) - usr << "There is an administrative lock on entering the game!" + to_chat(usr, "There is an administrative lock on entering the game!") return else if(ticker && ticker.mode && ticker.mode.explosion_in_progress) - usr << "The station is currently exploding. Joining would go poorly." + to_chat(usr, "The station is currently exploding. Joining would go poorly.") return if(!is_alien_whitelisted(src, GLOB.all_species[client.prefs.species])) - src << alert("You are currently not whitelisted to play [client.prefs.species].") + alert(src, "You are currently not whitelisted to play [client.prefs.species].") return 0 var/datum/species/S = GLOB.all_species[client.prefs.species] if(!(S.spawn_flags & SPECIES_CAN_JOIN)) - src << alert("Your current species, [client.prefs.species], is not available for play on the station.") + alert(src,"Your current species, [client.prefs.species], is not available for play on the station.") return 0 AttemptLateSpawn(href_list["SelectedJob"],client.prefs.spawnpoint) @@ -234,7 +241,7 @@ var/sql = "INSERT INTO erro_privacy VALUES (null, Now(), '[src.ckey]', '[option]')" var/DBQuery/query_insert = dbcon.NewQuery(sql) query_insert.Execute() - usr << "Thank you for your vote!" + to_chat(usr, "Thank you for your vote!") usr << browse(null,"window=privacypoll") if(!ready && href_list["preference"]) @@ -272,7 +279,7 @@ var/id_max = text2num(href_list["maxid"]) if( (id_max - id_min) > 100 ) //Basic exploit prevention - usr << "The option ID difference is too big. Please contact administration or the database admin." + to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") return for(var/optionid = id_min; optionid <= id_max; optionid++) @@ -291,7 +298,7 @@ var/id_max = text2num(href_list["maxoptionid"]) if( (id_max - id_min) > 100 ) //Basic exploit prevention - usr << "The option ID difference is too big. Please contact administration or the database admin." + to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.") return for(var/optionid = id_min; optionid <= id_max; optionid++) @@ -306,6 +313,15 @@ show_hidden_jobs = !show_hidden_jobs LateChoices() + if(href_list["give_feedback"]) + if(!SSsqlite.can_submit_feedback(my_client)) + return + + if(client.feedback_form) + client.feedback_form.display() // In case they closed the form early. + else + client.feedback_form = new(client) + /mob/new_player/proc/handle_server_news() if(!client) return @@ -325,6 +341,7 @@ popup.set_content(dat) popup.open() + /mob/new_player/proc/IsJobAvailable(rank) var/datum/job/job = job_master.GetJob(rank) if(!job) return 0 @@ -340,13 +357,13 @@ if (src != usr) return 0 if(!ticker || ticker.current_state != GAME_STATE_PLAYING) - usr << "The round is either not ready, or has already finished..." + to_chat(usr, "The round is either not ready, or has already finished...") return 0 if(!config.enter_allowed) - usr << "There is an administrative lock on entering the game!" + to_chat(usr, "There is an administrative lock on entering the game!") return 0 if(!IsJobAvailable(rank)) - src << alert("[rank] is not available. Please try another.") + alert(src,"[rank] is not available. Please try another.") return 0 if(!attempt_vr(src,"spawn_checks_vr",list())) return 0 // VOREStation Insert if(!client) @@ -354,8 +371,13 @@ //Find our spawning point. var/list/join_props = job_master.LateSpawn(client, rank) + + if(!join_props) + return + var/turf/T = join_props["turf"] var/join_message = join_props["msg"] + var/announce_channel = join_props["channel"] || "Common" if(!T || !join_message) return 0 @@ -405,18 +427,19 @@ //Grab some data from the character prefs for use in random news procs. - AnnounceArrival(character, rank, join_message) + AnnounceArrival(character, rank, join_message, announce_channel, character.z) else - AnnounceCyborg(character, rank, join_message) + AnnounceCyborg(character, rank, join_message, announce_channel, character.z) qdel(src) -/mob/new_player/proc/AnnounceCyborg(var/mob/living/character, var/rank, var/join_message) +/mob/new_player/proc/AnnounceCyborg(var/mob/living/character, var/rank, var/join_message, var/channel, var/zlevel) if (ticker.current_state == GAME_STATE_PLAYING) + var/list/zlevels = zlevel ? using_map.get_map_levels(zlevel, TRUE) : null if(character.mind.role_alt_title) rank = character.mind.role_alt_title // can't use their name here, since cyborg namepicking is done post-spawn, so we'll just say "A new Cyborg has arrived"/"A new Android has arrived"/etc. - global_announcer.autosay("A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer") + global_announcer.autosay("A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer", channel, zlevels) /mob/new_player/proc/LateChoices() var/name = client.prefs.be_random_name ? "friend" : client.prefs.real_name @@ -436,6 +459,8 @@ dat += "Choose from the following open/valid positions:
    " dat += "[show_hidden_jobs ? "Hide":"Show"] Hidden Jobs.
    " + + var/deferred = "" for(var/datum/job/job in job_master.occupations) if(job && IsJobAvailable(job.title)) // Checks for jobs with minimum age requirements @@ -449,9 +474,17 @@ continue var/active = 0 // Only players with the job assigned and AFK for less than 10 minutes count as active - for(var/mob/M in player_list) if(M.mind && M.client && M.mind.assigned_role == job.title && M.client.inactivity <= 10 * 60 * 10) + for(var/mob/M in player_list) if(M.mind && M.client && M.mind.assigned_role == job.title && M.client.inactivity <= 10 MINUTES) active++ - dat += "[job.title] ([job.current_positions]) (Active: [active])
    " + + var/string = "[job.title] ([job.current_positions]) (Active: [active])
    " + + if(job.offmap_spawn) //At the bottom + deferred += string + else + dat += string + + dat += deferred dat += "" src << browse(dat, "window=latechoices;size=300x640;can_close=1") diff --git a/code/modules/mob/new_player/poll.dm b/code/modules/mob/new_player/poll.dm index 0282af9fe9..e4f42774e9 100644 --- a/code/modules/mob/new_player/poll.dm +++ b/code/modules/mob/new_player/poll.dm @@ -103,7 +103,7 @@ break if(!found) - usr << "Poll question details not found." + to_chat(usr, "Poll question details not found.") return switch(polltype) @@ -360,7 +360,7 @@ break if(!validpoll) - usr << "Poll is not valid." + to_chat(usr, "Poll is not valid.") return var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM erro_poll_option WHERE id = [optionid] AND pollid = [pollid]") @@ -373,7 +373,7 @@ break if(!validoption) - usr << "Poll option is not valid." + to_chat(usr, "Poll option is not valid.") return var/alreadyvoted = 0 @@ -387,11 +387,11 @@ break if(!multichoice && alreadyvoted) - usr << "You already voted in this poll." + to_chat(usr, "You already voted in this poll.") return if(multichoice && (alreadyvoted >= multiplechoiceoptions)) - usr << "You already have more than [multiplechoiceoptions] logged votes on this poll. Enough is enough. Contact the database admin if this is an error." + to_chat(usr, "You already have more than [multiplechoiceoptions] logged votes on this poll. Enough is enough. Contact the database admin if this is an error.") return var/adminrank = "Player" @@ -402,7 +402,7 @@ var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]')") insert_query.Execute() - usr << "Vote successful." + to_chat(usr, "Vote successful.") usr << browse(null,"window=playerpoll") @@ -427,7 +427,7 @@ break if(!validpoll) - usr << "Poll is not valid." + to_chat(usr, "Poll is not valid.") return var/alreadyvoted = 0 @@ -440,7 +440,7 @@ break if(alreadyvoted) - usr << "You already sent your feedback for this poll." + to_chat(usr, "You already sent your feedback for this poll.") return var/adminrank = "Player" @@ -454,13 +454,13 @@ replytext = replacetext(replytext, "%BR%", "
    ") if(!text_pass) - usr << "The text you entered was blank, contained illegal characters or was too long. Please correct the text and submit again." + to_chat(usr, "The text you entered was blank, contained illegal characters or was too long. Please correct the text and submit again.") return var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_textreply (id ,datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (null, Now(), [pollid], '[usr.ckey]', '[usr.client.address]', '[replytext]', '[adminrank]')") insert_query.Execute() - usr << "Feedback logging successful." + to_chat(usr, "Feedback logging successful.") usr << browse(null,"window=playerpoll") @@ -485,7 +485,7 @@ break if(!validpoll) - usr << "Poll is not valid." + to_chat(usr, "Poll is not valid.") return var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM erro_poll_option WHERE id = [optionid] AND pollid = [pollid]") @@ -498,7 +498,7 @@ break if(!validoption) - usr << "Poll option is not valid." + to_chat(usr, "Poll option is not valid.") return var/alreadyvoted = 0 @@ -511,7 +511,7 @@ break if(alreadyvoted) - usr << "You already voted in this poll." + to_chat(usr, "You already voted in this poll.") return var/adminrank = "Player" @@ -522,5 +522,5 @@ var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO erro_poll_vote (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[usr.ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])") insert_query.Execute() - usr << "Vote successful." + to_chat(usr, "Vote successful.") usr << browse(null,"window=playerpoll") \ No newline at end of file diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index d8ad2a1297..81475ff2ab 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -30,7 +30,7 @@ backbag = rand(1,5) - pdachoice = rand(1,4) + pdachoice = rand(1,5) age = rand(current_species.min_age, current_species.max_age) b_type = RANDOM_BLOOD_TYPE if(H) @@ -194,6 +194,8 @@ b_skin = blue /datum/preferences/proc/dress_preview_mob(var/mob/living/carbon/human/mannequin) + if(!mannequin.dna) // Special handling for preview icons before SSAtoms has initailized. + mannequin.dna = new /datum/dna(null) copy_to(mannequin, TRUE) if(!equip_preview_mob) @@ -266,3 +268,24 @@ preview_icon.Blend(stamp, ICON_OVERLAY, 49, 1) preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. + +/datum/preferences/proc/get_highest_job() + var/datum/job/highJob + // Determine what job is marked as 'High' priority, and dress them up as such. + if(job_civilian_low & ASSISTANT) + highJob = job_master.GetJob("Assistant") + else + for(var/datum/job/job in job_master.occupations) + var/job_flag + switch(job.department_flag) + if(CIVILIAN) + job_flag = job_civilian_high + if(MEDSCI) + job_flag = job_medsci_high + if(ENGSEC) + job_flag = job_engsec_high + if(job.flag == job_flag) + highJob = job + break + + return highJob diff --git a/code/modules/mob/new_player/preferences_setup_vr.dm b/code/modules/mob/new_player/preferences_setup_vr.dm index 8b1760c65c..04910731c1 100644 --- a/code/modules/mob/new_player/preferences_setup_vr.dm +++ b/code/modules/mob/new_player/preferences_setup_vr.dm @@ -1,5 +1,7 @@ /datum/preferences/update_preview_icon() // Lines up and un-overlaps character edit previews. Also un-splits taurs. var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin(client_ckey) + if(!mannequin.dna) // Special handling for preview icons before SSAtoms has initailized. + mannequin.dna = new /datum/dna(null) mannequin.delete_inventory(TRUE) dress_preview_mob(mannequin) COMPILE_OVERLAYS(mannequin) diff --git a/code/modules/mob/new_player/skill.dm b/code/modules/mob/new_player/skill.dm index 5fb0e81745..b2d061d5a6 100644 --- a/code/modules/mob/new_player/skill.dm +++ b/code/modules/mob/new_player/skill.dm @@ -3,6 +3,7 @@ var/global/const SKILL_BASIC = 1 SKILL_ADEPT = 2 SKILL_EXPERT = 3 + SKILL_PROF = 4 /datum/skill/var ID = "none" // ID of the skill, used in code @@ -176,7 +177,7 @@ var/global/list/SKILL_PRE = list("Engineer" = SKILL_ENGINEER, "Roboticist" = SKI setup_skills() if(!M.skills || M.skills.len == 0) - user << "There are no skills to display." + to_chat(user, "There are no skills to display.") return var/HTML = "" diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index 90d4e32c8d..d11cc80594 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -119,6 +119,11 @@ icon_state = "hair_long_bedhead" flags = HAIR_TIEABLE + bedheadlongest + name = "Bedhead Longest" + icon_state = "hair_longest_bedhead" + flags = HAIR_TIEABLE + beehive name = "Beehive" icon_state = "hair_beehive" @@ -743,6 +748,11 @@ icon_state = "hair_rowbraid" flags = HAIR_TIEABLE + sabitsuki + name = "Sabitsuki" + icon_state = "hair_sabitsuki" + flags = HAIR_VERY_SHORT + scully name = "Scully" icon_state = "hair_scully" @@ -1294,6 +1304,11 @@ icon_state = "teshari_mushroom" species_allowed = list(SPECIES_TESHARI) + teshari_bald + name = "Bald (use with FBP)" + icon_state = "bald" + species_allowed = list(SPECIES_TESHARI) + // Vox things vox_braid_long name = "Long Vox braid" diff --git a/code/modules/mob/new_player/sprite_accessories_vr.dm b/code/modules/mob/new_player/sprite_accessories_vr.dm index f15a9f18f0..ec3cb570b2 100644 --- a/code/modules/mob/new_player/sprite_accessories_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_vr.dm @@ -8,8 +8,8 @@ //var/icon_add = 'icons/mob/human_face.dmi' //Already defined in sprite_accessories.dm line 49. var/color_blend_mode = ICON_MULTIPLY - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_SHADEKIN_YW) //This lets all races use the default hairstyles. //YW edits - + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_SHADEKIN_YW) //This lets all races use the default hairstyles. + astolfo name = "Astolfo" icon = 'icons/mob/human_face_vr.dmi' @@ -87,14 +87,14 @@ name = "Bald" icon_state = "bald" gender = MALE - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //Lets all the races be bald if they want. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //Lets all the races be bald if they want. ponytail6_fixed //Eggnerd's done with waiting for upstream fixes lmao. name = "Ponytail 6 but fixed" icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "hair_ponytail6" - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_NEVREAN, SPECIES_AKULA,SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_NEVREAN, SPECIES_AKULA,SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) una_hood name = "Cobra Hood" @@ -326,7 +326,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "kajam" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_keid @@ -334,7 +334,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "keid" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_adhara @@ -342,7 +342,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "adhara" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_kleeia @@ -350,7 +350,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "kleeia" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_mizar @@ -358,7 +358,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "mizar" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_apollo @@ -366,7 +366,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "apollo" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_belle @@ -374,7 +374,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "belle" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_bun @@ -382,7 +382,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "bun" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_jagged @@ -390,7 +390,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "jagged" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_curl @@ -398,7 +398,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "curl" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_hawk @@ -406,7 +406,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "hawk" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_anita @@ -414,7 +414,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "anita" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_short @@ -422,7 +422,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "short" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER vulp_hair_spike @@ -430,7 +430,7 @@ icon = 'icons/mob/human_face_vr.dmi' icon_add = 'icons/mob/human_face_vr_add.dmi' icon_state = "spike" - species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) + species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) gender = NEUTER //xeno stuffs @@ -512,20 +512,20 @@ /datum/sprite_accessory/facial_hair icon = 'icons/mob/human_face_or_vr.dmi' var/color_blend_mode = ICON_MULTIPLY - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This lets all races use the facial hair styles. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This lets all races use the facial hair styles. shaved name = "Shaved" icon_state = "bald" gender = NEUTER - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This needed to be manually defined, apparantly. + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This needed to be manually defined, apparantly. neck_fluff name = "Neck Fluff" icon = 'icons/mob/human_face_or_vr.dmi' icon_state = "facial_neckfluff" gender = NEUTER - species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) + species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) vulp_none name = "None" @@ -1090,7 +1090,7 @@ name = "Heterochromia" icon_state = "heterochromia" body_parts = list(BP_HEAD) - species_allowed = list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_ZORREN_FLAT, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This lets all races use the default hairstyles. + species_allowed = list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST) //This lets all races use the default hairstyles. werewolf_nose name = "Werewolf nose" diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 72b1d52b06..25d506d91a 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -19,7 +19,7 @@ set category = "IC" if(say_disabled) //This is here to try to identify lag problems - usr << "Speech is currently admin-disabled." + to_chat(usr, "Speech is currently admin-disabled.") return //VOREStation Edit Start @@ -36,7 +36,7 @@ /mob/proc/say_dead(var/message) if(say_disabled) //This is here to try to identify lag problems - usr << "Speech is currently admin-disabled." + to_chat(usr, "Speech is currently admin-disabled.") return if(!client) @@ -48,7 +48,7 @@ return if(!is_preference_enabled(/datum/client_preference/show_dsay)) - usr << "You have deadchat muted." + to_chat(usr, "You have deadchat muted.") return message = encode_html_emphasis(message) @@ -148,7 +148,7 @@ return GLOB.all_languages["Noise"] if(length(message) >= 2 && is_language_prefix(prefix)) - var/language_prefix = lowertext(copytext(message, 2 ,3)) + var/language_prefix = copytext(message, 2 ,3) var/datum/language/L = GLOB.language_keys[language_prefix] if (can_speak(L)) return L diff --git a/code/modules/mob/say_vr.dm b/code/modules/mob/say_vr.dm index 0e5bd02d45..5dbf853745 100644 --- a/code/modules/mob/say_vr.dm +++ b/code/modules/mob/say_vr.dm @@ -8,7 +8,7 @@ set desc = "Emote to nearby people (and your pred/prey)" if(say_disabled) //This is here to try to identify lag problems - usr << "Speech is currently admin-disabled." + to_chat(usr, "Speech is currently admin-disabled.") return message = sanitize_or_reflect(message,src) //VOREStation Edit - Reflect too-long messages (within reason) @@ -37,7 +37,7 @@ if(input) log_subtle(message,src) - message = "[src] [input]" + message = "[src] [input]" else return @@ -81,11 +81,11 @@ /proc/fail_to_chat(user,message) if(!message) - to_chat(user,"Your message was NOT SENT, either because it was FAR too long, or sanitized to nothing at all.") + to_chat(user, "Your message was NOT SENT, either because it was FAR too long, or sanitized to nothing at all.") return var/length = length(message) var/posts = CEILING((length/MAX_MESSAGE_LEN), 1) to_chat(user,message) - to_chat(user,"^ This message was NOT SENT ^ -- It was [length] characters, and the limit is [MAX_MESSAGE_LEN]. It would fit in [posts] separate messages.") + to_chat(user, "^ This message was NOT SENT ^ -- It was [length] characters, and the limit is [MAX_MESSAGE_LEN]. It would fit in [posts] separate messages.") #undef MAX_HUGE_MESSAGE_LEN diff --git a/code/modules/mob/skillset.dm b/code/modules/mob/skillset.dm new file mode 100644 index 0000000000..2877372491 --- /dev/null +++ b/code/modules/mob/skillset.dm @@ -0,0 +1,14 @@ + +// We don't actually have a skills system, so return max skill for everything. +/mob/proc/get_skill_value(skill_path) + return SKILL_EXPERT + +// A generic way of modifying success probabilities via skill values. Higher factor means skills have more effect. fail_chance is the chance at SKILL_NONE. +/mob/proc/skill_fail_chance(skill_path, fail_chance, no_more_fail = SKILL_EXPERT, factor = 1) + var/points = get_skill_value(skill_path) + if(points >= no_more_fail) + return 0 + else + return fail_chance * 2 ** (factor*(SKILL_BASIC - points)) + + return FALSE // We don't actually have a skills system, so never fail. diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 28673c9fd7..0e4e42bbeb 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -116,7 +116,7 @@ continue loc_landmark = tripai if (!loc_landmark) - O << "Oh god sorry we can't find an unoccupied AI spawn location, so we're spawning you on top of someone." + to_chat(O, "Oh god sorry we can't find an unoccupied AI spawn location, so we're spawning you on top of someone.") for(var/obj/effect/landmark/start/sloc in landmarks_list) if (sloc.name == "AI") loc_landmark = sloc @@ -212,7 +212,7 @@ new_xeno.a_intent = I_HURT new_xeno.key = key - new_xeno << "You are now an alien." + to_chat(new_xeno, "You are now an alien.") qdel(src) return @@ -234,7 +234,7 @@ new_corgi.a_intent = I_HURT new_corgi.key = key - new_corgi << "You are now a Corgi. Yap Yap!" + to_chat(new_corgi, "You are now a Corgi. Yap Yap!") qdel(src) return @@ -244,7 +244,7 @@ var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes if(!safe_animal(mobpath)) - usr << "Sorry but this mob type is currently unavailable." + to_chat(usr, "Sorry but this mob type is currently unavailable.") return if(transforming) @@ -267,7 +267,7 @@ new_mob.a_intent = I_HURT - new_mob << "You suddenly feel more... animalistic." + to_chat(new_mob, "You suddenly feel more... animalistic.") spawn() qdel(src) return @@ -278,14 +278,14 @@ var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes if(!safe_animal(mobpath)) - usr << "Sorry but this mob type is currently unavailable." + to_chat(usr, "Sorry but this mob type is currently unavailable.") return var/mob/new_mob = new mobpath(src.loc) new_mob.key = key new_mob.a_intent = I_HURT - new_mob << "You feel more... animalistic" + to_chat(new_mob, "You feel more... animalistic") qdel(src) diff --git a/code/modules/modular_computers/NTNet/NTNet.dm b/code/modules/modular_computers/NTNet/NTNet.dm index be42cec23f..8370683522 100644 --- a/code/modules/modular_computers/NTNet/NTNet.dm +++ b/code/modules/modular_computers/NTNet/NTNet.dm @@ -31,9 +31,10 @@ var/global/datum/ntnet/ntnet_global = new() /datum/ntnet/New() if(ntnet_global && (ntnet_global != src)) ntnet_global = src // There can be only one. - for(var/obj/machinery/ntnet_relay/R in machines) - relays.Add(R) - R.NTNet = src + if (SSatoms && SSatoms.initialized > INITIALIZATION_INSSATOMS) + for(var/obj/machinery/ntnet_relay/R in machines) + relays.Add(R) + R.NTNet = src build_software_lists() build_news_list() build_emails_list() diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm index 7c76fdd2e3..2e4d43c731 100644 --- a/code/modules/modular_computers/NTNet/NTNet_relay.dm +++ b/code/modules/modular_computers/NTNet/NTNet_relay.dm @@ -2,12 +2,13 @@ /obj/machinery/ntnet_relay name = "NTNet Quantum Relay" desc = "A very complex router and transmitter capable of connecting electronic devices together. Looks fragile." - use_power = 2 + use_power = USE_POWER_ACTIVE active_power_usage = 20000 //20kW, apropriate for machine that keeps massive cross-Zlevel wireless network operational. idle_power_usage = 100 icon_state = "bus" anchored = 1 density = 1 + circuit = /obj/item/weapon/circuitboard/ntnet_relay var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame. var/enabled = 1 // Set to 0 if the relay was turned off var/dos_failure = 0 // Set to 1 if the relay failed due to (D)DoS attack @@ -37,9 +38,9 @@ /obj/machinery/ntnet_relay/process() if(operable()) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) else - use_power = 1 + update_use_power(USE_POWER_IDLE) if(dos_overload) dos_overload = max(0, dos_overload - dos_dissipate) @@ -93,17 +94,16 @@ return 1 /obj/machinery/ntnet_relay/New() - uid = gl_uid - gl_uid++ - component_parts = list() - component_parts += new /obj/item/stack/cable_coil(src,15) - component_parts += new /obj/item/weapon/circuitboard/ntnet_relay(src) + ..() + assign_uid() + default_apply_parts() +/obj/machinery/ntnet_relay/Initialize() + . = ..() if(ntnet_global) ntnet_global.relays.Add(src) NTNet = ntnet_global ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]") - ..() /obj/machinery/ntnet_relay/Destroy() if(ntnet_global) @@ -113,24 +113,11 @@ for(var/datum/computer_file/program/ntnet_dos/D in dos_sources) D.target = null D.error = "Connection to quantum relay severed" + . = ..() + +/obj/machinery/ntnet_relay/attackby(var/obj/item/W as obj, var/mob/user as mob) + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return ..() - -/obj/machinery/ntnet_relay/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if(W.is_screwdriver()) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - panel_open = !panel_open - to_chat(user, "You [panel_open ? "open" : "close"] the maintenance hatch") - return - if(W.is_crowbar()) - if(!panel_open) - to_chat(user, "Open the maintenance panel first.") - return - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - to_chat(user, "You disassemble \the [src]!") - - for(var/atom/movable/A in component_parts) - A.forceMove(src.loc) - new /obj/structure/frame(src.loc) - qdel(src) - return - ..() \ No newline at end of file diff --git a/code/modules/modular_computers/computers/modular_computer/core.dm b/code/modules/modular_computers/computers/modular_computer/core.dm index 30d9cf6039..2848468da8 100644 --- a/code/modules/modular_computers/computers/modular_computer/core.dm +++ b/code/modules/modular_computers/computers/modular_computer/core.dm @@ -41,14 +41,14 @@ /obj/item/modular_computer/proc/install_default_programs() return 1 -/obj/item/modular_computer/New() +/obj/item/modular_computer/Initialize() START_PROCESSING(SSobj, src) install_default_hardware() if(hard_drive) install_default_programs() update_icon() update_verbs() - ..() + . = ..() /obj/item/modular_computer/Destroy() kill_program(1) diff --git a/code/modules/modular_computers/computers/modular_computer/interaction.dm b/code/modules/modular_computers/computers/modular_computer/interaction.dm index bf70bb7d28..c9ee11a77e 100644 --- a/code/modules/modular_computers/computers/modular_computer/interaction.dm +++ b/code/modules/modular_computers/computers/modular_computer/interaction.dm @@ -100,7 +100,7 @@ /obj/item/modular_computer/attack_ghost(var/mob/observer/ghost/user) if(enabled) ui_interact(user) - else if(check_rights(R_ADMIN, 0, user)) + else if(check_rights(R_ADMIN|R_EVENT, 0, user)) var/response = alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", "Yes", "No") if(response == "Yes") turn_on(user) diff --git a/code/modules/modular_computers/computers/modular_computer/power.dm b/code/modules/modular_computers/computers/modular_computer/power.dm index 48871619fa..b3746d55d4 100644 --- a/code/modules/modular_computers/computers/modular_computer/power.dm +++ b/code/modules/modular_computers/computers/modular_computer/power.dm @@ -16,6 +16,12 @@ return TRUE return FALSE +/obj/item/modular_computer/get_cell() + if(battery_module) + return battery_module.battery + else + ..() + // Tries to use power from APC, if present. /obj/item/modular_computer/proc/apc_power(var/power_usage = 0) apc_powered = TRUE diff --git a/code/modules/modular_computers/computers/modular_computer/variables.dm b/code/modules/modular_computers/computers/modular_computer/variables.dm index c746493ff8..8a1641a125 100644 --- a/code/modules/modular_computers/computers/modular_computer/variables.dm +++ b/code/modules/modular_computers/computers/modular_computer/variables.dm @@ -24,8 +24,8 @@ icon = null // This thing isn't meant to be used on it's own. Subtypes should supply their own icon. icon_state = null - //center_of_mass = null // No pixelshifting by placing on tables, etc. - //randpixel = 0 // And no random pixelshifting on-creation either. + center_of_mass = null // No pixelshifting by placing on tables, etc. + randpixel = 0 // And no random pixelshifting on-creation either. var/icon_state_unpowered = null // Icon state when the computer is turned off var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen. var/icon_state_screensaver = null diff --git a/code/modules/modular_computers/computers/subtypes/dev_console.dm b/code/modules/modular_computers/computers/subtypes/dev_console.dm index 32f24b6001..d7dbeed75d 100644 --- a/code/modules/modular_computers/computers/subtypes/dev_console.dm +++ b/code/modules/modular_computers/computers/subtypes/dev_console.dm @@ -1,7 +1,7 @@ /obj/item/modular_computer/console name = "console" desc = "A stationary computer." - icon = 'icons/obj/modular_console_vr.dmi' //VOREStation Edit + icon = 'icons/obj/modular_console.dmi' icon_state = "console" icon_state_unpowered = "console" icon_state_screensaver = "standby" diff --git a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm index 905df24590..929e72a166 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm @@ -46,23 +46,3 @@ card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(src) battery_module.charge_to_full() - -//VOREStation Add Start -/obj/item/modular_computer/laptop/preset/custom_loadout/rugged - name = "rugged laptop computer" - desc = "A rugged portable computer." - icon = 'icons/obj/modular_laptop_vr.dmi' - max_damage = 300 - broken_damage = 200 - -/obj/item/modular_computer/laptop/preset/custom_loadout/rugged/install_default_hardware() - ..() - processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) - tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) - hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/advanced(src) - network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) - nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) - card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) - battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(src) - battery_module.charge_to_full() -//VOREStation Add End \ No newline at end of file diff --git a/code/modules/modular_computers/computers/subtypes/preset_laptop_vr.dm b/code/modules/modular_computers/computers/subtypes/preset_laptop_vr.dm new file mode 100644 index 0000000000..5f7b2025b0 --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/preset_laptop_vr.dm @@ -0,0 +1,48 @@ +//Rugged laptop +/obj/item/modular_computer/laptop/preset/custom_loadout/rugged + name = "rugged laptop computer" + desc = "A rugged portable computer." + icon_state_unpowered = "ruggedplus-open" + icon_state = "ruggedplus-open" + icon_state_closed = "ruggedplus-closed" + + max_damage = 300 + broken_damage = 200 + +/obj/item/modular_computer/laptop/preset/custom_loadout/rugged/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/advanced(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(src) + battery_module.charge_to_full() + +//Fancy Schmancy - this is defined in normal file, just swapping the icon +/obj/item/modular_computer/laptop/preset/custom_loadout/elite + name = "elite laptop computer" + desc = "Fancy shcmancy laptop." + icon_state_unpowered = "elite-open" + icon_state = "elite-open" + icon_state_closed = "elite-closed" + +//Alien/Human hybrid tech +/obj/item/modular_computer/laptop/preset/custom_loadout/hybrid + name = "hybrid laptop computer" + desc = "Weird glowy alien/human crossover tech." + icon_state_unpowered = "hybrid-open" + icon_state = "hybrid-open" + icon_state_closed = "hybrid-closed" + +/obj/item/modular_computer/laptop/preset/custom_loadout/hybrid/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/photonic(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/super(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/lambda(src) + battery_module.charge_to_full() diff --git a/code/modules/modular_computers/computers/subtypes/preset_tablet_vr.dm b/code/modules/modular_computers/computers/subtypes/preset_tablet_vr.dm new file mode 100644 index 0000000000..9a87928618 --- /dev/null +++ b/code/modules/modular_computers/computers/subtypes/preset_tablet_vr.dm @@ -0,0 +1,53 @@ +/obj/item/modular_computer/tablet/preset/custom_loadout/rugged + name = "rugged tablet computer" + desc = "A rugged tablet computer." + icon = 'icons/obj/modular_tablet.dmi' + icon_state = "rugged" + icon_state_unpowered = "rugged" + max_damage = 300 + broken_damage = 200 + +/obj/item/modular_computer/tablet/preset/custom_loadout/rugged/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(src) + network_card = new/obj/item/weapon/computer_hardware/network_card(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/micro(src) + battery_module.charge_to_full() + +/obj/item/modular_computer/tablet/preset/custom_loadout/elite + name = "elite tablet computer" + desc = "A more expensive tablet computer." + icon = 'icons/obj/modular_tablet.dmi' + icon_state = "elite" + icon_state_unpowered = "elite" + +/obj/item/modular_computer/tablet/preset/custom_loadout/elite/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module(src) + battery_module.charge_to_full() + +/obj/item/modular_computer/tablet/preset/custom_loadout/hybrid + name = "hybrid tablet computer" + desc = "A human/alien hybrid tech tablet computer." + icon = 'icons/obj/modular_tablet.dmi' + icon_state = "hybrid" + icon_state_unpowered = "hybrid" + +/obj/item/modular_computer/tablet/preset/custom_loadout/hybrid/install_default_hardware() + ..() + processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/photonic/small(src) + tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src) + hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/small(src) + network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src) + nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src) + card_slot = new/obj/item/weapon/computer_hardware/card_slot(src) + battery_module = new/obj/item/weapon/computer_hardware/battery_module/lambda(src) + battery_module.charge_to_full() diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index afc02e8414..bb41552432 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -23,7 +23,7 @@ var/global/file_uid = 0 if(holder.holder2 && holder.holder2.active_program == src) holder.holder2.kill_program(1) holder = null - ..() + return ..() // Returns independent copy of this file. /datum/computer_file/proc/clone(var/rename = 0) diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 7124bdcd03..895b73b5fa 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -97,7 +97,7 @@ return 1 // Admin override - allows operation of any computer as aghosted admin, as if you had any required access. - if(istype(user, /mob/observer/dead) && check_rights(R_ADMIN, 0, user)) + if(istype(user, /mob/observer/dead) && check_rights(R_ADMIN|R_EVENT, 0, user)) return 1 if(!istype(user)) diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index 5bcac7adf8..aa122c0abf 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -44,14 +44,16 @@ data["id_owner"] = id_card && id_card.registered_name ? id_card.registered_name : "-----" data["id_name"] = id_card ? id_card.name : "-----" - data["command_jobs"] = format_jobs(command_positions) - data["engineering_jobs"] = format_jobs(engineering_positions) - data["medical_jobs"] = format_jobs(medical_positions) - data["science_jobs"] = format_jobs(science_positions) - data["security_jobs"] = format_jobs(security_positions) - data["cargo_jobs"] = format_jobs(cargo_positions) - data["civilian_jobs"] = format_jobs(civilian_positions) - data["centcom_jobs"] = format_jobs(get_all_centcom_jobs()) + var/list/departments = list() + for(var/D in SSjob.get_all_department_datums()) + var/datum/department/dept = D + if(!dept.assignable) // No AI ID cards for you. + continue + if(dept.centcom_only && !is_centcom) + continue + departments[++departments.len] = list("department_name" = dept.name, "jobs" = format_jobs(SSjob.get_job_titles_in_department(dept.name)) ) + + data["departments"] = departments data["all_centcom_access"] = is_centcom ? get_accesses(1) : null data["regions"] = get_accesses() diff --git a/code/modules/modular_computers/file_system/programs/command/comm.dm b/code/modules/modular_computers/file_system/programs/command/comm.dm index c174ced23b..fc2efa8820 100644 --- a/code/modules/modular_computers/file_system/programs/command/comm.dm +++ b/code/modules/modular_computers/file_system/programs/command/comm.dm @@ -131,7 +131,7 @@ if(announcment_cooldown) to_chat(usr, "Please allow at least one minute to pass between announcements") return TRUE - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|text + var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|message if(!input || !can_still_topic()) return 1 crew_announcement.Announce(input) @@ -165,7 +165,10 @@ if(!is_relay_online())//Contact Centcom has a check, Syndie doesn't to allow for Traitor funs. to_chat(usr, "No Emergency Bluespace Relay detected. Unable to transmit message.") return 1 - var/input = sanitize(input("Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text) + var/input = sanitize(input("Please choose a message to transmit to Centcomm via quantum entanglement. \ + Please be aware that this process is very expensive, and abuse will lead to... termination. \ + Transmission does not guarantee a response. There is a 30 second delay before you may send another message, \ + be clear, full and concise.", "Central Command Quantum Messaging") as null|message) if(!input || !can_still_topic()) return 1 CentCom_announce(input, usr) @@ -265,7 +268,7 @@ var/datum/signal/status_signal = new status_signal.source = src - status_signal.transmission_method = 1 + status_signal.transmission_method = TRANSMISSION_RADIO status_signal.data["command"] = command switch(command) diff --git a/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm index 20c4dde1ca..8c03238bff 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/alarm_monitor.dm @@ -42,7 +42,7 @@ /datum/nano_module/alarm_monitor/all/New() ..() - alarm_handlers = alarm_manager.all_handlers + alarm_handlers = SSalarm.all_handlers /datum/nano_module/alarm_monitor/engineering/New() ..() @@ -61,31 +61,35 @@ AH.unregister_alarm(object) /datum/nano_module/alarm_monitor/proc/all_alarms() + var/z = get_z(nano_host()) var/list/all_alarms = new() for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.visible_alarms() + all_alarms += AH.visible_alarms(z) return all_alarms /datum/nano_module/alarm_monitor/proc/major_alarms() + var/z = get_z(nano_host()) var/list/all_alarms = new() for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.major_alarms() + all_alarms += AH.major_alarms(z) return all_alarms // Modified version of above proc that uses slightly less resources, returns 1 if there is a major alarm, 0 otherwise. /datum/nano_module/alarm_monitor/proc/has_major_alarms() + var/z = get_z(nano_host()) for(var/datum/alarm_handler/AH in alarm_handlers) - if(AH.has_major_alarms()) + if(AH.has_major_alarms(z)) return 1 return 0 /datum/nano_module/alarm_monitor/proc/minor_alarms() + var/z = get_z(nano_host()) var/list/all_alarms = new() for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.minor_alarms() + all_alarms += AH.minor_alarms(z) return all_alarms @@ -104,9 +108,10 @@ var/list/data = host.initial_data() var/categories[0] + var/z = get_z(nano_host()) for(var/datum/alarm_handler/AH in alarm_handlers) categories[++categories.len] = list("category" = AH.category, "alarms" = list()) - for(var/datum/alarm/A in AH.major_alarms()) + for(var/datum/alarm/A in AH.visible_alarms(z)) var/cameras[0] var/lost_sources[0] @@ -118,7 +123,7 @@ lost_sources[++lost_sources.len] = AS.source_name categories[categories.len]["alarms"] += list(list( - "name" = sanitize(A.alarm_name()), + "name" = sanitize("[A.alarm_name()]" + "[A.max_severity() > 1 ? "(MAJOR)" : ""]"), "origin_lost" = A.origin == null, "has_cameras" = cameras.len, "cameras" = cameras, diff --git a/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm index 776e73ec7a..bfcaadee3e 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm @@ -47,12 +47,17 @@ /datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) var/list/data = host.initial_data() var/alarms[0] - var/turf/T = get_turf(nano_host()) + + var/z = get_z(nano_host()) + var/list/map_levels = using_map.get_map_levels(z) + data["map_levels"] = map_levels // TODO: Move these to a cache, similar to cameras for(var/obj/machinery/alarm/alarm in (monitored_alarms.len ? monitored_alarms : machines)) if(!monitored_alarms.len && alarm.alarms_hidden) continue + if(!(alarm.z in map_levels)) + continue alarms[++alarms.len] = list( "name" = sanitize(alarm.name), "ref"= "\ref[alarm]", @@ -61,7 +66,6 @@ "y" = alarm.y, "z" = alarm.z) data["alarms"] = alarms - data["map_levels"] = using_map.get_map_levels(T.z) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if(!ui) diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index 32117e845c..b5c086dc26 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -53,10 +53,15 @@ var/list/sensors = list() // Focus: If it remains null if no sensor is selected and UI will display sensor list, otherwise it will display sensor reading. var/obj/machinery/power/sensor/focus = null - var/turf/T = get_turf(nano_host()) + + var/z = get_z(nano_host()) + var/list/map_levels = using_map.get_map_levels(z) + data["map_levels"] = map_levels // Build list of data from sensor readings. for(var/obj/machinery/power/sensor/S in grid_sensors) + if(!(S.z in map_levels)) + continue sensors.Add(list(list( "name" = S.name_tag, "alarm" = S.check_grid_warning() @@ -67,7 +72,6 @@ data["all_sensors"] = sensors if(focus) data["focus"] = focus.return_reading_data() - data["map_levels"] = using_map.get_map_levels(T.z) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) diff --git a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm index d5ee88b670..7603b1115e 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm @@ -122,11 +122,19 @@ // Description: Refreshes local list of known devices. /datum/nano_module/rcon/proc/FindDevices() known_SMESs = new /list() + + var/z = get_z(nano_host()) + var/list/map_levels = using_map.get_map_levels(z) + for(var/obj/machinery/power/smes/buildable/SMES in machines) + if(!(SMES.z in map_levels)) + continue if(SMES.RCon_tag && (SMES.RCon_tag != "NO_TAG") && SMES.RCon) known_SMESs.Add(SMES) known_breakers = new /list() for(var/obj/machinery/power/breakerbox/breaker in machines) + if(!(breaker.z in map_levels)) + continue if(breaker.RCon_tag != "NO_TAG") known_breakers.Add(breaker) diff --git a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm index af0489a953..6939a44dff 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm @@ -41,10 +41,10 @@ // Refreshes list of active supermatter crystals /datum/nano_module/supermatter_monitor/proc/refresh() supermatters = list() - var/turf/T = get_turf(nano_host()) - if(!T) + var/z = get_z(nano_host()) + if(!z) return - var/valid_z_levels = (GetConnectedZlevels(T.z) & using_map.station_levels) + var/valid_z_levels = using_map.get_map_levels(z) for(var/obj/machinery/power/supermatter/S in machines) // Delaminating, not within coverage, not on a tile. if(S.grav_pulling || S.exploded || !(S.z in valid_z_levels) || !istype(S.loc, /turf/)) diff --git a/code/modules/modular_computers/file_system/programs/generic/camera.dm b/code/modules/modular_computers/file_system/programs/generic/camera.dm index a722dea6b6..e07d9ec293 100644 --- a/code/modules/modular_computers/file_system/programs/generic/camera.dm +++ b/code/modules/modular_computers/file_system/programs/generic/camera.dm @@ -15,6 +15,12 @@ return access_research if(NETWORK_ERT) return access_cent_specops + //VOREStation Add Start + if(NETWORK_TALON_SHIP) + return access_talon + if(NETWORK_TALON_HELMETS) + return access_talon + //VOREStation Add End if(network in using_map.station_networks) return access_security // Default for all other station networks @@ -62,8 +68,10 @@ data["networks"] = all_networks + var/list/map_levels = using_map.get_map_levels(get_z(nano_host()), TRUE) + if(current_network) - data["cameras"] = camera_repository.cameras_in_network(current_network) + data["cameras"] = camera_repository.cameras_in_network(current_network, map_levels) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) diff --git a/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm index 668d3f81e2..2b25951926 100644 --- a/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm +++ b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm @@ -1,7 +1,7 @@ /datum/computer_file/program/suit_sensors filename = "sensormonitor" filedesc = "Suit Sensors Monitoring" - nanomodule_path = /datum/nano_module/crew_monitor + nanomodule_path = /datum/nano_module/program/crew_monitor program_icon_state = "crew" program_key_state = "med_key" program_menu_icon = "heart" @@ -12,17 +12,14 @@ size = 11 - - - -/datum/nano_module/crew_monitor +/datum/nano_module/program/crew_monitor name = "Crew monitor" -/datum/nano_module/crew_monitor/Topic(href, href_list) +/datum/nano_module/program/crew_monitor/Topic(href, href_list) if(..()) return 1 var/turf/T = get_turf(nano_host()) // TODO: Allow setting any using_map.contact_levels from the interface. if (!T || !(T.z in using_map.player_levels)) - usr << "Unable to establish a connection: You're too far away from the station!" + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") return 0 if(href_list["track"]) if(isAI(usr)) @@ -32,20 +29,25 @@ AI.ai_actual_track(H) return 1 -/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/nano_module/program/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) var/list/data = host.initial_data() - var/turf/T = get_turf(nano_host()) data["isAI"] = isAI(user) - data["map_levels"] = using_map.get_map_levels(T.z, FALSE) + + var/z = get_z(nano_host()) + var/list/map_levels = using_map.get_map_levels(z, TRUE) + data["map_levels"] = map_levels + data["crewmembers"] = list() - for(var/z in data["map_levels"]) // VOREStation Edit - data["crewmembers"] += crew_repository.health_data(z) + for(var/zlevel in map_levels) + data["crewmembers"] += crew_repository.health_data(zlevel) if(!data["map_levels"].len) to_chat(user, "The crew monitor doesn't seem like it'll work here.") - if(ui) // VOREStation Addition - ui.close() // VOREStation Addition + if(program) + program.kill_program() + else if(ui) + ui.close() return ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) @@ -65,7 +67,7 @@ // should make the UI auto-update; doesn't seem to? ui.set_auto_update(1) -/*/datum/nano_module/crew_monitor/proc/scan() +/*/datum/nano_module/program/crew_monitor/proc/scan() for(var/mob/living/carbon/human/H in mob_list) if(istype(H.w_uniform, /obj/item/clothing/under)) var/obj/item/clothing/under/C = H.w_uniform diff --git a/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm b/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm index 28ca1b73f5..597065d27b 100644 --- a/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm +++ b/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm @@ -90,7 +90,7 @@ var/warrant_uid = 0 W.fields["auth"] = "Unauthorized" W.fields["arrestsearch"] = "arrest" if(temp == "search") - W.fields["namewarrant"] = "No location given" + W.fields["namewarrant"] = "No suspect/location given" // VOREStation edit W.fields["charges"] = "No reason given" W.fields["auth"] = "Unauthorized" W.fields["arrestsearch"] = "search" @@ -135,7 +135,9 @@ var/warrant_uid = 0 if(href_list["editwarrantauth"]) . = 1 - + if(!(access_hos in I.access)) // VOREStation edit begin + to_chat(user, "You don't have the access to do this!") + return // VOREStation edit end activewarrant.fields["auth"] = "[I.registered_name] - [I.assignment ? I.assignment : "(Unknown)"]" if(href_list["back"]) diff --git a/code/modules/modular_computers/file_system/programs/ships/navigation.dm b/code/modules/modular_computers/file_system/programs/ships/navigation.dm new file mode 100644 index 0000000000..dbe7aed5de --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/ships/navigation.dm @@ -0,0 +1,63 @@ +/datum/computer_file/program/ship_nav + filename = "navviewer" + filedesc = "Ship Navigational Screen" + nanomodule_path = /datum/nano_module/program/ship/nav + program_icon_state = "helm" + program_key_state = "generic_key" + program_menu_icon = "search" + extended_desc = "Displays a ship's location in the sector." + required_access = null + requires_ntnet = 1 + network_destination = "ship position sensors" + size = 4 + +/datum/nano_module/program/ship/nav + name = "Navigation Display" + +/datum/nano_module/program/ship/nav/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(!linked) + to_chat(user, "You don't appear to be on a spaceship...") + if(program) + program.kill_program() + else if(ui) + ui.close() + return + + var/list/data = list() + if(program) + data = program.get_header_data() + + var/turf/T = get_turf(linked) + var/obj/effect/overmap/visitable/sector/current_sector = locate() in T + + data["sector"] = current_sector ? current_sector.name : "Deep Space" + data["sector_info"] = current_sector ? current_sector.desc : "Not Available" + data["s_x"] = linked.x + data["s_y"] = linked.y + data["speed"] = round(linked.get_speed()*1000, 0.01) + data["accel"] = round(linked.get_acceleration()*1000, 0.01) + data["heading"] = linked.get_heading_degrees() + data["viewing"] = viewing_overmap(user) + + if(linked.get_speed()) + data["ETAnext"] = "[round(linked.ETA()/10)] seconds" + else + data["ETAnext"] = "N/A" + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "nav.tmpl", "[linked.name] Navigation Screen", 380, 530, state = state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/datum/nano_module/program/ship/nav/OnTopic(var/mob/user, var/list/href_list) + if(..()) + return TOPIC_HANDLED + + if (!linked) + return TOPIC_NOACTION + + if (href_list["viewing"]) + viewing_overmap(user) ? unlook(user) : look(user) + return TOPIC_REFRESH diff --git a/code/modules/modular_computers/file_system/programs/ships/ship.dm b/code/modules/modular_computers/file_system/programs/ships/ship.dm new file mode 100644 index 0000000000..1db653153b --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/ships/ship.dm @@ -0,0 +1,87 @@ +/datum/nano_module/program/ship + var/obj/effect/overmap/visitable/ship/linked + var/list/viewers + var/extra_view = 0 + +/datum/nano_module/program/ship/New() + ..() + sync_linked() + if(linked) + name = "[linked.name] [name]" + +/datum/nano_module/program/ship/Destroy() + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + unlook(M) + . = ..() + +/datum/nano_module/program/ship/proc/sync_linked() + var/obj/effect/overmap/visitable/ship/sector = get_overmap_sector(get_z(nano_host())) + if(!sector) + return + return attempt_hook_up_recursive(sector) + +/datum/nano_module/program/ship/proc/attempt_hook_up_recursive(obj/effect/overmap/visitable/ship/sector) + if(attempt_hook_up(sector)) + return sector + for(var/obj/effect/overmap/visitable/ship/candidate in sector) + if((. = .(candidate))) + return + +/datum/nano_module/program/ship/proc/attempt_hook_up(obj/effect/overmap/visitable/ship/sector) + if(!istype(sector)) + return + if(sector.check_ownership(nano_host())) + linked = sector + return 1 + +/datum/nano_module/program/ship/proc/look(var/mob/user) + if(linked) + user.machine = nano_host() + user.reset_view(linked) + user.set_viewsize(world.view + extra_view) + GLOB.moved_event.register(user, src, /datum/nano_module/program/ship/proc/unlook) + LAZYDISTINCTADD(viewers, weakref(user)) + +/datum/nano_module/program/ship/proc/unlook(var/mob/user) + user.reset_view() + user.set_viewsize() // reset to default + GLOB.moved_event.unregister(user, src, /datum/nano_module/program/ship/proc/unlook) + LAZYREMOVE(viewers, weakref(user)) + +/datum/nano_module/program/ship/proc/viewing_overmap(mob/user) + return (weakref(user) in viewers) + +/datum/nano_module/program/ship/proc/DefaultTopicState() + return global.default_state + +/datum/nano_module/program/ship/Topic(var/href, var/href_list = list(), var/datum/topic_state/state) + if((. = ..())) + return + state = state || DefaultTopicState() || global.default_state + if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) + CouldUseTopic(usr) + return OnTopic(usr, href_list, state) + CouldNotUseTopic(usr) + return TRUE + +/datum/nano_module/program/ship/proc/OnTopic(var/mob/user, var/href_list, var/datum/topic_state/state) + return TOPIC_NOACTION + +/datum/nano_module/program/ship/proc/CouldNotUseTopic(mob/user) + . = ..() + unlook(user) + +/datum/nano_module/program/ship/proc/CouldUseTopic(mob/user) + . = ..() + if(viewing_overmap(user)) + look(user) + +/datum/nano_module/program/ship/check_eye(var/mob/user) + if (!get_dist(user, nano_host()) > 1 || user.blinded || !linked ) + unlook(user) + return -1 + else + return 0 \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm index fb73609d84..95b020ca01 100644 --- a/code/modules/modular_computers/hardware/_hardware.dm +++ b/code/modules/modular_computers/hardware/_hardware.dm @@ -47,6 +47,7 @@ to_chat(user, "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]") /obj/item/weapon/computer_hardware/New(var/obj/L) + ..() w_class = hardware_size if(istype(L, /obj/item/modular_computer)) holder2 = L diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 3b7ea1164b..df16c5864f 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -76,4 +76,7 @@ /obj/item/weapon/computer_hardware/battery_module/proc/charge_to_full() if(battery) - battery.charge = battery.maxcharge \ No newline at end of file + battery.charge = battery.maxcharge + +/obj/item/weapon/computer_hardware/battery_module/get_cell() + return battery \ No newline at end of file diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index ecf0bab6f6..2f382e0f4d 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -79,22 +79,27 @@ var/global/ntnet_card_uid = 1 return 0 if(holder2) - var/turf/T = get_turf(holder2) - if(!istype(T)) //no reception in nullspace + var/holderz = get_z(holder2) + if(!holderz) //no reception in nullspace return 0 - if(T.z in using_map.station_levels) - // Computer is on station. Low/High signal depending on what type of network card you have - if(long_range) - return 2 - else - return 1 - if(T.z in using_map.contact_levels) //not on station, but close enough for radio signal to travel - if(long_range) // Computer is not on station, but it has upgraded network card. Low signal. - return 1 - - return 0 // Computer is not on station and does not have upgraded network card. No signal. + var/list/zlevels_in_range = using_map.get_map_levels(holderz, long_range) + var/best = 0 + for(var/relay in ntnet_global.relays) + var/obj/machinery/ntnet_relay/R = relay + //Relay is down + if(!R.operable()) + continue + //We're on the same z + if(R.z == holderz) + best = 2 + break // No point in going further + //Not on the same z but within range anyway + if(R.z in zlevels_in_range) + best = 1 + return best + return 0 // No computer! /obj/item/weapon/computer_hardware/network_card/Destroy() if(holder2 && (holder2.network_card == src)) holder2.network_card = null - ..() + return ..() diff --git a/code/modules/multi-tile/multi-tile.dm b/code/modules/multi-tile/multi-tile.dm index b8dd9ef512..3c590c5b6d 100644 --- a/code/modules/multi-tile/multi-tile.dm +++ b/code/modules/multi-tile/multi-tile.dm @@ -11,6 +11,8 @@ var/list/checked_turfs = list() for(var/turf/T in locs) var/turf/Tcheck = get_step(T, stepdir) + if(!Tcheck) //Map edge + continue if(Tcheck in checked_turfs) continue if(Tcheck in locs) diff --git a/code/modules/multiz/ladder_assembly_vr.dm b/code/modules/multiz/ladder_assembly_vr.dm index 3bb7b681fd..675cc1e969 100644 --- a/code/modules/multiz/ladder_assembly_vr.dm +++ b/code/modules/multiz/ladder_assembly_vr.dm @@ -70,7 +70,7 @@ state = CONSTRUCTION_WRENCHED to_chat(user, "You cut \the [src] free from the floor.") else - user << "You need more welding fuel to complete this task." + to_chat(user, "You need more welding fuel to complete this task.") return // Try to construct this into a real stairway. diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index e7375b5235..c293e2f263 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -15,6 +15,10 @@ /mob/proc/zMove(direction) if(eyeobj) return eyeobj.zMove(direction) + if(istype(loc,/obj/mecha)) + var/obj/mecha/mech = loc + return mech.relaymove(src,direction) + if(!can_ztravel()) to_chat(src, "You lack means of travel in that direction.") return @@ -29,6 +33,10 @@ to_chat(src, "There is nothing of interest in this direction.") return 0 + if(is_incorporeal()) + forceMove(destination) + return 1 + if(!start.CanZPass(src, direction)) to_chat(src, "\The [start] is in the way.") return 0 @@ -124,13 +132,13 @@ /mob/living/can_ztravel() if(incapacitated()) return FALSE - return hovering + return (hovering || is_incorporeal()) /mob/living/carbon/human/can_ztravel() if(incapacitated()) return FALSE - if(hovering) + if(hovering || is_incorporeal()) return TRUE if(flying) //VOREStation Edit. Allows movement up/down with wings. @@ -273,6 +281,8 @@ return FALSE /mob/living/can_fall() + if(is_incorporeal()) + return FALSE if(hovering) return FALSE return ..() @@ -582,4 +592,4 @@ // And hurt the floor. if(istype(hit_atom, /turf/simulated/floor)) var/turf/simulated/floor/ground = hit_atom - ground.break_tile() \ No newline at end of file + ground.break_tile() diff --git a/code/modules/multiz/open_space_controller.dm b/code/modules/multiz/open_space_controller.dm deleted file mode 100644 index e790f476dc..0000000000 --- a/code/modules/multiz/open_space_controller.dm +++ /dev/null @@ -1,105 +0,0 @@ -// -// Controller handling icon updates of open space turfs -// - -/var/global/open_space_initialised = FALSE -/var/global/datum/controller/process/open_space/OS_controller = null -/var/global/image/over_OS_darkness = image('icons/turf/open_space.dmi', "black_open") - -/datum/controller/process/open_space - var/list/turfs_to_process = list() // List of turfs queued for update. - var/list/turfs_to_process_old = null // List of turfs currently being updated. - -/datum/controller/process/open_space/setup() - . = ..() - name = "openspace" - schedule_interval = world.tick_lag // every second - start_delay = 30 SECONDS - OS_controller = src - over_OS_darkness.plane = OVER_OPENSPACE_PLANE - over_OS_darkness.layer = MOB_LAYER - initialize_open_space() - - // Pre-process open space once once before the round starts. Wait 20 seconds so other stuff has time to finish. - spawn(200) - doWork(1) - -/datum/controller/process/open_space/copyStateFrom(var/datum/controller/process/open_space/other) - . = ..() - OS_controller = src - -/datum/controller/process/open_space/doWork() - // We use a different list so any additions to the update lists during a delay from scheck() - // don't cause things to be cut from the list without being updated. - turfs_to_process_old = turfs_to_process - turfs_to_process = list() - - for(last_object in turfs_to_process_old) - var/turf/T = last_object - if(!QDELETED(T)) - update_turf(T) - SCHECK - -/datum/controller/process/open_space/proc/update_turf(var/turf/T) - for(var/atom/movable/A in T) - A.fall() - T.update_icon() - -/datum/controller/process/open_space/proc/add_turf(var/turf/T, var/recursive = 0) - ASSERT(isturf(T)) - turfs_to_process += T - if(recursive > 0) - var/turf/above = GetAbove(T) - if(above && isopenspace(above)) - add_turf(above, recursive) - -// Do the initial updates of open space turfs when the game starts. This will lag! -/datum/controller/process/open_space/proc/initialize_open_space() - // Do initial setup from bottom to top. - for(var/zlevel = 1 to world.maxz) - for(var/turf/simulated/open/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))) - add_turf(T) - open_space_initialised = TRUE - -/turf/Entered(atom/movable/AM) - . = ..() - if(open_space_initialised && !AM.invisibility && isobj(AM)) - var/turf/T = GetAbove(src) - if(isopenspace(T)) - // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].Entered([AM])") - OS_controller.add_turf(T, 1) - -/turf/Exited(atom/movable/AM) - . = ..() - if(open_space_initialised && !AM.invisibility && isobj(AM)) - var/turf/T = GetAbove(src) - if(isopenspace(T)) - // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].Exited([AM])") - OS_controller.add_turf(T, 1) - -/obj/update_icon() - . = ..() - if(open_space_initialised && !invisibility && isturf(loc)) - var/turf/T = GetAbove(src) - if(isopenspace(T)) - // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src].update_icon()") - OS_controller.add_turf(T, 1) - -// Ouch... this is painful. But is there any other way? -/* - No for now -/obj/New() - . = ..() - if(open_space_initialised && !invisibility) - var/turf/T = GetAbove(src) - if(isopenspace(T)) - // log_debug("[T] ([T.x],[T.y],[T.z]) queued for update for [src]New()") - OS_controller.add_turf(T, 1) -*/ - -// Just as New() we probably should hook Destroy() If we can think of something more efficient, lets hear it. -/obj/Destroy() - if(open_space_initialised && !invisibility && isturf(loc)) - var/turf/T = GetAbove(src) - if(isopenspace(T)) - OS_controller.add_turf(T, 1) - . = ..() // Important that this be at the bottom, or we will have been moved to nullspace. diff --git a/code/modules/multiz/pipes.dm b/code/modules/multiz/pipes.dm index 7b13845211..237e38562e 100644 --- a/code/modules/multiz/pipes.dm +++ b/code/modules/multiz/pipes.dm @@ -214,6 +214,26 @@ obj/machinery/atmospherics/pipe/zpipe/up/supply icon_connect_type = "-supply" color = PIPE_COLOR_BLUE +obj/machinery/atmospherics/pipe/zpipe/up/fuel + icon_state = "up-fuel" + name = "upwards fuel pipe" + desc = "A fuel pipe segment to connect upwards." + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +obj/machinery/atmospherics/pipe/zpipe/up/aux + icon_state = "up-aux" + name = "upwards aux pipe" + desc = "A aux pipe segment to connect upwards." + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN + obj/machinery/atmospherics/pipe/zpipe/down/scrubbers icon_state = "down-scrubbers" name = "downwards scrubbers pipe" @@ -233,3 +253,23 @@ obj/machinery/atmospherics/pipe/zpipe/down/supply layer = PIPES_SUPPLY_LAYER icon_connect_type = "-supply" color = PIPE_COLOR_BLUE + +obj/machinery/atmospherics/pipe/zpipe/down/fuel + icon_state = "down-fuel" + name = "downwards fuel pipe" + desc = "A fuel pipe segment to connect downwards." + connect_types = CONNECT_TYPE_FUEL + piping_layer = PIPING_LAYER_FUEL + layer = PIPES_FUEL_LAYER + icon_connect_type = "-fuel" + color = PIPE_COLOR_YELLOW + +obj/machinery/atmospherics/pipe/zpipe/down/aux + icon_state = "down-aux" + name = "upwards aux pipe" + desc = "A aux pipe segment to connect upwards." + connect_types = CONNECT_TYPE_AUX + piping_layer = PIPING_LAYER_AUX + layer = PIPES_AUX_LAYER + icon_connect_type = "-aux" + color = PIPE_COLOR_CYAN \ No newline at end of file diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index b3ea2c17ff..2b280eaac1 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -30,6 +30,11 @@ var/turf/below +/turf/simulated/open/vacuum + oxygen = 0 + nitrogen = 0 + temperature = TCMB + /turf/simulated/open/post_change() ..() update() @@ -56,7 +61,8 @@ below.update_icon() // So the 'ceiling-less' overlay gets added. for(var/atom/movable/A in src) A.fall() - OS_controller.add_turf(src, 1) + if(GLOB.open_space_initialised) + SSopen_space.add_turf(src, TRUE) // override to make sure nothing is hidden /turf/simulated/open/levelupdate() @@ -95,6 +101,8 @@ if(O.invisibility) continue // Ignore objects that have any form of invisibility if(O.loc != below) continue // Ignore multi-turf objects not directly below var/image/temp2 = image(O, dir = O.dir, layer = O.layer) + if(temp2.icon == null) + temp2.icon_state = null temp2.plane = src.plane temp2.color = O.color temp2.overlays += O.overlays @@ -103,7 +111,7 @@ add_overlay(o_img) if(!below_is_open) - add_overlay(over_OS_darkness) + add_overlay(SSopen_space.over_OS_darkness) return 0 return PROCESS_KILL @@ -150,8 +158,14 @@ var/turf/below = GetBelow(src) return !below || below.is_space() +/turf/simulated/open/is_solid_structure() + return locate(/obj/structure/lattice, src) //counts as solid structure if it has a lattice (same as space) + /turf/simulated/open/is_safe_to_enter(mob/living/L) if(L.can_fall()) + for(var/obj/O in contents) + if(!O.CanFallThru(L, GetBelow(src))) + return TRUE // Can't fall through this, like lattice or catwalk. if(!locate(/obj/structure/stairs) in GetBelow(src)) - return FALSE + return FALSE // Falling on stairs is safe. return ..() \ No newline at end of file diff --git a/code/modules/nano/interaction/admin.dm b/code/modules/nano/interaction/admin.dm index 59de0ce22e..dd4ce6ad17 100644 --- a/code/modules/nano/interaction/admin.dm +++ b/code/modules/nano/interaction/admin.dm @@ -4,4 +4,4 @@ /var/global/datum/topic_state/admin_state/admin_state = new() /datum/topic_state/admin_state/can_use_topic(var/src_object, var/mob/user) - return check_rights(R_ADMIN, 0, user) ? STATUS_INTERACTIVE : STATUS_CLOSE + return check_rights(R_ADMIN|R_EVENT, 0, user) ? STATUS_INTERACTIVE : STATUS_CLOSE diff --git a/code/modules/nano/modules/law_manager.dm b/code/modules/nano/modules/law_manager.dm index da971a0f87..3016a00fad 100644 --- a/code/modules/nano/modules/law_manager.dm +++ b/code/modules/nano/modules/law_manager.dm @@ -134,15 +134,15 @@ return 1 if(href_list["notify_laws"]) - owner << "Law Notice" + to_chat(owner, "Law Notice") owner.laws.show_laws(owner) if(isAI(owner)) var/mob/living/silicon/ai/AI = owner for(var/mob/living/silicon/robot/R in AI.connected_robots) - R << "Law Notice" + to_chat(R, "Law Notice") R.laws.show_laws(R) if(usr != owner) - usr << "Laws displayed." + to_chat(usr, "Laws displayed.") return 1 return 0 diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm index 52ac4fd5ea..d5a7cfaf69 100644 --- a/code/modules/nano/nanomapgen.dm +++ b/code/modules/nano/nanomapgen.dm @@ -32,32 +32,32 @@ endY = world.maxy if (currentZ < 0 || currentZ > world.maxz) - usr << "NanoMapGen: ERROR: currentZ ([currentZ]) must be between 1 and [world.maxz]" + to_chat(usr, "NanoMapGen: ERROR: currentZ ([currentZ]) must be between 1 and [world.maxz]") sleep(3) return NANOMAP_TERMINALERR if (startX > endX) - usr << "NanoMapGen: ERROR: startX ([startX]) cannot be greater than endX ([endX])" + to_chat(usr, "NanoMapGen: ERROR: startX ([startX]) cannot be greater than endX ([endX])") sleep(3) return NANOMAP_TERMINALERR if (startY > endX) - usr << "NanoMapGen: ERROR: startY ([startY]) cannot be greater than endY ([endY])" + to_chat(usr, "NanoMapGen: ERROR: startY ([startY]) cannot be greater than endY ([endY])") sleep(3) return NANOMAP_TERMINALERR var/icon/Tile = icon(file("nano/mapbase1200.png")) if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) - world.log << "NanoMapGen: ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]" + to_world_log("NanoMapGen: ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]") sleep(3) return NANOMAP_TERMINALERR Tile.Scale((endX - startX + 1) * NANOMAP_ICON_SIZE, (endY - startY + 1) * NANOMAP_ICON_SIZE) // VOREStation Edit - Scale image to actual size mapped. - world.log << "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])" - usr << "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])" + to_world_log("NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])") + to_chat(usr, "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])") var/count = 0; for(var/WorldX = startX, WorldX <= endX, WorldX++) @@ -73,18 +73,18 @@ count++ if (count % 8000 == 0) - world.log << "NanoMapGen: [count] tiles done" + to_world_log("NanoMapGen: [count] tiles done") sleep(1) var/mapFilename = "nanomap_z[currentZ]-new.png" - world.log << "NanoMapGen: sending [mapFilename] to client" + to_world_log("NanoMapGen: sending [mapFilename] to client") usr << browse(Tile, "window=picture;file=[mapFilename];display=0") - world.log << "NanoMapGen: Done." + to_world_log("NanoMapGen: Done.") - usr << "NanoMapGen: Done. File [mapFilename] uploaded to your cache." + to_chat(usr, "NanoMapGen: Done. File [mapFilename] uploaded to your cache.") if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) return NANOMAP_BADOUTPUT diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index e6df874652..b7adabffa2 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -472,7 +472,7 @@ nanoui is used to open and update nano browser uis var/list/send_data = get_send_data(data) - //user << list2json_usecache(send_data) // used for debugging //NANO DEBUG HOOK + //to_chat(user,list2json_usecache(send_data)) // used for debugging //NANO DEBUG HOOK user << output(list2params(list(strip_improper(json_encode(send_data)))),"[window_id].browser:receiveUpdateData") /** diff --git a/code/modules/nifsoft/nif.dm b/code/modules/nifsoft/nif.dm index 97c97291b4..03d482648f 100644 --- a/code/modules/nifsoft/nif.dm +++ b/code/modules/nifsoft/nif.dm @@ -146,6 +146,8 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable forceMove(parent) parent.implants += src spawn(0) //Let the character finish spawning yo. + if(!H) //Or letting them get deleted + return if(H.mind) owner = H.mind.name implant(H) @@ -260,7 +262,7 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable install_done = world.time + 1 MINUTE notify("Welcome back, [owner]! Performing quick-calibration...") else if(!owner) - install_done = world.time + 30 MINUTES + install_done = world.time + 35 MINUTES notify("Adapting to new user...") sleep(5 SECONDS) notify("Adjoining optic [human.isSynthetic() ? "interface" : "nerve"], please be patient.",TRUE) @@ -270,7 +272,7 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable stat = NIF_TEMPFAIL return FALSE - var/percent_done = (world.time - (install_done - (30 MINUTES))) / (30 MINUTES) + var/percent_done = (world.time - (install_done - (35 MINUTES))) / (35 MINUTES) if(human.client) human.client.screen.Add(global_hud.whitense) //This is the camera static @@ -299,9 +301,9 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable if(2) human.Weaken(5) to_chat(human,"A wave of weakness rolls over you.") - if(3) - human.Sleeping(5) - to_chat(human,"You suddenly black out!") + /*if(3) + human.Sleeping(5) //Disabled for being boring + to_chat(human,"You suddenly black out!")*/ //Finishing up if(1.0 to INFINITY) @@ -359,7 +361,7 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable /obj/item/device/nif/proc/notify(var/message,var/alert = 0) if(!human || stat == NIF_TEMPFAIL) return - to_chat(human,"\[\icon[src.big_icon]NIF\] displays, \"[message]\"") + to_chat(human,"\[[bicon(src.big_icon)]NIF\] displays, \"[message]\"") if(prob(1)) human.visible_message("\The [human] [pick(look_messages)].") if(alert) human << bad_sound @@ -580,7 +582,7 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable name = "bioadaptive NIF" desc = "A NIF that goes out of it's way to accomidate strange body types. \ Will function in species where it normally wouldn't." - durability = 25 + durability = 75 bioadap = TRUE //////////////////////////////// diff --git a/code/modules/nifsoft/nif_softshop.dm b/code/modules/nifsoft/nif_softshop.dm index 6abbeff162..2af77f43ad 100644 --- a/code/modules/nifsoft/nif_softshop.dm +++ b/code/modules/nifsoft/nif_softshop.dm @@ -6,8 +6,6 @@ icon = 'icons/obj/machines/ar_elements.dmi' icon_state = "proj" - icon_vend = "beacon_yes" - icon_deny = "beacon_no" products = list() contraband = list() @@ -97,8 +95,8 @@ var/mob/living/carbon/human/H = user if(!H.nif || !H.nif.stat == NIF_WORKING) - to_chat(H,"[src] seems unable to connect to your NIF...") - flick(icon_deny,entopic.my_image) + to_chat(H, "[src] seems unable to connect to your NIF...") + flick("[icon_state]-deny",entopic.my_image) return FALSE return ..() @@ -112,21 +110,21 @@ if(href_list["remove_coin"] && !istype(usr,/mob/living/silicon)) if(!coin) - usr << "There is no coin in this machine." + to_chat(usr, "There is no coin in this machine.") return coin.forceMove(src.loc) if(!usr.get_active_hand()) usr.put_in_hands(coin) - usr << "You remove \the [coin] from \the [src]" + to_chat(usr, "You remove \the [coin] from \the [src]") coin = null categories &= ~CAT_COIN if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf)))) if((href_list["vend"]) && (vend_ready) && (!currently_vending)) if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH - usr << "Access denied." //Unless emagged of course - flick(icon_deny,entopic.my_image) + to_chat(usr, "Access denied.") //Unless emagged of course + flick("[icon_state]-deny",entopic.my_image) return var/key = text2num(href_list["vend"]) @@ -142,14 +140,14 @@ var/list/soft_access = list(initial(path.access)) var/list/usr_access = usr.GetAccess() if(!has_access(soft_access, list(), usr_access) && !emagged) - usr << "You aren't authorized to buy [initial(path.name)]." - flick(icon_deny,entopic.my_image) + to_chat(usr, "You aren't authorized to buy [initial(path.name)].") + flick("[icon_state]-deny",entopic.my_image) return if(R.price <= 0) vend(R, usr) else if(istype(usr,/mob/living/silicon)) //If the item is not free, provide feedback if a synth is trying to buy something. - usr << "Artificial unit recognized. Artificial units cannot complete this transaction. Purchase canceled." + to_chat(usr, "Artificial unit recognized. Artificial units cannot complete this transaction. Purchase canceled.") return else currently_vending = R @@ -173,8 +171,8 @@ /obj/machinery/vending/nifsoft_shop/vend(datum/stored_item/vending_product/R, mob/user) var/mob/living/carbon/human/H = user if((!allowed(usr)) && !emagged && scan_id && istype(H)) //For SECURE VENDING MACHINES YEAH - usr << "Purchase not allowed." //Unless emagged of course - flick(icon_deny,entopic.my_image) + to_chat(usr, "Purchase not allowed.") //Unless emagged of course + flick("[icon_state]-deny",entopic.my_image) return vend_ready = 0 //One thing at a time!! status_message = "Installing..." @@ -183,13 +181,13 @@ if(R.category & CAT_COIN) if(!coin) - user << "You need to insert a coin to get this item." + to_chat(user, "You need to insert a coin to get this item.") return if(coin.string_attached) if(prob(50)) - user << "You successfully pull the coin out before \the [src] could swallow it." + to_chat(user, "You successfully pull the coin out before \the [src] could swallow it.") else - user << "You weren't able to pull the coin out fast enough, the machine ate it, string and all." + to_chat(user, "You weren't able to pull the coin out fast enough, the machine ate it, string and all.") qdel(coin) coin = null categories &= ~CAT_COIN @@ -207,7 +205,7 @@ spawn(vend_delay) R.amount-- new R.item_path(H.nif) - flick(icon_vend,entopic.my_image) + flick("[icon_state]-vend",entopic.my_image) if(has_logs) do_logging(R, user, 1) diff --git a/code/modules/nifsoft/software/01_vision.dm b/code/modules/nifsoft/software/01_vision.dm index a45cb5946f..7c7ca02079 100644 --- a/code/modules/nifsoft/software/01_vision.dm +++ b/code/modules/nifsoft/software/01_vision.dm @@ -141,7 +141,7 @@ illegal = TRUE access = 999 tick_flags = NIF_ACTIVETICK - planes_enabled = list(VIS_FULLBRIGHT) + planes_enabled = list(VIS_FULLBRIGHT, VIS_CLOAKED) vision_flags = (NIF_V_THERMALS) incompatible_with = list(NIF_MESONS,NIF_MATERIAL,NIF_NIGHTVIS) diff --git a/code/modules/nifsoft/software/06_screens.dm b/code/modules/nifsoft/software/06_screens.dm index 7c91162340..97f4c272a9 100644 --- a/code/modules/nifsoft/software/06_screens.dm +++ b/code/modules/nifsoft/software/06_screens.dm @@ -5,7 +5,7 @@ access = access_medical cost = 625 p_drain = 0.025 - var/datum/nano_module/crew_monitor/arscreen + var/datum/nano_module/program/crew_monitor/arscreen New() ..() diff --git a/code/modules/nifsoft/software/13_soulcatcher.dm b/code/modules/nifsoft/software/13_soulcatcher.dm index 1dd2c354c6..4cc7210bd4 100644 --- a/code/modules/nifsoft/software/13_soulcatcher.dm +++ b/code/modules/nifsoft/software/13_soulcatcher.dm @@ -71,12 +71,12 @@ proc/notify_into(var/message) var/sound = nif.good_sound - to_chat(nif.human,"\[\icon[nif.big_icon]NIF\] Soulcatcher displays, \"[message]\"") + to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] Soulcatcher displays, \"[message]\"") nif.human << sound for(var/brainmob in brainmobs) var/mob/living/carbon/brain/caught_soul/CS = brainmob - to_chat(CS,"\[\icon[nif.big_icon]NIF\] Soulcatcher displays, \"[message]\"") + to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] Soulcatcher displays, \"[message]\"") brainmob << sound proc/say_into(var/message, var/mob/living/sender, var/mob/eyeobj) @@ -88,10 +88,10 @@ //Not AR Projecting else - to_chat(nif.human,"\[\icon[nif.big_icon]NIF\] [sender_name] speaks, \"[message]\"") + to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] [sender_name] speaks, \"[message]\"") for(var/brainmob in brainmobs) var/mob/living/carbon/brain/caught_soul/CS = brainmob - to_chat(CS,"\[\icon[nif.big_icon]NIF\] [sender_name] speaks, \"[message]\"") + to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] [sender_name] speaks, \"[message]\"") log_nsay(message,nif.human.real_name,sender) @@ -104,10 +104,10 @@ //Not AR Projecting else - to_chat(nif.human,"\[\icon[nif.big_icon]NIF\] [sender_name] [message]") + to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] [sender_name] [message]") for(var/brainmob in brainmobs) var/mob/living/carbon/brain/caught_soul/CS = brainmob - to_chat(CS,"\[\icon[nif.big_icon]NIF\] [sender_name] [message]") + to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] [sender_name] [message]") log_nme(message,nif.human.real_name,sender) diff --git a/code/modules/nifsoft/software/14_commlink.dm b/code/modules/nifsoft/software/14_commlink.dm index 58dc33880e..66773dd711 100644 --- a/code/modules/nifsoft/software/14_commlink.dm +++ b/code/modules/nifsoft/software/14_commlink.dm @@ -86,9 +86,9 @@ var/name_used = M.GetVoice() var/rendered = null if(speaking) //Language being used - rendered = "\icon[icon_object] [name_used] [speaking.format_message(text, verb)]" + rendered = "[bicon(icon_object)] [name_used] [speaking.format_message(text, verb)]" else - rendered = "\icon[icon_object] [name_used] [verb], \"[text]\"" + rendered = "[bicon(icon_object)] [name_used] [verb], \"[text]\"" mob.show_message(rendered, 2) //Not supported by the internal one diff --git a/code/modules/nifsoft/software/15_misc.dm b/code/modules/nifsoft/software/15_misc.dm index 0351c8d19f..f6406ed186 100644 --- a/code/modules/nifsoft/software/15_misc.dm +++ b/code/modules/nifsoft/software/15_misc.dm @@ -137,7 +137,6 @@ to_chat(nif.human,"You set the size to [new_size]%") nif.human.visible_message("Swirling grey mist envelops [nif.human] as they change size!","Swirling streams of nanites wrap around you as you change size!") - nif.human.update_icons() //Apply matrix transform asap spawn(0) deactivate() diff --git a/code/modules/organs/internal/appendix.dm b/code/modules/organs/internal/appendix.dm index 01d7008a09..553c542529 100644 --- a/code/modules/organs/internal/appendix.dm +++ b/code/modules/organs/internal/appendix.dm @@ -29,11 +29,11 @@ if(inflamed == 1) if(prob(5)) - owner << "You feel a stinging pain in your abdomen!" + to_chat(owner, "You feel a stinging pain in your abdomen!") owner.emote("me", 1, "winces slightly.") if(inflamed > 1) if(prob(3)) - owner << "You feel a stabbing pain in your abdomen!" + to_chat(owner, "You feel a stabbing pain in your abdomen!") owner.emote("me", 1, "winces painfully.") owner.adjustToxLoss(1) if(inflamed > 2) @@ -41,7 +41,7 @@ owner.vomit() if(inflamed > 3) if(prob(1)) - owner << "Your abdomen is a world of pain!" + to_chat(owner, "Your abdomen is a world of pain!") owner.Weaken(10) var/obj/item/organ/external/groin = owner.get_organ(BP_GROIN) diff --git a/code/modules/organs/internal/augment.dm b/code/modules/organs/internal/augment.dm index d134ad12ca..569b5dda44 100644 --- a/code/modules/organs/internal/augment.dm +++ b/code/modules/organs/internal/augment.dm @@ -12,7 +12,7 @@ organ_verbs = list(/mob/living/carbon/human/proc/augment_menu) // Verbs added by the organ when present in the body. target_parent_classes = list() // Is the parent supposed to be organic, robotic, assisted? - forgiving_class = FALSE // Will the organ give its verbs when it isn't a perfect match? I.E., assisted in organic, synthetic in organic. + forgiving_class = TRUE // Will the organ give its verbs when it isn't a perfect match? I.E., assisted in organic, synthetic in organic. var/obj/item/integrated_object // Objects held by the organ, used for re-usable, deployable things. var/integrated_object_type // Object type the organ will spawn. diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index bb5a5cd65c..c697f9bea5 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -18,6 +18,22 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) var/clone_source = FALSE var/mob/living/carbon/brain/brainmob = null var/can_assist = TRUE + var/defib_timer = -1 + +/obj/item/organ/internal/brain/process() + ..() + if(owner && owner.stat != DEAD) // So there's a lower risk of ticking twice. + tick_defib_timer() + +// This is called by `process()` when the owner is alive, or brain is not in a body, and by `Life()` directly when dead. +/obj/item/organ/internal/brain/proc/tick_defib_timer() + if(preserved) // In an MMI/ice box/etc. + return + + if(!owner || owner.stat == DEAD) + defib_timer = max(--defib_timer, 0) + else + defib_timer = min(++defib_timer, (config.defib_timer MINUTES) / 2) /obj/item/organ/internal/brain/proc/can_assist() return can_assist @@ -65,6 +81,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) /obj/item/organ/internal/brain/New() ..() health = config.default_brain_health + defib_timer = (config.defib_timer MINUTES) / 2 spawn(5) if(brainmob && brainmob.client) brainmob.client.screen.len = null //clear the hud @@ -92,15 +109,15 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) brainmob.languages = H.languages - brainmob << "You feel slightly disoriented. That's normal when you're just \a [initial(src.name)]." + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just \a [initial(src.name)].") callHook("debrain", list(brainmob)) /obj/item/organ/internal/brain/examine(mob/user) // -- TLE ..(user) if(brainmob && brainmob.client)//if thar be a brain inside... the brain. - user << "You can feel the small spark of life still left in this one." + to_chat(user, "You can feel the small spark of life still left in this one.") else - user << "This one seems particularly lifeless. Perhaps it will regain some of its luster later.." + to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later..") /obj/item/organ/internal/brain/removed(var/mob/living/user) diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index 9d82a7749b..d70269b391 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -75,7 +75,7 @@ var/oldbroken = is_broken() ..() if(is_broken() && !oldbroken && owner && !owner.stat) - owner << "You go blind!" + to_chat(owner, "You go blind!") /obj/item/organ/internal/eyes/process() //Eye damage replaces the old eye_stat var. ..() diff --git a/code/modules/organs/internal/liver.dm b/code/modules/organs/internal/liver.dm index a81c71895f..1fcfac93bb 100644 --- a/code/modules/organs/internal/liver.dm +++ b/code/modules/organs/internal/liver.dm @@ -55,7 +55,7 @@ owner.custom_pain("There's a sharp pain in your upper-right abdomen!",1) if (. >= 2) if(prob(1) && owner.getToxLoss() < owner.getMaxHealth()*0.3) - //owner << "" //Toxins provide their own messages for pain + //to_chat(owner, "") //Toxins provide their own messages for pain owner.adjustToxLoss(5) //Not realistic to PA but there are basically no 'real' liver infections /obj/item/organ/internal/liver/grey diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index 07ef683e06..f123ea127b 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -15,28 +15,29 @@ if(is_bruised()) if(prob(4)) - spawn owner.emote("me", 1, "coughs up blood!") + spawn owner?.emote("me", 1, "coughs up blood!") owner.drip(10) if(prob(8)) - spawn owner.emote("me", 1, "gasps for air!") + spawn owner?.emote("me", 1, "gasps for air!") owner.AdjustLosebreath(15) if(owner.internal_organs_by_name[O_BRAIN]) // As the brain starts having Trouble, the lungs start malfunctioning. var/obj/item/organ/internal/brain/Brain = owner.internal_organs_by_name[O_BRAIN] if(Brain.get_control_efficiency() <= 0.8) if(prob(4 / max(0.1,Brain.get_control_efficiency()))) - spawn owner.emote("me", 1, "gasps for air!") + spawn owner?.emote("me", 1, "gasps for air!") owner.AdjustLosebreath(round(3 / max(0.1,Brain.get_control_efficiency()))) /obj/item/organ/internal/lungs/proc/rupture() - var/obj/item/organ/external/parent = owner.get_organ(parent_organ) - if(istype(parent)) - owner.custom_pain("You feel a stabbing pain in your [parent.name]!", 50) + if(owner) + var/obj/item/organ/external/parent = owner.get_organ(parent_organ) + if(istype(parent)) + owner.custom_pain("You feel a stabbing pain in your [parent.name]!", 50) bruise() /obj/item/organ/internal/lungs/handle_germ_effects() . = ..() //Up should return an infection level as an integer - if(!.) return + if(!. || !owner) return //Bacterial pneumonia if (. >= 1) @@ -54,6 +55,6 @@ ..() var/mob/living/carbon/human/H = null spawn(15) - if(ishuman(owner)) + if(owner && ishuman(owner)) H = owner color = H.species.blood_color diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index efdf8efd3e..41b7a0ba39 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -4,6 +4,7 @@ var/list/organ_cache = list() name = "organ" icon = 'icons/obj/surgery.dmi' germ_level = 0 + drop_sound = 'sound/items/drop/flesh.ogg' // Strings. var/organ_tag = "organ" // Unique identifier. @@ -159,7 +160,7 @@ var/list/organ_cache = list() /obj/item/organ/examine(mob/user) ..(user) if(status & ORGAN_DEAD) - user << "The decay has set in." + to_chat(user, "The decay has set in.") //A little wonky: internal organs stop calling this (they return early in process) when dead, but external ones cause further damage when dead /obj/item/organ/proc/handle_germ_effects() @@ -398,7 +399,7 @@ var/list/organ_cache = list() if(robotic >= ORGAN_ROBOT) return - user << "You take an experimental bite out of \the [src]." + to_chat(user, "You take an experimental bite out of \the [src].") var/datum/reagent/blood/B = locate(/datum/reagent/blood) in reagents.reagent_list blood_splatter(src,B,1) diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index bd4317787a..6ca1c97486 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -151,7 +151,7 @@ for(var/obj/item/I in contents) if(istype(I, /obj/item/organ)) continue - usr << "There is \a [I] sticking out of it." + to_chat(usr, "There is \a [I] sticking out of it.") return /obj/item/organ/external/attackby(obj/item/weapon/W as obj, mob/living/user as mob) @@ -411,11 +411,11 @@ else return 0 if(!damage_amount) - user << "Nothing to fix!" + to_chat(user, "Nothing to fix!") return 0 if(brute_dam + burn_dam >= min_broken_damage) //VOREStation Edit - Makes robotic limb damage scalable - user << "The damage is far too severe to patch over externally." + to_chat(user, "The damage is far too severe to patch over externally.") return 0 if(user == src.owner) @@ -426,12 +426,12 @@ grasp = "r_hand" if(grasp) - user << "You can't reach your [src.name] while holding [tool] in your [owner.get_bodypart_name(grasp)]." + to_chat(user, "You can't reach your [src.name] while holding [tool] in your [owner.get_bodypart_name(grasp)].") return 0 user.setClickCooldown(user.get_attack_speed(tool)) if(!do_mob(user, owner, 10)) - user << "You must stand still to do that." + to_chat(user, "You must stand still to do that.") return 0 switch(damage_type) @@ -690,7 +690,7 @@ Note that amputating the affected organ does in fact remove the infection from t if(. >= 3 && antibiotics < ANTIBIO_OD) //INFECTION_LEVEL_THREE if (!(status & ORGAN_DEAD)) status |= ORGAN_DEAD - owner << "You can't feel your [name] anymore..." + to_chat(owner, "You can't feel your [name] anymore...") owner.update_icons_body() for (var/obj/item/organ/external/child in children) child.germ_level += 110 //Burst of infection from a parent organ becoming necrotic diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index 4445f1ed67..1a79b2b14a 100644 --- a/code/modules/organs/organ_icon.dm +++ b/code/modules/organs/organ_icon.dm @@ -18,7 +18,7 @@ var/global/list/limb_icon_cache = list() h_col = null if(robotic >= ORGAN_ROBOT) var/datum/robolimb/franchise = all_robolimbs[model] - if(!(franchise && franchise.skin_tone)) + if(!(franchise && franchise.skin_tone) && !(franchise && franchise.skin_color)) if(human.synth_color) s_col = list(human.r_synth, human.g_synth, human.b_synth) return @@ -36,7 +36,7 @@ var/global/list/limb_icon_cache = list() h_col = null if(robotic >= ORGAN_ROBOT) var/datum/robolimb/franchise = all_robolimbs[model] - if(!(franchise && franchise.skin_tone)) + if(!(franchise && franchise.skin_tone) && !(franchise && franchise.skin_color)) return if(!isnull(dna.GetUIValue(DNA_UI_SKIN_TONE)) && (species.appearance_flags & HAS_SKIN_TONE)) s_tone = dna.GetUIValue(DNA_UI_SKIN_TONE) diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm index 5f3980d608..df7d81a142 100644 --- a/code/modules/organs/pain.dm +++ b/code/modules/organs/pain.dm @@ -18,7 +18,7 @@ mob/living/carbon/proc/custom_pain(message, power, force) // Anti message spam checks if(force || (message != last_pain_message) || (world.time >= next_pain_time)) last_pain_message = message - src << message + to_chat(src,message) next_pain_time = world.time + (100-power) mob/living/carbon/human/proc/handle_pain() diff --git a/code/modules/organs/robolimbs.dm b/code/modules/organs/robolimbs.dm index a2643f04d0..a0ca24e997 100644 --- a/code/modules/organs/robolimbs.dm +++ b/code/modules/organs/robolimbs.dm @@ -33,6 +33,14 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ if(!R.unavailable_at_chargen) chargen_robolimbs[R.company] = R //List only main brands and solo parts. + for(var/company in all_robolimbs) + var/datum/robolimb/R = all_robolimbs[company] + if(R.species_alternates) + for(var/species in R.species_alternates) + var/species_company = R.species_alternates[species] + if(species_company in all_robolimbs) + R.species_alternates[species] = all_robolimbs[species_company] + /datum/robolimb var/company = "Unbranded" // Shown when selecting the limb. var/desc = "A generic unbranded robotic prosthesis." // Seen when examining a limb. @@ -40,9 +48,11 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ var/unavailable_at_chargen // If set, not available at chargen. var/unavailable_to_build // If set, can't be constructed. var/lifelike // If set, appears organic. - var/skin_tone // If set, applies skin tone rather than part color + var/skin_tone // If set, applies skin tone rather than part color Overrides color. + var/skin_color // If set, applies skin color rather than part color. var/blood_color = "#030303" var/list/species_cannot_use = list(SPECIES_TESHARI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_XENOCHIMERA) //VOREStation Edit + var/list/species_alternates = list(SPECIES_TAJ = "Unbranded - Tajaran", SPECIES_UNATHI = "Unbranded - Unathi") //"Species Name" = "Robolimb Company" , List, when initialized, will become "Species Name" = RobolimbDatum, used for alternate species sprites. var/list/monitor_styles //If empty, the model of limbs offers a head compatible with monitors. var/parts = BP_ALL //Defines what parts said brand can replace on a body. var/health_hud_intensity = 1 // Intensity modifier for the health GUI indicator. @@ -72,10 +82,62 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_alt2.dmi' unavailable_to_build = 1 +/datum/robolimb/unbranded_tajaran + company = "Unbranded - Tajaran" + species_cannot_use = list(SPECIES_TESHARI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_UNATHI, SPECIES_SKRELL, SPECIES_ZADDAT) + suggested_species = SPECIES_TAJ + desc = "A simple robotic limb with feline design. Seems rather stiff." + icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_tajaran.dmi' + unavailable_to_build = 1 + +/datum/robolimb/unbranded_unathi + company = "Unbranded - Unathi" + species_cannot_use = list(SPECIES_TESHARI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_ZADDAT) + suggested_species = SPECIES_UNATHI + desc = "A simple robotic limb with reptilian design. Seems rather stiff." + icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_unathi.dmi' + unavailable_to_build = 1 + +/datum/robolimb/unbranded_teshari + company = "Unbranded - Teshari" + species_cannot_use = list(SPECIES_UNATHI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_ZADDAT) + suggested_species = SPECIES_TESHARI + desc = "A simple robotic limb with a small, raptor-like design. Seems rather stiff." + icon = 'icons/mob/human_races/cyberlimbs/unbranded/unbranded_teshari.dmi' + unavailable_to_build = 0 + /datum/robolimb/nanotrasen company = "NanoTrasen" desc = "A simple but efficient robotic limb, created by NanoTrasen." icon = 'icons/mob/human_races/cyberlimbs/nanotrasen/nanotrasen_main.dmi' + species_alternates = list(SPECIES_TAJ = "NanoTrasen - Tajaran", SPECIES_UNATHI = "NanoTrasen - Unathi") + +/datum/robolimb/nanotrasen_tajaran + company = "NanoTrasen - Tajaran" + species_cannot_use = list(SPECIES_TESHARI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_UNATHI, SPECIES_SKRELL, SPECIES_ZADDAT) + species_alternates = list(SPECIES_HUMAN = "NanoTrasen") + suggested_species = SPECIES_TAJ + desc = "A simple but efficient robotic limb, created by NanoTrasen." + icon = 'icons/mob/human_races/cyberlimbs/nanotrasen/nanotrasen_tajaran.dmi' + unavailable_to_build = 1 + +/datum/robolimb/nanotrasen_unathi + company = "NanoTrasen - Unathi" + species_cannot_use = list(SPECIES_TESHARI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_ZADDAT) + species_alternates = list(SPECIES_HUMAN = "NanoTrasen") + suggested_species = SPECIES_UNATHI + desc = "A simple but efficient robotic limb, created by NanoTrasen." + icon = 'icons/mob/human_races/cyberlimbs/nanotrasen/nanotrasen_unathi.dmi' + unavailable_to_build = 1 + +/datum/robolimb/cenilimicybernetics_teshari + company = "Cenilimi Cybernetics" + species_cannot_use = list(SPECIES_UNATHI, SPECIES_PROMETHEAN, SPECIES_DIONA, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_TAJ, SPECIES_SKRELL, SPECIES_ZADDAT) + species_alternates = list(SPECIES_HUMAN = "NanoTrasen") + suggested_species = SPECIES_TESHARI + desc = "Made by a Teshari-owned company, for Teshari." + icon = 'icons/mob/human_races/cyberlimbs/cenilimicybernetics/cenilimicybernetics_teshari.dmi' + unavailable_to_build = 1 /datum/robolimb/bishop company = "Bishop" @@ -104,6 +166,19 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ parts = list(BP_HEAD) monitor_styles = standard_monitor_styles +/datum/robolimb/gestaltframe + company = "Skrellian Exoskeleton" + desc = "This limb looks to be more like a strange.. puppet, than a prosthetic." + icon = 'icons/mob/human_races/cyberlimbs/veymed/dionaea/skrellian.dmi' + blood_color = "#63b521" + speech_bubble_appearance = "machine" + unavailable_to_build = 1 + species_cannot_use = list(SPECIES_TESHARI, SPECIES_PROMETHEAN, SPECIES_TAJ, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_UNATHI, SPECIES_SKRELL, SPECIES_ZADDAT) + suggested_species = SPECIES_DIONA + // Dionaea are naturally very tanky, so the robotic limbs are actually far weaker than their normal bodies. + robo_brute_mod = 1.3 + robo_burn_mod = 1.3 + /datum/robolimb/cybersolutions company = "Cyber Solutions" desc = "This limb is grey and rough, with little in the way of aesthetic." @@ -224,11 +299,25 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ unavailable_to_build = 1 lifelike = 1 skin_tone = 1 + species_alternates = list(SPECIES_SKRELL = "Vey-Med - Skrell") blood_color = "#CCCCCC" speech_bubble_appearance = "normal" //robo_brute_mod = 1.1 //VOREStation Edit //robo_burn_mod = 1.1 //VOREStation Edit +/datum/robolimb/veymed_skrell + company = "Vey-Med - Skrell" + desc = "This high quality limb is nearly indistinguishable from an organic one." + icon = 'icons/mob/human_races/cyberlimbs/veymed/veymed_skrell.dmi' + unavailable_to_build = 1 + lifelike = 1 + skin_color = TRUE + species_cannot_use = list(SPECIES_TESHARI, SPECIES_PROMETHEAN, SPECIES_TAJ, SPECIES_HUMAN, SPECIES_VOX, SPECIES_HUMAN_VATBORN, SPECIES_UNATHI, SPECIES_DIONA, SPECIES_ZADDAT) + blood_color = "#4451cf" + speech_bubble_appearance = "normal" + robo_brute_mod = 1.05 + robo_burn_mod = 1.05 + /datum/robolimb/wardtakahashi company = "Ward-Takahashi" desc = "This limb features sleek black and white polymers." @@ -312,8 +401,8 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ /obj/item/weapon/disk/limb name = "Limb Blueprints" desc = "A disk containing the blueprints for prosthetics." - icon = 'icons/obj/cloning.dmi' - icon_state = "datadisk2" + icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit + icon_state = "data-white" //VOREStation Edit var/company = "" /obj/item/weapon/disk/limb/New(var/newloc) @@ -343,6 +432,10 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ company = "Vey-Med" catalogue_data = list(/datum/category_item/catalogue/information/organization/vey_med) +// Bus disk for Diona mech parts. +/obj/item/weapon/disk/limb/veymed/diona + company = "Skrellian Exoskeleton" + /obj/item/weapon/disk/limb/wardtakahashi company = "Ward-Takahashi" catalogue_data = list(/datum/category_item/catalogue/information/organization/ward_takahashi) @@ -358,3 +451,37 @@ var/const/standard_monitor_styles = "blank=ipc_blank;\ /obj/item/weapon/disk/limb/nanotrasen company = "NanoTrasen" catalogue_data = list(/datum/category_item/catalogue/information/organization/nanotrasen) + +/obj/item/weapon/disk/species + name = "Species Bioprints" + desc = "A disk containing the blueprints for species-specific prosthetics." + icon = 'icons/obj/cloning.dmi' + icon_state = "datadisk2" + var/species = SPECIES_HUMAN + +/obj/item/weapon/disk/species/Initialize() + ..() + if(species) + name = "[species] [initial(name)]" + +/obj/item/weapon/disk/species/skrell + species = SPECIES_SKRELL + +/obj/item/weapon/disk/species/unathi + species = SPECIES_UNATHI + +/obj/item/weapon/disk/species/tajaran + species = SPECIES_TAJ + +/obj/item/weapon/disk/species/teshari + species = SPECIES_TESHARI + +// In case of bus, presently. +/obj/item/weapon/disk/species/diona + species = SPECIES_DIONA + +/obj/item/weapon/disk/species/zaddat + species = SPECIES_ZADDAT + +/obj/item/weapon/disk/limb/cenilimicybernetics + company = "Cenilimi Cybernetics" \ No newline at end of file diff --git a/code/modules/organs/robolimbs_vr.dm b/code/modules/organs/robolimbs_vr.dm index 8f0de65d43..a5ab8994cf 100644 --- a/code/modules/organs/robolimbs_vr.dm +++ b/code/modules/organs/robolimbs_vr.dm @@ -4,7 +4,7 @@ var/list/whitelisted_to //List of ckeys that are allowed to pick this in charsetup. //////////////// For-specific-character fluff ones ///////////////// -// arokha : Aronai Kadigan +// arokha : Aronai Sieyes /datum/robolimb/kitsuhana company = "Kitsuhana" desc = "This limb seems rather vulpine and fuzzy, with realistic-feeling flesh." diff --git a/code/modules/organs/subtypes/diona.dm b/code/modules/organs/subtypes/diona.dm index c0e4e60c98..7d43baf757 100644 --- a/code/modules/organs/subtypes/diona.dm +++ b/code/modules/organs/subtypes/diona.dm @@ -205,6 +205,37 @@ /obj/item/organ/internal/diona/node/removed() return +// A 'brain' for the tree, still becomes a mindless nymph when removed like any other. Satisfies the FBP code. +/obj/item/organ/internal/brain/cephalon + name = "cephalon mass" + parent_organ = BP_TORSO + vital = TRUE + +/obj/item/organ/internal/brain/cephalon/Initialize() + ..() + spawn(30 SECONDS) // FBP Dionaea need some way to be disassembled through surgery, if absolutely necessary. + if(!owner.isSynthetic()) + vital = FALSE + +/obj/item/organ/internal/brain/cephalon/robotize() + return + +/obj/item/organ/internal/brain/cephalon/mechassist() + return + +/obj/item/organ/internal/brain/cephalon/digitize() + return + +/obj/item/organ/internal/brain/cephalon/removed(var/mob/living/user, var/skip_nymph) + if(robotic >= ORGAN_ROBOT) + return ..() + var/mob/living/carbon/human/H = owner + ..() + if(!istype(H) || !H.organs || !H.organs.len) + H.death() + if(prob(50) && !skip_nymph && spawn_diona_nymph(get_turf(src))) + qdel(src) + /obj/item/organ/external/head/no_eyes/diona max_damage = 50 min_broken_damage = 25 diff --git a/code/modules/organs/subtypes/machine.dm b/code/modules/organs/subtypes/machine.dm index 0397a4e172..0e3a60fdd5 100644 --- a/code/modules/organs/subtypes/machine.dm +++ b/code/modules/organs/subtypes/machine.dm @@ -14,7 +14,7 @@ ..() // This is very ghetto way of rebooting an IPC. TODO better way. if(owner && owner.stat == DEAD) - owner.stat = 0 + owner.set_stat(CONSCIOUS) owner.visible_message("\The [owner] twitches visibly!") /obj/item/organ/internal/cell/emp_act(severity) @@ -68,7 +68,7 @@ stored_mmi.brainmob.languages = owner.languages if(owner && owner.stat == DEAD) - owner.stat = 0 + owner.set_stat(CONSCIOUS) dead_mob_list -= owner living_mob_list |= owner owner.visible_message("\The [owner] twitches visibly!") diff --git a/code/modules/organs/subtypes/nano.dm b/code/modules/organs/subtypes/nano.dm index b8fb26cd64..de89fef1d5 100644 --- a/code/modules/organs/subtypes/nano.dm +++ b/code/modules/organs/subtypes/nano.dm @@ -14,55 +14,55 @@ /obj/item/organ/external/head/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 30 min_broken_damage = 1000 //Inheritance vital = FALSE /obj/item/organ/external/arm/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 20 min_broken_damage = 1000 //Please vital = FALSE /obj/item/organ/external/arm/right/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 20 min_broken_damage = 1000 vital = FALSE /obj/item/organ/external/leg/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 20 min_broken_damage = 1000 vital = FALSE /obj/item/organ/external/leg/right/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 20 min_broken_damage = 1000 vital = FALSE /obj/item/organ/external/hand/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 20 min_broken_damage = 1000 vital = FALSE /obj/item/organ/external/hand/right/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 20 min_broken_damage = 1000 vital = FALSE /obj/item/organ/external/foot/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 20 min_broken_damage = 1000 vital = FALSE /obj/item/organ/external/foot/right/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 10 + max_damage = 20 min_broken_damage = 1000 vital = FALSE diff --git a/code/modules/overmap/README.dm b/code/modules/overmap/README.dm index 14a7a955a5..ac9fc8890e 100644 --- a/code/modules/overmap/README.dm +++ b/code/modules/overmap/README.dm @@ -53,6 +53,12 @@ Changes desitnation area depending on current sector ship is in. Currently updating is called in attack_hand(), until a better place is found. Currently no modifications were made to interface to display availability of landing area in sector. +************************************************************* +Landable Ships +************************************************************* +Ship - Vessel that can move around on the overmap. It's entire z-level(s) "move" conceptually. +Shuttles - Vessel that can jump to shuttle landmarks. Its areas move by transition_turfs. +Landable Ship - Vessel that can do both. Sits at a special shuttle landmark for overmap movement mode. ************************************************************* Guide to how make new sector diff --git a/code/modules/overmap/_defines.dm b/code/modules/overmap/_defines.dm deleted file mode 100644 index 6d17d8e398..0000000000 --- a/code/modules/overmap/_defines.dm +++ /dev/null @@ -1,101 +0,0 @@ -//Zlevel where overmap objects should be -#define OVERMAP_ZLEVEL 1 -//How far from the edge of overmap zlevel could randomly placed objects spawn -#define OVERMAP_EDGE 7 - -//list used to track which zlevels are being 'moved' by the proc below -var/list/moving_levels = list() -//Proc to 'move' stars in spess -//yes it looks ugly, but it should only fire when state actually change. -//null direction stops movement -proc/toggle_move_stars(zlevel, direction) - if(!zlevel) - return - - var/gen_dir = null - if(direction & (NORTH|SOUTH)) - gen_dir += "ns" - else if(direction & (EAST|WEST)) - gen_dir += "ew" - if(!direction) - gen_dir = null - - if (moving_levels["zlevel"] != gen_dir) - moving_levels["zlevel"] = gen_dir - for(var/x = 1 to world.maxx) - for(var/y = 1 to world.maxy) - var/turf/space/T = locate(x,y,zlevel) - if (istype(T)) - if(!gen_dir) - T.icon_state = "[((T.x + T.y) ^ ~(T.x * T.y) + T.z) % 25]" - else - T.icon_state = "speedspace_[gen_dir]_[rand(1,15)]" - for(var/atom/movable/AM in T) - if (!AM.anchored) - AM.throw_at(get_step(T,reverse_direction(direction)), 5, 1) - - -//list used to cache empty zlevels to avoid nedless map bloat -var/list/cached_space = list() - -proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A) - var/obj/effect/map/M = map_sectors["[T.z]"] - if (!M) - return - var/mapx = M.x - var/mapy = M.y - var/nx = 1 - var/ny = 1 - var/nz = M.map_z - - if(T.x <= TRANSITIONEDGE) - nx = world.maxx - TRANSITIONEDGE - 2 - ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - mapx = max(1, mapx-1) - - else if (A.x >= (world.maxx - TRANSITIONEDGE - 1)) - nx = TRANSITIONEDGE + 2 - ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - mapx = min(world.maxx, mapx+1) - - else if (T.y <= TRANSITIONEDGE) - ny = world.maxy - TRANSITIONEDGE -2 - nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - mapy = max(1, mapy-1) - - else if (A.y >= (world.maxy - TRANSITIONEDGE - 1)) - ny = TRANSITIONEDGE + 2 - nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - mapy = min(world.maxy, mapy+1) - - testing("[A] moving from [M] ([M.x], [M.y]) to ([mapx],[mapy]).") - - var/turf/map = locate(mapx,mapy,OVERMAP_ZLEVEL) - var/obj/effect/map/TM = locate() in map - if(TM) - nz = TM.map_z - testing("Destination: [TM]") - else - if(cached_space.len) - var/obj/effect/map/sector/temporary/cache = cached_space[cached_space.len] - cached_space -= cache - nz = cache.map_z - cache.x = mapx - cache.y = mapy - testing("Destination: *cached* [TM]") - else - world.maxz++ - nz = world.maxz - TM = new /obj/effect/map/sector/temporary(mapx, mapy, nz) - testing("Destination: *new* [TM]") - - var/turf/dest = locate(nx,ny,nz) - if(dest) - A.loc = dest - - if(istype(M, /obj/effect/map/sector/temporary)) - var/obj/effect/map/sector/temporary/source = M - if (source.can_die()) - testing("Catching [M] for future use") - source.loc = null - cached_space += source diff --git a/code/modules/overmap/bluespace_rift_vr.dm b/code/modules/overmap/bluespace_rift_vr.dm new file mode 100644 index 0000000000..198ccc2277 --- /dev/null +++ b/code/modules/overmap/bluespace_rift_vr.dm @@ -0,0 +1,44 @@ +/obj/effect/overmap/bluespace_rift + name = "bluespace rift" + desc = "Some sort of bluespace rift. Who knows where it leads?" + icon = 'icons/obj/overmap_vr.dmi' + icon_state = "portal" + color = "#2288FF" + + known = 0 //shows up on nav computers automatically + scannable = TRUE //if set to TRUE will show up on ship sensors for detailed scans + + var/obj/effect/overmap/bluespace_rift/partner + var/paused + +/obj/effect/overmap/bluespace_rift/Initialize(var/mapload, var/new_partner) + . = ..() + if(new_partner) + pair(new_partner) + +/obj/effect/overmap/bluespace_rift/proc/pair(var/obj/effect/overmap/bluespace_rift/new_partner) + if(istype(new_partner)) + partner = new_partner + new_partner.partner = src + +/obj/effect/overmap/bluespace_rift/proc/take_this(var/atom/movable/AM) + paused = TRUE + AM.forceMove(get_turf(src)) + paused = FALSE + +/obj/effect/overmap/bluespace_rift/Crossed(var/atom/movable/AM) + if(istype(AM, /obj/effect/overmap/visitable/ship) && !paused && partner) + partner.take_this(AM) + else + return ..() + +/obj/effect/overmap/bluespace_rift/attack_ghost(var/mob/observer/dead/user) + if(!partner && user?.client?.holder) + var/response = alert(user, "You appear to be staff. This rift has no exit point. If you want to make one, move to where you want it to go, and click 'Make Here', otherwise click 'Cancel'","Rift Exit","Cancel","Make Here") + if(response == "Make Here") + new type(get_turf(user), src) + else if(partner) + user.forceMove(get_turf(partner)) + to_chat(user, "Your ghostly form is pulled through the rift!") + else + return ..() diff --git a/code/modules/overmap/champagne.dm b/code/modules/overmap/champagne.dm new file mode 100644 index 0000000000..463f18eb2b --- /dev/null +++ b/code/modules/overmap/champagne.dm @@ -0,0 +1,115 @@ +// +// Champagne bottle for creating new landable ship shuttles in game from scratch. +// Note: It depends on the area being sane. In theory players could use this to make pretty much any room a shuttle. +// +/obj/item/champagne + name = "bottle of champagne" + desc = "Made from grapes grown in the champagne asteroid belt, the bubbly liquid inside is softly glowing. Suitable for christening boats, not so much for drinking." + description_info = "This lets you create a new overmap shuttle from scratch. \ + The shuttle must already be constructed and have an area defined (use the blueprints). \ + Only a single area is supported (for now). Build a shuttle console and hit it with the bottle." + icon = 'icons/obj/drinks.dmi' + icon_state = "champagne" + attack_verb = list("attacked", "bonked", "hit") + var/min_name_len = 4 // Refuse if shuttle tag is shorter than this. + var/max_name_len = 32 // Refuse if shuttle tag is longer than this. + var/max_area_turfs = 256 // Refuse if area has more than this many turfs. + +/obj/item/champagne/afterattack(var/atom/A, mob/user as mob, proximity) + if(!proximity) + return + var/obj/machinery/computer/shuttle_control/explore/comp = A + if(!istype(comp)) + return + + if(comp.shuttle_tag) + to_chat(user, "[comp] is already configured to link with [comp.shuttle_tag]") + return + + user.visible_message("[user] lifts [src] bottle over [comp]!") + var/shuttle_name = input("Choose a name for the shuttle", "New Shuttle Name") as null|text + if(!shuttle_name || QDELETED(src) || QDELETED(comp) || comp.shuttle_tag || user.incapacitated()) + return // After input() safety re-checks + + // Tons of safety checks here. Make sure they don't destroy everything. + if(length(shuttle_name) < min_name_len || length(shuttle_name) > max_name_len) + to_chat(user, "Name length must be between [min_name_len] and [max_name_len].") + return + if(shuttle_name in SSshuttles.shuttles) + to_chat(user, "Invalid name: Already in use.") + return + + var/area/my_area = get_area(comp) + if(!my_area || istype(my_area, /area/space)) + to_chat(user, "[comp] must be in a valid area to become a shuttle.") + return + if(my_area in SSshuttles.shuttle_areas) + to_chat(user, "[comp] is already in a shuttle.") + return + // Count turfs in the area + var/list/turfs = get_area_turfs(my_area) + if(turfs.len > max_area_turfs) + to_chat(user, "The new shuttle area is too large.") + return + + var/turf/comp_turf = get_turf(comp) + var/datum/shuttle/autodock/S = create_landable_shuttle(shuttle_name, comp_turf, my_area) + playsound(src, 'sound/effects/Glassbr3.ogg', 100, 0) + user.visible_message("[user] smashes [src] on [comp]", + "You smash [src] on [comp], christening a new landable ship named [S.name]", + "You hear glass shattering") + log_and_message_admins("[key_name_admin(user)] Created a new shuttle [S.name]. [ADMIN_JMP(comp_turf)]") + spawn(1 SECOND) + playsound(comp_turf, 'sound/voice/Serithi/Shuttlehere.ogg', 75, 0) + qdel(src) + + return TRUE + +/obj/item/champagne/proc/create_landable_shuttle(var/shuttle_name, var/turf/start_loc, var/area/shuttle_area) + // First things first, create the starting location landmark. + // WARNING - We can't figure out a good base_area or base_turf from inspecttion, as the shuttle is already built! + // For now its going to just do world.area and z level base turf. Beware! + var/area/base_area = world.area + var/base_turf = get_base_turf(get_z(start_loc)) + var/obj/effect/shuttle_landmark/automatic/champagne/starting_landmark = new(start_loc, base_area, base_turf) + + // Okay first things first create the shuttle Override to no areas to prevent runtimes, then add them in. + var/datum/shuttle/autodock/overmap/shuttle = new /datum/shuttle/autodock/overmap{shuttle_area = list()}(shuttle_name, starting_landmark) + LAZYADD(shuttle.shuttle_area, shuttle_area) + shuttle.refresh_fuel_ports_list() // We added area, refresh + shuttle.set_docking_codes(null) // It doesn't get to magically know docking codes. + + // Unfortunately we can't simply call initialize_shuttle because it takes a typepath. + // I Don't want to rewrite that right now, so we must emulate. This is a special case anyway. + // Emulation of SSshuttles.initialize_shuttle(): All it does is add to shuttle areas and populate objects + SSshuttles.shuttle_areas |= shuttle.shuttle_area + shuttle.populate_shuttle_objects() + + // Okay now create the SHIP part + var/obj/effect/overmap/visitable/ship/landable/champagne/newship = new(start_loc, shuttle_name, shuttle_name) + // It automatically registers itself with SSshuttles + + log_debug("Created new ship [newship] for shuttle [shuttle] at landmark [starting_landmark]") + return shuttle + +// +// Shuttle landmark dynamically generated at runtime by champagne bottle. +// +/obj/effect/shuttle_landmark/automatic/champagne + flags = SLANDMARK_FLAG_ZERO_G // Don't auto-set + +/obj/effect/shuttle_landmark/automatic/champagne/Initialize(mapload, base_area, base_turf) + src.base_turf = base_turf + src.base_area = base_area + . = ..() + +// +// Landable ship dynamically created at runtime by champagne bottle. +// +/obj/effect/overmap/visitable/ship/landable/champagne + desc = "Newly minted space faring vessel." + +/obj/effect/overmap/visitable/ship/landable/champagne/Initialize(mapload, _name, _shuttle) + src.name = _name + src.shuttle = _shuttle + . = ..() diff --git a/code/modules/overmap/events/event_handler.dm b/code/modules/overmap/events/event_handler.dm new file mode 100644 index 0000000000..67785e95ac --- /dev/null +++ b/code/modules/overmap/events/event_handler.dm @@ -0,0 +1,163 @@ +GLOBAL_DATUM_INIT(overmap_event_handler, /decl/overmap_event_handler, new) + +/decl/overmap_event_handler + var/list/hazard_by_turf + var/list/ship_events + +/decl/overmap_event_handler/New() + ..() + hazard_by_turf = list() + ship_events = list() + +// Populates overmap with random events! Should be called once at startup at some point. +/decl/overmap_event_handler/proc/create_events(var/z_level, var/overmap_size, var/number_of_events) + // Acquire the list of not-yet utilized overmap turfs on this Z-level + var/list/overmap_turfs = block(locate(OVERMAP_EDGE, OVERMAP_EDGE, z_level), locate(overmap_size - OVERMAP_EDGE, overmap_size - OVERMAP_EDGE, z_level)) + var/list/candidate_turfs = list() + for(var/Trf in overmap_turfs) + var/turf/T = Trf + if(!(locate(/obj/effect/overmap/visitable) in T)) + candidate_turfs += T + + for(var/i = 1 to number_of_events) + if(!candidate_turfs.len) + break + var/overmap_event_type = pick(subtypesof(/datum/overmap_event)) + var/datum/overmap_event/datum_spawn = new overmap_event_type + log_debug("Generating cloud of [datum_spawn.count] [datum_spawn] overmap event hazards") + + var/list/event_turfs = acquire_event_turfs(datum_spawn.count, datum_spawn.radius, candidate_turfs, datum_spawn.continuous) + candidate_turfs -= event_turfs + + for(var/event_turf in event_turfs) + var/type = pick(datum_spawn.hazards) + new type(event_turf) + + qdel(datum_spawn)//idk help how do I do this better? + +/decl/overmap_event_handler/proc/acquire_event_turfs(var/number_of_turfs, var/distance_from_origin, var/list/candidate_turfs, var/continuous = TRUE) + number_of_turfs = min(number_of_turfs, candidate_turfs.len) + candidate_turfs = candidate_turfs.Copy() // Not this proc's responsibility to adjust the given lists + + var/origin_turf = pick(candidate_turfs) + var/list/selected_turfs = list(origin_turf) + var/list/selection_turfs = list(origin_turf) + candidate_turfs -= origin_turf + + while(selection_turfs.len && selected_turfs.len < number_of_turfs) + var/selection_turf = pick(selection_turfs) + var/random_neighbour = get_random_neighbour(selection_turf, candidate_turfs, continuous, distance_from_origin) + + if(random_neighbour) + candidate_turfs -= random_neighbour + selected_turfs += random_neighbour + if(get_dist(origin_turf, random_neighbour) < distance_from_origin) + selection_turfs += random_neighbour + else + selection_turfs -= selection_turf + + return selected_turfs + +/decl/overmap_event_handler/proc/get_random_neighbour(var/turf/origin_turf, var/list/candidate_turfs, var/continuous = TRUE, var/range) + var/fitting_turfs + if(continuous) + fitting_turfs = origin_turf.CardinalTurfs(FALSE) + else + fitting_turfs = trange(range, origin_turf) + fitting_turfs = shuffle(fitting_turfs) + for(var/turf/T in fitting_turfs) + if(T in candidate_turfs) + return T + +/decl/overmap_event_handler/proc/start_hazard(var/obj/effect/overmap/visitable/ship/ship, var/obj/effect/overmap/event/hazard)//make these accept both hazards or events + if(!(ship in ship_events)) + ship_events += ship + + for(var/event_type in hazard.events) + if(is_event_active(ship, event_type, hazard.difficulty))//event's already active, don't bother + continue + var/datum/event_meta/EM = new(hazard.difficulty, "Overmap event - [hazard.name]", event_type, add_to_queue = FALSE, is_one_shot = TRUE) + var/datum/event/E = new event_type(EM) + E.startWhen = 0 + E.endWhen = INFINITY + // TODO - Leshana - Note: event.setup() is called before these are set! + E.affecting_z = ship.map_z + E.victim = ship + LAZYADD(ship_events[ship], E) + +/decl/overmap_event_handler/proc/stop_hazard(var/obj/effect/overmap/visitable/ship/ship, var/obj/effect/overmap/event/hazard) + for(var/event_type in hazard.events) + var/datum/event/E = is_event_active(ship, event_type, hazard.difficulty) + if(E) + E.kill() + LAZYREMOVE(ship_events[ship], E) + +/decl/overmap_event_handler/proc/is_event_active(var/ship, var/event_type, var/severity) + if(!ship_events[ship]) return + for(var/datum/event/E in ship_events[ship]) + if(E.type == event_type && E.severity == severity) + return E + +/decl/overmap_event_handler/proc/on_turf_entered(var/turf/new_loc, var/obj/effect/overmap/visitable/ship/ship, var/old_loc) + if(!istype(ship)) + return + if(new_loc == old_loc) + return + + for(var/obj/effect/overmap/event/E in hazard_by_turf[new_loc]) + start_hazard(ship, E) + +/decl/overmap_event_handler/proc/on_turf_exited(var/turf/old_loc, var/obj/effect/overmap/visitable/ship/ship, var/new_loc) + if(!istype(ship)) + return + if(new_loc == old_loc) + return + + for(var/obj/effect/overmap/event/E in hazard_by_turf[old_loc]) + if(is_event_included(hazard_by_turf[new_loc], E)) + continue // If new turf has the same event as well... keep it going! + stop_hazard(ship, E) + +/decl/overmap_event_handler/proc/update_hazards(var/turf/T)//catch all updater + if(!istype(T)) + return + + var/list/active_hazards = list() + for(var/obj/effect/overmap/event/E in T) + if(is_event_included(active_hazards, E, TRUE)) + continue + active_hazards += E + + if(!active_hazards.len) + hazard_by_turf -= T + else + hazard_by_turf |= T + hazard_by_turf[T] = active_hazards + + for(var/obj/effect/overmap/visitable/ship/ship in T) + for(var/datum/event/E in ship_events[ship]) + if(is_event_in_turf(E, T)) + continue + E.kill() + LAZYREMOVE(ship_events[ship], E) + + for(var/obj/effect/overmap/event/E in active_hazards) + start_hazard(ship, E) + +/decl/overmap_event_handler/proc/is_event_in_turf(var/datum/event/E, var/turf/T) + for(var/obj/effect/overmap/event/hazard in hazard_by_turf[T]) + if(E in hazard.events && E.severity == hazard.difficulty) + return TRUE + +/decl/overmap_event_handler/proc/is_event_included(var/list/hazards, var/obj/effect/overmap/event/E, var/equal_or_better)//this proc is only used so it can break out of 2 loops cleanly + for(var/obj/effect/overmap/event/A in hazards) + if(istype(A, E.type) || istype(E, A.type)) + if(same_entries(A.events, E.events)) + if(equal_or_better) + if(A.difficulty >= E.difficulty) + return TRUE + else + hazards -= A // TODO - Improve this SPAGHETTI CODE! Done only when called from update_hazards. ~Leshana + else + if(A.difficulty == E.difficulty) + return TRUE diff --git a/code/modules/overmap/events/generation.dm b/code/modules/overmap/events/generation.dm new file mode 100644 index 0000000000..9b70308866 --- /dev/null +++ b/code/modules/overmap/events/generation.dm @@ -0,0 +1,49 @@ +/* +** /datum/overmap_event - Descriptors of how/what to spawn during overmap event generation +*/ + +//These now are basically only used to spawn hazards. Will be useful when we need to spawn group of moving hazards +/datum/overmap_event + var/name = "map event" + var/radius = 2 // Radius of the spawn circle around chosen epicenter + var/count = 6 // How many hazards to spawn + var/hazards // List (or single) typepath of hazard to spawn + var/continuous = TRUE //if it should form continous blob, or can have gaps + +/datum/overmap_event/meteor + name = "asteroid field" + count = 15 + radius = 4 + continuous = FALSE + hazards = /obj/effect/overmap/event/meteor + +/datum/overmap_event/electric + name = "electrical storm" + count = 11 + radius = 3 + hazards = /obj/effect/overmap/event/electric + +/datum/overmap_event/dust + name = "dust cloud" + count = 16 + radius = 4 + hazards = /obj/effect/overmap/event/dust + +/datum/overmap_event/ion + name = "ion cloud" + count = 8 + radius = 3 + hazards = /obj/effect/overmap/event/ion + +/datum/overmap_event/carp + name = "carp shoal" + count = 8 + radius = 3 + continuous = FALSE + hazards = /obj/effect/overmap/event/carp + +/datum/overmap_event/carp/major + name = "carp school" + count = 5 + radius = 4 + hazards = /obj/effect/overmap/event/carp/major \ No newline at end of file diff --git a/code/modules/overmap/events/overmap_event.dm b/code/modules/overmap/events/overmap_event.dm new file mode 100644 index 0000000000..9add25a5c1 --- /dev/null +++ b/code/modules/overmap/events/overmap_event.dm @@ -0,0 +1,85 @@ +/* +** /obj/effect/overmap/event - Actual instances of event hazards on the overmap map +*/ + +// We don't subtype /obj/effect/overmap/visitable because that'll create sections one can travel to +// And with them "existing" on the overmap Z-level things quickly get odd. +/obj/effect/overmap/event + name = "event" + icon = 'icons/obj/overmap.dmi' + icon_state = "event" + opacity = 1 + var/list/events // List of event datum paths + var/list/event_icon_states // Randomly picked from + var/difficulty = EVENT_LEVEL_MODERATE + var/weaknesses //if the BSA can destroy them and with what + +/obj/effect/overmap/event/Initialize() + . = ..() + icon_state = pick(event_icon_states) + GLOB.overmap_event_handler.update_hazards(loc) + +/obj/effect/overmap/event/Move() + var/turf/old_loc = loc + . = ..() + if(.) + GLOB.overmap_event_handler.update_hazards(old_loc) + GLOB.overmap_event_handler.update_hazards(loc) + +/obj/effect/overmap/event/forceMove(atom/destination) + var/old_loc = loc + . = ..() + if(.) + GLOB.overmap_event_handler.update_hazards(old_loc) + GLOB.overmap_event_handler.update_hazards(loc) + +/obj/effect/overmap/event/Destroy()//takes a look at this one as well, make sure everything is A-OK + var/turf/T = loc + . = ..() + GLOB.overmap_event_handler.update_hazards(T) + +// +// Definitions for specific types! +// + +/obj/effect/overmap/event/meteor + name = "asteroid field" + events = list(/datum/event/meteor_wave/overmap) + event_icon_states = list("meteor1", "meteor2", "meteor3", "meteor4") + difficulty = EVENT_LEVEL_MAJOR + weaknesses = OVERMAP_WEAKNESS_MINING | OVERMAP_WEAKNESS_EXPLOSIVE + +/obj/effect/overmap/event/electric + name = "electrical storm" + events = list(/datum/event/electrical_storm/overmap) + opacity = 0 + event_icon_states = list("electrical1", "electrical2", "electrical3", "electrical4") + difficulty = EVENT_LEVEL_MAJOR + weaknesses = OVERMAP_WEAKNESS_EMP + +/obj/effect/overmap/event/dust + name = "dust cloud" + events = list(/datum/event/dust/overmap) + event_icon_states = list("dust1", "dust2", "dust3", "dust4") + weaknesses = OVERMAP_WEAKNESS_MINING | OVERMAP_WEAKNESS_EXPLOSIVE | OVERMAP_WEAKNESS_FIRE + +/obj/effect/overmap/event/ion + name = "ion cloud" + events = list(/datum/event/ionstorm/overmap) + opacity = 0 + event_icon_states = list("ion1", "ion2", "ion3", "ion4") + difficulty = EVENT_LEVEL_MAJOR + weaknesses = OVERMAP_WEAKNESS_EMP + +/obj/effect/overmap/event/carp + name = "carp shoal" + events = list(/datum/event/carp_migration/overmap) + opacity = 0 + difficulty = EVENT_LEVEL_MODERATE + event_icon_states = list("carp1", "carp2") + weaknesses = OVERMAP_WEAKNESS_EXPLOSIVE | OVERMAP_WEAKNESS_FIRE + +/obj/effect/overmap/event/carp/major + name = "carp school" + difficulty = EVENT_LEVEL_MAJOR + event_icon_states = list("carp3", "carp4") diff --git a/code/modules/overmap/helpers.dm b/code/modules/overmap/helpers.dm new file mode 100644 index 0000000000..e07be07ac0 --- /dev/null +++ b/code/modules/overmap/helpers.dm @@ -0,0 +1,5 @@ +/proc/get_overmap_sector(var/z) + if(using_map.use_overmap) + return map_sectors["[z]"] + else + return null diff --git a/code/modules/overmap/overmap_object.dm b/code/modules/overmap/overmap_object.dm new file mode 100644 index 0000000000..fd2798e7d6 --- /dev/null +++ b/code/modules/overmap/overmap_object.dm @@ -0,0 +1,36 @@ +/obj/effect/overmap + name = "map object" + icon = 'icons/obj/overmap.dmi' + icon_state = "object" + + var/known = 1 //shows up on nav computers automatically + var/scannable //if set to TRUE will show up on ship sensors for detailed scans + +//Overlay of how this object should look on other skyboxes +/obj/effect/overmap/proc/get_skybox_representation() + return + +/obj/effect/overmap/proc/get_scan_data(mob/user) + return desc + +/obj/effect/overmap/Initialize() + . = ..() + if(!global.using_map.use_overmap) + return INITIALIZE_HINT_QDEL + + if(known) + //layer = ABOVE_LIGHTING_LAYER + plane = PLANE_LIGHTING_ABOVE + for(var/obj/machinery/computer/ship/helm/H in global.machines) + H.get_known_sectors() + +/obj/effect/overmap/Crossed(var/obj/effect/overmap/visitable/other) + if(istype(other)) + for(var/obj/effect/overmap/visitable/O in loc) + SSskybox.rebuild_skyboxes(O.map_z) + +/obj/effect/overmap/Uncrossed(var/obj/effect/overmap/visitable/other) + if(istype(other)) + SSskybox.rebuild_skyboxes(other.map_z) + for(var/obj/effect/overmap/visitable/O in loc) + SSskybox.rebuild_skyboxes(O.map_z) diff --git a/code/modules/overmap/overmap_shuttle.dm b/code/modules/overmap/overmap_shuttle.dm new file mode 100644 index 0000000000..b614137db4 --- /dev/null +++ b/code/modules/overmap/overmap_shuttle.dm @@ -0,0 +1,180 @@ +#define waypoint_sector(waypoint) get_overmap_sector(get_z(waypoint)) + +/datum/shuttle/autodock/overmap + warmup_time = 10 + + var/range = 0 //how many overmap tiles can shuttle go, for picking destinations and returning. + var/fuel_consumption = 0 //Amount of moles of gas consumed per trip; If zero, then shuttle is magic and does not need fuel + var/list/obj/structure/fuel_port/fuel_ports //the fuel ports of the shuttle (but usually just one) + var/obj/effect/overmap/visitable/ship/landable/myship //my overmap ship object + + category = /datum/shuttle/autodock/overmap + var/skill_needed = SKILL_BASIC + var/operator_skill = SKILL_BASIC + +/datum/shuttle/autodock/overmap/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint) + ..(_name, start_waypoint) + refresh_fuel_ports_list() + +/datum/shuttle/autodock/overmap/Destroy() + . = ..() + myship = null + +/datum/shuttle/autodock/overmap/proc/refresh_fuel_ports_list() //loop through all + fuel_ports = list() + for(var/area/A in shuttle_area) + for(var/obj/structure/fuel_port/fuel_port_in_area in A) + fuel_port_in_area.parent_shuttle = src + fuel_ports += fuel_port_in_area + +/datum/shuttle/autodock/overmap/fuel_check() + if(!src.try_consume_fuel()) //insufficient fuel + for(var/area/A in shuttle_area) + for(var/mob/living/M in A) + M.show_message("You hear the shuttle engines sputter... perhaps it doesn't have enough fuel?", 1, + "The shuttle shakes but fails to take off.", 2) + return 0 //failure! + return 1 //sucess, continue with launch + +/datum/shuttle/autodock/overmap/proc/can_go() + if(!next_location) + return FALSE + if(moving_status == SHUTTLE_INTRANSIT) + return FALSE //already going somewhere, current_location may be an intransit location instead of in a sector + var/our_sector = waypoint_sector(current_location) + if(!our_sector && myship?.landmark && next_location == myship.landmark) + return TRUE //We're not on the overmap yet (admin spawned probably), and we're trying to hook up with our openspace sector + return get_dist(our_sector, waypoint_sector(next_location)) <= range + +/datum/shuttle/autodock/overmap/can_launch() + return ..() && can_go() + +/datum/shuttle/autodock/overmap/can_force() + return ..() && can_go() + +/datum/shuttle/autodock/overmap/get_travel_time() + var/distance_mod = get_dist(waypoint_sector(current_location),waypoint_sector(next_location)) + var/skill_mod = 0.2*(skill_needed - operator_skill) + return move_time * (1 + distance_mod + skill_mod) + +/datum/shuttle/autodock/overmap/process_launch() + if(prob(10*max(0, skill_needed - operator_skill))) + var/places = get_possible_destinations() + var/place = pick(places) + set_destination(places[place]) + ..() + +/datum/shuttle/autodock/overmap/proc/set_destination(var/obj/effect/shuttle_landmark/A) + if(A != current_location) + next_location = A + +/datum/shuttle/autodock/overmap/proc/get_possible_destinations() + var/list/res = list() + var/our_sector = waypoint_sector(current_location) + if(!our_sector && myship?.landmark) + res["Perform Test Jump"] = myship.landmark + return res //We're not on the overmap, maybe an admin spawned us on a non-sector map. We're broken until we connect to our space z-level. + for (var/obj/effect/overmap/visitable/S in range(get_turf(our_sector), range)) + var/list/waypoints = S.get_waypoints(name) + for(var/obj/effect/shuttle_landmark/LZ in waypoints) + if(LZ.is_valid(src)) + res["[waypoints[LZ]] - [LZ.name]"] = LZ + return res + +/datum/shuttle/autodock/overmap/get_location_name() + if(moving_status == SHUTTLE_INTRANSIT) + return "In transit" + return "[waypoint_sector(current_location)] - [current_location]" + +/datum/shuttle/autodock/overmap/get_destination_name() + if(!next_location) + return "None" + return "[waypoint_sector(next_location)] - [next_location]" + +/datum/shuttle/autodock/overmap/proc/try_consume_fuel() //returns 1 if sucessful, returns 0 if error (like insufficient fuel) + if(!fuel_consumption) + return 1 //shuttles with zero fuel consumption are magic and can always launch + if(!fuel_ports.len) + return 0 //Nowhere to get fuel from + var/list/obj/item/weapon/tank/fuel_tanks = list() + for(var/obj/structure/FP in fuel_ports) //loop through fuel ports and assemble list of all fuel tanks + var/obj/item/weapon/tank/FT = locate() in FP + if(FT) + fuel_tanks += FT + if(!fuel_tanks.len) + return 0 //can't launch if you have no fuel TANKS in the ports + var/total_flammable_gas_moles = 0 + for(var/obj/item/weapon/tank/FT in fuel_tanks) + total_flammable_gas_moles += FT.air_contents.get_by_flag(XGM_GAS_FUEL) + if(total_flammable_gas_moles < fuel_consumption) //not enough fuel + return 0 + // We are going to succeed if we got to here, so start consuming that fuel + var/fuel_to_consume = fuel_consumption + for(var/obj/item/weapon/tank/FT in fuel_tanks) //loop through tanks, consume their fuel one by one + var/fuel_available = FT.air_contents.get_by_flag(XGM_GAS_FUEL) + if(!fuel_available) // Didn't even have fuel. + continue + if(fuel_available >= fuel_to_consume) + FT.remove_air_by_flag(XGM_GAS_FUEL, fuel_to_consume) + return 1 //ALL REQUIRED FUEL HAS BEEN CONSUMED, GO FOR LAUNCH! + else //this tank doesn't have enough to launch shuttle by itself, so remove all its fuel, then continue loop + fuel_to_consume -= fuel_available + FT.remove_air_by_flag(XGM_GAS_FUEL, fuel_available) + +/obj/structure/fuel_port + name = "fuel port" + desc = "The fuel input port of the shuttle. Holds one fuel tank. Use a crowbar to open and close it." + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "fuel_port" + density = 0 + anchored = 1 + var/icon_closed = "fuel_port" + var/icon_empty = "fuel_port_empty" + var/icon_full = "fuel_port_full" + var/opened = 0 + var/parent_shuttle + +/obj/structure/fuel_port/Initialize() + . = ..() + new /obj/item/weapon/tank/phoron(src) + +/obj/structure/fuel_port/attack_hand(mob/user as mob) + if(!opened) + to_chat(user, "The door is secured tightly. You'll need a crowbar to open it.") + return + else if(contents.len > 0) + user.put_in_hands(contents[1]) + update_icon() + +/obj/structure/fuel_port/update_icon() + if(opened) + if(contents.len > 0) + icon_state = icon_full + else + icon_state = icon_empty + else + icon_state = icon_closed + ..() + +/obj/structure/fuel_port/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(W.is_crowbar()) + if(opened) + to_chat(user, "You tightly shut \the [src] door.") + playsound(src.loc, 'sound/effects/locker_close.ogg', 25, 0, -3) + opened = 0 + else + to_chat(user, "You open up \the [src] door.") + playsound(src.loc, 'sound/effects/locker_open.ogg', 15, 1, -3) + opened = 1 + else if(istype(W,/obj/item/weapon/tank)) + if(!opened) + to_chat(user, "\The [src] door is still closed!") + return + if(contents.len == 0) + user.unEquip(W, src) + W.forceMove(src) + update_icon() + +// Walls hide stuff inside them, but we want to be visible. +/obj/structure/fuel_port/hide() + return \ No newline at end of file diff --git a/code/modules/overmap/sectors.dm b/code/modules/overmap/sectors.dm index 5309b05096..9c3408eabf 100644 --- a/code/modules/overmap/sectors.dm +++ b/code/modules/overmap/sectors.dm @@ -1,124 +1,162 @@ - //=================================================================================== -//Hook for building overmap +//Overmap object representing zlevel(s) //=================================================================================== -var/global/list/map_sectors = list() - -/hook/startup/proc/build_map() - if(!config.use_overmap) - return 1 - testing("Building overmap...") - var/obj/effect/mapinfo/data - for(var/level in 1 to world.maxz) - data = locate("sector[level]") - if (data) - testing("Located sector \"[data.name]\" at [data.mapx],[data.mapy] corresponding to zlevel [level]") - map_sectors["[level]"] = new data.obj_type(data) - return 1 - -//=================================================================================== -//Metaobject for storing information about sector this zlevel is representing. -//Should be placed only once on every zlevel. -//=================================================================================== -/obj/effect/mapinfo/ - name = "map info metaobject" - icon = 'icons/mob/screen1.dmi' - icon_state = "x2" - invisibility = 101 - var/obj_type //type of overmap object it spawns - var/landing_area //type of area used as inbound shuttle landing, null if no shuttle landing area - var/zlevel - var/mapx //coordinates on the - var/mapy //overmap zlevel - var/known = 1 - -/obj/effect/mapinfo/New() - tag = "sector[z]" - zlevel = z - loc = null - -/obj/effect/mapinfo/sector - name = "generic sector" - obj_type = /obj/effect/map/sector - -/obj/effect/mapinfo/ship - name = "generic ship" - obj_type = /obj/effect/map/ship - - -//=================================================================================== -//Overmap object representing zlevel -//=================================================================================== - -/obj/effect/map +/obj/effect/overmap/visitable name = "map object" - icon = 'icons/obj/items.dmi' - icon_state = "sheet-plasteel" - var/map_z = 0 - var/area/shuttle/shuttle_landing - var/always_known = 1 + scannable = TRUE -/obj/effect/map/New(var/obj/effect/mapinfo/data) - map_z = data.zlevel - name = data.name - always_known = data.known - if (data.icon != 'icons/mob/screen1.dmi') - icon = data.icon - icon_state = data.icon_state - if(data.desc) - desc = data.desc - var/new_x = data.mapx ? data.mapx : rand(OVERMAP_EDGE, world.maxx - OVERMAP_EDGE) - var/new_y = data.mapy ? data.mapy : rand(OVERMAP_EDGE, world.maxy - OVERMAP_EDGE) - loc = locate(new_x, new_y, OVERMAP_ZLEVEL) + var/list/map_z = list() + var/list/extra_z_levels //if you need to manually insist that these z-levels are part of this sector, for things like edge-of-map step trigger transitions rather than multi-z complexes - if(data.landing_area) - shuttle_landing = locate(data.landing_area) + var/list/initial_generic_waypoints //store landmark_tag of landmarks that should be added to the actual lists below on init. + var/list/initial_restricted_waypoints //For use with non-automatic landmarks (automatic ones add themselves). -/obj/effect/map/CanPass(atom/movable/A) - testing("[A] attempts to enter sector\"[name]\"") - return 1 + var/list/generic_waypoints = list() //waypoints that any shuttle can use + var/list/restricted_waypoints = list() //waypoints for specific shuttles + var/docking_codes -/obj/effect/map/Crossed(atom/movable/A) - testing("[A] has entered sector\"[name]\"") - if (istype(A,/obj/effect/map/ship)) - var/obj/effect/map/ship/S = A - S.current_sector = src + var/start_x //Coordinates for self placing + var/start_y //will use random values if unset -/obj/effect/map/Uncrossed(atom/movable/A) - testing("[A] has left sector\"[name]\"") - if (istype(A,/obj/effect/map/ship)) - var/obj/effect/map/ship/S = A - S.current_sector = null + var/base = 0 //starting sector, counts as station_levels + var/in_space = 1 //can be accessed via lucky EVA -/obj/effect/map/sector + var/hide_from_reports = FALSE + + var/has_distress_beacon + +/obj/effect/overmap/visitable/Initialize() + . = ..() + if(. == INITIALIZE_HINT_QDEL) + return + + find_z_levels() // This populates map_z and assigns z levels to the ship. + register_z_levels() // This makes external calls to update global z level information. + + if(!global.using_map.overmap_z) + build_overmap() + + start_x = start_x || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE) + start_y = start_y || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE) + + forceMove(locate(start_x, start_y, global.using_map.overmap_z)) + + docking_codes = "[ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))]" + + testing("Located sector \"[name]\" at [start_x],[start_y], containing Z [english_list(map_z)]") + + LAZYADD(SSshuttles.sectors_to_initialize, src) //Queued for further init. Will populate the waypoint lists; waypoints not spawned yet will be added in as they spawn. + SSshuttles.process_init_queues() + +//This is called later in the init order by SSshuttles to populate sector objects. Importantly for subtypes, shuttles will be created by then. +/obj/effect/overmap/visitable/proc/populate_sector_objects() + +/obj/effect/overmap/visitable/proc/get_areas() + . = list() + for(var/area/A) + if (A.z in map_z) + . += A + +/obj/effect/overmap/visitable/proc/find_z_levels() + map_z = GetConnectedZlevels(z) + if(LAZYLEN(extra_z_levels)) + map_z |= extra_z_levels + +/obj/effect/overmap/visitable/proc/register_z_levels() + for(var/zlevel in map_z) + map_sectors["[zlevel]"] = src + + global.using_map.player_levels |= map_z + if(!in_space) + global.using_map.sealed_levels |= map_z + if(base) + global.using_map.station_levels |= map_z + global.using_map.contact_levels |= map_z + global.using_map.map_levels |= map_z + +/obj/effect/overmap/visitable/proc/get_space_zlevels() + if(in_space) + return map_z + else + return list() + +//Helper for init. +/obj/effect/overmap/visitable/proc/check_ownership(obj/object) + if((get_z(object) in map_z) && !(get_area(object) in SSshuttles.shuttle_areas)) + return 1 + +//If shuttle_name is false, will add to generic waypoints; otherwise will add to restricted. Does not do checks. +/obj/effect/overmap/visitable/proc/add_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name) + landmark.sector_set(src, shuttle_name) + if(shuttle_name) + LAZYADD(restricted_waypoints[shuttle_name], landmark) + else + generic_waypoints += landmark + +/obj/effect/overmap/visitable/proc/remove_landmark(obj/effect/shuttle_landmark/landmark, shuttle_name) + if(shuttle_name) + var/list/shuttles = restricted_waypoints[shuttle_name] + LAZYREMOVE(shuttles, landmark) + else + generic_waypoints -= landmark + +/obj/effect/overmap/visitable/proc/get_waypoints(var/shuttle_name) + . = list() + for(var/obj/effect/overmap/visitable/contained in src) + . += contained.get_waypoints(shuttle_name) + for(var/thing in generic_waypoints) + .[thing] = name + if(shuttle_name in restricted_waypoints) + for(var/thing in restricted_waypoints[shuttle_name]) + .[thing] = name + +/obj/effect/overmap/visitable/proc/generate_skybox() + return + +/obj/effect/overmap/visitable/MouseEntered(location, control, params) + openToolTip(user = usr, tip_src = src, params = params, title = name) + + ..() + +/obj/effect/overmap/visitable/MouseDown() + closeToolTip(usr) //No reason not to, really + + ..() + +/obj/effect/overmap/visitable/MouseExited() + closeToolTip(usr) //No reason not to, really + + ..() + +/obj/effect/overmap/visitable/sector name = "generic sector" desc = "Sector with some stuff in it." + icon_state = "sector" anchored = 1 -//Space stragglers go here +// Because of the way these are spawned, they will potentially have their invisibility adjusted by the turfs they are mapped on +// prior to being moved to the overmap. This blocks that. Use set_invisibility to adjust invisibility as needed instead. +/obj/effect/overmap/visitable/sector/hide() -/obj/effect/map/sector/temporary - name = "Deep Space" - icon_state = "" - always_known = 0 +/proc/build_overmap() + if(!global.using_map.use_overmap) + return 1 -/obj/effect/map/sector/temporary/New(var/nx, var/ny, var/nz) - loc = locate(nx, ny, OVERMAP_ZLEVEL) - map_z = nz - map_sectors["[map_z]"] = src - testing("Temporary sector at [x],[y] was created, corresponding zlevel is [map_z].") + testing("Building overmap...") + world.increment_max_z() + global.using_map.overmap_z = world.maxz -/obj/effect/map/sector/temporary/Destroy() - map_sectors["[map_z]"] = null - testing("Temporary sector at [x],[y] was deleted.") - if (can_die()) - testing("Associated zlevel disappeared.") - world.maxz-- + testing("Putting overmap on [global.using_map.overmap_z]") + var/area/overmap/A = new + for (var/square in block(locate(1,1,global.using_map.overmap_z), locate(global.using_map.overmap_size,global.using_map.overmap_size,global.using_map.overmap_z))) + var/turf/T = square + if(T.x == global.using_map.overmap_size || T.y == global.using_map.overmap_size) + T = T.ChangeTurf(/turf/unsimulated/map/edge) + else + T = T.ChangeTurf(/turf/unsimulated/map) + ChangeArea(T, A) -/obj/effect/map/sector/temporary/proc/can_die(var/mob/observer) - testing("Checking if sector at [map_z] can die.") - for(var/mob/M in player_list) - if(M != observer && M.z == map_z) - testing("There are people on it.") - return 0 + global.using_map.sealed_levels |= global.using_map.overmap_z + + testing("Overmap build complete.") return 1 diff --git a/code/modules/overmap/ships/computers/computer_shims.dm b/code/modules/overmap/ships/computers/computer_shims.dm new file mode 100644 index 0000000000..c685acc49d --- /dev/null +++ b/code/modules/overmap/ships/computers/computer_shims.dm @@ -0,0 +1,104 @@ +/* +** +** HELLO! DON'T COPY THINGS FROM HERE - READ THIS! +** +** The ship machines/computers ported from baystation expect certain procs and infrastruture that we don't have. +** I /could/ just port those computers to our code, but I actually *like* that infrastructure. But I +** don't have time (yet) to implement it fully in our codebase, so I'm shimming it here experimentally as a test +** bed for later implementing it on /obj/machinery and /obj/machinery/computer for everything. ~Leshana (March 2020) +*/ + +// +// Power +// + +// This will have this machine have its area eat this much power next tick, and not afterwards. Do not use for continued power draw. +/obj/machinery/proc/use_power_oneoff(var/amount, var/chan = -1) + return use_power(amount, chan) + +// Change one of the power consumption vars +/obj/machinery/proc/change_power_consumption(new_power_consumption, use_power_mode = USE_POWER_IDLE) + switch(use_power_mode) + if(USE_POWER_IDLE) + idle_power_usage = new_power_consumption + if(USE_POWER_ACTIVE) + active_power_usage = new_power_consumption + // No need to do anything else in our power scheme. + +// Defining directly here to avoid conflicts with existing set_broken procs in our codebase that behave differently. +/obj/machinery/atmospherics/unary/engine/proc/set_broken(var/new_state, var/cause) + if(!(stat & BROKEN) == !new_state) + return // Nothing changed + stat ^= BROKEN + update_icon() + + +// +// Compoenents +// + +/obj/machinery/proc/total_component_rating_of_type(var/part_type) + . = 0 + for(var/thing in component_parts) + if(istype(thing, part_type)) + var/obj/item/weapon/stock_parts/part = thing + . += part.rating + // Now isn't THIS a cool idea? + // for(var/path in uncreated_component_parts) + // if(ispath(path, part_type)) + // var/obj/item/weapon/stock_parts/comp = path + // . += initial(comp.rating) * uncreated_component_parts[path] + +// +// Skills +// +/obj/machinery/computer/ship + var/core_skill = /datum/skill/devices //The skill used for skill checks for this machine (mostly so subtypes can use different skills). + +// +// Topic +// + +/obj/machinery/computer/ship/proc/DefaultTopicState() + return global.default_state + +/obj/machinery/computer/ship/Topic(var/href, var/href_list = list(), var/datum/topic_state/state) + if((. = ..())) + return + state = state || DefaultTopicState() || global.default_state + if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) + CouldUseTopic(usr) + return OnTopic(usr, href_list, state) + CouldNotUseTopic(usr) + return TRUE + +/obj/machinery/computer/ship/proc/OnTopic(var/mob/user, var/href_list, var/datum/topic_state/state) + return TOPIC_NOACTION + +// +// Interaction +// + +// If you want to have interface interactions handled for you conveniently, use this. +// Return TRUE for handled. +// If you perform direct interactions in here, you are responsible for ensuring that full interactivity checks have been made (i.e CanInteract). +// The checks leading in to here only guarantee that the user should be able to view a UI. +/obj/machinery/computer/ship/proc/interface_interact(var/mob/user) + ui_interact(user) + return TRUE + +/obj/machinery/computer/ship/attack_ai(mob/user) + if(CanUseTopic(user, DefaultTopicState()) > STATUS_CLOSE) + return interface_interact(user) + +// After a recent rework this should mostly be safe. +/obj/machinery/computer/ship/attack_ghost(mob/user) + interface_interact(user) + +// If you don't call parent in this proc, you must make all appropriate checks yourself. +// If you do, you must respect the return value. +/obj/machinery/computer/ship/attack_hand(mob/user) + if((. = ..())) + return + if(CanUseTopic(user, DefaultTopicState()) > STATUS_CLOSE) + return interface_interact(user) diff --git a/code/modules/overmap/ships/computers/engine_control.dm b/code/modules/overmap/ships/computers/engine_control.dm index 01920ee2e0..c07fdf45cb 100644 --- a/code/modules/overmap/ships/computers/engine_control.dm +++ b/code/modules/overmap/ships/computers/engine_control.dm @@ -1,46 +1,25 @@ //Engine control and monitoring console -/obj/machinery/computer/engines +/obj/machinery/computer/ship/engines name = "engine control console" icon_keyboard = "tech_key" - icon_screen = "id" - var/state = "status" - var/list/engines = list() - var/obj/effect/map/ship/linked + icon_screen = "engines" + circuit = /obj/item/weapon/circuitboard/engine + var/display_state = "status" -/obj/machinery/computer/engines/Initialize() - . = ..() - linked = map_sectors["[z]"] - if (linked) - if (!linked.eng_control) - linked.eng_control = src - testing("Engines console at level [z] found a corresponding overmap object '[linked.name]'.") - else - testing("Engines console at level [z] was unable to find a corresponding overmap object.") - - for(var/datum/ship_engine/E in engines) - if (E.zlevel == z && !(E in engines)) - engines += E - -/obj/machinery/computer/engines/attack_hand(var/mob/user as mob) - if(..()) - user.unset_machine() - return - - if(!isAI(user)) - user.set_machine(src) - - ui_interact(user) - -/obj/machinery/computer/engines/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/computer/ship/engines/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(!linked) + display_reconnect_dialog(user, "ship control systems") return var/data[0] - data["state"] = state + data["state"] = display_state + data["global_state"] = linked.engines_state + data["global_limit"] = round(linked.thrust_limit*100) + var/total_thrust = 0 var/list/enginfo[0] - for(var/datum/ship_engine/E in engines) + for(var/datum/ship_engine/E in linked.engines) var/list/rdata[0] rdata["eng_type"] = E.name rdata["eng_on"] = E.is_on() @@ -48,54 +27,70 @@ rdata["eng_thrust_limiter"] = round(E.get_thrust_limit()*100) rdata["eng_status"] = E.get_status() rdata["eng_reference"] = "\ref[E]" + total_thrust += E.get_thrust() enginfo.Add(list(rdata)) data["engines_info"] = enginfo + data["total_thrust"] = total_thrust ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) - ui = new(user, src, ui_key, "engines_control.tmpl", "[linked.name] Engines Control", 380, 530) + ui = new(user, src, ui_key, "engines_control.tmpl", "[linked.name] Engines Control", 390, 530) ui.set_initial_data(data) ui.open() ui.set_auto_update(1) -/obj/machinery/computer/engines/Topic(href, href_list) +/obj/machinery/computer/ship/engines/OnTopic(var/mob/user, var/list/href_list, state) if(..()) - return 1 + return ..() if(href_list["state"]) - state = href_list["state"] + display_state = href_list["state"] + return TOPIC_REFRESH + + if(href_list["global_toggle"]) + linked.engines_state = !linked.engines_state + for(var/datum/ship_engine/E in linked.engines) + if(linked.engines_state == !E.is_on()) + E.toggle() + return TOPIC_REFRESH + + if(href_list["set_global_limit"]) + var/newlim = input("Input new thrust limit (0..100%)", "Thrust limit", linked.thrust_limit*100) as num + if(!CanInteract(user, state)) + return TOPIC_NOACTION + linked.thrust_limit = CLAMP(newlim/100, 0, 1) + for(var/datum/ship_engine/E in linked.engines) + E.set_thrust_limit(linked.thrust_limit) + return TOPIC_REFRESH + + if(href_list["global_limit"]) + linked.thrust_limit = CLAMP(linked.thrust_limit + text2num(href_list["global_limit"]), 0, 1) + for(var/datum/ship_engine/E in linked.engines) + E.set_thrust_limit(linked.thrust_limit) + return TOPIC_REFRESH if(href_list["engine"]) if(href_list["set_limit"]) var/datum/ship_engine/E = locate(href_list["engine"]) var/newlim = input("Input new thrust limit (0..100)", "Thrust limit", E.get_thrust_limit()) as num + if(!CanInteract(user, state)) + return var/limit = CLAMP(newlim/100, 0, 1) - if(E) + if(istype(E)) E.set_thrust_limit(limit) - + return TOPIC_REFRESH if(href_list["limit"]) var/datum/ship_engine/E = locate(href_list["engine"]) var/limit = CLAMP(E.get_thrust_limit() + text2num(href_list["limit"]), 0, 1) - if(E) + if(istype(E)) E.set_thrust_limit(limit) + return TOPIC_REFRESH if(href_list["toggle"]) var/datum/ship_engine/E = locate(href_list["engine"]) - if(E) + if(istype(E)) E.toggle() - - add_fingerprint(usr) - updateUsrDialog() - -/obj/machinery/computer/engines/proc/burn() - if(engines.len == 0) - return 0 - var/res = 0 - for(var/datum/ship_engine/E in engines) - res |= E.burn() - return res - -/obj/machinery/computer/engines/proc/get_total_thrust() - for(var/datum/ship_engine/E in engines) - . += E.get_thrust() + return TOPIC_REFRESH + return TOPIC_REFRESH + return TOPIC_NOACTION \ No newline at end of file diff --git a/code/modules/overmap/ships/computers/helm.dm b/code/modules/overmap/ships/computers/helm.dm index 0249fae482..70deb4347b 100644 --- a/code/modules/overmap/ships/computers/helm.dm +++ b/code/modules/overmap/ships/computers/helm.dm @@ -1,151 +1,189 @@ -/obj/machinery/computer/helm +// LEGACY_RECORD_STRUCTURE(all_waypoints, waypoint) +GLOBAL_LIST_EMPTY(all_waypoints) +/datum/computer_file/data/waypoint + var/list/fields + filetype = "WPT" + +/datum/computer_file/data/waypoint/New() + ..() + fields = list() + GLOB.all_waypoints.Add(src) + +/datum/computer_file/data/waypoint/Destroy() + . = ..() + GLOB.all_waypoints.Remove(src); +// End LEGACY_RECORD_STRUCTURE(all_waypoints, waypoint) + +/obj/machinery/computer/ship/helm name = "helm control console" - icon_keyboard = "med_key" - icon_screen = "id" - var/state = "status" - var/obj/effect/map/ship/linked //connected overmap object + icon_keyboard = "teleport_key" + icon_screen = "helm" + light_color = "#7faaff" + circuit = /obj/item/weapon/circuitboard/helm + core_skill = /datum/skill/pilot var/autopilot = 0 - var/manual_control = 0 var/list/known_sectors = list() var/dx //desitnation var/dy //coordinates + var/speedlimit = 1/(20 SECONDS) //top speed for autopilot, 5 + var/accellimit = 0.001 //manual limiter for acceleration + req_one_access = list(access_pilot) //VOREStation Edit -/obj/machinery/computer/helm/Initialize() +/obj/machinery/computer/ship/helm/Initialize() . = ..() - linked = map_sectors["[z]"] - if (linked) - if(!linked.nav_control) - linked.nav_control = src - testing("Helm console at level [z] found a corresponding overmap object '[linked.name]'.") - else - testing("Helm console at level [z] was unable to find a corresponding overmap object.") + get_known_sectors() - for(var/level in map_sectors) - var/obj/effect/map/sector/S = map_sectors["[level]"] - if (istype(S) && S.always_known) - var/datum/data/record/R = new() +/obj/machinery/computer/ship/helm/proc/get_known_sectors() + var/area/overmap/map = locate() in world + for(var/obj/effect/overmap/visitable/sector/S in map) + if (S.known) + var/datum/computer_file/data/waypoint/R = new() R.fields["name"] = S.name R.fields["x"] = S.x R.fields["y"] = S.y - known_sectors += R + known_sectors[S.name] = R -/obj/machinery/computer/helm/process() +/obj/machinery/computer/ship/helm/process() ..() if (autopilot && dx && dy) - var/turf/T = locate(dx,dy,1) + var/turf/T = locate(dx,dy,global.using_map.overmap_z) if(linked.loc == T) if(linked.is_still()) autopilot = 0 else linked.decelerate() - - var/brake_path = linked.get_brake_path() - - if(get_dist(linked.loc, T) > brake_path) - linked.accelerate(get_dir(linked.loc, T)) else - linked.decelerate() + var/brake_path = linked.get_brake_path() + var/direction = get_dir(linked.loc, T) + var/acceleration = min(linked.get_acceleration(), accellimit) + var/speed = linked.get_speed() + var/heading = linked.get_heading() + // Destination is current grid or speedlimit is exceeded + if ((get_dist(linked.loc, T) <= brake_path) || speed > speedlimit) + linked.decelerate() + // Heading does not match direction + else if (heading & ~direction) + linked.accelerate(turn(heading & ~direction, 180), accellimit) + // All other cases, move toward direction + else if (speed + acceleration <= speedlimit) + linked.accelerate(direction, accellimit) + linked.operator_skill = null//if this is on you can't dodge meteors return -/obj/machinery/computer/helm/relaymove(var/mob/user, direction) - if(manual_control && linked) - linked.relaymove(user,direction) +/obj/machinery/computer/ship/helm/relaymove(var/mob/user, direction) + if(viewing_overmap(user) && linked) + if(prob(user.skill_fail_chance(/datum/skill/pilot, 50, linked.skill_needed, factor = 1))) + direction = turn(direction,pick(90,-90)) + linked.relaymove(user, direction, accellimit) return 1 -/obj/machinery/computer/helm/check_eye(var/mob/user as mob) - if (!manual_control) - return -1 - if (!get_dist(user, src) > 1 || user.blinded || !linked ) - return -1 - return 0 - -/obj/machinery/computer/helm/attack_hand(var/mob/user as mob) - if(..()) - user.unset_machine() - manual_control = 0 - return - - if(!isAI(user)) - user.set_machine(src) - if(linked) - user.reset_view(linked) - - ui_interact(user) - -/obj/machinery/computer/helm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!linked) - return - +/obj/machinery/computer/ship/helm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] - data["state"] = state - data["sector"] = linked.current_sector ? linked.current_sector.name : "Deep Space" - data["sector_info"] = linked.current_sector ? linked.current_sector.desc : "Not Available" - data["s_x"] = linked.x - data["s_y"] = linked.y - data["dest"] = dy && dx - data["d_x"] = dx - data["d_y"] = dy - data["speed"] = linked.get_speed() - data["accel"] = round(linked.get_acceleration()) - data["heading"] = linked.get_heading() ? dir2angle(linked.get_heading()) : 0 - data["autopilot"] = autopilot - data["manual_control"] = manual_control + if(!linked) + display_reconnect_dialog(user, "helm") + else + var/turf/T = get_turf(linked) + var/obj/effect/overmap/visitable/sector/current_sector = locate() in T - var/list/locations[0] - for (var/datum/data/record/R in known_sectors) - var/list/rdata[0] - rdata["name"] = R.fields["name"] - rdata["x"] = R.fields["x"] - rdata["y"] = R.fields["y"] - rdata["reference"] = "\ref[R]" - locations.Add(list(rdata)) + data["sector"] = current_sector ? current_sector.name : "Deep Space" + data["sector_info"] = current_sector ? current_sector.desc : "Not Available" + data["landed"] = linked.get_landed_info() + data["s_x"] = linked.x + data["s_y"] = linked.y + data["dest"] = dy && dx + data["d_x"] = dx + data["d_y"] = dy + data["speedlimit"] = speedlimit ? speedlimit*1000 : "Halted" + data["accel"] = min(round(linked.get_acceleration()*1000, 0.01),accellimit*1000) + data["heading"] = linked.get_heading_degrees() + data["autopilot"] = autopilot + data["manual_control"] = viewing_overmap(user) + data["canburn"] = linked.can_burn() + data["accellimit"] = accellimit*1000 - data["locations"] = locations + var/speed = round(linked.get_speed()*1000, 0.01) + if(linked.get_speed() < SHIP_SPEED_SLOW) + speed = "[speed]" + if(linked.get_speed() > SHIP_SPEED_FAST) + speed = "[speed]" + data["speed"] = speed - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "helm.tmpl", "[linked.name] Helm Control", 380, 530) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + if(linked.get_speed()) + data["ETAnext"] = "[round(linked.ETA()/10)] seconds" + else + data["ETAnext"] = "N/A" -/obj/machinery/computer/helm/Topic(href, href_list) + var/list/locations[0] + for (var/key in known_sectors) + var/datum/computer_file/data/waypoint/R = known_sectors[key] + var/list/rdata[0] + rdata["name"] = R.fields["name"] + rdata["x"] = R.fields["x"] + rdata["y"] = R.fields["y"] + rdata["reference"] = "\ref[R]" + locations.Add(list(rdata)) + + data["locations"] = locations + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "helm.tmpl", "[linked.name] Helm Control", 565, 545) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/ship/helm/OnTopic(var/mob/user, var/list/href_list, state) if(..()) - return 1 + return TOPIC_HANDLED - if (!linked) - return + if(!linked) + return TOPIC_HANDLED if (href_list["add"]) - var/datum/data/record/R = new() + var/datum/computer_file/data/waypoint/R = new() var/sec_name = input("Input naviation entry name", "New navigation entry", "Sector #[known_sectors.len]") as text + if(!CanInteract(user,state)) + return TOPIC_NOACTION if(!sec_name) sec_name = "Sector #[known_sectors.len]" R.fields["name"] = sec_name + if(sec_name in known_sectors) + to_chat(user, "Sector with that name already exists, please input a different name.") + return TOPIC_REFRESH switch(href_list["add"]) if("current") R.fields["x"] = linked.x R.fields["y"] = linked.y if("new") var/newx = input("Input new entry x coordinate", "Coordinate input", linked.x) as num - R.fields["x"] = CLAMP(newx, 1, world.maxx) + if(!CanInteract(user,state)) + return TOPIC_REFRESH var/newy = input("Input new entry y coordinate", "Coordinate input", linked.y) as num + if(!CanInteract(user,state)) + return TOPIC_NOACTION + R.fields["x"] = CLAMP(newx, 1, world.maxx) R.fields["y"] = CLAMP(newy, 1, world.maxy) - known_sectors += R + known_sectors[sec_name] = R if (href_list["remove"]) - var/datum/data/record/R = locate(href_list["remove"]) - known_sectors.Remove(R) + var/datum/computer_file/data/waypoint/R = locate(href_list["remove"]) + if(R) + known_sectors.Remove(R.fields["name"]) + qdel(R) if (href_list["setx"]) var/newx = input("Input new destiniation x coordinate", "Coordinate input", dx) as num|null + if(!CanInteract(user,state)) + return if (newx) dx = CLAMP(newx, 1, world.maxx) if (href_list["sety"]) var/newy = input("Input new destiniation y coordinate", "Coordinate input", dy) as num|null + if(!CanInteract(user,state)) + return if (newy) dy = CLAMP(newy, 1, world.maxy) @@ -157,9 +195,20 @@ dx = 0 dy = 0 + if (href_list["speedlimit"]) + var/newlimit = input("Input new speed limit for autopilot (0 to brake)", "Autopilot speed limit", speedlimit*1000) as num|null + if(newlimit) + speedlimit = CLAMP(newlimit/1000, 0, 100) + if (href_list["accellimit"]) + var/newlimit = input("Input new acceleration limit", "Acceleration limit", accellimit*1000) as num|null + if(newlimit) + accellimit = max(newlimit/1000, 0) + if (href_list["move"]) var/ndir = text2num(href_list["move"]) - linked.relaymove(usr, ndir) + if(prob(user.skill_fail_chance(/datum/skill/pilot, 50, linked.skill_needed, factor = 1))) + ndir = turn(ndir,pick(90,-90)) + linked.relaymove(user, ndir, accellimit) if (href_list["brake"]) linked.decelerate() @@ -168,10 +217,73 @@ autopilot = !autopilot if (href_list["manual"]) - manual_control = !manual_control + viewing_overmap(user) ? unlook(user) : look(user) - if (href_list["state"]) - state = href_list["state"] - add_fingerprint(usr) + add_fingerprint(user) updateUsrDialog() + +/obj/machinery/computer/ship/navigation + name = "navigation console" + icon_keyboard = "generic_key" + icon_screen = "helm" + circuit = /obj/item/weapon/circuitboard/nav + +/obj/machinery/computer/ship/navigation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!linked) + display_reconnect_dialog(user, "Navigation") + return + + var/data[0] + + + var/turf/T = get_turf(linked) + var/obj/effect/overmap/visitable/sector/current_sector = locate() in T + + data["sector"] = current_sector ? current_sector.name : "Deep Space" + data["sector_info"] = current_sector ? current_sector.desc : "Not Available" + data["s_x"] = linked.x + data["s_y"] = linked.y + data["speed"] = round(linked.get_speed()*1000, 0.01) + data["accel"] = round(linked.get_acceleration()*1000, 0.01) + data["heading"] = linked.get_heading_degrees() + data["viewing"] = viewing_overmap(user) + + if(linked.get_speed()) + data["ETAnext"] = "[round(linked.ETA()/10)] seconds" + else + data["ETAnext"] = "N/A" + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "nav.tmpl", "[linked.name] Navigation Screen", 380, 530) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/ship/navigation/OnTopic(var/mob/user, var/list/href_list) + if(..()) + return TOPIC_HANDLED + + if (!linked) + return TOPIC_NOACTION + + if (href_list["viewing"]) + viewing_overmap(user) ? unlook(user) : look(user) + return TOPIC_REFRESH + +/obj/machinery/computer/ship/navigation/telescreen //little hacky but it's only used on one ship so it should be okay + icon_state = "tele_nav" + icon_keyboard = null + icon_screen = null + circuit = /obj/item/weapon/circuitboard/nav/tele + density = 0 + +/obj/machinery/computer/ship/navigation/telescreen/update_icon() + if(stat & NOPOWER || stat & BROKEN) + icon_state = "tele_off" + set_light(0) + else + icon_state = "tele_nav" + set_light(light_range_on, light_power_on) + ..() diff --git a/code/modules/overmap/ships/computers/sensors.dm b/code/modules/overmap/ships/computers/sensors.dm new file mode 100644 index 0000000000..c1571c12ef --- /dev/null +++ b/code/modules/overmap/ships/computers/sensors.dm @@ -0,0 +1,227 @@ +/obj/machinery/computer/ship/sensors + name = "sensors console" + icon_keyboard = "teleport_key" + icon_screen = "teleport" + light_color = "#77fff8" + circuit = /obj/item/weapon/circuitboard/sensors + extra_view = 4 + var/obj/machinery/shipsensors/sensors + +/obj/machinery/computer/ship/sensors/attempt_hook_up(obj/effect/overmap/visitable/ship/sector) + if(!(. = ..())) + return + find_sensors() + +/obj/machinery/computer/ship/sensors/proc/find_sensors() + if(!linked) + return + for(var/obj/machinery/shipsensors/S in global.machines) + if(linked.check_ownership(S)) + sensors = S + break + +/obj/machinery/computer/ship/sensors/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!linked) + display_reconnect_dialog(user, "sensors") + return + + var/data[0] + + data["viewing"] = viewing_overmap(user) + if(sensors) + data["on"] = sensors.use_power + data["range"] = sensors.range + data["health"] = sensors.health + data["max_health"] = sensors.max_health + data["heat"] = sensors.heat + data["critical_heat"] = sensors.critical_heat + if(sensors.health == 0) + data["status"] = "DESTROYED" + else if(!sensors.powered()) + data["status"] = "NO POWER" + else if(!sensors.in_vacuum()) + data["status"] = "VACUUM SEAL BROKEN" + else + data["status"] = "OK" + var/list/contacts = list() + for(var/obj/effect/overmap/O in view(7,linked)) + if(linked == O) + continue + if(!O.scannable) + continue + var/bearing = round(90 - ATAN2(O.x - linked.x, O.y - linked.y),5) + if(bearing < 0) + bearing += 360 + contacts.Add(list(list("name"=O.name, "ref"="\ref[O]", "bearing"=bearing))) + if(contacts.len) + data["contacts"] = contacts + else + data["status"] = "MISSING" + data["range"] = "N/A" + data["on"] = 0 + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "shipsensors.tmpl", "[linked.name] Sensors Control", 420, 530, src) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/ship/sensors/OnTopic(var/mob/user, var/list/href_list, state) + if(..()) + return TOPIC_HANDLED + + if (!linked) + return TOPIC_NOACTION + + if (href_list["viewing"]) + if(user && !isAI(user)) + viewing_overmap(user) ? unlook(user) : look(user) + return TOPIC_REFRESH + + if (href_list["link"]) + find_sensors() + return TOPIC_REFRESH + + if(sensors) + if (href_list["range"]) + var/nrange = input("Set new sensors range", "Sensor range", sensors.range) as num|null + if(!CanInteract(user,state)) + return TOPIC_NOACTION + if (nrange) + sensors.set_range(CLAMP(nrange, 1, world.view)) + return TOPIC_REFRESH + if (href_list["toggle"]) + sensors.toggle() + return TOPIC_REFRESH + + if (href_list["scan"]) + var/obj/effect/overmap/O = locate(href_list["scan"]) + if(istype(O) && !QDELETED(O) && (O in view(7,linked))) + playsound(loc, "sound/machines/dotprinter.ogg", 30, 1) + new/obj/item/weapon/paper/(get_turf(src), O.get_scan_data(user), "paper (Sensor Scan - [O])") + return TOPIC_HANDLED + +/obj/machinery/computer/ship/sensors/process() + ..() + if(!linked) + return + if(sensors && sensors.use_power && sensors.powered()) + var/sensor_range = round(sensors.range*1.5) + 1 + linked.set_light(sensor_range + 0.5, 4) + else + linked.set_light(0) + +/obj/machinery/shipsensors + name = "sensors suite" + desc = "Long range gravity scanner with various other sensors, used to detect irregularities in surrounding space. Can only run in vacuum to protect delicate quantum BS elements." //VOREStation Edit + icon = 'icons/obj/stationobjs_vr.dmi' //VOREStation Edit + icon_state = "sensors" + anchored = 1 + var/max_health = 200 + var/health = 200 + var/critical_heat = 50 // sparks and takes damage when active & above this heat + var/heat_reduction = 1.5 // mitigates this much heat per tick + var/heat = 0 + var/range = 1 + idle_power_usage = 5000 + +/obj/machinery/shipsensors/attackby(obj/item/weapon/W, mob/user) + var/damage = max_health - health + if(damage && istype(W, /obj/item/weapon/weldingtool)) + + var/obj/item/weapon/weldingtool/WT = W + + if(!WT.isOn()) + return + + if(WT.remove_fuel(0,user)) + to_chat(user, "You start repairing the damage to [src].") + playsound(src, 'sound/items/Welder.ogg', 100, 1) + if(do_after(user, max(5, damage / 5), src) && WT && WT.isOn()) + to_chat(user, "You finish repairing the damage to [src].") + take_damage(-damage) + else + to_chat(user, "You need more welding fuel to complete this task.") + return + return + ..() + +/obj/machinery/shipsensors/proc/in_vacuum() + var/turf/T=get_turf(src) + if(istype(T)) + var/datum/gas_mixture/environment = T.return_air() + if(environment && environment.return_pressure() > MINIMUM_PRESSURE_DIFFERENCE_TO_SUSPEND) + return 0 + return 1 + +/obj/machinery/shipsensors/update_icon() + if(use_power) + icon_state = "sensors" + else + icon_state = "sensors_off" + ..() + +/obj/machinery/shipsensors/examine(mob/user) + . = ..() + if(health <= 0) + to_chat(user, "\The [src] is wrecked.") + else if(health < max_health * 0.25) + to_chat(user, "\The [src] looks like it's about to break!") + else if(health < max_health * 0.5) + to_chat(user, "\The [src] looks seriously damaged!") + else if(health < max_health * 0.75) + to_chat(user, "\The [src] shows signs of damage!") + +/obj/machinery/shipsensors/bullet_act(var/obj/item/projectile/Proj) + take_damage(Proj.get_structure_damage()) + ..() + +/obj/machinery/shipsensors/proc/toggle() + if(!use_power && (health == 0 || !in_vacuum())) + return // No turning on if broken or misplaced. + if(!use_power) //need some juice to kickstart + use_power_oneoff(idle_power_usage*5) + update_use_power(!use_power) + update_icon() + +/obj/machinery/shipsensors/process() + if(use_power) //can't run in non-vacuum + if(!in_vacuum()) + toggle() + if(heat > critical_heat) + src.visible_message("\The [src] violently spews out sparks!") + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + + take_damage(rand(10,50)) + toggle() + heat += idle_power_usage/15000 + + if (heat > 0) + heat = max(0, heat - heat_reduction) + +/obj/machinery/shipsensors/power_change() + . = ..() + if(use_power && !powered()) + toggle() + +/obj/machinery/shipsensors/proc/set_range(nrange) + range = nrange + change_power_consumption(1500 * (range**2), USE_POWER_IDLE) //Exponential increase, also affects speed of overheating + +/obj/machinery/shipsensors/emp_act(severity) + if(!use_power) + return + take_damage(20/severity) + toggle() + +/obj/machinery/shipsensors/take_damage(value) + health = min(max(health - value, 0),max_health) + if(use_power && health == 0) + toggle() + +/obj/machinery/shipsensors/weak + heat_reduction = 0.2 + desc = "Miniaturized gravity scanner with various other sensors, used to detect irregularities in surrounding space. Can only run in vacuum to protect delicate quantum bluespace elements." \ No newline at end of file diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm new file mode 100644 index 0000000000..6e6304f1f8 --- /dev/null +++ b/code/modules/overmap/ships/computers/ship.dm @@ -0,0 +1,100 @@ +/* +While these computers can be placed anywhere, they will only function if placed on either a non-space, non-shuttle turf +with an /obj/effect/overmap/visitable/ship present elsewhere on that z level, or else placed in a shuttle area with an /obj/effect/overmap/visitable/ship +somewhere on that shuttle. Subtypes of these can be then used to perform ship overmap movement functions. +*/ +/obj/machinery/computer/ship + var/obj/effect/overmap/visitable/ship/linked + var/list/viewers // Weakrefs to mobs in direct-view mode. + var/extra_view = 0 // how much the view is increased by when the mob is in overmap mode. + +// A late init operation called in SSshuttles, used to attach the thing to the right ship. +/obj/machinery/computer/ship/proc/attempt_hook_up(obj/effect/overmap/visitable/ship/sector) + if(!istype(sector)) + return + if(sector.check_ownership(src)) + linked = sector + return 1 + +/obj/machinery/computer/ship/proc/sync_linked(var/user = null) + var/obj/effect/overmap/visitable/ship/sector = get_overmap_sector(z) + if(!sector) + return + . = attempt_hook_up_recursive(sector) + if(. && linked && user) + to_chat(user, "[src] reconnected to [linked]") + user << browse(null, "window=[src]") // close reconnect dialog + +/obj/machinery/computer/ship/proc/attempt_hook_up_recursive(obj/effect/overmap/visitable/ship/sector) + if(attempt_hook_up(sector)) + return sector + for(var/obj/effect/overmap/visitable/ship/candidate in sector) + if((. = .(candidate))) + return + +/obj/machinery/computer/ship/proc/display_reconnect_dialog(var/mob/user, var/flavor) + var/datum/browser/popup = new (user, "[src]", "[src]") + popup.set_content("
    Error
    Unable to connect to [flavor].
    Reconnect
    ") + popup.open() + +// In computer_shims for now - we had to define it. +// /obj/machinery/computer/ship/interface_interact(var/mob/user) +// ui_interact(user) +// return TRUE + +/obj/machinery/computer/ship/OnTopic(var/mob/user, var/list/href_list) + if(..()) + return TOPIC_HANDLED + if(href_list["sync"]) + sync_linked(user) + return TOPIC_REFRESH + if(href_list["close"]) + unlook(user) + user.unset_machine() + return TOPIC_HANDLED + return TOPIC_NOACTION + +// Management of mob view displacement. look to shift view to the ship on the overmap; unlook to shift back. + +/obj/machinery/computer/ship/proc/look(var/mob/user) + if(linked) + user.reset_view(linked) + user.set_viewsize(world.view + extra_view) + GLOB.moved_event.register(user, src, /obj/machinery/computer/ship/proc/unlook) + // TODO GLOB.stat_set_event.register(user, src, /obj/machinery/computer/ship/proc/unlook) + LAZYDISTINCTADD(viewers, weakref(user)) + +/obj/machinery/computer/ship/proc/unlook(var/mob/user) + user.reset_view() + user.set_viewsize() // reset to default + GLOB.moved_event.unregister(user, src, /obj/machinery/computer/ship/proc/unlook) + // TODO GLOB.stat_set_event.unregister(user, src, /obj/machinery/computer/ship/proc/unlook) + LAZYREMOVE(viewers, weakref(user)) + +/obj/machinery/computer/ship/proc/viewing_overmap(mob/user) + return (weakref(user) in viewers) + +/obj/machinery/computer/ship/CouldNotUseTopic(mob/user) + . = ..() + unlook(user) + +/obj/machinery/computer/ship/CouldUseTopic(mob/user) + . = ..() + if(viewing_overmap(user)) + look(user) + +/obj/machinery/computer/ship/check_eye(var/mob/user) + if (!get_dist(user, src) > 1 || user.blinded || !linked ) + unlook(user) + return -1 + else + return 0 + +/obj/machinery/computer/ship/sensors/Destroy() + sensors = null + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + unlook(M) + . = ..() \ No newline at end of file diff --git a/code/modules/overmap/ships/computers/shuttle.dm b/code/modules/overmap/ships/computers/shuttle.dm index 09bf47512d..42904d2a42 100644 --- a/code/modules/overmap/ships/computers/shuttle.dm +++ b/code/modules/overmap/ships/computers/shuttle.dm @@ -1,139 +1,46 @@ //Shuttle controller computer for shuttles going between sectors -/datum/shuttle/ferry/var/range = 0 //how many overmap tiles can shuttle go, for picking destinatiosn and returning. /obj/machinery/computer/shuttle_control/explore - name = "exploration shuttle console" - shuttle_tag = "Exploration" - req_access = list() - var/landing_type //area for shuttle ship-side - var/obj/effect/map/destination //current destination - var/obj/effect/map/home //current destination + name = "general shuttle control console" + circuit = /obj/item/weapon/circuitboard/shuttle_console/explore + ui_template = "shuttle_control_console_exploration.tmpl" -/obj/machinery/computer/shuttle_control/explore/Initialize() +/obj/machinery/computer/shuttle_control/explore/get_ui_data(var/datum/shuttle/autodock/overmap/shuttle) . = ..() - home = map_sectors["[z]"] - shuttle_tag = "[shuttle_tag]-[z]" - if(!shuttle_controller.shuttles[shuttle_tag]) - var/datum/shuttle/ferry/shuttle = new() - shuttle.warmup_time = 10 - shuttle.area_station = locate(landing_type) - shuttle.area_offsite = shuttle.area_station - shuttle_controller.shuttles[shuttle_tag] = shuttle - shuttle_controller.process_shuttles += shuttle - testing("Exploration shuttle '[shuttle_tag]' at z-level [z] successfully added.") + if(istype(shuttle)) + var/total_gas = 0 + for(var/obj/structure/fuel_port/FP in shuttle.fuel_ports) //loop through fuel ports + var/obj/item/weapon/tank/fuel_tank = locate() in FP + if(fuel_tank) + total_gas += fuel_tank.air_contents.total_moles -//Sets destination to new sector. Can be null. -/obj/machinery/computer/shuttle_control/explore/proc/update_destination(var/obj/effect/map/D) - destination = D - if(destination && shuttle_controller.shuttles[shuttle_tag]) - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - shuttle.area_offsite = destination.shuttle_landing - testing("Shuttle controller [shuttle_tag] now sends shuttle to [destination]") - shuttle_controller.shuttles[shuttle_tag] = shuttle + var/fuel_span = "good" + if(total_gas < shuttle.fuel_consumption * 2) + fuel_span = "bad" -//Gets all sectors with landing zones in shuttle's range -/obj/machinery/computer/shuttle_control/explore/proc/get_possible_destinations() - var/list/res = list() - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - for (var/obj/effect/map/S in orange(shuttle.range, home)) - if(S.shuttle_landing) - res += S - return res + . += list( + "destination_name" = shuttle.get_destination_name(), + "can_pick" = shuttle.moving_status == SHUTTLE_IDLE, + "fuel_usage" = shuttle.fuel_consumption * 100, + "remaining_fuel" = round(total_gas, 0.01) * 100, + "fuel_span" = fuel_span + ) -//Checks if current destination is still reachable -/obj/machinery/computer/shuttle_control/explore/proc/check_destination() - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - return shuttle && destination && get_dist(home, destination) <= shuttle.range +/obj/machinery/computer/shuttle_control/explore/handle_topic_href(var/datum/shuttle/autodock/overmap/shuttle, var/list/href_list) + if(ismob(usr)) + var/mob/user = usr + shuttle.operator_skill = user.get_skill_value(/datum/skill/pilot) -/obj/machinery/computer/shuttle_control/explore/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - if (!istype(shuttle)) - return - - //If we are already there, or can't reach place anymore, reset destination - if(!shuttle.location && !check_destination()) - destination = null - - //check if shuttle can fly at all - var/can_go = !isnull(destination) - var/current_destination = destination ? destination.name : "None" - //shuttle doesn't need destination set to return home, as long as it's in range. - if(shuttle.location) - current_destination = "Return" - var/area/offsite = shuttle.area_offsite - var/obj/effect/map/cur_loc = map_sectors["[offsite.z]"] - can_go = (get_dist(home,cur_loc) <= shuttle.range) - - //disable picking locations if there are none, or shuttle is already off-site - var/list/possible_d = get_possible_destinations() - var/can_pick = !shuttle.location && possible_d.len - - var/shuttle_state - switch(shuttle.moving_status) - if(SHUTTLE_IDLE) shuttle_state = "idle" - if(SHUTTLE_WARMUP) shuttle_state = "warmup" - if(SHUTTLE_INTRANSIT) shuttle_state = "in_transit" - - var/shuttle_status - switch (shuttle.process_state) - if(IDLE_STATE) - if (shuttle.in_use) - shuttle_status = "Busy." - else if (!shuttle.location) - shuttle_status = "Standing-by at station." - else - shuttle_status = "Standing-by at offsite location." - if(WAIT_LAUNCH, FORCE_LAUNCH) - shuttle_status = "Shuttle has received command and will depart shortly." - if(WAIT_ARRIVE) - shuttle_status = "Proceeding to destination." - if(WAIT_FINISH) - shuttle_status = "Arriving at destination now." - - data = list( - "destination_name" = current_destination, - "can_pick" = can_pick, - "shuttle_status" = shuttle_status, - "shuttle_state" = shuttle_state, - "has_docking" = shuttle.docking_controller? 1 : 0, - "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, - "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, - "can_launch" = can_go && shuttle.can_launch(), - "can_cancel" = can_go && shuttle.can_cancel(), - "can_force" = can_go && shuttle.can_force(), - ) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - - if (!ui) - ui = new(user, src, ui_key, "shuttle_control_console_exploration.tmpl", "[shuttle_tag] Shuttle Control", 470, 310) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/shuttle_control/explore/Topic(href, href_list) - if(..()) - return 1 - - usr.set_machine(src) - src.add_fingerprint(usr) - - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - if (!istype(shuttle)) + if((. = ..()) != null) return if(href_list["pick"]) - var/obj/effect/map/self = map_sectors["[z]"] - if(self) - var/list/possible_d = get_possible_destinations() - var/obj/effect/map/D - if(possible_d.len) - D = input("Choose shuttle destination", "Shuttle Destination") as null|anything in possible_d - update_destination(D) - - if(href_list["move"]) - shuttle.launch(src) - if(href_list["force"]) - shuttle.force_launch(src) - else if(href_list["cancel"]) - shuttle.cancel_launch(src) \ No newline at end of file + var/list/possible_d = shuttle.get_possible_destinations() + var/D + if(possible_d.len) + D = input("Choose shuttle destination", "Shuttle Destination") as null|anything in possible_d + else + to_chat(usr,"No valid landing sites in range.") + possible_d = shuttle.get_possible_destinations() + if(CanInteract(usr, global.default_state) && (D in possible_d)) + shuttle.set_destination(possible_d[D]) + return TOPIC_REFRESH diff --git a/code/modules/overmap/ships/engines/engine.dm b/code/modules/overmap/ships/engines/engine.dm index 6e927e6431..a5d3bc7016 100644 --- a/code/modules/overmap/ships/engines/engine.dm +++ b/code/modules/overmap/ships/engines/engine.dm @@ -3,58 +3,43 @@ var/list/ship_engines = list() /datum/ship_engine var/name = "ship engine" - var/obj/machinery/engine //actual engine object - var/zlevel = 0 + var/obj/machinery/holder //actual engine object -/datum/ship_engine/New(var/obj/machinery/holder) - engine = holder - zlevel = holder.z - for(var/obj/machinery/computer/engines/E in machines) - if (E.z == zlevel && !(src in E.engines)) - E.engines += src - break +/datum/ship_engine/New(var/obj/machinery/_holder) + ..() + holder = _holder + ship_engines += src -//Tries to fire the engine. If successfull, returns 1 +/datum/ship_engine/proc/can_burn() + return 0 + +//Tries to fire the engine. Returns thrust /datum/ship_engine/proc/burn() - if(!engine) - die() - return 1 + return 0 //Returns status string for this engine /datum/ship_engine/proc/get_status() - if(!engine) - die() return "All systems nominal" /datum/ship_engine/proc/get_thrust() - if(!engine) - die() - return 100 + return 1 //Sets thrust limiter, a number between 0 and 1 /datum/ship_engine/proc/set_thrust_limit(var/new_limit) - if(!engine) - die() return 1 /datum/ship_engine/proc/get_thrust_limit() - if(!engine) - die() return 1 /datum/ship_engine/proc/is_on() - if(!engine) - die() return 1 /datum/ship_engine/proc/toggle() - if(!engine) - die() return 1 -/datum/ship_engine/proc/die() - for(var/obj/machinery/computer/engines/E in machines) - if (E.z == zlevel) - E.engines -= src - break - qdel(src) \ No newline at end of file +/datum/ship_engine/Destroy() + ship_engines -= src + for(var/obj/effect/overmap/visitable/ship/S in SSshuttles.ships) + S.engines -= src + holder = null + . = ..() \ No newline at end of file diff --git a/code/modules/overmap/ships/engines/gas_thruster.dm b/code/modules/overmap/ships/engines/gas_thruster.dm new file mode 100644 index 0000000000..04101984c4 --- /dev/null +++ b/code/modules/overmap/ships/engines/gas_thruster.dm @@ -0,0 +1,218 @@ +//Gas nozzle engine +/datum/ship_engine/gas_thruster + name = "gas thruster" + var/obj/machinery/atmospherics/unary/engine/nozzle + +/datum/ship_engine/gas_thruster/New(var/obj/machinery/_holder) + ..() + nozzle = _holder + +/datum/ship_engine/gas_thruster/Destroy() + nozzle = null + . = ..() + +/datum/ship_engine/gas_thruster/get_status() + return nozzle.get_status() + +/datum/ship_engine/gas_thruster/get_thrust() + return nozzle.get_thrust() + +/datum/ship_engine/gas_thruster/burn() + return nozzle.burn() + +/datum/ship_engine/gas_thruster/set_thrust_limit(var/new_limit) + nozzle.thrust_limit = new_limit + +/datum/ship_engine/gas_thruster/get_thrust_limit() + return nozzle.thrust_limit + +/datum/ship_engine/gas_thruster/is_on() + if(nozzle.use_power && nozzle.operable()) + if(nozzle.next_on > world.time) + return -1 + else + return 1 + return 0 + +/datum/ship_engine/gas_thruster/toggle() + if(nozzle.use_power) + nozzle.update_use_power(USE_POWER_OFF) + else + if(nozzle.blockage) + if(nozzle.check_blockage()) + return + nozzle.update_use_power(USE_POWER_IDLE) + if(nozzle.stat & NOPOWER)//try again + nozzle.power_change() + if(nozzle.is_on())//if everything is in working order, start booting! + nozzle.next_on = world.time + nozzle.boot_time + +/datum/ship_engine/gas_thruster/can_burn() + return nozzle.is_on() && nozzle.check_fuel() + +//Actual thermal nozzle engine object + +/obj/machinery/atmospherics/unary/engine + name = "rocket nozzle" + desc = "Simple rocket nozzle, expelling gas at hypersonic velocities to propell the ship." + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "nozzle" + opacity = 1 + density = 1 + can_atmos_pass = ATMOS_PASS_NO + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_FUEL + + // construct_state = /decl/machine_construction/default/panel_closed + // maximum_component_parts = list(/obj/item/weapon/stock_parts = 6)//don't want too many, let upgraded component shine + // uncreated_component_parts = list(/obj/item/weapon/stock_parts/power/apc/buildable = 1) + + use_power = USE_POWER_OFF + power_channel = EQUIP + idle_power_usage = 1000 + + var/datum/ship_engine/gas_thruster/controller + var/thrust_limit = 1 //Value between 1 and 0 to limit the resulting thrust + var/volume_per_burn = 15 //20 litres(with bin) + var/charge_per_burn = 3600 + var/boot_time = 35 + var/next_on + var/blockage + +/obj/machinery/atmospherics/unary/engine/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + return 0 + +/obj/machinery/atmospherics/unary/engine/Initialize() + . = ..() + controller = new(src) + update_nearby_tiles(need_rebuild=1) + + for(var/ship in SSshuttles.ships) + var/obj/effect/overmap/visitable/ship/S = ship + if(S.check_ownership(src)) + S.engines |= controller + if(dir != S.fore_dir) + set_broken(TRUE) + break + +/obj/machinery/atmospherics/unary/engine/Destroy() + QDEL_NULL(controller) + update_nearby_tiles() + . = ..() + +/obj/machinery/atmospherics/unary/engine/proc/get_status() + . = list() + .+= "Location: [get_area(src)]." + if(stat & NOPOWER) + .+= "Insufficient power to operate." + if(!check_fuel()) + .+= "Insufficient fuel for a burn." + if(stat & BROKEN) + .+= "Inoperable engine configuration." + if(blockage) + .+= "Obstruction of airflow detected." + + .+= "Propellant total mass: [round(air_contents.get_mass(),0.01)] kg." + .+= "Propellant used per burn: [round(air_contents.get_mass() * volume_per_burn * thrust_limit / air_contents.volume,0.01)] kg." + .+= "Propellant pressure: [round(air_contents.return_pressure()/1000,0.1)] MPa." + . = jointext(.,"
    ") + +/obj/machinery/atmospherics/unary/engine/power_change() + . = ..() + if(stat & NOPOWER) + update_use_power(USE_POWER_OFF) + +/obj/machinery/atmospherics/unary/engine/proc/is_on() + return use_power && operable() && (next_on < world.time) + +/obj/machinery/atmospherics/unary/engine/proc/check_fuel() + return air_contents.total_moles > 5 // minimum fuel usage is five moles, for EXTREMELY hot mix or super low pressure + +/obj/machinery/atmospherics/unary/engine/proc/get_thrust() + if(!is_on() || !check_fuel()) + return 0 + var/used_part = volume_per_burn * thrust_limit / air_contents.volume + . = calculate_thrust(air_contents, used_part) + return + +/obj/machinery/atmospherics/unary/engine/proc/check_blockage() + blockage = FALSE + var/exhaust_dir = reverse_direction(dir) + var/turf/A = get_step(src, exhaust_dir) + var/turf/B = A + while(isturf(A) && !(istype(A, /turf/space) || isopenspace(A))) + if((B.c_airblock(A)) & AIR_BLOCKED) + blockage = TRUE + break + B = A + A = get_step(A, exhaust_dir) + return blockage + +/obj/machinery/atmospherics/unary/engine/proc/burn() + if(!is_on()) + return 0 + if(!check_fuel() || (0 < use_power_oneoff(charge_per_burn)) || check_blockage()) + audible_message(src,"[src] coughs once and goes silent!") + update_use_power(USE_POWER_OFF) + return 0 + + var/datum/gas_mixture/removed = air_contents.remove_ratio(volume_per_burn * thrust_limit / air_contents.volume) + if(!removed) + return 0 + . = calculate_thrust(removed) + playsound(loc, 'sound/machines/thruster.ogg', 100 * thrust_limit, 0, world.view * 4, 0.1) + if(network) + network.update = 1 + + var/exhaust_dir = reverse_direction(dir) + var/turf/T = get_step(src,exhaust_dir) + if(T) + T.assume_air(removed) + new/obj/effect/engine_exhaust(T, exhaust_dir, air_contents.check_combustability() && air_contents.temperature >= PHORON_MINIMUM_BURN_TEMPERATURE) + +/obj/machinery/atmospherics/unary/engine/proc/calculate_thrust(datum/gas_mixture/propellant, used_part = 1) + return round(sqrt(propellant.get_mass() * used_part * sqrt(air_contents.return_pressure()/200)),0.1) + +/obj/machinery/atmospherics/unary/engine/RefreshParts() + ..() + //allows them to upgrade the max limit of fuel intake (which only gives diminishing returns) for increase in max thrust but massive reduction in fuel economy + var/bin_upgrade = 5 * CLAMP(total_component_rating_of_type(/obj/item/weapon/stock_parts/matter_bin), 0, 6)//5 litre per rank + volume_per_burn = bin_upgrade ? initial(volume_per_burn) + bin_upgrade : 2 //Penalty missing part: 10% fuel use, no thrust + boot_time = bin_upgrade ? initial(boot_time) - bin_upgrade : initial(boot_time) * 2 + //energy cost - thb all of this is to limit the use of back up batteries + var/energy_upgrade = CLAMP(total_component_rating_of_type(/obj/item/weapon/stock_parts/capacitor), 0.1, 6) + charge_per_burn = initial(charge_per_burn) / energy_upgrade + change_power_consumption(initial(idle_power_usage) / energy_upgrade, USE_POWER_IDLE) + +//Exhaust effect +/obj/effect/engine_exhaust + name = "engine exhaust" + icon = 'icons/effects/effects.dmi' + icon_state = "smoke" + light_color = "#ed9200" + anchored = 1 + +/obj/effect/engine_exhaust/New(var/turf/nloc, var/ndir, var/flame) + ..(nloc) + if(flame) + icon_state = "exhaust" + nloc.hotspot_expose(1000,125) + set_light(0.5, 3) + set_dir(ndir) + QDEL_IN(src, 20) + +/obj/item/weapon/circuitboard/unary_atmos/engine //why don't we move this elsewhere? + name = T_BOARD("gas thruster") + icon_state = "mcontroller" + build_path = /obj/machinery/atmospherics/unary/engine + origin_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 2) + req_components = list( + /obj/item/stack/cable_coil = 30, + /obj/item/pipe = 2, + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/stock_parts/capacitor = 2) + +// Not Implemented - Variant that pulls power from cables. Too complicated without bay's power components. +// /obj/machinery/atmospherics/unary/engine/terminal +// base_type = /obj/machinery/atmospherics/unary/engine +// stock_part_presets = list(/decl/stock_part_preset/terminal_setup) +// uncreated_component_parts = list(/obj/item/weapon/stock_parts/power/terminal/buildable = 1) diff --git a/code/modules/overmap/ships/engines/ion_thruster.dm b/code/modules/overmap/ships/engines/ion_thruster.dm new file mode 100644 index 0000000000..3252159391 --- /dev/null +++ b/code/modules/overmap/ships/engines/ion_thruster.dm @@ -0,0 +1,86 @@ +/datum/ship_engine/ion + name = "ion thruster" + var/obj/machinery/ion_engine/thruster + +/datum/ship_engine/ion/New(var/obj/machinery/_holder) + ..() + thruster = _holder + +/datum/ship_engine/ion/Destroy() + thruster = null + . = ..() + +/datum/ship_engine/ion/get_status() + return thruster.get_status() + +/datum/ship_engine/ion/get_thrust() + return thruster.get_thrust() + +/datum/ship_engine/ion/burn() + return thruster.burn() + +/datum/ship_engine/ion/set_thrust_limit(var/new_limit) + thruster.thrust_limit = new_limit + +/datum/ship_engine/ion/get_thrust_limit() + return thruster.thrust_limit + +/datum/ship_engine/ion/is_on() + return thruster.on && thruster.powered() + +/datum/ship_engine/ion/toggle() + thruster.on = !thruster.on + +/datum/ship_engine/ion/can_burn() + return thruster.on && thruster.powered() + +/obj/machinery/ion_engine + name = "ion propulsion device" + desc = "An advanced ion propulsion device, using energy and minutes amount of gas to generate thrust." + icon = 'icons/turf/shuttle_parts.dmi' + icon_state = "nozzle" + power_channel = ENVIRON + idle_power_usage = 100 + anchored = TRUE + // construct_state = /decl/machine_construction/default/panel_closed + var/datum/ship_engine/ion/controller + var/thrust_limit = 1 + var/on = 1 + var/burn_cost = 7500 + var/generated_thrust = 2.5 + +/obj/machinery/ion_engine/Initialize() + . = ..() + controller = new(src) + +/obj/machinery/ion_engine/Destroy() + QDEL_NULL(controller) + . = ..() + +/obj/machinery/ion_engine/proc/get_status() + . = list() + .+= "Location: [get_area(src)]." + if(!powered()) + .+= "Insufficient power to operate." + + . = jointext(.,"
    ") + +/obj/machinery/ion_engine/proc/burn() + if(!on && !powered()) + return 0 + use_power_oneoff(burn_cost) + . = thrust_limit * generated_thrust + +/obj/machinery/ion_engine/proc/get_thrust() + return thrust_limit * generated_thrust * on + +/obj/item/weapon/circuitboard/engine/ion + name = T_BOARD("ion propulsion device") + board_type = "machine" + icon_state = "mcontroller" + build_path = /obj/machinery/ion_engine + origin_tech = list(TECH_POWER = 1, TECH_ENGINEERING = 2) + req_components = list( + /obj/item/stack/cable_coil = 2, + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/stock_parts/capacitor = 2) \ No newline at end of file diff --git a/code/modules/overmap/ships/landable.dm b/code/modules/overmap/ships/landable.dm new file mode 100644 index 0000000000..0a28eefd8d --- /dev/null +++ b/code/modules/overmap/ships/landable.dm @@ -0,0 +1,173 @@ +// These come with shuttle functionality. Need to be assigned a (unique) shuttle datum name. +// Mapping location doesn't matter, so long as on a map loaded at the same time as the shuttle areas. +// Multiz shuttles currently not supported. Non-autodock shuttles currently not supported. + +/obj/effect/overmap/visitable/ship/landable + var/shuttle // Name of associated shuttle. Must be autodock. + var/obj/effect/shuttle_landmark/ship/landmark // Record our open space landmark for easy reference. + var/multiz = 0 // Index of multi-z levels, starts at 0 + var/status = SHIP_STATUS_LANDED + icon_state = "shuttle" + moving_state = "shuttle_moving" + +/obj/effect/overmap/visitable/ship/landable/Destroy() + GLOB.shuttle_moved_event.unregister(SSshuttles.shuttles[shuttle], src) + return ..() + +/obj/effect/overmap/visitable/ship/landable/can_burn() + if(status != SHIP_STATUS_OVERMAP) + return 0 + return ..() + +/obj/effect/overmap/visitable/ship/landable/burn() + if(status != SHIP_STATUS_OVERMAP) + return 0 + return ..() + +/obj/effect/overmap/visitable/ship/landable/check_ownership(obj/object) + var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle] + if(!shuttle_datum) + return + var/list/areas = shuttle_datum.find_childfree_areas() + if(get_area(object) in areas) + return 1 + +// We autobuild our z levels. +/obj/effect/overmap/visitable/ship/landable/find_z_levels() + for(var/i = 0 to multiz) + world.increment_max_z() + map_z += world.maxz + + var/turf/center_loc = locate(round(world.maxx/2), round(world.maxy/2), world.maxz) + landmark = new (center_loc, shuttle) + add_landmark(landmark, shuttle) + + var/visitor_dir = fore_dir + for(var/landmark_name in list("FORE", "PORT", "AFT", "STARBOARD")) + var/turf/visitor_turf = get_ranged_target_turf(center_loc, visitor_dir, round(min(world.maxx/4, world.maxy/4))) + var/obj/effect/shuttle_landmark/visiting_shuttle/visitor_landmark = new (visitor_turf, landmark, landmark_name) + add_landmark(visitor_landmark) + visitor_dir = turn(visitor_dir, 90) + + if(multiz) + new /obj/effect/landmark/map_data(center_loc, (multiz + 1)) + +/obj/effect/overmap/visitable/ship/landable/get_areas() + var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle] + if(!shuttle_datum) + return list() + return shuttle_datum.find_childfree_areas() + +/obj/effect/overmap/visitable/ship/landable/populate_sector_objects() + ..() + var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle] + if(istype(shuttle_datum,/datum/shuttle/autodock/overmap)) + var/datum/shuttle/autodock/overmap/oms = shuttle_datum + oms.myship = src + GLOB.shuttle_moved_event.register(shuttle_datum, src, .proc/on_shuttle_jump) + on_landing(landmark, shuttle_datum.current_location) // We "land" at round start to properly place ourselves on the overmap. + +/obj/effect/shuttle_landmark/ship + name = "Open Space" + landmark_tag = "ship" + flags = SLANDMARK_FLAG_AUTOSET | SLANDMARK_FLAG_ZERO_G + var/shuttle_name + var/list/visitors // landmark -> visiting shuttle stationed there + +/obj/effect/shuttle_landmark/ship/Initialize(mapload, shuttle_name) + landmark_tag += "_[shuttle_name]" + src.shuttle_name = shuttle_name + . = ..() + +/obj/effect/shuttle_landmark/ship/Destroy() + var/obj/effect/overmap/visitable/ship/landable/ship = get_overmap_sector(z) + if(istype(ship) && ship.landmark == src) + ship.landmark = null + . = ..() + +/obj/effect/shuttle_landmark/ship/cannot_depart(datum/shuttle/shuttle) + if(LAZYLEN(visitors)) + return "Grappled by other shuttle; cannot manouver." + +/obj/effect/shuttle_landmark/visiting_shuttle + flags = SLANDMARK_FLAG_AUTOSET | SLANDMARK_FLAG_ZERO_G + var/obj/effect/shuttle_landmark/ship/core_landmark + +/obj/effect/shuttle_landmark/visiting_shuttle/Initialize(mapload, obj/effect/shuttle_landmark/ship/master, _name) + core_landmark = master + name = _name + landmark_tag = master.shuttle_name + _name + GLOB.destroyed_event.register(master, src, /datum/proc/qdel_self) + . = ..() + +/obj/effect/shuttle_landmark/visiting_shuttle/Destroy() + GLOB.destroyed_event.unregister(core_landmark, src) + LAZYREMOVE(core_landmark.visitors, src) + core_landmark = null + . = ..() + +/obj/effect/shuttle_landmark/visiting_shuttle/is_valid(datum/shuttle/shuttle) + . = ..() + if(!.) + return + var/datum/shuttle/boss_shuttle = SSshuttles.shuttles[core_landmark.shuttle_name] + if(boss_shuttle.current_location != core_landmark) + return FALSE // Only available when our governing shuttle is in space. + if(shuttle == boss_shuttle) // Boss shuttle only lands on main landmark + return FALSE + +/obj/effect/shuttle_landmark/visiting_shuttle/shuttle_arrived(datum/shuttle/shuttle) + LAZYSET(core_landmark.visitors, src, shuttle) + GLOB.shuttle_moved_event.register(shuttle, src, .proc/shuttle_left) + +/obj/effect/shuttle_landmark/visiting_shuttle/proc/shuttle_left(datum/shuttle/shuttle, obj/effect/shuttle_landmark/old_landmark, obj/effect/shuttle_landmark/new_landmark) + if(old_landmark == src) + GLOB.shuttle_moved_event.unregister(shuttle, src) + LAZYREMOVE(core_landmark.visitors, src) + +/obj/effect/overmap/visitable/ship/landable/proc/on_shuttle_jump(datum/shuttle/given_shuttle, obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into) + if(given_shuttle != SSshuttles.shuttles[shuttle]) + return + var/datum/shuttle/autodock/auto = given_shuttle + if(into == auto.landmark_transition) + status = SHIP_STATUS_TRANSIT + on_takeoff(from, into) + return + if(into == landmark) + status = SHIP_STATUS_OVERMAP + on_takeoff(from, into) + return + status = SHIP_STATUS_LANDED + on_landing(from, into) + +/obj/effect/overmap/visitable/ship/landable/proc/on_landing(obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into) + var/obj/effect/overmap/visitable/target = get_overmap_sector(get_z(into)) + var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle] + if(into.landmark_tag == shuttle_datum.motherdock) // If our motherdock is a landable ship, it won't be found properly here so we need to find it manually. + for(var/obj/effect/overmap/visitable/ship/landable/landable in SSshuttles.ships) + if(landable.shuttle == shuttle_datum.mothershuttle) + target = landable + break + if(!target || target == src) + return + forceMove(target) + halt() + +/obj/effect/overmap/visitable/ship/landable/proc/on_takeoff(obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into) + if(!isturf(loc)) + forceMove(get_turf(loc)) + unhalt() + +/obj/effect/overmap/visitable/ship/landable/get_landed_info() + switch(status) + if(SHIP_STATUS_LANDED) + var/obj/effect/overmap/visitable/location = loc + if(istype(loc, /obj/effect/overmap/visitable/sector)) + return "Landed on \the [location.name]. Use secondary thrust to get clear before activating primary engines." + if(istype(loc, /obj/effect/overmap/visitable/ship)) + return "Docked with \the [location.name]. Use secondary thrust to get clear before activating primary engines." + return "Docked with an unknown object." + if(SHIP_STATUS_TRANSIT) + return "Maneuvering under secondary thrust." + if(SHIP_STATUS_OVERMAP) + return "In open space." \ No newline at end of file diff --git a/code/modules/overmap/ships/ship.dm b/code/modules/overmap/ships/ship.dm index 0abd450a69..4dcecc5d6e 100644 --- a/code/modules/overmap/ships/ship.dm +++ b/code/modules/overmap/ships/ship.dm @@ -1,116 +1,207 @@ -/obj/effect/map/ship +#define SHIP_MOVE_RESOLUTION 0.00001 +#define MOVING(speed) abs(speed) >= min_speed +#define SANITIZE_SPEED(speed) SIGN(speed) * CLAMP(abs(speed), 0, max_speed) +#define CHANGE_SPEED_BY(speed_var, v_diff) \ + v_diff = SANITIZE_SPEED(v_diff);\ + if(!MOVING(speed_var + v_diff)) \ + {speed_var = 0};\ + else \ + {speed_var = SANITIZE_SPEED((speed_var + v_diff)/(1 + speed_var*v_diff/(max_speed ** 2)))} +// Uses Lorentzian dynamics to avoid going too fast. + +/obj/effect/overmap/visitable/ship name = "generic ship" desc = "Space faring vessel." - icon_state = "sheet-sandstone" - var/vessel_mass = 9000 //tonnes, random number - var/default_delay = 60 - var/list/speed = list(0,0) - var/last_burn = 0 - var/list/last_movement = list(0,0) - var/fore_dir = NORTH - var/rotate = 1 //For proc rotate + icon_state = "ship" + var/moving_state = "ship_moving" - var/obj/effect/map/current_sector - var/obj/machinery/computer/helm/nav_control - var/obj/machinery/computer/engines/eng_control + var/vessel_mass = 10000 //tonnes, arbitrary number, affects acceleration provided by engines + var/vessel_size = SHIP_SIZE_LARGE //arbitrary number, affects how likely are we to evade meteors + var/max_speed = 1/(1 SECOND) //"speed of light" for the ship, in turfs/tick. + var/min_speed = 1/(2 MINUTES) // Below this, we round speed to 0 to avoid math errors. -/obj/effect/map/ship/Initialize() + var/list/speed = list(0,0) //speed in x,y direction + var/last_burn = 0 //worldtime when ship last acceleated + var/burn_delay = 1 SECOND //how often ship can do burns + var/list/last_movement = list(0,0) //worldtime when ship last moved in x,y direction + var/fore_dir = NORTH //what dir ship flies towards for purpose of moving stars effect procs + + var/list/engines = list() + var/engines_state = 0 //global on/off toggle for all engines + var/thrust_limit = 1 //global thrust limit for all engines, 0..1 + var/halted = 0 //admin halt or other stop. + var/skill_needed = SKILL_ADEPT //piloting skill needed to steer it without going in random dir + var/operator_skill + +/obj/effect/overmap/visitable/ship/Initialize() . = ..() - for(var/obj/machinery/computer/engines/E in machines) - if (E.z == map_z) - eng_control = E - break - for(var/obj/machinery/computer/helm/H in machines) - if (H.z == map_z) - nav_control = H - break + min_speed = round(min_speed, SHIP_MOVE_RESOLUTION) + max_speed = round(max_speed, SHIP_MOVE_RESOLUTION) + SSshuttles.ships += src START_PROCESSING(SSobj, src) -/obj/effect/map/ship/relaymove(mob/user, direction) - accelerate(direction) +/obj/effect/overmap/visitable/ship/Destroy() + STOP_PROCESSING(SSobj, src) + SSshuttles.ships -= src + . = ..() -/obj/effect/map/ship/proc/is_still() - return !(speed[1] || speed[2]) +/obj/effect/overmap/visitable/ship/relaymove(mob/user, direction, accel_limit) + accelerate(direction, accel_limit) + operator_skill = user.get_skill_value(/datum/skill/pilot) -/obj/effect/map/ship/proc/get_acceleration() - return eng_control.get_total_thrust()/vessel_mass +/obj/effect/overmap/visitable/ship/proc/is_still() + return !MOVING(speed[1]) && !MOVING(speed[2]) -/obj/effect/map/ship/proc/get_speed() - return round(sqrt(speed[1]*speed[1] + speed[2]*speed[2])) +/obj/effect/overmap/visitable/ship/get_scan_data(mob/user) + . = ..() + if(!is_still()) + . += "
    Heading: [get_heading_degrees()], speed [get_speed() * 1000]" -/obj/effect/map/ship/proc/get_heading() +//Projected acceleration based on information from engines +/obj/effect/overmap/visitable/ship/proc/get_acceleration() + return round(get_total_thrust()/get_vessel_mass(), SHIP_MOVE_RESOLUTION) + +//Does actual burn and returns the resulting acceleration +/obj/effect/overmap/visitable/ship/proc/get_burn_acceleration() + return round(burn() / get_vessel_mass(), SHIP_MOVE_RESOLUTION) + +/obj/effect/overmap/visitable/ship/proc/get_vessel_mass() + . = vessel_mass + for(var/obj/effect/overmap/visitable/ship/ship in src) + . += ship.get_vessel_mass() + +/obj/effect/overmap/visitable/ship/proc/get_speed() + return round(sqrt(speed[1] ** 2 + speed[2] ** 2), SHIP_MOVE_RESOLUTION) + +// Get heading in BYOND dir bits +/obj/effect/overmap/visitable/ship/proc/get_heading() var/res = 0 - if(speed[1]) + if(MOVING(speed[1])) if(speed[1] > 0) res |= EAST else res |= WEST - if(speed[2]) + if(MOVING(speed[2])) if(speed[2] > 0) res |= NORTH else res |= SOUTH return res -/obj/effect/map/ship/proc/adjust_speed(n_x, n_y) - speed[1] = CLAMP(speed[1] + n_x, -default_delay, default_delay) - speed[2] = CLAMP(speed[2] + n_y, -default_delay, default_delay) - if(is_still()) - toggle_move_stars(map_z) - else - toggle_move_stars(map_z, fore_dir) +// Get heading in degrees (like a compass heading) +/obj/effect/overmap/visitable/ship/proc/get_heading_degrees() + return (ATAN2(speed[2], speed[1]) + 360) % 360 // Yes ATAN2(y, x) is correct to get clockwise degrees -/obj/effect/map/ship/proc/can_burn() - if (!eng_control) - return 0 - if (world.time < last_burn + 10) - return 0 - if (!eng_control.burn()) - return 0 - return 1 +/obj/effect/overmap/visitable/ship/proc/adjust_speed(n_x, n_y) + CHANGE_SPEED_BY(speed[1], n_x) + CHANGE_SPEED_BY(speed[2], n_y) + for(var/zz in map_z) + if(is_still()) + toggle_move_stars(zz) + else + toggle_move_stars(zz, fore_dir) + update_icon() -/obj/effect/map/ship/proc/get_brake_path() +/obj/effect/overmap/visitable/ship/proc/get_brake_path() if(!get_acceleration()) return INFINITY - return get_speed()/get_acceleration() + if(is_still()) + return 0 + if(!burn_delay) + return 0 + if(!get_speed()) + return 0 + var/num_burns = get_speed()/get_acceleration() + 2 //some padding in case acceleration drops form fuel usage + var/burns_per_grid = 1/ (burn_delay * get_speed()) + return round(num_burns/burns_per_grid) -#define SIGN(X) (X == 0 ? 0 : (X > 0 ? 1 : -1)) -/obj/effect/map/ship/proc/decelerate() - if(!is_still() && can_burn()) +/obj/effect/overmap/visitable/ship/proc/decelerate() + if(((speed[1]) || (speed[2])) && can_burn()) if (speed[1]) - adjust_speed(-SIGN(speed[1]) * min(get_acceleration(),abs(speed[1])), 0) + adjust_speed(-SIGN(speed[1]) * min(get_burn_acceleration(),abs(speed[1])), 0) if (speed[2]) - adjust_speed(0, -SIGN(speed[2]) * min(get_acceleration(),abs(speed[2]))) + adjust_speed(0, -SIGN(speed[2]) * min(get_burn_acceleration(),abs(speed[2]))) last_burn = world.time -/obj/effect/map/ship/proc/accelerate(direction) +/obj/effect/overmap/visitable/ship/proc/accelerate(direction, accel_limit) if(can_burn()) last_burn = world.time - + var/acceleration = min(get_burn_acceleration(), accel_limit) if(direction & EAST) - adjust_speed(get_acceleration(), 0) + adjust_speed(acceleration, 0) if(direction & WEST) - adjust_speed(-get_acceleration(), 0) + adjust_speed(-acceleration, 0) if(direction & NORTH) - adjust_speed(0, get_acceleration()) + adjust_speed(0, acceleration) if(direction & SOUTH) - adjust_speed(0, -get_acceleration()) + adjust_speed(0, -acceleration) - -/obj/effect/map/ship/proc/rotate(var/direction) - var/matrix/M = matrix() - M.Turn(dir2angle(direction)) - src.transform = M //Rotate ship - -/obj/effect/map/ship/process() - if(!is_still()) +/obj/effect/overmap/visitable/ship/process() + if(!halted && !is_still()) var/list/deltas = list(0,0) for(var/i=1, i<=2, i++) - if(speed[i] && world.time > last_movement[i] + default_delay - abs(speed[i])) - deltas[i] = speed[i] > 0 ? 1 : -1 + if(MOVING(speed[i]) && world.time > last_movement[i] + 1/abs(speed[i])) + deltas[i] = SIGN(speed[i]) last_movement[i] = world.time var/turf/newloc = locate(x + deltas[1], y + deltas[2], z) if(newloc) Move(newloc) - if(rotate) - rotate(get_heading()) + update_icon() + +/obj/effect/overmap/visitable/ship/update_icon() + if(!is_still()) + icon_state = moving_state + dir = get_heading() + else + icon_state = initial(icon_state) + ..() + +/obj/effect/overmap/visitable/ship/proc/burn() + for(var/datum/ship_engine/E in engines) + . += E.burn() + +/obj/effect/overmap/visitable/ship/proc/get_total_thrust() + for(var/datum/ship_engine/E in engines) + . += E.get_thrust() + +/obj/effect/overmap/visitable/ship/proc/can_burn() + if(halted) + return 0 + if (world.time < last_burn + burn_delay) + return 0 + for(var/datum/ship_engine/E in engines) + . |= E.can_burn() + +//deciseconds to next step +/obj/effect/overmap/visitable/ship/proc/ETA() + . = INFINITY + for(var/i=1, i<=2, i++) + if(MOVING(speed[i])) + . = min(last_movement[i] - world.time + 1/abs(speed[i]), .) + . = max(.,0) + +/obj/effect/overmap/visitable/ship/proc/halt() + adjust_speed(-speed[1], -speed[2]) + halted = 1 + +/obj/effect/overmap/visitable/ship/proc/unhalt() + if(!SSshuttles.overmap_halted) + halted = 0 + +/obj/effect/overmap/visitable/ship/proc/get_helm_skill()//delete this mover operator skill to overmap obj + return operator_skill + +/obj/effect/overmap/visitable/ship/populate_sector_objects() + ..() + for(var/obj/machinery/computer/ship/S in global.machines) + S.attempt_hook_up(src) + for(var/datum/ship_engine/E in ship_engines) + if(check_ownership(E.holder)) + engines |= E + +/obj/effect/overmap/visitable/ship/proc/get_landed_info() + return "This ship cannot land." + +#undef MOVING +#undef SANITIZE_SPEED +#undef CHANGE_SPEED_BY \ No newline at end of file diff --git a/code/modules/overmap/spacetravel.dm b/code/modules/overmap/spacetravel.dm new file mode 100644 index 0000000000..aec5c06f75 --- /dev/null +++ b/code/modules/overmap/spacetravel.dm @@ -0,0 +1,114 @@ +//list used to cache empty zlevels to avoid nedless map bloat +var/list/cached_space = list() + +//Space stragglers go here + +/obj/effect/overmap/visitable/sector/temporary + name = "Deep Space" + invisibility = 101 + known = 0 + +/obj/effect/overmap/visitable/sector/temporary/New(var/nx, var/ny, var/nz) + loc = locate(nx, ny, global.using_map.overmap_z) + x = nx + y = ny + map_z += nz + map_sectors["[nz]"] = src + testing("Temporary sector at [x],[y] was created, corresponding zlevel is [nz].") + +/obj/effect/overmap/visitable/sector/temporary/Destroy() + map_sectors["[map_z]"] = null + testing("Temporary sector at [x],[y] was deleted.") + +/obj/effect/overmap/visitable/sector/temporary/proc/can_die(var/mob/observer) + testing("Checking if sector at [map_z[1]] can die.") + for(var/mob/M in global.player_list) + if(M != observer && (M.z in map_z)) + testing("There are people on it.") + return 0 + return 1 + +proc/get_deepspace(x,y) + var/obj/effect/overmap/visitable/sector/temporary/res = locate(x,y,global.using_map.overmap_z) + if(istype(res)) + return res + else if(cached_space.len) + res = cached_space[cached_space.len] + cached_space -= res + res.x = x + res.y = y + return res + else + return new /obj/effect/overmap/visitable/sector/temporary(x, y, global.using_map.get_empty_zlevel()) + +/atom/movable/proc/lost_in_space() + for(var/atom/movable/AM in contents) + if(!AM.lost_in_space()) + return FALSE + return TRUE + +/mob/lost_in_space() + return isnull(client) + +/mob/living/carbon/human/lost_in_space() + return isnull(client) && !key && stat == DEAD + +proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A) + if (!T || !A) + return + + var/obj/effect/overmap/visitable/M = get_overmap_sector(T.z) + if (!M) + return + + if(A.lost_in_space()) + if(!QDELETED(A)) + qdel(A) + return + + var/nx = 1 + var/ny = 1 + var/nz = 1 + + if(T.x <= TRANSITIONEDGE) + nx = world.maxx - TRANSITIONEDGE - 2 + ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (A.x >= (world.maxx - TRANSITIONEDGE - 1)) + nx = TRANSITIONEDGE + 2 + ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (T.y <= TRANSITIONEDGE) + ny = world.maxy - TRANSITIONEDGE -2 + nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + else if (A.y >= (world.maxy - TRANSITIONEDGE - 1)) + ny = TRANSITIONEDGE + 2 + nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + testing("[A] spacemoving from [M] ([M.x], [M.y]).") + + var/turf/map = locate(M.x,M.y,global.using_map.overmap_z) + var/obj/effect/overmap/visitable/TM + for(var/obj/effect/overmap/visitable/O in map) + if(O != M && O.in_space && prob(50)) + TM = O + break + if(!TM) + TM = get_deepspace(M.x,M.y) + nz = pick(TM.get_space_zlevels()) + + var/turf/dest = locate(nx,ny,nz) + if(istype(dest)) + A.forceMove(dest) + if(ismob(A)) + var/mob/D = A + if(D.pulling) + D.pulling.forceMove(dest) + + if(istype(M, /obj/effect/overmap/visitable/sector/temporary)) + var/obj/effect/overmap/visitable/sector/temporary/source = M + if (source.can_die()) + testing("Caching [M] for future use") + source.loc = null + cached_space += source diff --git a/code/modules/overmap/turfs.dm b/code/modules/overmap/turfs.dm new file mode 100644 index 0000000000..fc9f0b16eb --- /dev/null +++ b/code/modules/overmap/turfs.dm @@ -0,0 +1,102 @@ +//Dimension of overmap (squares 4 lyfe) +var/global/list/map_sectors = list() + +/area/overmap + name = "System Map" + icon_state = "start" + requires_power = 0 + base_turf = /turf/unsimulated/map + +/turf/unsimulated/map + icon = 'icons/turf/space.dmi' + icon_state = "map" + initialized = FALSE // TODO - Fix unsimulated turf initialization so this override is not necessary! + +/turf/unsimulated/map/edge + opacity = 1 + density = 1 + var/map_is_to_my + var/turf/unsimulated/map/edge/wrap_buddy + +/turf/unsimulated/map/edge/Initialize() + . = ..() + //This could be done by using the using_map.overmap_size much faster, HOWEVER, doing it programatically to 'find' + // the edges this way allows for 'sub overmaps' elsewhere and whatnot. + for(var/side in alldirs) //The order of this list is relevant: It should definitely break on finding a cardinal FIRST. + var/turf/T = get_step(src, side) + if(T?.type == /turf/unsimulated/map) //Not a wall, not something else, EXACTLY a flat map turf. + map_is_to_my = side + break + + if(map_is_to_my) + var/turf/T = get_step(src, map_is_to_my) //Should be a normal map turf + while(istype(T, /turf/unsimulated/map)) + T = get_step(T, map_is_to_my) //Could be a wall if the map is only 1 turf big + if(istype(T, /turf/unsimulated/map/edge)) + wrap_buddy = T + break + +/turf/unsimulated/map/edge/Destroy() + wrap_buddy = null + return ..() + +/turf/unsimulated/map/edge/Bumped(var/atom/movable/AM) + if(wrap_buddy?.map_is_to_my) + AM.forceMove(get_step(wrap_buddy, wrap_buddy.map_is_to_my)) + else + . = ..() + +/turf/unsimulated/map/Initialize() + . = ..() + name = "[x]-[y]" + var/list/numbers = list() + + if(x == 1 || x == global.using_map.overmap_size) + numbers += list("[round(y/10)]","[round(y%10)]") + if(y == 1 || y == global.using_map.overmap_size) + numbers += "-" + if(y == 1 || y == global.using_map.overmap_size) + numbers += list("[round(x/10)]","[round(x%10)]") + + for(var/i = 1 to numbers.len) + var/image/I = image('icons/effects/numbers.dmi',numbers[i]) + I.pixel_x = 5*i - 2 + I.pixel_y = world.icon_size/2 - 3 + if(y == 1) + I.pixel_y = 3 + I.pixel_x = 5*i + 4 + if(y == global.using_map.overmap_size) + I.pixel_y = world.icon_size - 9 + I.pixel_x = 5*i + 4 + if(x == 1) + I.pixel_x = 5*i - 2 + if(x == global.using_map.overmap_size) + I.pixel_x = 5*i + 2 + add_overlay(I) + +/turf/unsimulated/map/Entered(var/atom/movable/O, var/atom/oldloc) + ..() + if(istype(O, /obj/effect/overmap/visitable/ship)) + GLOB.overmap_event_handler.on_turf_entered(src, O, oldloc) + +/turf/unsimulated/map/Exited(var/atom/movable/O, var/atom/newloc) + ..() + if(istype(O, /obj/effect/overmap/visitable/ship)) + GLOB.overmap_event_handler.on_turf_exited(src, O, newloc) + +//list used to track which zlevels are being 'moved' by the proc below +var/list/moving_levels = list() +//Proc to 'move' stars in spess +//yes it looks ugly, but it should only fire when state actually change. +//null direction stops movement +proc/toggle_move_stars(zlevel, direction) + if(!zlevel) + return + + if (moving_levels["[zlevel]"] != direction) + moving_levels["[zlevel]"] = direction + + var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)) + for(var/turf/space/T in spaceturfs) + T.toggle_transit(direction) + CHECK_TICK diff --git a/code/modules/paperwork/adminpaper.dm b/code/modules/paperwork/adminpaper.dm index 6ed6ad93fc..353090efed 100644 --- a/code/modules/paperwork/adminpaper.dm +++ b/code/modules/paperwork/adminpaper.dm @@ -40,11 +40,15 @@ var/originhash = md5("[origin]") var/timehash = copytext(md5("[world.time]"),1,10) var/text = null - var/logo = alert(usr, "Do you want the header of your fax to have a NanoTrasen or SolGov logo?","Fax Logo","NanoTrasen","SolGov") + var/logo = alert(usr, "Do you want the header of your fax to have a NanoTrasen, SolGov, or Trader logo?","Fax Logo","NanoTrasen","SolGov","Trader") //VOREStation Add - Trader if(logo == "SolGov") logo = "sglogo.png" - else + //VOREStation Edit/Add + else if(logo == "NanoTrasen") logo = "ntlogo.png" + else + logo = "trader.png" + //VOREStation Edit/Add End //TODO change logo based on who you're contacting. text = "

    " text += "[origin] Quantum Uplink Signed Message
    " @@ -80,7 +84,7 @@ obj/item/weapon/paper/admin/proc/updateDisplay() if(href_list["write"]) var/id = href_list["write"] if(free_space <= 0) - usr << "There isn't enough space left on \the [src] to write anything." + to_chat(usr, "There isn't enough space left on \the [src] to write anything.") return var/t = sanitize(input("Enter what you want to write:", "Write", null, null) as message, free_space, extra = 0) @@ -96,7 +100,7 @@ obj/item/weapon/paper/admin/proc/updateDisplay() if(fields > 50)//large amount of fields creates a heavy load on the server, see updateinfolinks() and addtofield() - usr << "Too many fields. Sorry, you can't do this." + to_chat(usr, "Too many fields. Sorry, you can't do this.") fields = last_fields_value return @@ -152,4 +156,4 @@ obj/item/weapon/paper/admin/proc/updateDisplay() return /obj/item/weapon/paper/admin/get_signature() - return input(usr, "Enter the name you wish to sign the paper with (will prompt for multiple entries, in order of entry)", "Signature") as text|null \ No newline at end of file + return input(usr, "Enter the name you wish to sign the paper with (will prompt for multiple entries, in order of entry)", "Signature") as text|null diff --git a/code/modules/paperwork/carbonpaper.dm b/code/modules/paperwork/carbonpaper.dm index 7eb9843cc3..18357400d2 100644 --- a/code/modules/paperwork/carbonpaper.dm +++ b/code/modules/paperwork/carbonpaper.dm @@ -42,10 +42,10 @@ copy.name = "Copy - " + c.name copy.fields = c.fields copy.updateinfolinks() - usr << "You tear off the carbon-copy!" + to_chat(usr, "You tear off the carbon-copy!") c.copied = 1 copy.iscopy = 1 copy.update_icon() c.update_icon() else - usr << "There are no more carbon copies attached to this paper!" + to_chat(usr, "There are no more carbon copies attached to this paper!") diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index 94510629c5..64f02d2188 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -50,7 +50,7 @@ W.loc = src if(istype(W, /obj/item/weapon/paper)) toppaper = W - user << "You clip the [W] onto \the [src]." + to_chat(user, "You clip the [W] onto \the [src].") update_icon() else if(istype(toppaper) && istype(W, /obj/item/weapon/pen)) @@ -103,7 +103,7 @@ usr.drop_item() W.loc = src haspen = W - usr << "You slot the pen into \the [src]." + to_chat(usr, "You slot the pen into \the [src].") else if(href_list["write"]) var/obj/item/weapon/P = locate(href_list["write"]) @@ -164,7 +164,7 @@ var/obj/item/P = locate(href_list["top"]) if(P && (P.loc == src) && istype(P, /obj/item/weapon/paper) ) toppaper = P - usr << "You move [P.name] to the top." + to_chat(usr, "You move [P.name] to the top.") //Update everything attack_self(usr) diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 069e6c3405..5353b2cf18 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -1,5 +1,5 @@ var/list/obj/machinery/photocopier/faxmachine/allfaxes = list() -var/list/admin_departments = list("[using_map.boss_name]", "Virgo-Prime Governmental Authority", "Supply") // Vorestation Edit +var/list/admin_departments = list("[using_map.boss_name]", "Virgo-Prime Governmental Authority", "Virgo-Erigonne Job Boards", "Supply") // Vorestation Edit var/list/alldepartments = list() var/list/adminfaxes = list() //cache for faxes that have been sent to admins @@ -12,7 +12,7 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins insert_anim = "faxsend" req_one_access = list(access_lawyer, access_heads, access_armory, access_qm) - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 200 circuit = /obj/item/weapon/circuitboard/fax @@ -236,8 +236,8 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins msg += "Receiving '[sent.name]' via secure connection ... view message" for(var/client/C in admins) - if(check_rights((R_ADMIN|R_MOD),0,C)) - C << msg + if(check_rights((R_ADMIN|R_MOD|R_EVENT),0,C)) + to_chat(C,msg) C << 'sound/effects/printer.ogg' // VoreStation Edit Start diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index d1effe7492..374fb647c5 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -37,7 +37,9 @@ user.drop_item() P.loc = src icon_state = "[initial(icon_state)]-open" - sleep(5) + flick("[initial(icon_state)]-open",src) + playsound(loc, 'sound/bureaucracy/filingcabinet.ogg', 50, 1) + sleep(40) icon_state = initial(icon_state) updateUsrDialog() else if(P.is_wrench()) @@ -98,9 +100,10 @@ if(istype(P) && (P.loc == src) && src.Adjacent(usr)) usr.put_in_hands(P) updateUsrDialog() - icon_state = "[initial(icon_state)]-open" + flick("[initial(icon_state)]-open",src) + playsound(loc, 'sound/bureaucracy/filingcabinet.ogg', 50, 1) spawn(0) - sleep(5) + sleep(20) icon_state = initial(icon_state) /* diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index f24ce267df..8ea288a856 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -1,10 +1,11 @@ /obj/item/weapon/folder name = "folder" desc = "A folder." - icon = 'icons/obj/bureaucracy.dmi' + icon = 'icons/obj/bureaucracy_yw.dmi' //ywedit reverts to use old folder icon icon_state = "folder" w_class = ITEMSIZE_SMALL pressure_resistance = 2 + drop_sound = 'sound/items/drop/paper.ogg' /obj/item/weapon/folder/blue desc = "A blue folder." @@ -64,7 +65,7 @@ if(istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/weapon/photo) || istype(W, /obj/item/weapon/paper_bundle)) user.drop_item() W.loc = src - user << "You put the [W] into \the [src]." + to_chat(user, "You put the [W] into \the [src].") update_icon() else if(istype(W, /obj/item/weapon/pen)) var/n_name = sanitizeSafe(input(usr, "What would you like to label the folder?", "Folder Labelling", null) as text, MAX_NAME_LEN) diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index 12f99f150f..8c12f654f6 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -19,30 +19,30 @@ return // don't set a label if(!labels_left) - user << "No labels left." + to_chat(user, "No labels left.") return if(!label || !length(label)) - user << "No text set." + to_chat(user, "No text set.") return if(length(A.name) + length(label) > 64) - user << "Label too big." + to_chat(user, "Label too big.") return if(ishuman(A)) - user << "The label refuses to stick to [A.name]." + to_chat(user, "The label refuses to stick to [A.name].") return if(issilicon(A)) - user << "The label refuses to stick to [A.name]." + to_chat(user, "The label refuses to stick to [A.name].") return if(isobserver(A)) - user << "[src] passes through [A.name]." + to_chat(user, "[src] passes through [A.name].") return if(istype(A, /obj/item/weapon/reagent_containers/glass)) - user << "The label can't stick to the [A.name]. (Try using a pen)" + to_chat(user, "The label can't stick to the [A.name]. (Try using a pen)") return if(istype(A, /obj/machinery/portable_atmospherics/hydroponics)) var/obj/machinery/portable_atmospherics/hydroponics/tray = A if(!tray.mechanical) - user << "How are you going to label that?" + to_chat(user, "How are you going to label that?") return tray.labelled = label spawn(1) @@ -56,13 +56,13 @@ mode = !mode icon_state = "labeler[mode]" if(mode) - user << "You turn on \the [src]." + to_chat(user, "You turn on \the [src].") //Now let them chose the text. var/str = sanitizeSafe(input(user,"Label text?","Set label",""), MAX_NAME_LEN) if(!str || !length(str)) - user << "Invalid text." + to_chat(user, "Invalid text.") return label = str - user << "You set the text to '[str]'." + to_chat(user, "You set the text to '[str]'.") else - user << "You turn off \the [src]." \ No newline at end of file + to_chat(user, "You turn off \the [src].") \ No newline at end of file diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index fe3e218639..f247e2b2a3 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -19,6 +19,7 @@ slot_flags = SLOT_HEAD body_parts_covered = HEAD attack_verb = list("bapped") + drop_sound = 'sound/items/drop/paper.ogg' var/info //What's actually written on the paper. var/info_links //A different version of the paper which includes html links at fields and EOF @@ -99,15 +100,21 @@ return -/obj/item/weapon/paper/New() +/obj/item/weapon/paper/New(var/newloc, var/text, var/title) ..() pixel_y = rand(-8, 8) pixel_x = rand(-9, 9) stamps = "" + if(!isnull(title)) + name = title + if(name != "paper") desc = "This is a paper titled '" + name + "'." + if(!isnull(text)) + info = text + if(info != initial(info)) info = html_encode(info) info = replacetext(info, "\n", "
    ") @@ -138,7 +145,7 @@ if(in_range(user, src) || istype(user, /mob/observer/dead)) show_content(usr) else - user << "You have to go closer if you want to read it." + to_chat(user, "You have to go closer if you want to read it.") return /obj/item/weapon/paper/proc/show_content(var/mob/user, var/forceshow=0) @@ -155,7 +162,7 @@ set src in usr if((CLUMSY in usr.mutations) && prob(50)) - usr << "You cut yourself on the paper." + to_chat(usr, "You cut yourself on the paper.") return var/n_name = sanitizeSafe(input(usr, "What would you like to label the paper?", "Paper Labelling", null) as text, MAX_NAME_LEN) @@ -176,6 +183,7 @@ //crumple dat paper info = stars(info,85) user.visible_message("\The [user] crumples \the [src] into a ball!") + playsound(src, 'sound/bureaucracy/papercrumple.ogg', 50, 1) icon_state = "scrap" return user.examinate(src) @@ -385,6 +393,7 @@ user.visible_message("[user] holds \the [P] up to \the [src], it looks like [TU.hes] trying to burn it!", \ "You hold \the [P] up to \the [src], burning it slowly.") + playsound(src.loc, 'sound/bureaucracy/paperburn.ogg', 50, 1) spawn(20) if(get_dist(src, user) < 2 && user.get_active_hand() == P && P.lit) @@ -398,7 +407,7 @@ qdel(src) else - user << "You must hold \the [P] steady to burn \the [src]." + to_chat(user, "You must hold \the [P] steady to burn \the [src].") /obj/item/weapon/paper/Topic(href, href_list) @@ -411,7 +420,7 @@ //var/t = strip_html_simple(input(usr, "What text do you wish to add to " + (id=="end" ? "the end of the paper" : "field "+id) + "?", "[name]", null),8192) as message if(free_space <= 0) - usr << "There isn't enough space left on \the [src] to write anything." + to_chat(usr, "There isn't enough space left on \the [src] to write anything.") return var/t = sanitize(input("Enter what you want to write:", "Write", null, null) as message, MAX_PAPER_MESSAGE_LEN, extra = 0) @@ -450,7 +459,7 @@ // check for exploits for(var/bad in paper_blacklist) if(findtext(t,bad)) - usr << "You think to yourself, \"Hm.. this is only paper...\"" + to_chat(usr, "You think to yourself, \"Hm.. this is only paper...\"") log_admin("PAPER: [usr] ([usr.ckey]) tried to use forbidden word in [src]: [bad].") message_admins("PAPER: [usr] ([usr.ckey]) tried to use forbidden word in [src]: [bad].") return @@ -464,7 +473,7 @@ if(fields > 50)//large amount of fields creates a heavy load on the server, see updateinfolinks() and addtofield() - usr << "Too many fields. Sorry, you can't do this." + to_chat(usr, "Too many fields. Sorry, you can't do this.") fields = last_fields_value return @@ -478,6 +487,8 @@ usr << browse("[name][info_links][stamps]", "window=[name]") // Update the window + playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 10) + update_icon() /obj/item/weapon/paper/get_worn_icon_state(var/slot_name) @@ -500,7 +511,7 @@ if (istype(P, /obj/item/weapon/paper/carbon)) var/obj/item/weapon/paper/carbon/C = P if (!C.iscopy && !C.copied) - user << "Take off the carbon copy first." + to_chat(user, "Take off the carbon copy first.") add_fingerprint(user) return var/obj/item/weapon/paper_bundle/B = new(src.loc) @@ -536,7 +547,7 @@ src.loc = get_turf(h_user) if(h_user.client) h_user.client.screen -= src h_user.put_in_hands(B) - user << "You clip the [P.name] to [(src.name == "paper") ? "the paper" : src.name]." + to_chat(user, "You clip the [P.name] to [(src.name == "paper") ? "the paper" : src.name].") src.loc = B P.loc = B @@ -546,7 +557,7 @@ else if(istype(P, /obj/item/weapon/pen)) if(icon_state == "scrap") - usr << "\The [src] is too crumpled to write on." + to_chat(usr, "\The [src] is too crumpled to write on.") return var/obj/item/weapon/pen/robopen/RP = P @@ -577,7 +588,7 @@ if(istype(P, /obj/item/weapon/stamp/clown)) if(!clown) - user << "You are totally unable to use the stamp. HONK!" + to_chat(user, "You are totally unable to use the stamp. HONK!") return if(!ico) @@ -590,7 +601,8 @@ stamped += P.type overlays += stampoverlay - user << "You stamp the paper with your rubber stamp." + playsound(src, 'sound/bureaucracy/stamp.ogg', 50, 1) + to_chat(user, "You stamp the paper with your rubber stamp.") else if(istype(P, /obj/item/weapon/flame)) burnpaper(P, user) @@ -661,4 +673,4 @@ /obj/item/weapon/paper/manifest name = "supply manifest" - var/is_copy = 1 \ No newline at end of file + var/is_copy = 1 diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm index 5a15777b72..43b1a8c36d 100644 --- a/code/modules/paperwork/paper_bundle.dm +++ b/code/modules/paperwork/paper_bundle.dm @@ -12,6 +12,7 @@ layer = MOB_LAYER pressure_resistance = 1 attack_verb = list("bapped") + drop_sound = 'sound/items/drop/paper.ogg' var/page = 1 // current page var/list/pages = list() // Ordered list of pages as they are to be displayed. Can be different order than src.contents. @@ -22,7 +23,7 @@ if (istype(W, /obj/item/weapon/paper/carbon)) var/obj/item/weapon/paper/carbon/C = W if (!C.iscopy && !C.copied) - user << "Take off the carbon copy first." + to_chat(user, "Take off the carbon copy first.") add_fingerprint(user) return // adding sheets @@ -41,7 +42,7 @@ O.add_fingerprint(usr) pages.Add(O) - user << "You add \the [W.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name]." + to_chat(user, "You add \the [W.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name].") qdel(W) else if(istype(W, /obj/item/weapon/tape_roll)) @@ -58,9 +59,9 @@ /obj/item/weapon/paper_bundle/proc/insert_sheet_at(mob/user, var/index, obj/item/weapon/sheet) if(istype(sheet, /obj/item/weapon/paper)) - user << "You add [(sheet.name == "paper") ? "the paper" : sheet.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name]." + to_chat(user, "You add [(sheet.name == "paper") ? "the paper" : sheet.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name].") else if(istype(sheet, /obj/item/weapon/photo)) - user << "You add [(sheet.name == "photo") ? "the photo" : sheet.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name]." + to_chat(user, "You add [(sheet.name == "photo") ? "the photo" : sheet.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name].") user.drop_from_inventory(sheet) sheet.loc = src @@ -92,13 +93,13 @@ qdel(src) else - user << "You must hold \the [P] steady to burn \the [src]." + to_chat(user, "You must hold \the [P] steady to burn \the [src].") /obj/item/weapon/paper_bundle/examine(mob/user) if(..(user, 1)) src.show_content(user) else - user << "It is too far away." + to_chat(user, "It is too far away.") return /obj/item/weapon/paper_bundle/proc/show_content(mob/user as mob) @@ -202,7 +203,7 @@ set category = "Object" set src in usr - usr << "You loosen the bundle." + to_chat(usr, "You loosen the bundle.") for(var/obj/O in src) O.loc = usr.loc O.layer = initial(O.layer) diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 850f8f6c8e..65003ee71e 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -28,10 +28,10 @@ if (H.hand) temp = H.organs_by_name["l_hand"] if(temp && !temp.is_usable()) - user << "You try to move your [temp.name], but cannot!" + to_chat(user, "You try to move your [temp.name], but cannot!") return - user << "You pick up the [src]." + to_chat(user, "You pick up the [src].") user.put_in_hands(src) return @@ -43,7 +43,7 @@ if (H.hand) temp = H.organs_by_name["l_hand"] if(temp && !temp.is_usable()) - user << "You try to move your [temp.name], but cannot!" + to_chat(user, "You try to move your [temp.name], but cannot!") return var/response = "" if(!papers.len > 0) @@ -73,9 +73,9 @@ P.loc = user.loc user.put_in_hands(P) - user << "You take [P] out of the [src]." + to_chat(user, "You take [P] out of the [src].") else - user << "[src] is empty!" + to_chat(user, "[src] is empty!") add_fingerprint(user) return @@ -87,7 +87,7 @@ user.drop_item() i.loc = src - user << "You put [i] in [src]." + to_chat(user, "You put [i] in [src].") papers.Add(i) update_icon() amount++ @@ -96,9 +96,9 @@ /obj/item/weapon/paper_bin/examine(mob/user) if(get_dist(src, user) <= 1) if(amount) - user << "There " + (amount > 1 ? "are [amount] papers" : "is one paper") + " in the bin." + to_chat(user, "There " + (amount > 1 ? "are [amount] papers" : "is one paper") + " in the bin.") else - user << "There are no papers in the bin." + to_chat(user, "There are no papers in the bin.") return diff --git a/code/modules/paperwork/papershredder.dm b/code/modules/paperwork/papershredder.dm index d626e6f04b..4c927b376e 100644 --- a/code/modules/paperwork/papershredder.dm +++ b/code/modules/paperwork/papershredder.dm @@ -9,7 +9,7 @@ var/shred_anim = "shredder-shredding" density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 200 power_channel = EQUIP diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 3f585f323b..dbfd7855ff 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -23,7 +23,11 @@ matter = list(DEFAULT_WALL_MATERIAL = 10) var/colour = "black" //what colour the ink is! pressure_resistance = 2 + drop_sound = 'sound/items/drop/accessory.ogg' +/obj/item/weapon/pen/attack_self(var/mob/user) + to_chat(user, "Click.") + playsound(loc, 'sound/items/penclick.ogg', 50, 1) /obj/item/weapon/pen/blue desc = "It's a normal blue ink pen." @@ -44,6 +48,11 @@ var/selectedColor = 1 var/colors = list("black","blue","red") +/obj/item/weapon/pen/AltClick(mob/user) + to_chat(user, "Click.") + playsound(loc, 'sound/items/penclick.ogg', 50, 1) + return + /obj/item/weapon/pen/multi/attack_self(mob/user) if(++selectedColor > 3) selectedColor = 1 @@ -55,7 +64,7 @@ else icon_state = "pen_[colour]" - user << "Changed color to '[colour].'" + to_chat(user, "Changed color to '[colour].'") /obj/item/weapon/pen/invisible desc = "It's an invisble pen marker." @@ -88,6 +97,98 @@ var/trans = reagents.trans_to_mob(M, 30, CHEM_BLOOD) add_attack_logs(user,M,"Injected with [src.name] containing [contained], trasferred [trans] units") +/* + * Blade pens. + */ + +/obj/item/weapon/pen/blade + desc = "It's a normal black ink pen." + description_antag = "This pen can be transformed into a dangerous melee and thrown assassination weapon with an Alt-Click.\ + When active, it cannot be caught safely." + name = "pen" + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "pen" + item_state = "pen" + slot_flags = SLOT_BELT | SLOT_EARS + throwforce = 3 + w_class = ITEMSIZE_TINY + throw_speed = 7 + throw_range = 15 + armor_penetration = 20 + + var/active = 0 + var/active_embed_chance = 0 + var/active_force = 15 + var/active_throwforce = 30 + var/active_w_class = ITEMSIZE_NORMAL + var/active_icon_state + var/default_icon_state + +/obj/item/weapon/pen/blade/Initialize() + ..() + active_icon_state = "[icon_state]-x" + default_icon_state = icon_state + +/obj/item/weapon/pen/blade/AltClick(mob/user) + ..() + if(active) + deactivate(user) + else + activate(user) + + to_chat(user, "You [active ? "de" : ""]activate \the [src]'s blade.") + +/obj/item/weapon/pen/blade/proc/activate(mob/living/user) + if(active) + return + active = 1 + icon_state = active_icon_state + embed_chance = active_embed_chance + force = active_force + throwforce = active_throwforce + sharp = 1 + edge = 1 + w_class = active_w_class + playsound(user, 'sound/weapons/saberon.ogg', 15, 1) + damtype = SEARING + catchable = FALSE + + attack_verb |= list(\ + "slashed",\ + "cut",\ + "shredded",\ + "stabbed"\ + ) + +/obj/item/weapon/pen/blade/proc/deactivate(mob/living/user) + if(!active) + return + playsound(user, 'sound/weapons/saberoff.ogg', 15, 1) + active = 0 + icon_state = default_icon_state + embed_chance = initial(embed_chance) + force = initial(force) + throwforce = initial(throwforce) + sharp = initial(sharp) + edge = initial(edge) + w_class = initial(w_class) + damtype = BRUTE + catchable = TRUE + +/obj/item/weapon/pen/blade/blue + desc = "It's a normal blue ink pen." + icon_state = "pen_blue" + colour = "blue" + +/obj/item/weapon/pen/blade/red + desc = "It's a normal red ink pen." + icon_state = "pen_red" + colour = "red" + +/obj/item/weapon/pen/blade/fountain + desc = "A well made fountain pen." + icon_state = "pen_fountain" + /* * Sleepy Pens */ @@ -164,7 +265,7 @@ colour = COLOR_WHITE else colour = COLOR_BLACK - usr << "You select the [lowertext(selected_type)] ink container." + to_chat(usr, "You select the [lowertext(selected_type)] ink container.") /* @@ -186,7 +287,7 @@ /obj/item/weapon/pen/crayon/suicide_act(mob/user) var/datum/gender/TU = gender_datums[user.get_visible_gender()] - viewers(user) << "[user] is jamming the [src.name] up [TU.his] nose and into [TU.his] brain. It looks like [TU.he] [TU.is] trying to commit suicide." + to_chat(viewers(user),"[user] is jamming the [src.name] up [TU.his] nose and into [TU.his] brain. It looks like [TU.he] [TU.is] trying to commit suicide.") return (BRUTELOSS|OXYLOSS) /obj/item/weapon/pen/crayon/New() diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index d5e04d97a4..eab9984d0f 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -6,7 +6,7 @@ var/insert_anim = "bigscanner1" anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 200 power_channel = EQUIP diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 733dbff5bc..e56063e0ba 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -53,9 +53,9 @@ var/global/photo_count = 0 /obj/item/weapon/photo/examine(mob/user) if(in_range(user, src)) show(user) - user << desc + to_chat(user,desc) else - user << "It is too far away." + to_chat(user, "It is too far away.") /obj/item/weapon/photo/proc/show(mob/user as mob) user << browse_rsc(img, "tmp_photo_[id].png") @@ -140,7 +140,7 @@ var/global/photo_count = 0 var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7) if(nsize) size = nsize - usr << "Camera will now take [size]x[size] photos." + to_chat(usr, "Camera will now take [size]x[size] photos.") /obj/item/device/camera/attack(mob/living/carbon/human/M as mob, mob/user as mob) return @@ -151,15 +151,15 @@ var/global/photo_count = 0 src.icon_state = icon_on else src.icon_state = icon_off - user << "You switch the camera [on ? "on" : "off"]." + to_chat(user, "You switch the camera [on ? "on" : "off"].") return /obj/item/device/camera/attackby(obj/item/I as obj, mob/user as mob) if(istype(I, /obj/item/device/camera_film)) if(pictures_left) - user << "[src] still has some film in it!" + to_chat(user, "[src] still has some film in it!") return - user << "You insert [I] into [src]." + to_chat(user, "You insert [I] into [src].") user.drop_item() qdel(I) pictures_left = pictures_max @@ -245,7 +245,7 @@ var/global/photo_count = 0 pictures_left-- desc = "A polaroid camera. It has [pictures_left] photos left." - user << "[pictures_left] photos left." + to_chat(user, "[pictures_left] photos left.") icon_state = icon_off on = 0 spawn(64) diff --git a/code/modules/paperwork/silicon_photography.dm b/code/modules/paperwork/silicon_photography.dm index a3da9704ed..b33b325648 100644 --- a/code/modules/paperwork/silicon_photography.dm +++ b/code/modules/paperwork/silicon_photography.dm @@ -25,10 +25,10 @@ var/mob/living/silicon/robot/C = usr if(C.connected_ai) C.connected_ai.aiCamera.injectaialbum(p.copy(1), " (synced from [C.name])") - C.connected_ai << "Image uploaded by [C.name]" - usr << "Image synced to remote database" //feedback to the Cyborg player that the picture was taken + to_chat(C.connected_ai, "Image uploaded by [C.name]") + to_chat(usr, "Image synced to remote database") //feedback to the Cyborg player that the picture was taken else - usr << "Image recorded" + to_chat(usr, "Image recorded") // Always save locally injectaialbum(p) @@ -39,7 +39,7 @@ var/list/nametemp = list() var/find if(cam.aipictures.len == 0) - usr << "No images saved" + to_chat(usr, "No images saved") return for(var/obj/item/weapon/photo/t in cam.aipictures) nametemp += t.name @@ -58,7 +58,7 @@ return selection.show(usr) - usr << selection.desc + to_chat(usr,selection.desc) /obj/item/device/camera/siliconcam/proc/deletepicture(obj/item/device/camera/siliconcam/cam) var/selection = selectpicture(cam) @@ -67,7 +67,7 @@ return aipictures -= selection - usr << "Local image deleted" + to_chat(usr, "Local image deleted") /obj/item/device/camera/siliconcam/ai_camera/can_capture_turf(turf/T, mob/user) var/mob/living/silicon/ai = user @@ -81,15 +81,15 @@ /obj/item/device/camera/siliconcam/proc/camera_mode_off() src.in_camera_mode = 0 - usr << "Camera Mode deactivated" + to_chat(usr, "Camera Mode deactivated") /obj/item/device/camera/siliconcam/proc/camera_mode_on() src.in_camera_mode = 1 - usr << "Camera Mode activated" + to_chat(usr, "Camera Mode activated") /obj/item/device/camera/siliconcam/ai_camera/printpicture(mob/user, obj/item/weapon/photo/p) injectaialbum(p) - usr << "Image recorded" + to_chat(usr, "Image recorded") /obj/item/device/camera/siliconcam/robot_camera/printpicture(mob/user, obj/item/weapon/photo/p) injectmasteralbum(p) diff --git a/code/modules/paperwork/stamps.dm b/code/modules/paperwork/stamps.dm index 7840333ef3..9039df41ea 100644 --- a/code/modules/paperwork/stamps.dm +++ b/code/modules/paperwork/stamps.dm @@ -1,7 +1,7 @@ /obj/item/weapon/stamp name = "rubber stamp" desc = "A rubber stamp for stamping important documents." - icon = 'icons/obj/bureaucracy.dmi' + icon = 'icons/obj/bureaucracy_yw.dmi' //YWedit reverts to use old stamp icons icon_state = "stamp-qm" item_state = "stamp" throwforce = 0 @@ -44,6 +44,7 @@ /obj/item/weapon/stamp/denied name = "\improper DENIED rubber stamp" icon_state = "stamp-deny" + attack_verb = list("DENIED") /obj/item/weapon/stamp/clown name = "clown's rubber stamp" diff --git a/code/modules/power/antimatter/control.dm b/code/modules/power/antimatter/control.dm index 1f1c01bab2..46f05bfd80 100644 --- a/code/modules/power/antimatter/control.dm +++ b/code/modules/power/antimatter/control.dm @@ -5,7 +5,7 @@ icon_state = "control" anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 active_power_usage = 1000 @@ -156,12 +156,12 @@ src.anchored = 0 disconnect_from_network() else - user << "Once bolted and linked to a shielding unit it the [src.name] is unable to be moved!" + to_chat(user, "Once bolted and linked to a shielding unit it the [src.name] is unable to be moved!") return if(istype(W, /obj/item/weapon/am_containment)) if(fueljar) - user << "There is already a [fueljar] inside!" + to_chat(user, "There is already a [fueljar] inside!") return fueljar = W user.remove_from_mob(W) @@ -211,10 +211,10 @@ /obj/machinery/power/am_control_unit/proc/toggle_power() active = !active if(active) - use_power = 2 + update_use_power(USE_POWER_ACTIVE) visible_message("The [src.name] starts up.") else - use_power = 1 + update_use_power(USE_POWER_IDLE) visible_message("The [src.name] shuts down.") update_icon() return diff --git a/code/modules/power/antimatter/engine.dm b/code/modules/power/antimatter/engine.dm index 007e67ab22..aa1c2245d6 100644 --- a/code/modules/power/antimatter/engine.dm +++ b/code/modules/power/antimatter/engine.dm @@ -42,9 +42,9 @@ if(istype(F, /obj/item/weapon/fuel/H)) if(injecting) - user << "Theres already a fuel rod in the injector!" + to_chat(user, "Theres already a fuel rod in the injector!") return - user << "You insert the rod into the injector" + to_chat(user, "You insert the rod into the injector") injecting = 1 var/fuel = F.fuel qdel(F) @@ -55,9 +55,9 @@ if(istype(F, /obj/item/weapon/fuel/antiH)) if(injecting) - user << "Theres already a fuel rod in the injector!" + to_chat(user, "Theres already a fuel rod in the injector!") return - user << "You insert the rod into the injector" + to_chat(user, "You insert the rod into the injector") injecting = 1 var/fuel = F.fuel qdel(F) diff --git a/code/modules/power/antimatter/fuel.dm b/code/modules/power/antimatter/fuel.dm index 707b756ede..4b0310f85f 100644 --- a/code/modules/power/antimatter/fuel.dm +++ b/code/modules/power/antimatter/fuel.dm @@ -26,7 +26,7 @@ if(istype(F, /obj/item/weapon/fuel/antiH)) src.fuel += F.fuel F.fuel = 0 - user << "You have added the anti-Hydrogen to the storage ring, it now contains [src.fuel]kg" + to_chat(user, "You have added the anti-Hydrogen to the storage ring, it now contains [src.fuel]kg") if(istype(F, /obj/item/weapon/fuel/H)) src.fuel += F.fuel qdel(F) @@ -35,7 +35,7 @@ if(istype(F, /obj/item/weapon/fuel/H)) src.fuel += F.fuel F.fuel = 0 - user << "You have added the Hydrogen to the storage ring, it now contains [src.fuel]kg" + to_chat(user, "You have added the Hydrogen to the storage ring, it now contains [src.fuel]kg") if(istype(F, /obj/item/weapon/fuel/antiH)) src.fuel += F.fuel qdel(src) @@ -69,14 +69,14 @@ /obj/item/weapon/fuel/examine(mob/user) if(get_dist(src, user) <= 1) - user << "A magnetic storage ring, it contains [fuel]kg of [content ? content : "nothing"]." + to_chat(user, "A magnetic storage ring, it contains [fuel]kg of [content ? content : "nothing"].") /obj/item/weapon/fuel/proc/injest(mob/M as mob) switch(content) if("Anti-Hydrogen") M.gib() //Yikes! if("Hydrogen") - M << "You feel very light, as if you might just float away..." + to_chat(M, "You feel very light, as if you might just float away...") qdel(src) return diff --git a/code/modules/power/antimatter/shielding.dm b/code/modules/power/antimatter/shielding.dm index a3549b7a2f..2b7b881b09 100644 --- a/code/modules/power/antimatter/shielding.dm +++ b/code/modules/power/antimatter/shielding.dm @@ -16,7 +16,7 @@ proc/cardinalrange(var/center) anchored = 1 density = 1 dir = 1 - use_power = 0//Living things generally dont use power + use_power = USE_POWER_OFF //Living things generally dont use power idle_power_usage = 0 active_power_usage = 0 diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 6f5c99fe66..fc07078c96 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -62,12 +62,13 @@ /obj/machinery/power/apc name = "area power controller" desc = "A control terminal for the area electrical systems." - icon = 'icons/obj/power_vr.dmi' //VOREStation Edit - New Icon + icon = 'icons/obj/power_yw.dmi' //YW edit, reverts to Old APC sprite icon_state = "apc0" plane = TURF_PLANE layer = ABOVE_TURF_LAYER anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF + clicksound = "switch" req_access = list(access_engine_equip) var/area/area var/areastring = null @@ -103,8 +104,8 @@ var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment var/longtermpower = 10 - var/emergency_lights = FALSE var/datum/wires/apc/wires = null + var/emergency_lights = FALSE var/update_state = -1 var/update_overlay = -1 var/is_critical = 0 @@ -166,8 +167,8 @@ if (building) set_dir(ndir) - pixel_x = (src.dir & 3)? 0 : (src.dir == 4 ? 24 : -24) - pixel_y = (src.dir & 3)? (src.dir ==1 ? 24 : -24) : 0 + pixel_x = (src.dir & 3)? 0 : (src.dir == 4 ? 26 : -26) //VOREStation Edit -> 24 to 26 + pixel_y = (src.dir & 3)? (src.dir ==1 ? 26 : -26) : 0 //VOREStation Edit -> 24 to 26 if (building==0) init() else @@ -252,27 +253,27 @@ /obj/machinery/power/apc/examine(mob/user) if(..(user, 1)) if(stat & BROKEN) - to_chat(user,"This APC is broken.") + to_chat(user, "This APC is broken.") return if(opened) if(has_electronics && terminal) - to_chat(user,"The cover is [opened==2?"removed":"open"] and [ cell ? "a power cell is installed" : "the power cell is missing"].") + to_chat(user, "The cover is [opened==2?"removed":"open"] and [ cell ? "a power cell is installed" : "the power cell is missing"].") else if (!has_electronics && terminal) - to_chat(user,"The frame is wired, but the electronics are missing.") + to_chat(user, "The frame is wired, but the electronics are missing.") else if (has_electronics && !terminal) - to_chat(user,"The electronics are installed, but not wired.") + to_chat(user, "The electronics are installed, but not wired.") else /* if (!has_electronics && !terminal) */ - to_chat(user,"It's just an empty metal frame.") + to_chat(user, "It's just an empty metal frame.") else if (wiresexposed) - to_chat(user,"The cover is closed and the wires are exposed.") + to_chat(user, "The cover is closed and the wires are exposed.") else if ((locked && emagged) || hacker) //Some things can cause locked && emagged. Malf AI causes hacker. - to_chat(user,"The cover is closed, but the panel is unresponsive.") + to_chat(user, "The cover is closed, but the panel is unresponsive.") else if(!locked && emagged) //Normal emag does this. - to_chat(user,"The cover is closed, but the panel is flashing an error.") + to_chat(user, "The cover is closed, but the panel is flashing an error.") else - to_chat(user,"The cover is closed.") + to_chat(user, "The cover is closed.") // update the APC icon to show the three base states @@ -461,10 +462,10 @@ if (W.is_crowbar() && opened) if (has_electronics==1) if (terminal) - to_chat(user,"Disconnect the wires first.") + to_chat(user, "Disconnect the wires first.") return playsound(src, W.usesound, 50, 1) - to_chat(user,"You begin to remove the power control board...") //lpeters - fixed grammar issues //Ner - grrrrrr + to_chat(user, "You begin to remove the power control board...") //lpeters - fixed grammar issues //Ner - grrrrrr if(do_after(user, 50 * W.toolspeed)) if (has_electronics==1) has_electronics = 0 @@ -484,20 +485,20 @@ update_icon() else if (W.is_crowbar() && !(stat & BROKEN) ) if(coverlocked && !(stat & MAINT)) - to_chat(user,"The cover is locked and cannot be opened.") + to_chat(user, "The cover is locked and cannot be opened.") return else opened = 1 update_icon() else if (istype(W, /obj/item/weapon/cell) && opened) // trying to put a cell inside if(cell) - to_chat(user,"The [src.name] already has a power cell installed.") + to_chat(user, "The [src.name] already has a power cell installed.") return if (stat & MAINT) - to_chat(user,"You need to install the wiring and electronics first.") + to_chat(user, "You need to install the wiring and electronics first.") return if(W.w_class != ITEMSIZE_NORMAL) - to_chat(user,"\The [W] is too [W.w_class < 3? "small" : "large"] to work here.") + to_chat(user, "\The [W] is too [W.w_class < 3? "small" : "large"] to work here.") return user.drop_item() @@ -511,26 +512,26 @@ else if (W.is_screwdriver()) // haxing if(opened) if (cell) - to_chat(user,"Remove the power cell first.") + to_chat(user, "Remove the power cell first.") return else if (has_electronics==1 && terminal) has_electronics = 2 stat &= ~MAINT playsound(src.loc, W.usesound, 50, 1) - to_chat(user,"You screw the circuit electronics into place.") + to_chat(user, "You screw the circuit electronics into place.") else if (has_electronics==2) has_electronics = 1 stat |= MAINT playsound(src.loc, W.usesound, 50, 1) - to_chat(user,"You unfasten the electronics.") + to_chat(user, "You unfasten the electronics.") else /* has_electronics==0 */ - to_chat(user,"There is nothing to secure.") + to_chat(user, "There is nothing to secure.") return update_icon() else wiresexposed = !wiresexposed - to_chat(user,"The wires have been [wiresexposed ? "exposed" : "unexposed"].") + to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"].") playsound(src, W.usesound, 50, 1) update_icon() @@ -540,11 +541,11 @@ else if (istype(W, /obj/item/stack/cable_coil) && !terminal && opened && has_electronics!=2) var/turf/T = loc if(istype(T) && !T.is_plating()) - to_chat(user,"You must remove the floor plating in front of the APC first.") + to_chat(user, "You must remove the floor plating in front of the APC first.") return var/obj/item/stack/cable_coil/C = W if(C.get_amount() < 10) - to_chat(user,"You need ten lengths of cable for that.") + to_chat(user, "You need ten lengths of cable for that.") return user.visible_message("[user.name] adds cables to the APC frame.", \ "You start adding cables to the APC frame...") @@ -567,7 +568,7 @@ else if (W.is_wirecutter() && terminal && opened && has_electronics!=2) var/turf/T = loc if(istype(T) && !T.is_plating()) - to_chat(user,"You must remove the floor plating in front of the APC first.") + to_chat(user, "You must remove the floor plating in front of the APC first.") return user.visible_message("[user.name] starts dismantling the [src]'s power terminal.", \ "You begin to cut the cables...") @@ -581,7 +582,7 @@ if(usr.stunned) return new /obj/item/stack/cable_coil(loc,10) - to_chat(user,"You cut the cables and dismantle the power terminal.") + to_chat(user, "You cut the cables and dismantle the power terminal.") qdel(terminal) else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && !((stat & BROKEN))) user.visible_message("[user.name] inserts the power control board into [src].", \ @@ -591,15 +592,15 @@ if(has_electronics==0) has_electronics = 1 reboot() - to_chat(user,"You place the power control board inside the frame.") + to_chat(user, "You place the power control board inside the frame.") qdel(W) else if (istype(W, /obj/item/weapon/module/power_control) && opened && has_electronics==0 && ((stat & BROKEN))) - to_chat(user,"The [src] is too broken for that. Repair it first.") + to_chat(user, "The [src] is too broken for that. Repair it first.") return else if (istype(W, /obj/item/weapon/weldingtool) && opened && has_electronics==0 && !terminal) var/obj/item/weapon/weldingtool/WT = W if (WT.get_fuel() < 3) - to_chat(user,"You need more welding fuel to complete this task.") + to_chat(user, "You need more welding fuel to complete this task.") return user.visible_message("[user.name] begins cutting apart [src] with the [WT.name].", \ "You start welding the APC frame...", \ @@ -668,27 +669,27 @@ if (!opened && wiresexposed && (istype(W, /obj/item/device/multitool) || W.is_wirecutter() || istype(W, /obj/item/device/assembly/signaler))) return src.attack_hand(user) //Placeholder until someone can do take_damage() for APCs or something. - to_chat(user,"The [src.name] looks too sturdy to bash open with \the [W.name].") + to_chat(user, "The [src.name] looks too sturdy to bash open with \the [W.name].") // attack with hand - remove cell (if cover open) or interact with the APC /obj/machinery/power/apc/verb/togglelock(mob/user as mob) if(emagged) - to_chat(user,"The panel is unresponsive.") + to_chat(user, "The panel is unresponsive.") else if(opened) - to_chat(user,"You must close the cover to swipe an ID card.") + to_chat(user, "You must close the cover to swipe an ID card.") else if(wiresexposed) - to_chat(user,"You must close the wire panel.") + to_chat(user, "You must close the wire panel.") else if(stat & (BROKEN|MAINT)) - to_chat(user,"Nothing happens.") + to_chat(user, "Nothing happens.") else if(hacker) - to_chat(user,"Access denied.") + to_chat(user, "Access denied.") else if(src.allowed(usr) && !isWireCut(APC_WIRE_IDSCAN)) locked = !locked - to_chat(user,"You [ locked ? "lock" : "unlock"] the APC interface.") + to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.") update_icon() else - to_chat(user,"Access denied.") + to_chat(user, "Access denied.") /obj/machinery/power/apc/AltClick(mob/user) ..() @@ -697,17 +698,17 @@ /obj/machinery/power/apc/emag_act(var/remaining_charges, var/mob/user) if (!(emagged || hacker)) // trying to unlock with an emag card if(opened) - to_chat(user,"You must close the cover to do that.") + to_chat(user, "You must close the cover to do that.") else if(wiresexposed) - to_chat(user,"You must close the wire panel first.") + to_chat(user, "You must close the wire panel first.") else if(stat & (BROKEN|MAINT)) - to_chat(user,"The [src] isn't working.") + to_chat(user, "The [src] isn't working.") else flick("apc-spark", src) if (do_after(user,6)) emagged = 1 locked = 0 - to_chat(user,"You emag the APC interface.") + to_chat(user, "You emag the APC interface.") update_icon() return 1 @@ -835,7 +836,7 @@ if (!ui) // the ui does not exist, so we'll create a new() one // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 520, data["siliconUser"] ? 490 : 465) + ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 520, data["siliconUser"] ? 490 : 465) // when the ui is first opened this is the data it will use ui.set_initial_data(data) // open the new ui window @@ -853,13 +854,13 @@ area.power_environ = (environ >= POWERCHAN_ON) // if (area.name == "AI Chamber") // spawn(10) -// world << " [area.name] [area.power_equip]" +// to_world(" [area.name] [area.power_equip]") else area.power_light = 0 area.power_equip = 0 area.power_environ = 0 // if (area.name == "AI Chamber") -// world << "[area.power_equip]" +// to_world("[area.power_equip]") area.power_change() /obj/machinery/power/apc/proc/isWireCut(var/wireIndex) @@ -878,10 +879,10 @@ if(!user.IsAdvancedToolUser()) return 0 if(user.restrained()) - to_chat(user,"Your hands must be free to use [src].") + to_chat(user, "Your hands must be free to use [src].") return 0 if(user.lying) - to_chat(user,"You must stand to use [src]!") + to_chat(user, "You must stand to use [src]!") return 0 autoflag = 5 if (istype(user, /mob/living/silicon)) @@ -896,14 +897,14 @@ if(aidisabled && !permit) if(!loud) - to_chat(user,"\The AI control for [src] has been disabled!") + to_chat(user, "\The AI control for [src] has been disabled!") return 0 else if (!in_range(src, user) || !istype(src.loc, /turf)) return 0 var/mob/living/carbon/human/H = user if (istype(H) && prob(H.getBrainLoss())) - to_chat(user,"You momentarily forget how to use [src].") + to_chat(user, "You momentarily forget how to use [src].") return 0 return 1 @@ -918,10 +919,10 @@ if(isobserver(usr) ) var/mob/observer/dead/O = usr //Added to allow admin nanoUI interactions. if(!O.can_admin_interact() ) //NanoUI /should/ make this not needed, but better safe than sorry. - to_chat(usr,"Try as you might, your ghostly fingers can't press the buttons.") + to_chat(usr, "Try as you might, your ghostly fingers can't press the buttons.") return 1 else - to_chat(usr,"You must unlock the panel to use this!") + to_chat(usr, "You must unlock the panel to use this!") return 1 if (href_list["lock"]) @@ -974,7 +975,7 @@ else if (href_list["toggleaccess"]) if(istype(usr, /mob/living/silicon)) if(emagged || (stat & (BROKEN|MAINT))) - to_chat(usr,"The APC does not respond to the command.") + to_chat(usr, "The APC does not respond to the command.") return locked = !locked update_icon() diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 4b8cb09a82..ab535081b6 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -515,6 +515,7 @@ obj/structure/cable/proc/cableColor(var/colorC) item_state = "coil" attack_verb = list("whipped", "lashed", "disciplined", "flogged") stacktype = /obj/item/stack/cable_coil + drop_sound = 'sound/items/drop/accessory.ogg' /obj/item/stack/cable_coil/cyborg name = "cable coil synthesizer" @@ -555,6 +556,15 @@ obj/structure/cable/proc/cableColor(var/colorC) if(!S || S.robotic < ORGAN_ROBOT || S.open == 3) return ..() + if(S.organ_tag == BP_HEAD) + if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) + to_chat(user, "You can't apply [src] through [H.head]!") + return 1 + else + if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) + to_chat(user, "You can't apply [src] through [H.wear_suit]!") + return 1 + var/use_amt = min(src.amount, CEILING(S.burn_dam/5, 1), 5) if(can_use(use_amt)) if(S.robo_repair(5*use_amt, BURN, "some damaged wiring", src, user)) @@ -574,7 +584,7 @@ obj/structure/cable/proc/cableColor(var/colorC) name = "cable piece" else icon_state = "coil" - name = "cable coil" + name = initial(name) /obj/item/stack/cable_coil/proc/set_cable_color(var/selected_color, var/user) if(!selected_color) diff --git a/code/modules/power/cable_heavyduty.dm b/code/modules/power/cable_heavyduty.dm index c90c936c4d..692dcd73d9 100644 --- a/code/modules/power/cable_heavyduty.dm +++ b/code/modules/power/cable_heavyduty.dm @@ -18,10 +18,10 @@ return if(W.is_wirecutter()) - usr << "These cables are too tough to be cut with those [W.name]." + to_chat(usr, "These cables are too tough to be cut with those [W.name].") return else if(istype(W, /obj/item/stack/cable_coil)) - usr << "You will need heavier cables to connect to these." + to_chat(usr, "You will need heavier cables to connect to these.") return else ..() diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 15c5fd11f6..79f232f508 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -263,5 +263,5 @@ /obj/item/weapon/cell/suicide_act(mob/user) var/datum/gender/TU = gender_datums[user.get_visible_gender()] - viewers(user) << "\The [user] is licking the electrodes of \the [src]! It looks like [TU.he] [TU.is] trying to commit suicide." + to_chat(viewers(user),"\The [user] is licking the electrodes of \the [src]! It looks like [TU.he] [TU.is] trying to commit suicide.") return (FIRELOSS) \ No newline at end of file diff --git a/code/modules/power/cells/device_cells.dm b/code/modules/power/cells/device_cells.dm index b2ecbfdb98..5639f85c43 100644 --- a/code/modules/power/cells/device_cells.dm +++ b/code/modules/power/cells/device_cells.dm @@ -17,7 +17,7 @@ /obj/item/weapon/cell/device/weapon //Aka adv name = "advanced device power cell" desc = "A small upgraded power cell designed to power handheld devices." - icon_state = "acell" + icon_state = "wcell" maxcharge = 2400 charge_amount = 20 origin_tech = list(TECH_POWER = 2) @@ -30,10 +30,13 @@ /obj/item/weapon/cell/device/super name = "super device power cell" desc = "A small upgraded power cell designed to power handheld devices." - icon_state = "uscell" + icon = 'icons/obj/power_yw.dmi' + icon_state = "swcell" maxcharge = 3600 charge_amount = 20 origin_tech = list(TECH_POWER = 3) + overlay_half_state = "swcell-partial" + overlay_full_state = "swcell-full" /obj/item/weapon/cell/device/super/empty/Initialize() . = ..() @@ -43,10 +46,13 @@ /obj/item/weapon/cell/device/hyper name = "hyper device power cell" desc = "A small upgraded power cell designed to hold much more power for handheld devices." - icon_state = "wcell" + icon = 'icons/obj/power_yw.dmi' + icon_state = "hwcell" maxcharge = 4800 charge_amount = 20 origin_tech = list(TECH_POWER = 4) + overlay_half_state = "hwcell-partial" + overlay_full_state = "hwcell-full" /obj/item/weapon/cell/device/hyper/empty/Initialize() . = ..() @@ -57,7 +63,7 @@ /obj/item/weapon/cell/device/weapon/recharge name = "self-charging weapon power cell" desc = "A small power cell designed to power handheld weaponry. This one recharges itself." -// icon_state = "wcell" //TODO: Different sprite +// icon_state = "wcell" //doesn't matter, not seen anywhere (all weapons that have it are batterylocked) self_recharge = TRUE charge_amount = 120 charge_delay = 75 diff --git a/code/modules/power/fractal_reactor.dm b/code/modules/power/fractal_reactor.dm index 4270fdbc85..59b6196d30 100644 --- a/code/modules/power/fractal_reactor.dm +++ b/code/modules/power/fractal_reactor.dm @@ -18,7 +18,7 @@ /obj/machinery/power/fractal_reactor/New() ..() if(!mapped_in) - world << "WARNING: Map testing power source activated at: X:[src.loc.x] Y:[src.loc.y] Z:[src.loc.z]" + to_world("WARNING: Map testing power source activated at: X:[src.loc.x] Y:[src.loc.y] Z:[src.loc.z]") /obj/machinery/power/fractal_reactor/process() if(!powernet && !powernet_connection_failed) diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm index 99b0346e62..9cc907e052 100644 --- a/code/modules/power/fusion/core/_core.dm +++ b/code/modules/power/fusion/core/_core.dm @@ -13,7 +13,7 @@ var/list/fusion_cores = list() icon = 'icons/obj/machines/power/fusion.dmi' icon_state = "core0" density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 50 active_power_usage = 500 //multiplied by field strength anchored = 0 @@ -70,7 +70,7 @@ var/list/fusion_cores = list() owned_field = new(loc, src) owned_field.ChangeFieldStrength(field_strength) icon_state = "core1" - use_power = 2 + update_use_power(USE_POWER_ACTIVE) . = 1 /obj/machinery/power/fusion_core/proc/Shutdown(var/force_rupture) @@ -82,7 +82,7 @@ var/list/fusion_cores = list() owned_field.RadiateAll() qdel(owned_field) owned_field = null - use_power = 1 + update_use_power(USE_POWER_IDLE) /obj/machinery/power/fusion_core/proc/AddParticles(var/name, var/quantity = 1) if(owned_field) diff --git a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm index 80a512b91b..0ffab7db07 100644 --- a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm +++ b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm @@ -7,7 +7,7 @@ var/list/fuel_injectors = list() density = 1 anchored = 0 req_access = list(access_engine) - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 500 @@ -103,13 +103,13 @@ var/list/fuel_injectors = list() if(!injecting && cur_assembly) icon_state = "injector1" injecting = 1 - use_power = 1 + update_use_power(USE_POWER_IDLE) /obj/machinery/fusion_fuel_injector/proc/StopInjecting() if(injecting) injecting = 0 icon_state = "injector0" - use_power = 0 + update_use_power(USE_POWER_OFF) /obj/machinery/fusion_fuel_injector/proc/Inject() if(!injecting) diff --git a/code/modules/power/fusion/gyrotron/gyrotron.dm b/code/modules/power/fusion/gyrotron/gyrotron.dm index 539e9cbda6..bdbdf9a3df 100644 --- a/code/modules/power/fusion/gyrotron/gyrotron.dm +++ b/code/modules/power/fusion/gyrotron/gyrotron.dm @@ -6,7 +6,7 @@ var/list/gyrotrons = list() desc = "It is a heavy duty industrial gyrotron suited for powering fusion reactors." icon_state = "emitter-off" req_access = list(access_engine) - use_power = 1 + use_power = USE_POWER_IDLE active_power_usage = 50000 circuit = /obj/item/weapon/circuitboard/gyrotron diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 2eadfc520a..d9186b548f 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -1,11 +1,13 @@ +GLOBAL_LIST_EMPTY(all_turbines) + /obj/machinery/power/generator name = "thermoelectric generator" desc = "It's a high efficiency thermoelectric generator." - icon_state = "teg" + icon_state = "teg-unassembled" density = 1 anchored = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 //Watts, I hope. Just enough to do the computer and display things. var/max_power = 500000 @@ -27,12 +29,16 @@ /obj/machinery/power/generator/Initialize() soundloop = new(list(src), FALSE) desc = initial(desc) + " Rated for [round(max_power/1000)] kW." - spawn(1) - reconnect() - return ..() + GLOB.all_turbines += src + ..() //Not returned, because... + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/power/generator/LateInitialize() + reconnect() /obj/machinery/power/generator/Destroy() QDEL_NULL(soundloop) + GLOB.all_turbines -= src return ..() //generators connect in dir and reverse_dir(dir) directions @@ -62,13 +68,26 @@ circ2 = null /obj/machinery/power/generator/proc/updateicon() - if(stat & (NOPOWER|BROKEN)) - overlays.Cut() + icon_state = anchored ? "teg-assembled" : "teg-unassembled" + cut_overlays() + if (circ1) + circ1.temperature_overlay = null + if (circ2) + circ2.temperature_overlay = null + if (stat & (NOPOWER|BROKEN)) + return 1 else - overlays.Cut() - - if(lastgenlev != 0) - overlays += image('icons/obj/power.dmi', "teg-op[lastgenlev]") + if (lastgenlev != 0) + add_overlay("teg-op[lastgenlev]") + if (circ1 && circ2) + var/extreme = (lastgenlev > 9) ? "ex" : "" + if (circ1.last_temperature < circ2.last_temperature) + circ1.temperature_overlay = "circ-[extreme]cold" + circ2.temperature_overlay = "circ-[extreme]hot" + else + circ1.temperature_overlay = "circ-[extreme]hot" + circ2.temperature_overlay = "circ-[extreme]cold" + return 1 /obj/machinery/power/generator/process() if(!circ1 || !circ2 || !anchored || stat & (BROKEN|NOPOWER)) @@ -156,7 +175,7 @@ user.visible_message("[user.name] [anchored ? "secures" : "unsecures"] the bolts holding [src.name] to the floor.", \ "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \ "You hear a ratchet.") - use_power = anchored + update_use_power(anchored ? USE_POWER_IDLE : USE_POWER_ACTIVE) if(anchored) // Powernet connection stuff. connect_to_network() else diff --git a/code/modules/power/generator_type2.dm b/code/modules/power/generator_type2.dm index 7ca407ac9f..a52675b706 100644 --- a/code/modules/power/generator_type2.dm +++ b/code/modules/power/generator_type2.dm @@ -4,7 +4,7 @@ icon_state = "teg" anchored = 1 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/obj/machinery/atmospherics/unary/generator_input/input1 var/obj/machinery/atmospherics/unary/generator_input/input2 @@ -13,15 +13,13 @@ var/lastgenlev = -1 -/obj/machinery/power/generator_type2/New() - ..() - spawn(5) - input1 = locate(/obj/machinery/atmospherics/unary/generator_input) in get_step(src,turn(dir, 90)) - input2 = locate(/obj/machinery/atmospherics/unary/generator_input) in get_step(src,turn(dir, -90)) - if(!input1 || !input2) - stat |= BROKEN - updateicon() - +/obj/machinery/power/generator_type2/Initialize() + . = ..() + input1 = locate(/obj/machinery/atmospherics/unary/generator_input) in get_step(src,turn(dir, 90)) + input2 = locate(/obj/machinery/atmospherics/unary/generator_input) in get_step(src,turn(dir, -90)) + if(!input1 || !input2) + stat |= BROKEN + updateicon() /obj/machinery/power/generator_type2/proc/updateicon() @@ -69,7 +67,7 @@ hot_air.temperature = hot_air.temperature - energy_transfer/hot_air_heat_capacity cold_air.temperature = cold_air.temperature + heat/cold_air_heat_capacity - //world << "POWER: [lastgen] W generated at [efficiency*100]% efficiency and sinks sizes [cold_air_heat_capacity], [hot_air_heat_capacity]" + //to_world("POWER: [lastgen] W generated at [efficiency*100]% efficiency and sinks sizes [cold_air_heat_capacity], [hot_air_heat_capacity]") if(input1.network) input1.network.update = 1 diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 2c73ebd641..d71da42e9e 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -16,7 +16,7 @@ icon_state = "TheSingGen" anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 200 active_power_usage = 1000 var/on = 1 @@ -24,27 +24,17 @@ var/effectiverange = 25 // Borrows code from cloning computer -/obj/machinery/computer/gravity_control_computer/New() - ..() - spawn(5) - updatemodules() - return - return - -/obj/machinery/gravity_generator/New() - ..() - spawn(5) - locatelocalareas() - return - return - - +/obj/machinery/computer/gravity_control_computer/Initialize() + . = ..() + updatemodules() +/obj/machinery/gravity_generator/Initialize() + . = ..() + locatelocalareas() + /obj/machinery/computer/gravity_control_computer/proc/updatemodules() src.gravity_generator = findgenerator() - - /obj/machinery/gravity_generator/proc/locatelocalareas() for(var/area/A in range(src,effectiverange)) if(A.name == "Space") @@ -55,10 +45,10 @@ /obj/machinery/computer/gravity_control_computer/proc/findgenerator() var/obj/machinery/gravity_generator/foundgenerator = null for(dir in list(NORTH,EAST,SOUTH,WEST)) - //world << "SEARCHING IN [dir]" + //to_world("SEARCHING IN [dir]") foundgenerator = locate(/obj/machinery/gravity_generator/, get_step(src, dir)) if (!isnull(foundgenerator)) - //world << "FOUND" + //to_world("FOUND") break return foundgenerator @@ -127,13 +117,13 @@ if((A in G.localareas) && (G.on)) break if(!G) - A.gravitychange(0,A) + A.gravitychange(0) else for(var/area/A in gravity_generator:localareas) gravity_generator:on = 1 - A.gravitychange(1,A) + A.gravitychange(1) src.updateUsrDialog() return diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 5e1271827a..b5f43750c6 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -75,12 +75,14 @@ var/global/list/light_type_cache = list() to_chat(user, "This casing doesn't support power cells for backup power.") /obj/machinery/light_construct/attack_hand(mob/user) + . = ..() + if(.) + return . // obj/machinery/attack_hand returns 1 if user can't use the machine if(cell) user.visible_message("[user] removes [cell] from [src]!","You remove [cell].") user.put_in_hands(cell) cell.update_icon() cell = null - add_fingerprint(user) /obj/machinery/light_construct/attackby(obj/item/weapon/W as obj, mob/user as mob) src.add_fingerprint(user) @@ -101,6 +103,7 @@ var/global/list/light_type_cache = list() cell = W add_fingerprint(user) return + if (W.is_wrench()) if (src.stage == 1) playsound(src, W.usesound, 75, 1) @@ -209,7 +212,7 @@ var/global/list/light_type_cache = list() anchored = 1 plane = MOB_PLANE layer = ABOVE_MOB_LAYER - use_power = 2 + use_power = USE_POWER_ACTIVE idle_power_usage = 2 active_power_usage = 10 power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list @@ -231,6 +234,8 @@ var/global/list/light_type_cache = list() var/current_alert = null // Which alert are we showing right now? //VOREStation Edit End + var/auto_flicker = FALSE // If true, will constantly flicker, so long as someone is around to see it (otherwise its a waste of CPU). + var/obj/item/weapon/cell/emergency_light/cell var/start_with_cell = TRUE // if true, this fixture generates a very weak cell at roundstart @@ -241,8 +246,6 @@ var/global/list/light_type_cache = list() var/bulb_emergency_pow_mul = 0.75 // the multiplier for determining the light's power in emergency mode var/bulb_emergency_pow_min = 0.5 // the minimum value for the light's power in emergency mode - var/auto_flicker = FALSE // If true, will constantly flicker, so long as someone is around to see it (otherwise its a waste of CPU). - /obj/machinery/light/flicker auto_flicker = TRUE @@ -279,11 +282,14 @@ var/global/list/light_type_cache = list() /obj/machinery/light/flamp/New(atom/newloc, obj/machinery/light_construct/construct = null) ..(newloc, construct) - if(start_with_cell && !no_emergency) - cell = new/obj/item/weapon/cell/emergency_light(src) if(construct) + start_with_cell = FALSE lamp_shade = 0 update_icon() + else + if(start_with_cell && !no_emergency) + cell = new/obj/item/weapon/cell/emergency_light(src) + /obj/machinery/light/flamp/flicker auto_flicker = TRUE @@ -322,6 +328,8 @@ var/global/list/light_type_cache = list() construct.transfer_fingerprints_to(src) set_dir(construct.dir) else + if(start_with_cell && !no_emergency) + cell = new/obj/item/weapon/cell/emergency_light(src) var/obj/item/weapon/light/L = get_light_type_instance(light_type) update_from_bulb(L) if(prob(L.broken_chance)) @@ -429,14 +437,14 @@ var/global/list/light_type_cache = list() on = 0 set_light(0) else - use_power = 2 + update_use_power(USE_POWER_ACTIVE) set_light(brightness_range, brightness_power, brightness_color) else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off()) use_power = 1 emergency_mode = TRUE START_PROCESSING(SSobj, src) else - use_power = 2 + update_use_power(USE_POWER_IDLE) set_light(0) update_icon() @@ -668,19 +676,24 @@ var/global/list/light_type_cache = list() if(status != LIGHT_OK) break on = !on update(0) + if(!on) // Only play when the light turns off. + playsound(src, 'sound/effects/light_flicker.ogg', 50, 1) sleep(rand(5, 15)) on = (status == LIGHT_OK) update(0) flickering = 0 // ai attack - turn on/off emergency lighting for a specific fixture - /obj/machinery/light/attack_ai(mob/user) no_emergency = !no_emergency to_chat(user, "Emergency lights for this fixture have been [no_emergency ? "disabled" : "enabled"].") update(FALSE) return +// ai alt click - Make light flicker. Very important for atmosphere. +/obj/machinery/light/AIAltClick(mob/user) + flicker(1) + /obj/machinery/light/flamp/attack_ai(mob/user) attack_hand() return diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 2443f3ea27..4515b6125c 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -6,7 +6,7 @@ icon_state = "portgen0" density = 1 anchored = 0 - use_power = 0 + use_power = USE_POWER_OFF var/active = 0 var/power_gen = 5000 diff --git a/code/modules/power/port_gen_vr.dm b/code/modules/power/port_gen_vr.dm new file mode 100644 index 0000000000..e6e805e1d7 --- /dev/null +++ b/code/modules/power/port_gen_vr.dm @@ -0,0 +1,11 @@ +/obj/machinery/power/port_gen/pacman/super/potato + name = "nuclear reactor" + desc = "PTTO-3, an industrial all-in-one nuclear power plant by Neo-Chernobyl GmbH. It uses uranium as a fuel source. Rated for 200 kW max safe output." + icon_state = "potato" + time_per_sheet = 1152 //same power output, but a 50 sheet stack will last 4 hours at max safe power + power_gen = 40000 //watts + max_power_output = 5 + max_safe_output = 6 + temperature_gain = 80 //how much the temperature increases per power output level, in degrees per level + max_temperature = 450 + anchored = 1 diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 1725638548..194cdb4eb4 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -11,7 +11,7 @@ icon = 'icons/obj/power.dmi' anchored = 1.0 var/datum/powernet/powernet = null - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 active_power_usage = 0 @@ -246,7 +246,7 @@ //remove the old powernet and replace it with a new one throughout the network. /proc/propagate_network(var/obj/O, var/datum/powernet/PN) - //world.log << "propagating new network" + //to_world_log("propagating new network") var/list/worklist = list() var/list/found_machines = list() var/index = 1 diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index d8f57baf3b..2710d69788 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -148,8 +148,8 @@ // return a knot cable (O-X) if one is present in the turf // null if there's none /turf/proc/get_cable_node() - if(!istype(src, /turf/simulated/floor)) - return null + //if(!istype(src, /turf/simulated/floor)) //VOREStation Removal - Why? + //return null //VOREStation Removal - Why? for(var/obj/structure/cable/C in src) if(C.d1 == 0) return C diff --git a/code/modules/power/profiling.dm b/code/modules/power/profiling.dm index 3cf8c3a0b9..185e413d60 100644 --- a/code/modules/power/profiling.dm +++ b/code/modules/power/profiling.dm @@ -34,14 +34,14 @@ var/global/list/power_update_requests_by_area = list() if(enable_power_update_profiling) enable_power_update_profiling = 0 - usr << "Area power update profiling disabled." + to_chat(usr, "Area power update profiling disabled.") message_admins("[key_name(src)] toggled area power update profiling off.") log_admin("[key_name(src)] toggled area power update profiling off.") else enable_power_update_profiling = 1 power_last_profile_time = world.time - usr << "Area power update profiling enabled." + to_chat(usr, "Area power update profiling enabled.") message_admins("[key_name(src)] toggled area power update profiling on.") log_admin("[key_name(src)] toggled area power update profiling on.") @@ -54,9 +54,9 @@ var/global/list/power_update_requests_by_area = list() if(!check_rights(R_DEBUG)) return - usr << "Total profiling time: [power_profiled_time] ticks" + to_chat(usr, "Total profiling time: [power_profiled_time] ticks") for (var/M in power_update_requests_by_machine) - usr << "[M] = [power_update_requests_by_machine[M]]" + to_chat(usr, "[M] = [power_update_requests_by_machine[M]]") /client/proc/view_power_update_stats_area() set name = "View Area Power Update Statistics By Area" @@ -65,7 +65,7 @@ var/global/list/power_update_requests_by_area = list() if(!check_rights(R_DEBUG)) return - usr << "Total profiling time: [power_profiled_time] ticks" - usr << "Total profiling time: [power_profiled_time] ticks" + to_chat(usr, "Total profiling time: [power_profiled_time] ticks") + to_chat(usr, "Total profiling time: [power_profiled_time] ticks") for (var/A in power_update_requests_by_area) - usr << "[A] = [power_update_requests_by_area[A]]" \ No newline at end of file + to_chat(usr, "[A] = [power_update_requests_by_area[A]]") \ No newline at end of file diff --git a/code/modules/power/sensors/sensor_monitoring.dm b/code/modules/power/sensors/sensor_monitoring.dm index 7868e16786..e243b7f75b 100644 --- a/code/modules/power/sensors/sensor_monitoring.dm +++ b/code/modules/power/sensors/sensor_monitoring.dm @@ -7,7 +7,7 @@ name = "Power Monitoring Console" desc = "Computer designed to remotely monitor power levels around the station" icon_keyboard = "power_key" - icon_screen = "power:0" + icon_screen = "power_monitor" light_color = "#ffcc33" //computer stuff @@ -15,7 +15,7 @@ anchored = 1.0 circuit = /obj/item/weapon/circuitboard/powermonitor var/alerting = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 300 active_power_usage = 300 var/datum/nano_module/power_monitor/power_monitor @@ -31,9 +31,9 @@ /obj/machinery/computer/power_monitor/update_icon() if(!(stat & (NOPOWER|BROKEN))) if(alerting) - icon_screen = "power:1" + icon_screen = "[initial(icon_screen)]" else - icon_screen = "power:0" + icon_screen = "[initial(icon_screen)]_warn" ..() */ // On creation automatically connects to active sensors. This is delayed to ensure sensors already exist. diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index d8712a0e4c..6478ff999a 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -8,7 +8,7 @@ anchored = 1 density = 0 unacidable = 1 - use_power = 0 + use_power = USE_POWER_OFF light_range = 4 flags = PROXMOVE var/obj/machinery/field_generator/FG1 = null diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index f847813923..2e6cda0049 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -10,7 +10,7 @@ req_access = list(access_engine_equip) var/id = null - use_power = 0 //uses powernet power, not APC power + use_power = USE_POWER_OFF //uses powernet power, not APC power active_power_usage = 30000 //30 kW laser. I guess that means 30 kJ per shot. var/active = 0 diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 8155027bc2..6a510f0060 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -20,7 +20,7 @@ field_generator power level display icon_state = "Field_Gen" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/const/num_power_levels = 6 // Total number of power level icon has var/Varedit_start = 0 var/Varpower = 0 @@ -83,7 +83,7 @@ field_generator power level display if(state == 2) if(get_dist(src, user) <= 1)//Need to actually touch the thing to turn it on if(src.active >= 1) - user << "You are unable to turn off the [src.name] once it is online." + to_chat(user, "You are unable to turn off the [src.name] once it is online.") return 1 else user.visible_message("[user.name] turns on the [src.name]", \ @@ -95,13 +95,13 @@ field_generator power level display src.add_fingerprint(user) else - user << "The [src] needs to be firmly secured to the floor first." + to_chat(user, "The [src] needs to be firmly secured to the floor first.") return /obj/machinery/field_generator/attackby(obj/item/W, mob/user) if(active) - user << "The [src] needs to be off." + to_chat(user, "The [src] needs to be off.") return else if(W.is_wrench()) switch(state) @@ -120,13 +120,13 @@ field_generator power level display "You hear ratchet") src.anchored = 0 if(2) - user << "The [src.name] needs to be unwelded from the floor." + to_chat(user, "The [src.name] needs to be unwelded from the floor.") return else if(istype(W, /obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/WT = W switch(state) if(0) - user << "The [src.name] needs to be wrenched to the floor." + to_chat(user, "The [src.name] needs to be wrenched to the floor.") return if(1) if (WT.remove_fuel(0,user)) @@ -137,7 +137,7 @@ field_generator power level display if (do_after(user,20 * WT.toolspeed)) if(!src || !WT.isOn()) return state = 2 - user << "You weld the field generator to the floor." + to_chat(user, "You weld the field generator to the floor.") else return if(2) @@ -149,7 +149,7 @@ field_generator power level display if (do_after(user,20 * WT.toolspeed)) if(!src || !WT.isOn()) return state = 1 - user << "You cut the [src] free from the floor." + to_chat(user, "You cut the [src] free from the floor.") else return else diff --git a/code/modules/power/singularity/generator.dm b/code/modules/power/singularity/generator.dm index c49c605598..bb470811ba 100644 --- a/code/modules/power/singularity/generator.dm +++ b/code/modules/power/singularity/generator.dm @@ -6,7 +6,7 @@ icon_state = "TheSingGen" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/energy = 0 var/creation_type = /obj/singularity diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index 2c3e99318d..e54b46c7f0 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -78,7 +78,7 @@ var/radiation = (energy*2) M.apply_effect((radiation*3),IRRADIATE,0) M.updatehealth() - //M << "You feel odd." + //to_chat(M, "You feel odd.") /obj/effect/accelerated_particle/proc/move(var/lag) diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm index 7460830f21..f5481afd4c 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm @@ -258,7 +258,7 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin icon_state = "none" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 active_power_usage = 0 var/construction_state = 0 @@ -383,10 +383,10 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin if(src.construction_state < 3)//Was taken apart, update state update_state() if(use_power) - use_power = 0 + update_use_power(USE_POWER_OFF) src.construction_state = temp_state if(src.construction_state >= 3) - use_power = 1 + update_use_power(USE_POWER_IDLE) update_icon() return 1 return 0 diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index bcdd0d5036..8cb370924b 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -8,7 +8,7 @@ reference = "control_box" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 500 active_power_usage = 70000 //70 kW per unit of strength construction_state = 0 @@ -42,7 +42,7 @@ /obj/machinery/particle_accelerator/control_box/update_state() if(construction_state < 3) - update_use_power(0) + update_use_power(USE_POWER_OFF) assembled = 0 active = 0 for(var/obj/structure/particle_accelerator/part in connected_parts) @@ -52,7 +52,7 @@ connected_parts = list() return if(!part_scan()) - update_use_power(1) + update_use_power(USE_POWER_IDLE) active = 0 connected_parts = list() @@ -138,9 +138,9 @@ ..() if(stat & NOPOWER) active = 0 - update_use_power(0) + update_use_power(USE_POWER_OFF) else if(!stat && construction_state == 3) - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/particle_accelerator/control_box/process() @@ -212,13 +212,13 @@ message_admins("PA Control Computer turned [active ?"ON":"OFF"] by [key_name(usr, usr.client)](?) in ([x],[y],[z] - JMP)",0,1) log_game("PACCEL([x],[y],[z]) [key_name(usr)] turned [active?"ON":"OFF"].") if(active) - update_use_power(2) + update_use_power(USE_POWER_ACTIVE) for(var/obj/structure/particle_accelerator/part in connected_parts) part.strength = src.strength part.powered = 1 part.update_icon() else - update_use_power(1) + update_use_power(USE_POWER_IDLE) for(var/obj/structure/particle_accelerator/part in connected_parts) part.strength = null part.powered = 0 diff --git a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm index 921b000078..5a4176ee89 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm @@ -9,7 +9,7 @@ icon_state = "smasher" anchored = 0 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/successful_craft = FALSE // Are we waiting to be emptied? var/image/material_layer // Holds the image used for the filled overlay. diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index b75a81e1e4..bdcc0ce66f 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -37,7 +37,7 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) energy = starting_energy ..() - START_PROCESSING(SSobj, src) + START_PROCESSING(SSobj, src) for(var/obj/machinery/power/singularity_beacon/singubeacon in machines) if(singubeacon.active) target = singubeacon @@ -427,11 +427,11 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) if (istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M if(istype(H.glasses,/obj/item/clothing/glasses/meson) && current_size != STAGE_SUPER) - H << "You look directly into The [src.name], good thing you had your protective eyewear on!" + to_chat(H, "You look directly into The [src.name], good thing you had your protective eyewear on!") return else - H << "You look directly into The [src.name], but your eyewear does absolutely nothing to protect you from it!" - M << "You look directly into The [src.name] and feel [current_size == STAGE_SUPER ? "helpless" : "weak"]." + to_chat(H, "You look directly into The [src.name], but your eyewear does absolutely nothing to protect you from it!") + to_chat(M, "You look directly into The [src.name] and feel [current_size == STAGE_SUPER ? "helpless" : "weak"].") M.apply_effect(3, STUN) for(var/mob/O in viewers(M, null)) O.show_message(text("[] stares blankly at The []!", M, src), 1) @@ -448,8 +448,8 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) to_chat(M, "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") to_chat(M, "Miraculously, it fails to kill you.") else - M << "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat." - M << "You don't even have a moment to react as you are reduced to ashes by the intense radiation." + to_chat(M, "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") + to_chat(M, "You don't even have a moment to react as you are reduced to ashes by the intense radiation.") M.dust() SSradiation.radiate(src, rand(energy)) return diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 8628315601..a3ee1a2033 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -11,8 +11,9 @@ icon_state = "smes" density = 1 anchored = 1 - use_power = 0 + use_power = USE_POWER_OFF circuit = /obj/item/weapon/circuitboard/smes + clicksound = "switch" var/capacity = 5e6 // maximum charge var/charge = 1e6 // actual charge @@ -55,36 +56,32 @@ charge -= smes_amt return smes_amt / SMESRATE +/obj/machinery/power/smes/Initialize() + . = ..() + if(!powernet) + connect_to_network() -/obj/machinery/power/smes/New() - ..() - spawn(5) - if(!powernet) - connect_to_network() + dir_loop: + for(var/d in cardinal) + var/turf/T = get_step(src, d) + for(var/obj/machinery/power/terminal/term in T) + if(term && term.dir == turn(d, 180)) + terminal = term + break dir_loop + if(!terminal) + stat |= BROKEN + return + terminal.master = src + if(!terminal.powernet) + terminal.connect_to_network() + update_icon() - dir_loop: - for(var/d in cardinal) - var/turf/T = get_step(src, d) - for(var/obj/machinery/power/terminal/term in T) - if(term && term.dir == turn(d, 180)) - terminal = term - break dir_loop - if(!terminal) - stat |= BROKEN - return - terminal.master = src - if(!terminal.powernet) - terminal.connect_to_network() - update_icon() - - - - - if(!should_be_mapped) - warning("Non-buildable or Non-magical SMES at [src.x]X [src.y]Y [src.z]Z") - - return + if(!should_be_mapped) + warning("Non-buildable or Non-magical SMES at [src.x]X [src.y]Y [src.z]Z") +/obj/machinery/power/smes/Destroy() + terminal = null + return ..() /obj/machinery/power/smes/disconnect_terminal() if(terminal) @@ -94,22 +91,22 @@ return 0 /obj/machinery/power/smes/update_icon() - overlays.Cut() + cut_overlays() if(stat & BROKEN) return - overlays += image('icons/obj/power.dmi', "smes-op[outputting]") + add_overlay("smes-op[outputting]") if(inputting == 2) - overlays += image('icons/obj/power.dmi', "smes-oc2") + add_overlay("smes-oc2") else if (inputting == 1) - overlays += image('icons/obj/power.dmi', "smes-oc1") + add_overlay("smes-oc1") else if(input_attempt) - overlays += image('icons/obj/power.dmi', "smes-oc0") + add_overlay("smes-oc0") var/clevel = chargedisplay() if(clevel>0) - overlays += image('icons/obj/power.dmi', "smes-og[clevel]") + add_overlay("smes-og[clevel]") return diff --git a/code/modules/power/smes_vr.dm b/code/modules/power/smes_vr.dm new file mode 100644 index 0000000000..a333660a43 --- /dev/null +++ b/code/modules/power/smes_vr.dm @@ -0,0 +1,34 @@ +/obj/machinery/power/smes/buildable/hybrid + name = "hybrid power storage unit" + desc = "A high-capacity superconducting magnetic energy storage (SMES) unit, modified with alien technology to generate small amounts of power from seemingly nowhere." + icon = 'icons/obj/power_vr.dmi' + var/recharge_rate = 10000 + var/overlay_icon = 'icons/obj/power_vr.dmi' + +/obj/machinery/power/smes/buildable/hybrid/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(W.is_screwdriver() || W.is_wirecutter()) + to_chat(user,"\The [src] full of weird alien technology that's best not messed with.") + return 0 + +/obj/machinery/power/smes/buildable/hybrid/update_icon() + cut_overlays() + if(stat & BROKEN) return + + add_overlay("smes-op[outputting]") + + if(inputting == 2) + add_overlay("smes-oc2") + else if (inputting == 1) + add_overlay("smes-oc1") + else + if(input_attempt) + add_overlay("smes-oc0") + + var/clevel = chargedisplay() + if(clevel>0) + add_overlay("smes-og[clevel]") + return + +/obj/machinery/power/smes/buildable/hybrid/process() + charge += min(recharge_rate, capacity - charge) + ..() diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index a6c9c2d2ab..f38cf26b61 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -13,7 +13,7 @@ GLOBAL_LIST_EMPTY(solars_list) icon_state = "sp_base" anchored = 1 density = 1 - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 active_power_usage = 0 var/id = 0 @@ -287,7 +287,7 @@ GLOBAL_LIST_EMPTY(solars_list) icon_state = "solar" anchored = 1 density = 1 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 250 var/id = 0 var/cdir = 0 diff --git a/code/modules/power/supermatter/setup_supermatter.dm b/code/modules/power/supermatter/setup_supermatter.dm index 7477623805..c0b63d9ce1 100644 --- a/code/modules/power/supermatter/setup_supermatter.dm +++ b/code/modules/power/supermatter/setup_supermatter.dm @@ -126,7 +126,7 @@ GLOBAL_LIST_BOILERPLATE(all_engine_setup_markers, /obj/effect/engine_setup) log_and_message_admins("## WARNING: Unable to locate pump at [x] [y] [z]!") return SETUP_WARNING P.target_pressure = P.max_pressure_setting - P.use_power = 1 + P.update_use_power(USE_POWER_IDLE) P.update_icon() return SETUP_OK @@ -259,7 +259,7 @@ GLOBAL_LIST_BOILERPLATE(all_engine_setup_markers, /obj/effect/engine_setup) return SETUP_WARNING F.rebuild_filtering_list() - F.use_power = 1 + F.update_use_power(USE_POWER_IDLE) F.update_icon() return SETUP_OK diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 739699ae31..20463bf05b 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -168,6 +168,8 @@ C.electrocute_act(500, src, def_zone = BP_TORSO) /proc/tesla_zap(atom/source, zap_range = 3, power, explosive = FALSE, stun_mobs = TRUE) + if(!source) // Some mobs and maybe some objects delete themselves when they die. + return . = source.dir if(power < 1000) return diff --git a/code/modules/power/tesla/telsa_construction.dm b/code/modules/power/tesla/telsa_construction.dm index 5cb11b9fe6..5a190e8bda 100644 --- a/code/modules/power/tesla/telsa_construction.dm +++ b/code/modules/power/tesla/telsa_construction.dm @@ -26,6 +26,8 @@ matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) req_components = list() -/datum/category_item/autolathe/engineering/grounding_rod - name = "grounding rod electronics" - path = /obj/item/weapon/circuitboard/grounding_rod +/datum/design/item/autolathe/engineering/grounding_rod //yw add turned into a design for new autolathe + name = "grouding rod electronics" + id = "grounding_rods" + build_path = /obj/item/weapon/circuitboard/grounding_rod + materials = list(DEFAULT_WALL_MATERIAL = 62, "glass" = 62)// yw add end turned into a design for new autolathe diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index a6eab0dbe0..673b2ece86 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -10,7 +10,7 @@ icon_state = "tracker" anchored = 1 density = 1 - use_power = 0 + use_power = USE_POWER_OFF var/id = 0 var/sun_angle = 0 // sun angle as set by sun datum diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 0f9608f49c..4ac7144069 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -3,10 +3,12 @@ desc = "A bullet casing." icon = 'icons/obj/ammo.dmi' icon_state = "s-casing" + randpixel = 10 slot_flags = SLOT_BELT | SLOT_EARS throwforce = 1 w_class = ITEMSIZE_TINY preserve_item = 1 + drop_sound = 'sound/items/drop/ring.ogg' var/leaves_residue = 1 var/caliber = "" //Which kind of guns it can be loaded into @@ -18,8 +20,7 @@ ..() if(ispath(projectile_type)) BB = new projectile_type(src) - pixel_x = rand(-10, 10) - pixel_y = rand(-10, 10) + randpixel_xy() //removes the projectile from the ammo casing /obj/item/ammo_casing/proc/expend() diff --git a/code/modules/projectiles/dnalocking.dm b/code/modules/projectiles/dnalocking.dm index e4ff374fc4..3b966332ac 100644 --- a/code/modules/projectiles/dnalocking.dm +++ b/code/modules/projectiles/dnalocking.dm @@ -18,17 +18,17 @@ if(!attached_lock.controller_lock) if(!attached_lock.stored_dna && !(M.dna in attached_lock.stored_dna)) - M << "\The [src] buzzes and displays a symbol showing the gun already contains your DNA." + to_chat(M, "\The [src] buzzes and displays a symbol showing the gun already contains your DNA.") return 0 else attached_lock.stored_dna += M.dna - M << "\The [src] pings and a needle flicks out from the grip, taking a DNA sample from you." + to_chat(M, "\The [src] pings and a needle flicks out from the grip, taking a DNA sample from you.") if(!attached_lock.controller_dna) attached_lock.controller_dna = M.dna - M << "\The [src] processes the dna sample and pings, acknowledging you as the primary controller." + to_chat(M, "\The [src] processes the dna sample and pings, acknowledging you as the primary controller.") return 1 else - M << "\The [src] buzzes and displays a locked symbol. It is not allowing DNA samples at this time." + to_chat(M, "\The [src] buzzes and displays a locked symbol. It is not allowing DNA samples at this time.") return 0 /obj/item/weapon/gun/verb/give_dna() @@ -41,19 +41,19 @@ var/mob/living/M = user if(!attached_lock.controller_lock) if(!authorized_user(M)) - M << "\The [src] buzzes and displays an invalid user symbol." + to_chat(M, "\The [src] buzzes and displays an invalid user symbol.") return 0 else attached_lock.stored_dna -= user.dna - M << "\The [src] beeps and clears the DNA it has stored." + to_chat(M, "\The [src] beeps and clears the DNA it has stored.") if(M.dna == attached_lock.controller_dna) attached_lock.controller_dna = null - M << "\The [src] beeps and removes you as the primary controller." + to_chat(M, "\The [src] beeps and removes you as the primary controller.") if(attached_lock.controller_lock) attached_lock.controller_lock = 0 return 1 else - M << "\The [src] buzzes and displays a locked symbol. It is not allowing DNA modifcation at this time." + to_chat(M, "\The [src] buzzes and displays a locked symbol. It is not allowing DNA modifcation at this time.") return 0 /obj/item/weapon/gun/verb/remove_dna() @@ -67,12 +67,12 @@ if(authorized_user(M) && user.dna == attached_lock.controller_dna) if(!attached_lock.controller_lock) attached_lock.controller_lock = 1 - M << "\The [src] beeps and displays a locked symbol, informing you it will no longer allow DNA samples." + to_chat(M, "\The [src] beeps and displays a locked symbol, informing you it will no longer allow DNA samples.") else attached_lock.controller_lock = 0 - M << "\The [src] beeps and displays an unlocked symbol, informing you it will now allow DNA samples." + to_chat(M, "\The [src] beeps and displays an unlocked symbol, informing you it will now allow DNA samples.") else - M << "\The [src] buzzes and displays an invalid user symbol." + to_chat(M, "\The [src] buzzes and displays an invalid user symbol.") /obj/item/weapon/gun/verb/allow_dna() set name = "Toggle DNA Samples Allowance" diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 8f1c01be21..561ebeb641 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -49,6 +49,7 @@ origin_tech = list(TECH_COMBAT = 1) attack_verb = list("struck", "hit", "bashed") zoomdevicename = "scope" + drop_sound = 'sound/items/drop/gun.ogg' var/automatic = 0 var/burst = 1 @@ -388,6 +389,9 @@ handle_click_empty(user) break + if(i == 1) // So one burst only makes one message and not 3+ messages. + handle_firing_text(user, target, pointblank, reflex) + process_accuracy(projectile, user, target, i, held_twohanded) if(pointblank) @@ -416,14 +420,6 @@ if(one_handed_penalty >= 20) to_chat(user, "You struggle to keep \the [src] pointed at the correct position with just one hand!") - var/target_for_log - if(ismob(target)) - target_for_log = target - else - target_for_log = "[target.name]" - - add_attack_logs(user,target_for_log,"Fired gun [src.name] ([reflex ? "REFLEX" : "MANUAL"])") - //update timing user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) user.setMoveCooldown(move_delay) @@ -534,11 +530,9 @@ src.visible_message("*click click*") playsound(src.loc, 'sound/weapons/empty.ogg', 100, 1) -//called after successfully firing -/obj/item/weapon/gun/proc/handle_post_fire(mob/user, atom/target, var/pointblank=0, var/reflex=0) - if(fire_anim) - flick(fire_anim, src) - +// Called when the user is about to fire. +// Moved from handle_post_fire() because if using a laser, the message for when someone got shot would show up before the firing message. +/obj/item/weapon/gun/proc/handle_firing_text(mob/user, atom/target, pointblank = FALSE, reflex = FALSE) if(silenced) to_chat(user, "You fire \the [src][pointblank ? " point blank at \the [target]":""][reflex ? " by reflex":""]") for(var/mob/living/L in oview(2,user)) @@ -555,6 +549,19 @@ "You hear a [fire_sound_text]!" ) + var/target_for_log + if(ismob(target)) + target_for_log = target + else + target_for_log = "[target.name]" + + add_attack_logs(user, target_for_log, "Fired gun '[src.name]' ([reflex ? "REFLEX" : "MANUAL"])") + +//called after successfully firing +/obj/item/weapon/gun/proc/handle_post_fire(mob/user, atom/target, var/pointblank=0, var/reflex=0) + if(fire_anim) + flick(fire_anim, src) + if(muzzle_flash) set_light(muzzle_flash) diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index c28e2b0588..43263bf54a 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -76,12 +76,17 @@ var/start_nutrition = H.nutrition var/end_nutrition = 0 - H.nutrition -= rechargeamt / 10 + H.nutrition -= rechargeamt / 15 end_nutrition = H.nutrition - if(start_nutrition - max(0, end_nutrition) < rechargeamt / 10) - H.remove_blood((rechargeamt / 10) - (start_nutrition - max(0, end_nutrition))) + if(start_nutrition - max(0, end_nutrition) < rechargeamt / 15) + + if(H.isSynthetic()) + H.adjustToxLoss((rechargeamt / 15) - (start_nutrition - max(0, end_nutrition))) + + else + H.remove_blood((rechargeamt / 15) - (start_nutrition - max(0, end_nutrition))) power_supply.give(rechargeamt) //... to recharge 1/5th the battery update_icon() @@ -109,12 +114,12 @@ /obj/item/weapon/gun/energy/proc/load_ammo(var/obj/item/C, mob/user) if(istype(C, /obj/item/weapon/cell)) if(self_recharge || battery_lock) - user << "[src] does not have a battery port." + to_chat(user, "[src] does not have a battery port.") return if(istype(C, accept_cell_type)) var/obj/item/weapon/cell/P = C if(power_supply) - user << "[src] already has a power cell." + to_chat(user, "[src] already has a power cell.") else user.visible_message("[user] is reloading [src].", "You start to insert [P] into [src].") if(do_after(user, 5 * P.w_class)) @@ -126,12 +131,12 @@ update_icon() update_held_icon() else - user << "This cell is not fitted for [src]." + to_chat(user, "This cell is not fitted for [src].") return /obj/item/weapon/gun/energy/proc/unload_ammo(mob/user) if(self_recharge || battery_lock) - user << "[src] does not have a battery port." + to_chat(user, "[src] does not have a battery port.") return if(power_supply) user.put_in_hands(power_supply) @@ -142,7 +147,7 @@ update_icon() update_held_icon() else - user << "[src] does not have a power cell." + to_chat(user, "[src] does not have a power cell.") /obj/item/weapon/gun/energy/attackby(var/obj/item/A as obj, mob/user as mob) ..() @@ -162,8 +167,8 @@ var/obj/item/rig_module/module = src.loc if(module.holder && module.holder.wearer) var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.back) - var/obj/item/weapon/rig/suit = H.back + if(istype(H) && H.get_rig()) + var/obj/item/weapon/rig/suit = H.get_rig() if(istype(suit)) return suit.cell return null diff --git a/code/modules/vore/fluffstuff/guns/bsharpoon.dm b/code/modules/projectiles/guns/energy/bsharpoon_vr.dm similarity index 91% rename from code/modules/vore/fluffstuff/guns/bsharpoon.dm rename to code/modules/projectiles/guns/energy/bsharpoon_vr.dm index e8ffea51cd..99598504b4 100644 --- a/code/modules/vore/fluffstuff/guns/bsharpoon.dm +++ b/code/modules/projectiles/guns/energy/bsharpoon_vr.dm @@ -62,22 +62,30 @@ var/turf/FromTurf = mode ? get_turf(user) : get_turf(A) var/turf/ToTurf = mode ? get_turf(A) : get_turf(user) + var/recievefailchance = 5 + var/sendfailchance = 5 + if(istype(user, /mob/living)) + var/mob/living/L = user + if(LAZYLEN(L.buckled_mobs)) + for(var/rider in L.buckled_mobs) + sendfailchance += 15 + if(mode) if(user in FromTurf) - if(prob(5)) + if(prob(sendfailchance)) user.forceMove(pick(trange(24,user))) else user.forceMove(ToTurf) else for(var/obj/O in FromTurf) if(O.anchored) continue - if(prob(5)) + if(prob(recievefailchance)) O.forceMove(pick(trange(24,user))) else O.forceMove(ToTurf) for(var/mob/living/M in FromTurf) - if(prob(5)) + if(prob(recievefailchance)) M.forceMove(pick(trange(24,user))) else M.forceMove(ToTurf) diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/cell_loaded.dm b/code/modules/projectiles/guns/energy/cell_loaded_vr/cell_loaded.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/cell_loaded/cell_loaded.dm rename to code/modules/projectiles/guns/energy/cell_loaded_vr/cell_loaded.dm diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/ml3m.dm b/code/modules/projectiles/guns/energy/cell_loaded_vr/ml3m.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/cell_loaded/ml3m.dm rename to code/modules/projectiles/guns/energy/cell_loaded_vr/ml3m.dm diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/ml3m_cells.dm b/code/modules/projectiles/guns/energy/cell_loaded_vr/ml3m_cells.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/cell_loaded/ml3m_cells.dm rename to code/modules/projectiles/guns/energy/cell_loaded_vr/ml3m_cells.dm diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/nsfw.dm b/code/modules/projectiles/guns/energy/cell_loaded_vr/nsfw.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/cell_loaded/nsfw.dm rename to code/modules/projectiles/guns/energy/cell_loaded_vr/nsfw.dm diff --git a/code/modules/vore/fluffstuff/guns/cell_loaded/nsfw_cells.dm b/code/modules/projectiles/guns/energy/cell_loaded_vr/nsfw_cells.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/cell_loaded/nsfw_cells.dm rename to code/modules/projectiles/guns/energy/cell_loaded_vr/nsfw_cells.dm diff --git a/code/modules/vore/fluffstuff/guns/crestrose.dm b/code/modules/projectiles/guns/energy/crestrose_vr.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/crestrose.dm rename to code/modules/projectiles/guns/energy/crestrose_vr.dm diff --git a/code/modules/vore/fluffstuff/guns/dominator.dm b/code/modules/projectiles/guns/energy/dominator_vr.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/dominator.dm rename to code/modules/projectiles/guns/energy/dominator_vr.dm diff --git a/code/modules/vore/fluffstuff/guns/gunsword.dm b/code/modules/projectiles/guns/energy/gunsword_vr.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/gunsword.dm rename to code/modules/projectiles/guns/energy/gunsword_vr.dm diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index f6321947b2..45c6364c1b 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -12,7 +12,7 @@ origin_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 2) matter = list(DEFAULT_WALL_MATERIAL = 2000) projectile_type = /obj/item/projectile/beam/midlaser -// one_handed_penalty = 30 + one_handed_penalty = 30 firemodes = list( list(mode_name="normal", fire_delay=8, projectile_type=/obj/item/projectile/beam/midlaser, charge_cost = 240), @@ -125,13 +125,14 @@ flux in a nuclear reactor core. This incredible technology may help YOU achieve high excitation rates with small laser volumes!" icon_state = "lasercannon" item_state = null + wielded_item_state = "mhdhowitzer-wielded" //Placeholder origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 3, TECH_POWER = 3) slot_flags = SLOT_BELT|SLOT_BACK projectile_type = /obj/item/projectile/beam/heavylaser/cannon battery_lock = 1 fire_delay = 20 w_class = ITEMSIZE_LARGE -// one_handed_penalty = 90 // The thing's heavy and huge. + one_handed_penalty = 90 // The thing's heavy and huge. accuracy = 45 charge_cost = 600 @@ -162,10 +163,12 @@ ionized beams, this is a weapon to kill from a distance." icon_state = "sniper" item_state = "sniper" - item_state_slots = list(slot_r_hand_str = "z8carbine", slot_l_hand_str = "z8carbine") //placeholder + item_state_slots = list(slot_r_hand_str = "lsniper", slot_l_hand_str = "lsniper") + wielded_item_state = "lsniper-wielded" origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 5, TECH_POWER = 4) projectile_type = /obj/item/projectile/beam/sniper slot_flags = SLOT_BACK + action_button_name = "Use Scope" battery_lock = 1 charge_cost = 600 fire_delay = 35 @@ -173,8 +176,10 @@ w_class = ITEMSIZE_HUGE // So it can't fit in a backpack. accuracy = -45 //shooting at the hip scoped_accuracy = 0 -// requires_two_hands = 1 -// one_handed_penalty = 60 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand. + one_handed_penalty = 60 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand. + +/obj/item/weapon/gun/energy/sniperrifle/ui_action_click() + scope() /obj/item/weapon/gun/energy/sniperrifle/verb/scope() set category = "Object" @@ -192,6 +197,7 @@ origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 4, TECH_POWER = 3) projectile_type = /obj/item/projectile/beam/sniper slot_flags = SLOT_BACK + action_button_name = "Aim Down Sights" charge_cost = 1300 fire_delay = 20 force = 8 @@ -200,6 +206,9 @@ scoped_accuracy = 15 var/scope_multiplier = 1.5 +/obj/item/weapon/gun/energy/monorifle/ui_action_click() + sights() + /obj/item/weapon/gun/energy/monorifle/verb/sights() set category = "Object" set name = "Aim Down Sights" @@ -234,7 +243,7 @@ /obj/item/weapon/gun/energy/lasertag/special_check(var/mob/living/carbon/human/M) if(ishuman(M)) if(!istype(M.wear_suit, required_vest)) - M << "You need to be wearing your laser tag vest!" + to_chat(M, "You need to be wearing your laser tag vest!") return 0 return ..() diff --git a/code/modules/projectiles/guns/energy/laser_vr.dm b/code/modules/projectiles/guns/energy/laser_vr.dm new file mode 100644 index 0000000000..756ef967d5 --- /dev/null +++ b/code/modules/projectiles/guns/energy/laser_vr.dm @@ -0,0 +1,302 @@ +/* TUTORIAL + "icon" is the file with the HUD/ground icon for the item + "icon_state" is the iconstate in this file for the item + "icon_override" is the file with the on-mob icons, can be the same file + "item_state" is the iconstate for the on-mob icons: + item_state_s is used for worn uniforms on mobs + item_state_r and item_state_l are for being held in each hand + + "item_state_slots" can replace "item_state", it is a list: + item_state_slots["slotname1"] = "item state for that slot" + item_state_slots["slotname2"] = "item state for that slot" + + on guns, in particular: + item_state being null makes it look for exactly the icon_state in the on-mob file, + including any 0,75,etc appended from the energy bar setting + item_state being present prevents different mode sprites, sadly, but you may + be able to override this on the gun itself with a proc +*/ + +/* TEMPLATE +//ckey:Character Name +/obj/item/weapon/gun/type/fluff/charactername + name = "" + desc = "" + + icon = 'icons/vore/custom_guns_vr.dmi' + icon_state = "myicon" + + icon_override = 'icons/vore/custom_guns_vr.dmi' + item_state = "myicon" + +*/ + +// For general use +/obj/item/weapon/gun/energy/imperial + name = "imperial energy pistol" + desc = "An elegant weapon developed by the Imperium Auream. Their weaponsmiths have cleverly found a way to make a gun that is only about the size of an average energy pistol, yet with the fire power of a laser carbine." + icon_state = "ge_pistol" + item_state = "ge_pistol" + fire_sound = 'sound/weapons/mandalorian.ogg' + icon = 'icons/obj/gun_vr.dmi' + item_icons = list(slot_r_hand_str = 'icons/obj/gun_vr.dmi', slot_l_hand_str = 'icons/obj/gun_vr.dmi') // WORK YOU FUCKING CUNT PIECE OF SHIT BASTARD STUPID BITCH ITEM ICON AAAAHHHH + item_state_slots = list(slot_r_hand_str = "ge_pistol_r", slot_l_hand_str = "ge_pistol_l") + slot_flags = SLOT_BELT + w_class = ITEMSIZE_NORMAL + force = 10 + origin_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 2) + matter = list(DEFAULT_WALL_MATERIAL = 2000) + projectile_type = /obj/item/projectile/beam/imperial + +// Removed because gun64_vr.dmi guns don't work. +/* +//-----------------------G44 Energy Variant-------------------- +/obj/item/weapon/gun/energy/gun/burst/g44e + name = "G44 Energy Rifle" + desc = "The G44 Energy is a laser variant of the G44 lightweight assault rifle manufactured by the National Armory of Gaia. Though almost exclusively to the United Federation's Military Assault Command Operations Department (MACOs) and Starfleet, it is occassionally sold to security departments for their stun capabilities." + icon = 'icons/obj/gun64_vr.dmi' + icon_state = "g44estun100" + item_state = "energystun100" //This is temporary. + fire_sound = 'sound/weapons/Taser.ogg' + charge_cost = 100 + force = 8 + w_class = ITEMSIZE_LARGE + fire_delay = 6 + pixel_x = -16 + + projectile_type = /obj/item/projectile/beam/stun/weak + origin_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 2, TECH_ILLEGAL = 3) + modifystate = "g44estun" + + one_handed_penalty = 60 + + firemodes = list( + list(mode_name="stun", burst=1, projectile_type=/obj/item/projectile/beam/stun/weak, modifystate="g44estun", fire_sound='sound/weapons/Taser.ogg', charge_cost = 100), + list(mode_name="stun burst", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,0,0), dispersion=list(0.0, 0.2, 0.5), projectile_type=/obj/item/projectile/beam/stun/weak, modifystate="g44estun", fire_sound='sound/weapons/Taser.ogg'), + list(mode_name="lethal", burst=1, projectile_type=/obj/item/projectile/beam/burstlaser, modifystate="g44ekill", fire_sound='sound/weapons/Laser.ogg', charge_cost = 200), + list(mode_name="lethal burst", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,0,0), dispersion=list(0.0, 0.2, 0.5), projectile_type=/obj/item/projectile/beam/burstlaser, modifystate="g44ekill", fire_sound='sound/weapons/Laser.ogg'), + ) +*/ + +//////////////////// Energy Weapons //////////////////// + +// ------------ Energy Luger ------------ +/obj/item/weapon/gun/energy/gun/eluger + name = "energy Luger" + desc = "The finest sidearm produced by RauMauser. Although its battery cannot be removed, its ergonomic design makes it easy to shoot, allowing for rapid follow-up shots. It also has the ability to toggle between stun and kill." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "elugerstun100" + item_state = "gun" + fire_delay = null // Lugers are quite comfortable to shoot, thus allowing for more controlled follow-up shots. Rate of fire similar to a laser carbine. + battery_lock = 1 // In exchange for balance, you cannot remove the battery. Also there's no sprite for that and I fucking suck at sprites. -Ace + origin_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 2, TECH_ILLEGAL = 2) // Illegal tech cuz Space Nazis + modifystate = "elugerstun" + fire_sound = 'sound/weapons/Taser.ogg' + firemodes = list( + list(mode_name="stun", charge_cost=120,projectile_type=/obj/item/projectile/beam/stun, modifystate="elugerstun", fire_sound='sound/weapons/Taser.ogg'), + list(mode_name="lethal", charge_cost=240,projectile_type=/obj/item/projectile/beam/eluger, modifystate="elugerkill", fire_sound='sound/weapons/eluger.ogg'), + ) + +//////////////////// Eris Ported Guns //////////////////// +//HoP gun +/obj/item/weapon/gun/energy/gun/martin + name = "holdout energy gun" + desc = "The FS PDW E \"Martin\" is small holdout e-gun. Don't miss!" + icon = 'icons/obj/gun_vr.dmi' + icon_state = "PDW" + item_state = "gun" + w_class = ITEMSIZE_SMALL + projectile_type = /obj/item/projectile/beam/stun + charge_cost = 1200 + charge_meter = 0 + modifystate = null + battery_lock = 1 + fire_sound = 'sound/weapons/Taser.ogg' + origin_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 2) + firemodes = list( + list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun, fire_sound='sound/weapons/Taser.ogg', charge_cost = 600), + list(mode_name="lethal", projectile_type=/obj/item/projectile/beam, fire_sound='sound/weapons/Laser.ogg', charge_cost = 1200), + ) + +/obj/item/weapon/gun/energy/gun/martin/proc/update_mode() + var/datum/firemode/current_mode = firemodes[sel_mode] + switch(current_mode.name) + if("stun") add_overlay("taser_pdw") + if("lethal") add_overlay("lazer_pdw") + +/obj/item/weapon/gun/energy/gun/martin/update_icon() + cut_overlays() + update_mode() + +///////////////////////////////////////////////////// +//////////////////// Custom Ammo //////////////////// +///////////////////////////////////////////////////// +//---------------- Beams ---------------- +/obj/item/projectile/beam/eluger + name = "laser beam" + icon_state = "xray" + light_color = "#00FF00" + muzzle_type = /obj/effect/projectile/muzzle/xray + tracer_type = /obj/effect/projectile/tracer/xray + impact_type = /obj/effect/projectile/impact/xray + +/obj/item/projectile/beam/imperial + name = "laser beam" + fire_sound = 'sound/weapons/mandalorian.ogg' + icon_state = "darkb" + light_color = "#8837A3" + muzzle_type = /obj/effect/projectile/muzzle/darkmatter + tracer_type = /obj/effect/projectile/tracer/darkmatter + impact_type = /obj/effect/projectile/impact/darkmatter + +/obj/item/projectile/beam/stun/kin21 + name = "kinh21 stun beam" + icon_state = "omnilaser" + light_color = "#0000FF" + muzzle_type = /obj/effect/projectile/muzzle/laser_omni + tracer_type = /obj/effect/projectile/tracer/laser_omni + impact_type = /obj/effect/projectile/impact/laser_omni + +//Gun Locking Mechanism +/obj/item/weapon/gun/energy/locked + req_access = list(access_armory) //for toggling safety + var/locked = 1 + var/lockable = 1 + +/obj/item/weapon/gun/energy/locked/attackby(obj/item/I, mob/user) + var/obj/item/weapon/card/id/id = I.GetID() + if(istype(id) && lockable) + if(check_access(id)) + locked = !locked + to_chat(user, "You [locked ? "enable" : "disable"] the safety lock on \the [src].") + else + to_chat(user, "Access denied.") + user.visible_message("[user] swipes \the [I] against \the [src].") + else + return ..() + +/obj/item/weapon/gun/energy/locked/emag_act(var/remaining_charges,var/mob/user) + ..() + if(lockable) + locked = !locked + to_chat(user, "You [locked ? "enable" : "disable"] the safety lock on \the [src]!") + +/obj/item/weapon/gun/energy/locked/special_check(mob/user) + if(locked) + var/turf/T = get_turf(src) + if(T.z in using_map.map_levels) + to_chat(user, "The safety device prevents the gun from firing this close to the facility.") + return 0 + return ..() + +//Expedition Frontier Phaser +/obj/item/weapon/gun/energy/locked/frontier + name = "frontier phaser" + desc = "An extraordinarily rugged laser weapon, built to last and requiring effectively no maintenance. Includes a built-in crank charger for recharging away from civilization. This one has a safety interlock that prevents firing while in proximity to the facility." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "phaser" + item_state = "phaser" + item_icons = list(slot_l_hand_str = 'icons/mob/items/lefthand_guns_vr.dmi', slot_r_hand_str = 'icons/mob/items/righthand_guns_vr.dmi', "slot_belt" = 'icons/mob/belt_vr.dmi') + fire_sound = 'sound/weapons/laser2.ogg' + origin_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 2, TECH_POWER = 4) + charge_cost = 300 + + battery_lock = 1 + unacidable = 1 + + var/recharging = 0 + var/phase_power = 75 + + projectile_type = /obj/item/projectile/beam + firemodes = list( + list(mode_name="lethal", fire_delay=12, projectile_type=/obj/item/projectile/beam, charge_cost = 300), + list(mode_name="low-power", fire_delay=8, projectile_type=/obj/item/projectile/beam/weaklaser, charge_cost = 60), + ) + +/obj/item/weapon/gun/energy/locked/frontier/unload_ammo(var/mob/user) + if(recharging) + return + recharging = 1 + update_icon() + user.visible_message("[user] opens \the [src] and starts pumping the handle.", \ + "You open \the [src] and start pumping the handle.") + while(recharging) + if(!do_after(user, 10, src)) + break + playsound(get_turf(src),'sound/items/change_drill.ogg',25,1) + if(power_supply.give(phase_power) < phase_power) + break + + recharging = 0 + update_icon() + +/obj/item/weapon/gun/energy/locked/frontier/update_icon() + if(recharging) + icon_state = "[initial(icon_state)]_pump" + update_held_icon() + return + ..() + +/obj/item/weapon/gun/energy/locked/frontier/emp_act(severity) + return ..(severity+2) + +/obj/item/weapon/gun/energy/locked/frontier/ex_act() //|rugged| + return + +/obj/item/weapon/gun/energy/locked/frontier/unlocked + desc = "An extraordinarily rugged laser weapon, built to last and requiring effectively no maintenance. Includes a built-in crank charger for recharging away from civilization." + req_access = newlist() //for toggling safety + locked = 0 + lockable = 0 + +//Phaser Carbine - Reskinned phaser +/obj/item/weapon/gun/energy/locked/frontier/carbine + name = "frontier carbine" + desc = "An ergonomically improved version of the venerable frontier phaser, the carbine is a fairly new weapon, and has only been produced in limited numbers so far. Includes a built-in crank charger for recharging away from civilization. This one has a safety interlock that prevents firing while in proximity to the facility." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "carbinekill" + item_state = "retro" + item_icons = list(slot_l_hand_str = 'icons/mob/items/lefthand_guns.dmi', slot_r_hand_str = 'icons/mob/items/righthand_guns.dmi') + + modifystate = "carbinekill" + firemodes = list( + list(mode_name="lethal", fire_delay=12, projectile_type=/obj/item/projectile/beam, modifystate="carbinekill", charge_cost = 300), + list(mode_name="low-power", fire_delay=8, projectile_type=/obj/item/projectile/beam/weaklaser, modifystate="carbinestun", charge_cost = 60), + ) + +/obj/item/weapon/gun/energy/locked/frontier/carbine/update_icon() + if(recharging) + icon_state = "[modifystate]_pump" + update_held_icon() + return + ..() + +/obj/item/weapon/gun/energy/locked/frontier/carbine/unlocked + desc = "An ergonomically improved version of the venerable frontier phaser, the carbine is a fairly new weapon, and has only been produced in limited numbers so far." + req_access = newlist() //for toggling safety + locked = 0 + lockable = 0 + +//Expeditionary Holdout Phaser Pistol +/obj/item/weapon/gun/energy/locked/frontier/holdout + name = "holdout frontier phaser" + desc = "An minaturized weapon designed for the purpose of expeditionary support to defend themselves on the field. Includes a built-in crank charger for recharging away from civilization. This one has a safety interlock that prevents firing while in proximity to the facility." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "holdoutkill" + item_state = null + phase_power = 100 + + w_class = ITEMSIZE_SMALL + charge_cost = 600 + modifystate = "holdoutkill" + firemodes = list( + list(mode_name="lethal", fire_delay=12, projectile_type=/obj/item/projectile/beam, modifystate="holdoutkill", charge_cost = 600), + list(mode_name="low-power", fire_delay=8, projectile_type=/obj/item/projectile/beam/weaklaser, modifystate="holdoutstun", charge_cost = 120), + list(mode_name="stun", fire_delay=12, projectile_type=/obj/item/projectile/beam/stun/med, modifystate="holdoutshock", charge_cost = 300), + ) + +/obj/item/weapon/gun/energy/locked/frontier/holdout/unlocked + desc = "An minaturized weapon designed for the purpose of expeditionary support to defend themselves on the field. Includes a built-in crank charger for recharging away from civilization." + req_access = newlist() //for toggling safety + locked = 0 + lockable = 0 diff --git a/code/modules/projectiles/guns/energy/netgun_vr.dm b/code/modules/projectiles/guns/energy/netgun_vr.dm index 0ed0b9fc51..c4246f69c9 100644 --- a/code/modules/projectiles/guns/energy/netgun_vr.dm +++ b/code/modules/projectiles/guns/energy/netgun_vr.dm @@ -4,32 +4,12 @@ for when you want criminals to stop acting like they're on a 20th century British comedy sketch show." catalogue_data = list(/datum/category_item/catalogue/information/organization/hephaestus) icon = 'icons/obj/gun_vr.dmi' - icon_state = "hunter" + icon_state = "netgun" item_state = "gun" // Placeholder - mode_name = "stun" + charge_meter = 0 fire_sound = 'sound/weapons/eluger.ogg' origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 5, TECH_MAGNET = 3) - projectile_type = /obj/item/projectile/beam/stun/blue - charge_cost = 240 - fire_delay = 5 - - firemodes = list( - list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun/blue, fire_sound='sound/weapons/Taser.ogg', charge_cost=240, fire_delay=5), - list(mode_name="capture", projectile_type=/obj/item/projectile/beam/energy_net, fire_sound = 'sound/weapons/eluger.ogg', charge_cost=1200, fire_delay=50) - ) - -/obj/item/weapon/gun/energy/netgun/update_icon() - overlays.Cut() - - if(power_supply) - var/ratio = power_supply.charge / power_supply.maxcharge - - if(power_supply.charge < charge_cost) - ratio = 0 - else - ratio = max(round(ratio, 0.25) * 100, 25) - - overlays += "[initial(icon_state)]_cell" - overlays += "[initial(icon_state)]_[ratio]" - overlays += "[initial(icon_state)]_[mode_name]" + projectile_type = /obj/item/projectile/beam/energy_net + charge_cost = 800 + fire_delay = 50 diff --git a/code/modules/projectiles/guns/energy/particle.dm b/code/modules/projectiles/guns/energy/particle.dm index c586d19617..a9ce49bf52 100644 --- a/code/modules/projectiles/guns/energy/particle.dm +++ b/code/modules/projectiles/guns/energy/particle.dm @@ -134,9 +134,9 @@ /obj/item/weapon/gun/energy/particle/attackby(var/obj/item/A as obj, mob/user as mob) if(istype(A, /obj/item/pressurelock)) if(safetycatch) - user << "\The [src] already has a [attached_safety]." + to_chat(user, "\The [src] already has a [attached_safety].") return - user << "You insert \the [A] into \the [src]." + to_chat(user, "You insert \the [A] into \the [src].") user.drop_item() A.loc = src attached_safety = A @@ -145,9 +145,9 @@ if(istype(A, /obj/item/weapon/tool/screwdriver)) if(safetycatch && attached_safety) - user << "You begin removing \the [attached_safety] from \the [src]." + to_chat(user, "You begin removing \the [attached_safety] from \the [src].") if(do_after(user, 25)) - user << "You remove \the [attached_safety] from \the [src]." + to_chat(user, "You remove \the [attached_safety] from \the [src].") user.put_in_hands(attached_safety) safetycatch = 0 attached_safety = null diff --git a/code/modules/vore/fluffstuff/guns/protector.dm b/code/modules/projectiles/guns/energy/protector_vr.dm similarity index 78% rename from code/modules/vore/fluffstuff/guns/protector.dm rename to code/modules/projectiles/guns/energy/protector_vr.dm index 6032b62654..eff6ff1b7a 100644 --- a/code/modules/vore/fluffstuff/guns/protector.dm +++ b/code/modules/projectiles/guns/energy/protector_vr.dm @@ -1,5 +1,5 @@ // -------------- Protector ------------- -/obj/item/weapon/gun/energy/protector +/obj/item/weapon/gun/energy/gun/protector name = "small energy gun" desc = "The WT-98a 'Protector' is a common sidearm developed by Ward-Takahashi GMC. It features a powerful stun mode, and \ an alert-level-locked lethal mode, only usable when the connected jurisdiction allows. It also features an integrated flashlight!" @@ -30,14 +30,16 @@ flight_x_offset = 0 flight_y_offset = 0 + w_class = ITEMSIZE_SMALL + firemodes = list( - list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun/protector, modifystate="stun", fire_sound='sound/weapons/Taser.ogg'), - list(mode_name="lethal", projectile_type=/obj/item/projectile/beam, modifystate="kill", fire_sound='sound/weapons/gauss_shoot.ogg'), - ) + list(mode_name="stun", projectile_type=/obj/item/projectile/beam/stun/med, modifystate="stun", charge_cost = 400), + list(mode_name="lethal", projectile_type=/obj/item/projectile/beam, modifystate="kill", charge_cost = 800), + ) var/emagged = FALSE -/obj/item/weapon/gun/energy/protector/special_check(mob/user) +/obj/item/weapon/gun/energy/gun/protector/special_check(mob/user) if(!emagged && mode_name == "lethal" && get_security_level() == "green") to_chat(user,"The trigger refuses to depress while on the lethal setting under security level green!") return FALSE @@ -45,7 +47,7 @@ return ..() -/obj/item/weapon/gun/energy/protector/emag_act(var/remaining_charges,var/mob/user) +/obj/item/weapon/gun/energy/gun/protector/emag_act(var/remaining_charges,var/mob/user) ..() if(!emagged) emagged = TRUE @@ -54,7 +56,7 @@ return TRUE //Update icons from /tg/, so fancy! Use this more! -/obj/item/weapon/gun/energy/protector/update_icon() +/obj/item/weapon/gun/energy/gun/protector/update_icon() overlays.Cut() var/ratio = 0 @@ -98,13 +100,7 @@ item_state = itemState */ - -// Protector beams -/obj/item/projectile/beam/stun/protector - name = "protector stun beam" - icon_state = "omnilaser" //A little more cyan - light_color = "#00C6FF" - agony = 50 //Normal is 40 when this was set - muzzle_type = /obj/effect/projectile/muzzle/laser_omni - tracer_type = /obj/effect/projectile/tracer/laser_omni - impact_type = /obj/effect/projectile/impact/laser_omni +/obj/item/weapon/gun/energy/gun/protector/unlocked + emagged = TRUE + name = "small energy gun" + desc = "The LAEP95 'Protector' is another firearm from Lawson Arms and "+TSC_HEPH+", unlike the Perun this is designed for issue to non-security staff. It contains a detachable cell. It also features an integrated flashlight!" diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index 30eb8ecd37..d2e5edf997 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -26,7 +26,7 @@ charge_cost = 120 /obj/item/weapon/gun/energy/pulse_rifle/destroyer/attack_self(mob/living/user as mob) - user << "[src.name] has three settings, and they are all DESTROY." + to_chat(user, "[src.name] has three settings, and they are all DESTROY.") //WHY? /obj/item/weapon/gun/energy/pulse_rifle/M1911 diff --git a/code/modules/vore/fluffstuff/guns/pummeler.dm b/code/modules/projectiles/guns/energy/pummeler_vr.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/pummeler.dm rename to code/modules/projectiles/guns/energy/pummeler_vr.dm diff --git a/code/modules/vore/fluffstuff/guns/sickshot.dm b/code/modules/projectiles/guns/energy/sickshot_vr.dm similarity index 100% rename from code/modules/vore/fluffstuff/guns/sickshot.dm rename to code/modules/projectiles/guns/energy/sickshot_vr.dm diff --git a/code/modules/projectiles/guns/energy/temperature.dm b/code/modules/projectiles/guns/energy/temperature.dm index cf9706effd..9343f9116c 100644 --- a/code/modules/projectiles/guns/energy/temperature.dm +++ b/code/modules/projectiles/guns/energy/temperature.dm @@ -12,3 +12,7 @@ list(mode_name="endothermic beam", projectile_type = /obj/item/projectile/temp, charge_cost = 240), list(mode_name="exothermic beam", projectile_type = /obj/item/projectile/temp/hot, charge_cost = 240), ) + +/obj/item/weapon/gun/energy/temperature/mounted + self_recharge = 1 + use_external_power = 1 diff --git a/code/modules/projectiles/guns/launcher.dm b/code/modules/projectiles/guns/launcher.dm index 869f6c8b60..412a032d3a 100644 --- a/code/modules/projectiles/guns/launcher.dm +++ b/code/modules/projectiles/guns/launcher.dm @@ -15,7 +15,7 @@ //Override this to avoid a runtime with suicide handling. /obj/item/weapon/gun/launcher/handle_suicide(mob/living/user) - user << "Shooting yourself with \a [src] is pretty tricky. You can't seem to manage it." + to_chat(user, "Shooting yourself with \a [src] is pretty tricky. You can't seem to manage it.") return /obj/item/weapon/gun/launcher/proc/update_release_force(obj/item/projectile) diff --git a/code/modules/projectiles/guns/launcher/crossbow.dm b/code/modules/projectiles/guns/launcher/crossbow.dm index a8458b1676..b94c2ab232 100644 --- a/code/modules/projectiles/guns/launcher/crossbow.dm +++ b/code/modules/projectiles/guns/launcher/crossbow.dm @@ -40,7 +40,7 @@ /obj/item/weapon/arrow/rod/removed(mob/user) if(throwforce == 15) // The rod has been superheated - we don't want it to be useable when removed from the bow. - user << "[src] shatters into a scattering of overstressed metal shards as it leaves the crossbow." + to_chat(user , "[src] shatters into a scattering of overstressed metal shards as it leaves the crossbow.") var/obj/item/weapon/material/shard/shrapnel/S = new() S.loc = get_turf(src) qdel(src) @@ -68,7 +68,7 @@ /obj/item/weapon/gun/launcher/crossbow/consume_next_projectile(mob/user=null) if(tension <= 0) - user << "\The [src] is not drawn back!" + to_chat(user, "\The [src] is not drawn back!") return null return bolt @@ -96,7 +96,7 @@ /obj/item/weapon/gun/launcher/crossbow/proc/draw(var/mob/user as mob) if(!bolt) - user << "You don't have anything nocked to [src]." + to_chat(user, "You don't have anything nocked to [src].") return if(user.restrained()) @@ -122,7 +122,7 @@ if(tension >= max_tension) tension = max_tension - usr << "[src] clunks as you draw the string to its maximum tension!" + to_chat(usr, "[src] clunks as you draw the string to its maximum tension!") return user.visible_message("[usr] draws back the string of [src]!","You continue drawing back the string of [src]!") @@ -157,20 +157,20 @@ user.drop_item() cell = W cell.loc = src - user << "You jam [cell] into [src] and wire it to the firing coil." + to_chat(user, "You jam [cell] into [src] and wire it to the firing coil.") superheat_rod(user) else - user << "[src] already has a cell installed." + to_chat(user, "[src] already has a cell installed.") else if(W.is_screwdriver()) if(cell) var/obj/item/C = cell C.loc = get_turf(user) - user << "You jimmy [cell] out of [src] with [W]." + to_chat(user, "You jimmy [cell] out of [src] with [W].") playsound(src, W.usesound, 50, 1) cell = null else - user << "[src] doesn't have a cell installed." + to_chat(user, "[src] doesn't have a cell installed.") else ..() @@ -181,7 +181,7 @@ if(bolt.throwforce >= 15) return if(!istype(bolt,/obj/item/weapon/arrow/rod)) return - user << "[bolt] plinks and crackles as it begins to glow red-hot." + to_chat(user, "[bolt] plinks and crackles as it begins to glow red-hot.") bolt.throwforce = 15 bolt.icon_state = "metal-rod-superheated" cell.use(500) @@ -210,22 +210,27 @@ /obj/item/weapon/crossbowframe/examine(mob/user) ..(user) switch(buildstate) - if(1) user << "It has a loose rod frame in place." - if(2) user << "It has a steel backbone welded in place." - if(3) user << "It has a steel backbone and a cell mount installed." - if(4) user << "It has a steel backbone, plastic lath and a cell mount installed." - if(5) user << "It has a steel cable loosely strung across the lath." + if(1) + to_chat(user, "It has a loose rod frame in place.") + if(2) + to_chat(user, "It has a steel backbone welded in place.") + if(3) + to_chat(user, "It has a steel backbone and a cell mount installed.") + if(4) + to_chat(user, "It has a steel backbone, plastic lath and a cell mount installed.") + if(5) + to_chat(user, "It has a steel cable loosely strung across the lath.") /obj/item/weapon/crossbowframe/attackby(obj/item/W as obj, mob/user as mob) if(istype(W,/obj/item/stack/rods)) if(buildstate == 0) var/obj/item/stack/rods/R = W if(R.use(3)) - user << "You assemble a backbone of rods around the wooden stock." + to_chat(user, "You assemble a backbone of rods around the wooden stock.") buildstate++ update_icon() else - user << "You need at least three rods to complete this task." + to_chat(user, "You need at least three rods to complete this task.") return else if(istype(W, /obj/item/weapon/weldingtool)) if(buildstate == 1) @@ -233,7 +238,7 @@ if(T.remove_fuel(0,user)) if(!src || !T.isOn()) return playsound(src, W.usesound, 50, 1) - user << "You weld the rods into place." + to_chat(user, "You weld the rods into place.") buildstate++ update_icon() return @@ -241,33 +246,33 @@ var/obj/item/stack/cable_coil/C = W if(buildstate == 2) if(C.use(5)) - user << "You wire a crude cell mount into the top of the crossbow." + to_chat(user, "You wire a crude cell mount into the top of the crossbow.") buildstate++ update_icon() else - user << "You need at least five segments of cable coil to complete this task." + to_chat(user, "You need at least five segments of cable coil to complete this task.") return else if(buildstate == 4) if(C.use(5)) - user << "You string a steel cable across the crossbow's lath." + to_chat(user, "You string a steel cable across the crossbow's lath.") buildstate++ update_icon() else - user << "You need at least five segments of cable coil to complete this task." + to_chat(user, "You need at least five segments of cable coil to complete this task.") return else if(istype(W,/obj/item/stack/material) && W.get_material_name() == "plastic") if(buildstate == 3) var/obj/item/stack/material/P = W if(P.use(3)) - user << "You assemble and install a heavy plastic lath onto the crossbow." + to_chat(user, "You assemble and install a heavy plastic lath onto the crossbow.") buildstate++ update_icon() else - user << "You need at least three plastic sheets to complete this task." + to_chat(user, "You need at least three plastic sheets to complete this task.") return else if(W.is_screwdriver()) if(buildstate == 5) - user << "You secure the crossbow's various parts." + to_chat(user, "You secure the crossbow's various parts.") playsound(src, W.usesound, 50, 1) new /obj/item/weapon/gun/launcher/crossbow(get_turf(src)) qdel(src) diff --git a/code/modules/projectiles/guns/launcher/grenade_launcher.dm b/code/modules/projectiles/guns/launcher/grenade_launcher.dm index 38d84f384a..c016fe0797 100644 --- a/code/modules/projectiles/guns/launcher/grenade_launcher.dm +++ b/code/modules/projectiles/guns/launcher/grenade_launcher.dm @@ -30,29 +30,29 @@ if(next) grenades -= next //Remove grenade from loaded list. chambered = next - M << "You pump [src], loading \a [next] into the chamber." + to_chat(M, "You pump [src], loading \a [next] into the chamber.") else - M << "You pump [src], but the magazine is empty." + to_chat(M, "You pump [src], but the magazine is empty.") update_icon() /obj/item/weapon/gun/launcher/grenade/examine(mob/user) if(..(user, 2)) var/grenade_count = grenades.len + (chambered? 1 : 0) - user << "Has [grenade_count] grenade\s remaining." + to_chat(user, "Has [grenade_count] grenade\s remaining.") if(chambered) - user << "\A [chambered] is chambered." + to_chat(user, "\A [chambered] is chambered.") /obj/item/weapon/gun/launcher/grenade/proc/load(obj/item/weapon/grenade/G, mob/user) if(G.loadable) if(grenades.len >= max_grenades) - user << "[src] is full." + to_chat(user, "[src] is full.") return user.remove_from_mob(G) G.loc = src grenades.Insert(1, G) //add to the head of the list, so that it is loaded on the next pump user.visible_message("[user] inserts \a [G] into [src].", "You insert \a [G] into [src].") return - user << "[G] doesn't seem to fit in the [src]!" + to_chat(user, "[G] doesn't seem to fit in the [src]!") /obj/item/weapon/gun/launcher/grenade/proc/unload(mob/user) if(grenades.len) @@ -62,7 +62,7 @@ user.visible_message("[user] removes \a [G] from [src].", "You remove \a [G] from [src].") playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) else - user << "[src] is empty." + to_chat(user, "[src] is empty.") /obj/item/weapon/gun/launcher/grenade/attack_self(mob/user) pump(user) @@ -105,14 +105,14 @@ /obj/item/weapon/gun/launcher/grenade/underslung/load(obj/item/weapon/grenade/G, mob/user) if(G.loadable) if(chambered) - user << "[src] is already loaded." + to_chat(user, "[src] is already loaded.") return user.remove_from_mob(G) G.loc = src chambered = G user.visible_message("[user] load \a [G] into [src].", "You load \a [G] into [src].") return - user << "[G] doesn't seem to fit in the [src]!" + to_chat(user, "[G] doesn't seem to fit in the [src]!") /obj/item/weapon/gun/launcher/grenade/underslung/unload(mob/user) if(chambered) @@ -121,4 +121,4 @@ playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) chambered = null else - user << "[src] is empty." \ No newline at end of file + to_chat(user, "[src] is empty.") \ No newline at end of file diff --git a/code/modules/projectiles/guns/launcher/pneumatic.dm b/code/modules/projectiles/guns/launcher/pneumatic.dm index 45be1c017e..c9bfa465f6 100644 --- a/code/modules/projectiles/guns/launcher/pneumatic.dm +++ b/code/modules/projectiles/guns/launcher/pneumatic.dm @@ -35,14 +35,14 @@ var/N = input("Percentage of tank used per shot:","[src]") as null|anything in possible_pressure_amounts if (N) pressure_setting = N - usr << "You dial the pressure valve to [pressure_setting]%." + to_chat(usr, "You dial the pressure valve to [pressure_setting]%.") /obj/item/weapon/gun/launcher/pneumatic/proc/eject_tank(mob/user) //Remove the tank. if(!tank) - user << "There's no tank in [src]." + to_chat(user, "There's no tank in [src].") return - user << "You twist the valve and pop the tank out of [src]." + to_chat(user, "You twist the valve and pop the tank out of [src].") user.put_in_hands(tank) tank = null update_icon() @@ -52,10 +52,10 @@ var/obj/item/removing = item_storage.contents[item_storage.contents.len] item_storage.remove_from_storage(removing, src.loc) user.put_in_hands(removing) - user << "You remove [removing] from the hopper." + to_chat(user, "You remove [removing] from the hopper.") playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) else - user << "There is nothing to remove in \the [src]." + to_chat(user, "There is nothing to remove in \the [src].") /obj/item/weapon/gun/launcher/pneumatic/attack_hand(mob/user as mob) if(user.get_inactive_hand() == src) @@ -79,7 +79,7 @@ if(!item_storage.contents.len) return null if (!tank) - user << "There is no gas tank in [src]!" + to_chat(user, "There is no gas tank in [src]!") return null var/environment_pressure = 10 @@ -91,7 +91,7 @@ fire_pressure = (tank.air_contents.return_pressure() - environment_pressure)*pressure_setting/100 if(fire_pressure < 10) - user << "There isn't enough gas in the tank to fire [src]." + to_chat(user, "There isn't enough gas in the tank to fire [src].") return null var/obj/item/launched = item_storage.contents[1] @@ -101,11 +101,11 @@ /obj/item/weapon/gun/launcher/pneumatic/examine(mob/user) if(!..(user, 2)) return - user << "The valve is dialed to [pressure_setting]%." + to_chat(user, "The valve is dialed to [pressure_setting]%.") if(tank) - user << "The tank dial reads [tank.air_contents.return_pressure()] kPa." + to_chat(user, "The tank dial reads [tank.air_contents.return_pressure()] kPa.") else - user << "Nothing is attached to the tank valve!" + to_chat(user, "Nothing is attached to the tank valve!") /obj/item/weapon/gun/launcher/pneumatic/update_release_force(obj/item/projectile) if(tank) @@ -152,18 +152,23 @@ /obj/item/weapon/cannonframe/examine(mob/user) ..(user) switch(buildstate) - if(1) user << "It has a pipe segment installed." - if(2) user << "It has a pipe segment welded in place." - if(3) user << "It has an outer chassis installed." - if(4) user << "It has an outer chassis welded in place." - if(5) user << "It has a transfer valve installed." + if(1) + to_chat(user, "It has a pipe segment installed.") + if(2) + to_chat(user, "It has a pipe segment welded in place.") + if(3) + to_chat(user, "It has an outer chassis installed.") + if(4) + to_chat(user, "It has an outer chassis welded in place.") + if(5) + to_chat(user, "It has a transfer valve installed.") /obj/item/weapon/cannonframe/attackby(obj/item/W as obj, mob/user as mob) if(istype(W,/obj/item/pipe)) if(buildstate == 0) user.drop_from_inventory(W) qdel(W) - user << "You secure the piping inside the frame." + to_chat(user, "You secure the piping inside the frame.") buildstate++ update_icon() return @@ -171,17 +176,17 @@ if(buildstate == 2) var/obj/item/stack/material/M = W if(M.use(5)) - user << "You assemble a chassis around the cannon frame." + to_chat(user, "You assemble a chassis around the cannon frame.") buildstate++ update_icon() else - user << "You need at least five metal sheets to complete this task." + to_chat(user, "You need at least five metal sheets to complete this task.") return else if(istype(W,/obj/item/device/transfer_valve)) if(buildstate == 4) user.drop_from_inventory(W) qdel(W) - user << "You install the transfer valve and connect it to the piping." + to_chat(user, "You install the transfer valve and connect it to the piping.") buildstate++ update_icon() return @@ -191,7 +196,7 @@ if(T.remove_fuel(0,user)) if(!src || !T.isOn()) return playsound(src, W.usesound, 100, 1) - user << "You weld the pipe into place." + to_chat(user, "You weld the pipe into place.") buildstate++ update_icon() if(buildstate == 3) @@ -199,7 +204,7 @@ if(T.remove_fuel(0,user)) if(!src || !T.isOn()) return playsound(src, W.usesound, 100, 1) - user << "You weld the metal chassis together." + to_chat(user, "You weld the metal chassis together.") buildstate++ update_icon() if(buildstate == 5) @@ -207,7 +212,7 @@ if(T.remove_fuel(0,user)) if(!src || !T.isOn()) return playsound(src, W.usesound, 100, 1) - user << "You weld the valve into place." + to_chat(user, "You weld the valve into place.") new /obj/item/weapon/gun/launcher/pneumatic(get_turf(src)) qdel(src) return diff --git a/code/modules/projectiles/guns/launcher/rocket.dm b/code/modules/projectiles/guns/launcher/rocket.dm index 21fdbb14f6..13bb0025d6 100644 --- a/code/modules/projectiles/guns/launcher/rocket.dm +++ b/code/modules/projectiles/guns/launcher/rocket.dm @@ -19,7 +19,7 @@ /obj/item/weapon/gun/launcher/rocket/examine(mob/user) if(!..(user, 2)) return - user << "[rockets.len] / [max_rockets] rockets." + to_chat(user, "[rockets.len] / [max_rockets] rockets.") /obj/item/weapon/gun/launcher/rocket/attackby(obj/item/I as obj, mob/user as mob) if(istype(I, /obj/item/ammo_casing/rocket)) @@ -27,10 +27,10 @@ user.drop_item() I.loc = src rockets += I - user << "You put the rocket in [src]." - user << "[rockets.len] / [max_rockets] rockets." + to_chat(user, "You put the rocket in [src].") + to_chat(user, "[rockets.len] / [max_rockets] rockets.") else - usr << "[src] cannot hold more rockets." + to_chat(usr, "[src] cannot hold more rockets.") /obj/item/weapon/gun/launcher/rocket/consume_next_projectile() if(rockets.len) diff --git a/code/modules/projectiles/guns/launcher/syringe_gun.dm b/code/modules/projectiles/guns/launcher/syringe_gun.dm index 434f317d88..17778ffae4 100644 --- a/code/modules/projectiles/guns/launcher/syringe_gun.dm +++ b/code/modules/projectiles/guns/launcher/syringe_gun.dm @@ -20,7 +20,7 @@ /obj/item/weapon/syringe_cartridge/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/weapon/reagent_containers/syringe)) syringe = I - user << "You carefully insert [syringe] into [src]." + to_chat(user, "You carefully insert [syringe] into [src].") user.remove_from_mob(syringe) syringe.loc = src sharp = 1 @@ -29,7 +29,7 @@ /obj/item/weapon/syringe_cartridge/attack_self(mob/user) if(syringe) - user << "You remove [syringe] from [src]." + to_chat(user, "You remove [syringe] from [src].") playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) user.put_in_hands(syringe) syringe = null @@ -105,10 +105,10 @@ /obj/item/weapon/gun/launcher/syringe/attack_hand(mob/living/user as mob) if(user.get_inactive_hand() == src) if(!darts.len) - user << "[src] is empty." + to_chat(user, "[src] is empty.") return if(next) - user << "[src]'s cover is locked shut." + to_chat(user, "[src]'s cover is locked shut.") return var/obj/item/weapon/syringe_cartridge/C = darts[1] darts -= C @@ -122,7 +122,7 @@ if(istype(A, /obj/item/weapon/syringe_cartridge)) var/obj/item/weapon/syringe_cartridge/C = A if(darts.len >= max_darts) - user << "[src] is full!" + to_chat(user, "[src] is full!") return user.remove_from_mob(C) C.loc = src diff --git a/code/modules/projectiles/guns/magnetic/gasthrower.dm b/code/modules/projectiles/guns/magnetic/gasthrower.dm new file mode 100644 index 0000000000..742b5bd6c5 --- /dev/null +++ b/code/modules/projectiles/guns/magnetic/gasthrower.dm @@ -0,0 +1,78 @@ +/obj/item/weapon/gun/magnetic/gasthrower + name = "phoronthrower" + desc = "A modernized flamethrower utilizing pressurized phoron gas as both a propellant and combustion medium." + description_fluff = "A weapon designed to effectively combat the threat posed by Almachi soldiers without the danger of other forms of flamethrower." + icon_state = "gasthrower" + item_state = "bore" + wielded_item_state = "bore-wielded" + icon = 'icons/obj/railgun.dmi' + one_handed_penalty = 20 + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 4, TECH_ILLEGAL = 2, TECH_PHORON = 4) + w_class = ITEMSIZE_LARGE + slowdown = 1 + + burst = 3 + burst_delay = 1 + + fire_sound = 'sound/weapons/towelwipe.ogg' + + removable_components = TRUE + gun_unreliable = 0 + + load_type = /obj/item/weapon/tank + projectile_type = /obj/item/projectile/scatter/flamethrower + + power_cost = 250 + +/obj/item/weapon/gun/magnetic/gasthrower/check_ammo() + if(!loaded || !istype(loaded, load_type)) + return 0 + + var/obj/item/weapon/tank/Tank = loaded + + Tank.air_contents.update_values() // Safety + + var/turf/T = get_turf(src) + + var/phoron_amt = Tank.air_contents.gas["phoron"] + var/co2_amt = Tank.air_contents.gas["carbon_dioxide"] + var/oxy_amt = Tank.air_contents.gas["oxygen"] + var/n2o_amt = Tank.air_contents.gas["sleeping_agent"] + + if(isnull(co2_amt)) + co2_amt = 0 + + if(isnull(oxy_amt)) + oxy_amt = 0 + + if(isnull(n2o_amt)) + n2o_amt = 0 + + var/phoron_mix_proper = TRUE + if(!phoron_amt || phoron_amt < max(0.25, 3 + co2_amt - oxy_amt - (n2o_amt / 2))) + phoron_mix_proper = FALSE + + if(Tank.air_contents.return_pressure() >= T.air.return_pressure() && phoron_mix_proper) + return 1 + + return 0 + +/obj/item/weapon/gun/magnetic/gasthrower/use_ammo() + var/obj/item/weapon/tank/Tank = loaded + + var/moles_to_pull = 0.25 + + Tank.air_contents.remove(moles_to_pull) + +/obj/item/weapon/gun/magnetic/gasthrower/show_ammo(var/mob/user) + ..() + + if(loaded) + var/obj/item/weapon/tank/T = loaded + to_chat(user, "\The [T]'s pressure meter shows: [T.air_contents.return_pressure()] kpa.") + + switch(check_ammo()) + if(TRUE) + to_chat(user, "\The [src]'s display registers a proper fuel mixture.") + if(FALSE) + to_chat(user, "\The [src]'s display registers an improper fuel mixture.") diff --git a/code/modules/projectiles/guns/magnetic/magnetic_construction.dm b/code/modules/projectiles/guns/magnetic/magnetic_construction.dm index e8d38169fd..f37c44849c 100644 --- a/code/modules/projectiles/guns/magnetic/magnetic_construction.dm +++ b/code/modules/projectiles/guns/magnetic/magnetic_construction.dm @@ -91,11 +91,19 @@ . = ..(user,2) if(.) switch(construction_stage) - if(2) to_chat(user, "It has a metal frame loosely shaped around the stock.") - if(3) to_chat(user, "It has a metal frame duct-taped to the stock.") - if(4) to_chat(user, "It has a length of pipe attached to the body.") - if(4) to_chat(user, "It has a length of pipe welded to the body.") - if(6) to_chat(user, "It has a cable mount and capacitor jack wired to the frame.") - if(7) to_chat(user, "It has a single superconducting coil threaded onto the barrel.") - if(8) to_chat(user, "It has a pair of superconducting coils threaded onto the barrel.") - if(9) to_chat(user, "It has three superconducting coils attached to the body, waiting to be secured.") + if(2) + to_chat(user, "It has a metal frame loosely shaped around the stock.") + if(3) + to_chat(user, "It has a metal frame duct-taped to the stock.") + if(4) + to_chat(user, "It has a length of pipe attached to the body.") + if(4) + to_chat(user, "It has a length of pipe welded to the body.") + if(6) + to_chat(user, "It has a cable mount and capacitor jack wired to the frame.") + if(7) + to_chat(user, "It has a single superconducting coil threaded onto the barrel.") + if(8) + to_chat(user, "It has a pair of superconducting coils threaded onto the barrel.") + if(9) + to_chat(user, "It has three superconducting coils attached to the body, waiting to be secured.") diff --git a/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm b/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm index a2934d29e1..158ed31f67 100644 --- a/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm +++ b/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm @@ -119,7 +119,7 @@ removable_components = TRUE - initial_cell_type = /obj/item/weapon/cell/high + initial_cell_type = /obj/item/weapon/cell/device/weapon initial_capacitor_type = /obj/item/weapon/stock_parts/capacitor fire_delay = 8 @@ -127,7 +127,7 @@ slot_flags = SLOT_BACK slowdown = 0 - slowdown_held = 0 + slowdown_held = 0.3 slowdown_worn = 0 power_cost = 400 @@ -150,14 +150,16 @@ w_class = ITEMSIZE_NORMAL - initial_cell_type = /obj/item/weapon/cell/high + slowdown_held = 0.1 + + initial_cell_type = /obj/item/weapon/cell/device/weapon initial_capacitor_type = /obj/item/weapon/stock_parts/capacitor slot_flags = SLOT_BELT|SLOT_HOLSTER firemodes = list( - list(mode_name="lethal", power_cost = 2000, projectile_type = /obj/item/projectile/bullet/magnetic/heated, burst=1, fire_delay=8, move_delay=null, one_handed_penalty=0), - list(mode_name="stun", power_cost = 1500, projectile_type = /obj/item/projectile/energy/electrode/stunshot, burst=1, fire_delay=5, move_delay=null, one_handed_penalty=0), + list(mode_name="lethal", power_cost = 500, projectile_type = /obj/item/projectile/bullet/magnetic/heated, burst=1, fire_delay=10, move_delay=null, one_handed_penalty=0), + list(mode_name="stun", power_cost = 350, projectile_type = /obj/item/projectile/energy/electrode/strong, burst=1, fire_delay=7, move_delay=null, one_handed_penalty=0), ) /obj/item/weapon/gun/magnetic/railgun/heater/pistol/hos @@ -168,8 +170,8 @@ description_antag = "This weapon starts with a DNA locking chip attached. Using an EMAG on the weapon will disarm it, and allow you to use the chip as your own." firemodes = list( - list(mode_name="lethal", power_cost = 1500, projectile_type = /obj/item/projectile/bullet/magnetic/heated, burst=1, fire_delay=8, move_delay=null, one_handed_penalty=0), - list(mode_name="stun", power_cost = 1200, projectile_type = /obj/item/projectile/energy/electrode/stunshot, burst=1, fire_delay=5, move_delay=null, one_handed_penalty=0), + list(mode_name="lethal", power_cost = 400, projectile_type = /obj/item/projectile/bullet/magnetic/heated, burst=1, fire_delay=8, move_delay=null, one_handed_penalty=0), + list(mode_name="stun", power_cost = 300, projectile_type = /obj/item/projectile/energy/electrode/strong, burst=1, fire_delay=5, move_delay=null, one_handed_penalty=0), ) /obj/item/weapon/gun/magnetic/railgun/flechette/sif @@ -184,9 +186,9 @@ slot_flags = SLOT_BACK - slowdown = 0.3 + slowdown_held = 0.3 - power_cost = 200 + power_cost = 300 projectile_type = /obj/item/projectile/bullet/magnetic/flechette/hunting empty_sound = 'sound/weapons/smg_empty_alarm.ogg' diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index d0549bbf4c..b7b797a1a3 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -106,12 +106,12 @@ if(istype(A, /obj/item/ammo_magazine)) var/obj/item/ammo_magazine/AM = A if(!(load_method & AM.mag_type) || caliber != AM.caliber || allowed_magazines && !is_type_in_list(A, allowed_magazines)) - user << "[AM] won't load into [src]!" + to_chat(user, "[AM] won't load into [src]!") return switch(AM.mag_type) if(MAGAZINE) if(ammo_magazine) - user << "[src] already has a magazine loaded." //already a magazine here + to_chat(user, "[src] already has a magazine loaded.") //already a magazine here return user.remove_from_mob(AM) AM.loc = src @@ -120,7 +120,7 @@ playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1) if(SPEEDLOADER) if(loaded.len >= max_shells) - user << "[src] is full!" + to_chat(user, "[src] is full!") return var/count = 0 for(var/obj/item/ammo_casing/C in AM.stored_ammo) @@ -140,7 +140,7 @@ if(!(load_method & SINGLE_CASING) || caliber != C.caliber) return //incompatible if(loaded.len >= max_shells) - user << "[src] is full." + to_chat(user, "[src] is full.") return user.remove_from_mob(C) @@ -154,7 +154,7 @@ if(!(load_method & SINGLE_CASING)) return //incompatible - user << "You start loading \the [src]." + to_chat(user, "You start loading \the [src].") sleep(1 SECOND) for(var/obj/item/ammo_casing/ammo in storage.contents) if(caliber != ammo.caliber) @@ -163,7 +163,7 @@ load_ammo(ammo, user) if(loaded.len >= max_shells) - user << "[src] is full." + to_chat(user, "[src] is full.") break sleep(1 SECOND) @@ -196,7 +196,7 @@ user.visible_message("[user] removes \a [C] from [src].", "You remove \a [C] from [src].") playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) else - user << "[src] is empty." + to_chat(user, "[src] is empty.") update_icon() /obj/item/weapon/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob) @@ -232,8 +232,8 @@ /obj/item/weapon/gun/projectile/examine(mob/user) ..(user) if(ammo_magazine) - user << "It has \a [ammo_magazine] loaded." - user << "Has [getAmmo()] round\s remaining." + to_chat(user, "It has \a [ammo_magazine] loaded.") + to_chat(user, "Has [getAmmo()] round\s remaining.") return /obj/item/weapon/gun/projectile/proc/getAmmo() diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index a9d3125253..c48bc499aa 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -83,7 +83,7 @@ allowed_magazines = list(/obj/item/ammo_magazine/m545) projectile_type = /obj/item/projectile/bullet/rifle/a545 -// one_handed_penalty = 30 + one_handed_penalty = 30 firemodes = list( list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null), @@ -140,7 +140,7 @@ auto_eject = 1 auto_eject_sound = 'sound/weapons/smg_empty_alarm.ogg' -// one_handed_penalty = 60 + one_handed_penalty = 60 burst_delay = 4 firemodes = list( @@ -188,15 +188,16 @@ /obj/item/weapon/gun/projectile/automatic/z8/examine(mob/user) ..() if(launcher.chambered) - user << "\The [launcher] has \a [launcher.chambered] loaded." + to_chat(user, "\The [launcher] has \a [launcher.chambered] loaded.") else - user << "\The [launcher] is empty." + to_chat(user, "\The [launcher] is empty.") /obj/item/weapon/gun/projectile/automatic/l6_saw name = "light machine gun" desc = "A rather traditionally made L6 SAW with a pleasantly lacquered wooden pistol grip. 'Aussec Armoury-2531' is engraved on the reciever. Uses 5.45mm rounds. It's also compatible with magazines from STS-35 assault rifles." icon_state = "l6closed100" item_state = "l6closed" + wielded_item_state = "genericLMG-wielded" w_class = ITEMSIZE_LARGE force = 10 slot_flags = 0 @@ -209,7 +210,7 @@ allowed_magazines = list(/obj/item/ammo_magazine/m545saw, /obj/item/ammo_magazine/m545) projectile_type = /obj/item/projectile/bullet/rifle/a545 -// one_handed_penalty = 90 + one_handed_penalty = 90 var/cover_open = 0 @@ -230,13 +231,13 @@ /obj/item/weapon/gun/projectile/automatic/l6_saw/special_check(mob/user) if(cover_open) - user << "[src]'s cover is open! Close it before firing!" + to_chat(user, "[src]'s cover is open! Close it before firing!") return 0 return ..() /obj/item/weapon/gun/projectile/automatic/l6_saw/proc/toggle_cover(mob/user) cover_open = !cover_open - user << "You [cover_open ? "open" : "close"] [src]'s cover." + to_chat(user, "You [cover_open ? "open" : "close"] [src]'s cover.") update_icon() update_held_icon() @@ -263,13 +264,13 @@ /obj/item/weapon/gun/projectile/automatic/l6_saw/load_ammo(var/obj/item/A, mob/user) if(!cover_open) - user << "You need to open the cover to load [src]." + to_chat(user, "You need to open the cover to load [src].") return ..() /obj/item/weapon/gun/projectile/automatic/l6_saw/unload_ammo(mob/user, var/allow_dump=1) if(!cover_open) - user << "You need to open the cover to unload [src]." + to_chat(user, "You need to open the cover to unload [src].") return ..() @@ -278,6 +279,7 @@ desc = "The AS-24 is a rugged looking automatic shotgun produced for the military by Gurov Projectile Weapons LLC. For very obvious reasons, it's illegal to own in many juristictions. Uses 12g rounds." icon_state = "ashot" item_state = null + wielded_item_state = "woodarifle-wielded" //Placeholder w_class = ITEMSIZE_LARGE force = 10 caliber = "12g" @@ -288,7 +290,7 @@ allowed_magazines = list(/obj/item/ammo_magazine/m12gdrum) projectile_type = /obj/item/projectile/bullet/shotgun -// one_handed_penalty = 60 + one_handed_penalty = 60 firemodes = list( list(mode_name="semiauto", burst=1, fire_delay=0), @@ -375,6 +377,7 @@ desc = "The bullpup configured GP3000 is a battle rifle produced by Gurov Projectile Weapons LLC. It is sold almost exclusively to standing armies. Uses 7.62mm rounds." icon_state = "bullpup-small" item_state = "bullpup" + wielded_item_state = "sexyrifle-wielded" //Placeholder, this is a bullpup at least w_class = ITEMSIZE_LARGE force = 10 caliber = "7.62mm" @@ -385,7 +388,7 @@ allowed_magazines = list(/obj/item/ammo_magazine/m762, /obj/item/ammo_magazine/m762m) projectile_type = /obj/item/projectile/bullet/rifle/a762 -// one_handed_penalty = 45 + one_handed_penalty = 45 firemodes = list( list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null), diff --git a/code/modules/projectiles/guns/projectile/automatic_vr.dm b/code/modules/projectiles/guns/projectile/automatic_vr.dm index 79789292ee..5d4cd08eaf 100644 --- a/code/modules/projectiles/guns/projectile/automatic_vr.dm +++ b/code/modules/projectiles/guns/projectile/automatic_vr.dm @@ -1,2 +1,226 @@ /obj/item/weapon/gun/projectile/automatic/wt550/lethal - magazine_type = /obj/item/ammo_magazine/m9mmt \ No newline at end of file + magazine_type = /obj/item/ammo_magazine/m9mmt + +//////////////////////////////////////////////////////////// +//////////////////// Projectile Weapons //////////////////// +//////////////////////////////////////////////////////////// +// For general use +/obj/item/weapon/gun/projectile/automatic/battlerifle + name = "\improper USDF service rifle" + desc = "You had your chance to be afraid before you joined my beloved Corps! But, to guide you back to the true path, I have brought this motivational device! Uses 9.5x40mm rounds." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "battlerifle" + icon_override = 'icons/obj/gun_vr.dmi' + item_state = "battlerifle_i" + item_icons = null + w_class = ITEMSIZE_LARGE + recoil = 2 // The battlerifle was known for its nasty recoil. + max_shells = 36 + caliber = "9.5x40mm" + origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 2) + magazine_type = /obj/item/ammo_magazine/m95 + allowed_magazines = list(/obj/item/ammo_magazine/m95) + fire_sound = 'sound/weapons/battlerifle.ogg' + load_method = MAGAZINE + slot_flags = SLOT_BACK + one_handed_penalty = 60 // The weapon itself is heavy + +// For general use +/obj/item/weapon/gun/projectile/automatic/pdw + name = "personal defense weapon" + desc = "The X-9MM is a select-fire personal defense weapon designed in-house by Xing Private Security. It was made to compete with the WT550 Saber, but never caught on with NanoTrasen. Uses 9mm rounds." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "pdw" + item_state = "c20r" // Placeholder + w_class = ITEMSIZE_NORMAL + caliber = "9mm" + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 2) + slot_flags = SLOT_BELT + load_method = MAGAZINE + magazine_type = /obj/item/ammo_magazine/m9mml + allowed_magazines = list(/obj/item/ammo_magazine/m9mm, /obj/item/ammo_magazine/m9mml) + + firemodes = list( + list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null), + list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=6, burst_accuracy=list(0,-15,-30), dispersion=list(0.0, 0.6, 0.6)) + ) + +/obj/item/weapon/gun/projectile/automatic/pdw/update_icon(var/ignore_inhands) + ..() + if(istype(ammo_magazine,/obj/item/ammo_magazine/m9mm)) + icon_state = "pdw-short" + else + icon_state = (ammo_magazine)? "pdw" : "pdw-empty" + if(!ignore_inhands) update_held_icon() + +// For general use +/obj/item/weapon/gun/projectile/automatic/stg + name = "\improper Sturmgewehr" + desc = "An STG-560 built by RauMauser. Experience the terror of the Siegfried line, redone for the 26th century! The Kaiser would be proud. Uses unique 7.92x33mm Kurz rounds." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "stg60" + item_state = "arifle" + w_class = ITEMSIZE_LARGE + max_shells = 30 + caliber = "7.92x33mm" + origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 2, TECH_ILLEGAL = 6) + magazine_type = /obj/item/ammo_magazine/mtg + allowed_magazines = list(/obj/item/ammo_magazine/mtg) + load_method = MAGAZINE + +/obj/item/weapon/gun/projectile/automatic/stg/update_icon(var/ignore_inhands) + ..() + icon_state = (ammo_magazine)? "stg60" : "stg60-empty" + item_state = (ammo_magazine)? "arifle" : "arifle-empty" + if(!ignore_inhands) update_held_icon() + +// Removed because gun64_vr.dmi guns don't work. +/* +//-----------------------UF-ARC---------------------------------- +/obj/item/weapon/gun/projectile/automatic/carbine/fluff/ufarc + name = "UF-ARC" + desc = "The UF-ARC is a lightweight assault rifle manufactured by the National Armory of Gaia and sold almost exclusively to the United Federation's standing army, the Military Assault Command Operations Department (MACOs)." + icon = 'icons/obj/gun64_vr.dmi' + icon_state = "ufarc" + icon_override = 'icons/obj/gun_vr.dmi' + item_state = "battlerifle_i" + item_icons = null + pixel_x = -16 + +/obj/item/weapon/gun/projectile/automatic/carbine/fluff/ufarc/update_icon(var/ignore_inhands) + ..() + // TODO - Fix this for spriting different size magazines + icon_state = (ammo_magazine)? "ufarc" : "ufarc-empty" + item_state = (ammo_magazine)? "bullpup" : "bullpup-empty" + if(!ignore_inhands) update_held_icon() + + + +//-----------------------G44---------------------------------- +/obj/item/weapon/gun/projectile/automatic/carbine/fluff/g44 + name = "G44 Rifle" + desc = "The G44 is a lightweight assault rifle manufactured by the National Armory of Gaia and sold almost exclusively to the United Federation's standing army, the Military Assault Command Operations Department (MACOs)." + icon = 'icons/obj/gun64_vr.dmi' + icon_state = "g44" + item_state = "bullpup" + pixel_x = -16 + +/obj/item/weapon/gun/projectile/automatic/carbine/fluff/g44/update_icon(var/ignore_inhands) + ..() + // TODO - Fix this for spriting different size magazines + icon_state = (ammo_magazine)? "g44" : "g44-empty" + item_state = (ammo_magazine)? "bullpup" : "bullpup-empty" + if(!ignore_inhands) update_held_icon() +*/ + +//////////////////// Eris Ported Guns //////////////////// +// No idea what this is for. +/obj/item/weapon/gun/projectile/automatic/sol + name = "\improper \"Sol\" SMG" + desc = "The FS 9x19mm \"Sol\" is a compact and reliable submachine gun. Uses 9mm rounds." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "SMG-IS" + item_state = "wt550" + w_class = ITEMSIZE_LARGE + slot_flags = SLOT_BELT + caliber = "9mm" + magazine_type = /obj/item/ammo_magazine/m9mm + allowed_magazines = list(/obj/item/ammo_magazine/m9mm) + load_method = MAGAZINE + multi_aim = 1 + burst_delay = 2 + origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 2) + firemodes = list( + list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, burst_accuracy=null, dispersion=null), + list(mode_name="3-round bursts", burst=3, fire_delay=null, move_delay=4, burst_accuracy=list(0,-15,-15), dispersion=list(0.0, 0.6, 1.0)), + ) + +/obj/item/weapon/gun/projectile/automatic/sol/proc/update_charge() + if(!ammo_magazine) + return + var/ratio = ammo_magazine.stored_ammo.len / ammo_magazine.max_ammo + if(ratio < 0.25 && ratio != 0) + ratio = 0.25 + ratio = round(ratio, 0.25) * 100 + overlays += "smg_[ratio]" + +/obj/item/weapon/gun/projectile/automatic/sol/update_icon() + icon_state = (ammo_magazine)? "SMG-IS" : "SMG-IS-empty" + overlays.Cut() + update_charge() + +//--------------- StG-60 ---------------- +/obj/item/ammo_magazine/m792 + name = "box mag (7.92x33mm Kurz)" + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "stg_30rnd" + caliber = "7.92x33mm" + ammo_type = /obj/item/ammo_casing/a792 + max_ammo = 30 + mag_type = MAGAZINE + +/obj/item/ammo_casing/a792 + desc = "A 7.92x33mm Kurz casing." + icon_state = "rifle-casing" + caliber = "7.92x33mm" + projectile_type = /obj/item/projectile/bullet/rifle/a762 + +/obj/item/ammo_magazine/mtg/empty + initial_ammo = 0 + +//------------- Battlerifle ------------- +/obj/item/ammo_magazine/m95 + name = "box mag (9.5x40mm)" + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "battlerifle" + caliber = "9.5x40mm" + ammo_type = /obj/item/ammo_casing/a95 + max_ammo = 36 + mag_type = MAGAZINE + multiple_sprites = 1 + +/obj/item/ammo_casing/a95 + desc = "A 9.5x40mm bullet casing." + icon_state = "rifle-casing" + caliber = "9.5x40mm" + projectile_type = /obj/item/projectile/bullet/rifle/a95 + +/obj/item/projectile/bullet/rifle/a95 + damage = 40 + +/obj/item/ammo_magazine/m95/empty + initial_ammo = 0 + +//---------------- PDW ------------------ +/obj/item/ammo_magazine/m9mml + name = "\improper SMG magazine (9mm)" + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "smg" + origin_tech = list(TECH_COMBAT = 2) + mag_type = MAGAZINE + matter = list(DEFAULT_WALL_MATERIAL = 1800) + caliber = "9mm" + ammo_type = /obj/item/ammo_casing/a9mm + max_ammo = 30 + multiple_sprites = 1 + +/obj/item/ammo_magazine/m9mml/empty + initial_ammo = 0 + +/obj/item/ammo_magazine/m9mml/ap + name = "\improper SMG magazine (9mm armor-piercing)" + ammo_type = /obj/item/ammo_casing/a9mm/ap + +/* Seems to have been de-coded? +/obj/item/ammo_magazine/m9mml/flash + name = "\improper SMG magazine (9mm flash)" + ammo_type = /obj/item/ammo_casing/a9mmf + +/obj/item/ammo_magazine/m9mml/rubber + name = "\improper SMG magazine (9mm rubber)" + ammo_type = /obj/item/ammo_casing/a9mmr + +/obj/item/ammo_magazine/m9mml/practice + name = "\improper SMG magazine (9mm practice)" + ammo_type = /obj/item/ammo_casing/a9mmp +*/ diff --git a/code/modules/projectiles/guns/projectile/boltaction.dm b/code/modules/projectiles/guns/projectile/boltaction.dm index 8e579d659f..0555fb31de 100644 --- a/code/modules/projectiles/guns/projectile/boltaction.dm +++ b/code/modules/projectiles/guns/projectile/boltaction.dm @@ -27,7 +27,7 @@ // Stole hacky terrible code from doublebarrel shotgun. -Spades /obj/item/weapon/gun/projectile/shotgun/pump/rifle/ceremonial/attackby(var/obj/item/A as obj, mob/user as mob) if(istype(A, /obj/item/weapon/surgical/circular_saw) || istype(A, /obj/item/weapon/melee/energy) || istype(A, /obj/item/weapon/pickaxe/plasmacutter) && w_class != ITEMSIZE_NORMAL) - user << "You begin to shorten the barrel and stock of \the [src]." + to_chat(user, "You begin to shorten the barrel and stock of \the [src].") if(loaded.len) afterattack(user, user) playsound(user, fire_sound, 50, 1) diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm index 4958accb64..861773521b 100644 --- a/code/modules/projectiles/guns/projectile/dartgun.dm +++ b/code/modules/projectiles/guns/projectile/dartgun.dm @@ -105,25 +105,25 @@ // return ..() if (beakers.len) - user << "[src] contains:" + to_chat(user, "[src] contains:") for(var/obj/item/weapon/reagent_containers/glass/beaker/B in beakers) if(B.reagents && B.reagents.reagent_list.len) for(var/datum/reagent/R in B.reagents.reagent_list) - user << "[R.volume] units of [R.name]" + to_chat(user, "[R.volume] units of [R.name]") /obj/item/weapon/gun/projectile/dartgun/attackby(obj/item/I as obj, mob/user as mob) if(istype(I, /obj/item/weapon/reagent_containers/glass)) if(!istype(I, container_type)) - user << "[I] doesn't seem to fit into [src]." + to_chat(user, "[I] doesn't seem to fit into [src].") return if(beakers.len >= max_beakers) - user << "[src] already has [max_beakers] beakers in it - another one isn't going to fit!" + to_chat(user, "[src] already has [max_beakers] beakers in it - another one isn't going to fit!") return var/obj/item/weapon/reagent_containers/glass/beaker/B = I user.drop_item() B.loc = src beakers += B - user << "You slot [B] into [src]." + to_chat(user, "You slot [B] into [src].") src.updateUsrDialog() return 1 ..() @@ -194,7 +194,7 @@ if(index <= beakers.len) if(beakers[index]) var/obj/item/weapon/reagent_containers/glass/beaker/B = beakers[index] - usr << "You remove [B] from [src]." + to_chat(usr, "You remove [B] from [src].") mixing -= B beakers -= B B.loc = get_turf(src) diff --git a/code/modules/projectiles/guns/projectile/dartgun_vr.dm b/code/modules/projectiles/guns/projectile/dartgun_vr.dm new file mode 100644 index 0000000000..dda197ccc6 --- /dev/null +++ b/code/modules/projectiles/guns/projectile/dartgun_vr.dm @@ -0,0 +1,29 @@ +//-----------------------Tranq Gun---------------------------------- +/obj/item/weapon/gun/projectile/dartgun/tranq + name = "tranquilizer gun" + desc = "A gas-powered dart gun designed by the National Armory of Gaia. This gun is used primarily by United Federation special forces for Tactical Espionage missions. Don't forget your bandana." + icon_state = "tranqgun" + item_state = null + + caliber = "dart" + fire_sound = 'sound/weapons/empty.ogg' + fire_sound_text = "a metallic click" + recoil = 0 + silenced = 1 + load_method = MAGAZINE + magazine_type = /obj/item/ammo_magazine/chemdart + allowed_magazines = list(/obj/item/ammo_magazine/chemdart) + auto_eject = 0 + +/obj/item/weapon/gun/projectile/dartgun/tranq/update_icon() + if(!ammo_magazine) + icon_state = "tranqgun" + return 1 + + if(!ammo_magazine.stored_ammo || ammo_magazine.stored_ammo.len) + icon_state = "tranqgun" + else if(ammo_magazine.stored_ammo.len > 5) + icon_state = "tranqgun" + else + icon_state = "tranqgun" + return 1 diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 7cda02ac1b..1acf57b452 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -35,14 +35,14 @@ if(!M.mind) return 0 var/job = M.mind.assigned_role if(job != "Detective" && job != "Security Officer" && job != "Warden" && job != "Head of Security") - M << "You don't feel cool enough to name this gun, chump." + to_chat(M, "You don't feel cool enough to name this gun, chump.") return 0 var/input = sanitizeSafe(input("What do you want to name the gun?", ,""), MAX_NAME_LEN) if(src && input && !M.stat && in_range(M,src)) name = input - M << "You name the gun [input]. Say hello to your new friend." + to_chat(M, "You name the gun [input]. Say hello to your new friend.") return 1 /obj/item/weapon/gun/projectile/colt/detective/verb/reskin_gun() @@ -65,7 +65,7 @@ if(src && choice && !M.stat && in_range(M,src)) icon_state = options[choice] unique_reskin = options[choice] - M << "Your gun is now sprited as [choice]. Say hello to your new friend." + to_chat(M, "Your gun is now sprited as [choice]. Say hello to your new friend.") return 1 /*//apart of reskins that have two sprites, touching may result in frustration and breaks @@ -200,7 +200,7 @@ if(!user.item_is_in_hands(src)) ..() return - user << "You unscrew [silenced] from [src]." + to_chat(user, "You unscrew [silenced] from [src].") user.put_in_hands(silenced) silenced = 0 w_class = ITEMSIZE_SMALL @@ -211,10 +211,10 @@ /obj/item/weapon/gun/projectile/pistol/attackby(obj/item/I as obj, mob/living/user as mob) if(istype(I, /obj/item/weapon/silencer)) if(!user.item_is_in_hands(src)) //if we're not in his hands - user << "You'll need [src] in your hands to do that." + to_chat(user, "You'll need [src] in your hands to do that.") return user.drop_item() - user << "You screw [I] onto [src]." + to_chat(user, "You screw [I] onto [src].") silenced = I //dodgy? w_class = ITEMSIZE_NORMAL I.loc = src //put the silencer into the gun diff --git a/code/modules/projectiles/guns/projectile/pistol_vr.dm b/code/modules/projectiles/guns/projectile/pistol_vr.dm index 7a76b12552..66d70ff5be 100644 --- a/code/modules/projectiles/guns/projectile/pistol_vr.dm +++ b/code/modules/projectiles/guns/projectile/pistol_vr.dm @@ -9,4 +9,66 @@ magazine_type = /obj/item/ammo_magazine/m9mm/large/preban // Spawns with big magazines that are legal. /obj/item/weapon/gun/projectile/p92x/large/preban/hp - magazine_type = /obj/item/ammo_magazine/m9mm/large/preban/hp // Spawns with legal hollow-point mag \ No newline at end of file + magazine_type = /obj/item/ammo_magazine/m9mm/large/preban/hp // Spawns with legal hollow-point mag + +//////////////////// Eris Ported Guns //////////////////// +//HoS Gun +/obj/item/weapon/gun/projectile/lamia + name = "FS HG .44 \"Lamia\"" + desc = "Uses .44 rounds." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "Headdeagle" + item_state = "revolver" + caliber = ".44" + magazine_type = /obj/item/ammo_magazine/m44/rubber + allowed_magazines = list(/obj/item/ammo_magazine/m44,/obj/item/ammo_magazine/m44/rubber) + load_method = MAGAZINE + auto_eject = 1 + auto_eject_sound = 'sound/weapons/smg_empty_alarm.ogg' + origin_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 4) + +/obj/item/weapon/gun/projectile/lamia/update_icon() + overlays.Cut() + if(!ammo_magazine) + return + var/ratio = ammo_magazine.stored_ammo.len * 100 / ammo_magazine.max_ammo + ratio = round(ratio, 33) + overlays += "deagle_[ratio]" + +//Civilian gun +/obj/item/weapon/gun/projectile/giskard + name = "\improper \"Giskard\" holdout pistol" + desc = "The FS HG .380 \"Giskard\" can even fit into the pocket! Uses .380 rounds." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "giskardcivil" + caliber = ".380" + magazine_type = /obj/item/ammo_magazine/m380 + allowed_magazines = list(/obj/item/ammo_magazine/m380) + load_method = MAGAZINE + w_class = ITEMSIZE_SMALL + fire_sound = 'sound/weapons/gunshot_pathetic.ogg' + origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 3) + +/obj/item/weapon/gun/projectile/giskard/update_icon() + ..() + if(ammo_magazine && ammo_magazine.stored_ammo.len) + icon_state = "giskardcivil" + else + icon_state = "giskardcivil_empty" + +//Not so civilian gun +/obj/item/weapon/gun/projectile/giskard/olivaw + name = "\improper \"Olivaw\" holdout burst-pistol" + desc = "The FS HG .380 \"Olivaw\" is a more advanced version of the \"Giskard\". This one seems to have a two-round burst-fire mode. Uses .380 rounds." + icon_state = "olivawcivil" + firemodes = list( + list(mode_name="semiauto", burst=1, fire_delay=1.2, move_delay=null, burst_accuracy=null, dispersion=null), + list(mode_name="2-round bursts", burst=2, fire_delay=0.2, move_delay=4, burst_accuracy=list(0,-15), dispersion=list(1.2, 1.8)), + ) + +/obj/item/weapon/gun/projectile/giskard/olivaw/update_icon() + ..() + if(ammo_magazine && ammo_magazine.stored_ammo.len) + icon_state = "olivawcivil" + else + icon_state = "olivawcivil_empty" diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index ee04e27653..c9f6f917c8 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -56,14 +56,14 @@ var/mob/M = usr if(!M.mind) return 0 if(!M.mind.assigned_role == "Detective") - M << "You don't feel cool enough to name this gun, chump." + to_chat(M, "You don't feel cool enough to name this gun, chump.") return 0 var/input = sanitizeSafe(input("What do you want to name the gun?", ,""), MAX_NAME_LEN) if(src && input && !M.stat && in_range(M,src)) name = input - M << "You name the gun [input]. Say hello to your new friend." + to_chat(M, "You name the gun [input]. Say hello to your new friend.") return 1 /obj/item/weapon/gun/projectile/revolver/detective45 @@ -85,14 +85,14 @@ obj/item/weapon/gun/projectile/revolver/detective45/verb/rename_gun() if(!M.mind) return 0 var/job = M.mind.assigned_role if(job != "Detective") - M << "You don't feel cool enough to name this gun, chump." + to_chat(M, "You don't feel cool enough to name this gun, chump.") return 0 var/input = sanitizeSafe(input("What do you want to name the gun?", ,""), MAX_NAME_LEN) if(src && input && !M.stat && in_range(M,src)) name = input - M << "You name the gun [input]. Say hello to your new friend." + to_chat(M, "You name the gun [input]. Say hello to your new friend.") return 1 /obj/item/weapon/gun/projectile/revolver/detective45/verb/reskin_gun() @@ -114,7 +114,7 @@ obj/item/weapon/gun/projectile/revolver/detective45/verb/rename_gun() var/choice = input(M,"Choose your sprite!","Resprite Gun") in options if(src && choice && !M.stat && in_range(M,src)) icon_state = options[choice] - M << "Your gun is now sprited as [choice]. Say hello to your new friend." + to_chat(M, "Your gun is now sprited as [choice]. Say hello to your new friend.") return 1 diff --git a/code/modules/projectiles/guns/projectile/revolver_vr.dm b/code/modules/projectiles/guns/projectile/revolver_vr.dm new file mode 100644 index 0000000000..3537cc4865 --- /dev/null +++ b/code/modules/projectiles/guns/projectile/revolver_vr.dm @@ -0,0 +1,115 @@ +//////////////////// Eris Ported Guns //////////////////// +//Detective gun +/obj/item/weapon/gun/projectile/revolver/consul + name = "\improper \"Consul\" Revolver" + desc = "Are you feeling lucky, punk? Uses .44 rounds." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "inspector" + item_state = "revolver" + caliber = ".44" + ammo_type = /obj/item/ammo_casing/a44/rubber + handle_casings = CYCLE_CASINGS + origin_tech = list(TECH_COMBAT = 3, TECH_MATERIAL = 3) + +/obj/item/weapon/gun/projectile/revolver/consul/proc/update_charge() + if(loaded.len==0) + overlays += "inspector_off" + else + overlays += "inspector_on" + +/obj/item/weapon/gun/projectile/revolver/consul/update_icon() + overlays.Cut() + update_charge() + +//.357 special ammo +/obj/item/ammo_magazine/s357/stun + name = "speedloader (.357 stun)" + desc = "A speedloader for .357 revolvers." + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "s357" + caliber = ".357" + ammo_type = /obj/item/ammo_casing/a357/stun + + +/obj/item/ammo_casing/a357/stun + desc = "A .357 stun bullet casing." + caliber = ".357" + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "stun357" + projectile_type = /obj/item/projectile/energy/electrode/stunshot/strong + +/obj/item/ammo_magazine/s357/rubber + name = "speedloader (.357 rubber)" + desc = "A speedloader for .357 revolvers." + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "r357" + caliber = ".357" + ammo_type = /obj/item/ammo_casing/a357/rubber + + +/obj/item/ammo_casing/a357/rubber + desc = "A .357 rubber bullet casing." + caliber = ".357" + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "rubber357" + projectile_type = /obj/item/projectile/bullet/pistol/rubber/strong + +/obj/item/ammo_magazine/s357/flash + name = "speedloader (.357 flash)" + desc = "A speedloader for .357 revolvers." + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "f357" + caliber = ".357" + ammo_type = /obj/item/ammo_casing/a357/flash + +/obj/item/ammo_casing/a357/flash + desc = "A .357 flash bullet casing." + caliber = ".357" + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "flash357" + projectile_type = /obj/item/projectile/energy/flash/strong + +//.380 +/obj/item/ammo_casing/a380 + desc = "A .380 bullet casing." + caliber = ".380" + projectile_type = /obj/item/projectile/bullet/pistol + +/obj/item/ammo_magazine/m380 + name = "magazine (.380)" + icon_state = "9x19p" + origin_tech = list(TECH_COMBAT = 2) + mag_type = MAGAZINE + matter = list(DEFAULT_WALL_MATERIAL = 480) + caliber = ".380" + ammo_type = /obj/item/ammo_casing/a380 + max_ammo = 8 + multiple_sprites = 1 + +//.44 +/obj/item/ammo_casing/a44/rubber + icon_state = "r-casing" + desc = "A .44 rubber bullet casing." + projectile_type = /obj/item/projectile/bullet/pistol/rubber/strong + +/obj/item/ammo_magazine/m44/rubber + desc = "A magazine for .44 less-than-lethal ammo." + ammo_type = /obj/item/ammo_casing/a44/rubber + +//.44 speedloaders +/obj/item/ammo_magazine/s44 + name = "speedloader (.44)" + desc = "A speedloader for .44 revolvers." + icon = 'icons/obj/ammo_vr.dmi' + icon_state = "s357" + caliber = ".44" + matter = list(DEFAULT_WALL_MATERIAL = 1260) + ammo_type = /obj/item/ammo_casing/a44 + max_ammo = 6 + multiple_sprites = 1 + mag_type = SPEEDLOADER + +/obj/item/ammo_magazine/s44/rubber + name = "speedloader (.44 rubber)" + icon_state = "r357" + ammo_type = /obj/item/ammo_casing/a44/rubber diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index 315c6edca0..74896b6b6a 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -104,7 +104,7 @@ //this is largely hacky and bad :( -Pete /obj/item/weapon/gun/projectile/shotgun/doublebarrel/attackby(var/obj/item/A as obj, mob/user as mob) if(istype(A, /obj/item/weapon/surgical/circular_saw) || istype(A, /obj/item/weapon/melee/energy) || istype(A, /obj/item/weapon/pickaxe/plasmacutter)) - user << "You begin to shorten the barrel of \the [src]." + to_chat(user, "You begin to shorten the barrel of \the [src].") if(loaded.len) var/burstsetting = burst burst = 2 diff --git a/code/modules/projectiles/guns/projectile/shotgun_vr.dm b/code/modules/projectiles/guns/projectile/shotgun_vr.dm new file mode 100644 index 0000000000..09e90d4ffe --- /dev/null +++ b/code/modules/projectiles/guns/projectile/shotgun_vr.dm @@ -0,0 +1,11 @@ +// For general use +/obj/item/weapon/gun/projectile/shotgun/pump/USDF + name = "\improper USDF tactical shotgun" + desc = "All you greenhorns who wanted to see Xenomorphs up close... this is your lucky day. Uses 12g rounds." + icon = 'icons/obj/gun_vr.dmi' + icon_state = "haloshotgun" + icon_override = 'icons/obj/gun_vr.dmi' + item_state = "haloshotgun_i" + item_icons = null + ammo_type = /obj/item/ammo_casing/a12g + max_shells = 12 diff --git a/code/modules/projectiles/guns/projectile/shotgun_yw.dm b/code/modules/projectiles/guns/projectile/shotgun_yw.dm index eb806a4b98..cae96986f8 100644 --- a/code/modules/projectiles/guns/projectile/shotgun_yw.dm +++ b/code/modules/projectiles/guns/projectile/shotgun_yw.dm @@ -50,4 +50,36 @@ /obj/item/ammo_magazine/m12gdrumjack/empty name = "drum magazine (12 gauge)" - initial_ammo = 0 \ No newline at end of file + initial_ammo = 0 + +//scattering shots, old buckshot +/obj/item/ammo_casing/a12g/scatter + name = "scatter shell" + desc = "A 12 gauge scattering shell" + icon = 'icons/obj/ammo_yw.dmi' + icon_state = "scattershell" + projectile_type = /obj/item/projectile/scatter/shotgun + +/obj/item/projectile/scatter/shotgun + name = "Shotgun scatter projectile" + spread_submunition_damage = FALSE + submunition_spread_max = 60 + submunition_spread_min = 50 + submunitions = list( + /obj/item/projectile/bullet/shotgun/scatterprojectile = 6 + ) +/obj/item/projectile/bullet/shotgun/scatterprojectile + name = "pellet" + fire_sound = 'sound/weapons/Gunshot_shotgun.ogg' + damage = 13 + +/obj/item/weapon/storage/box/scattershot + name = "box of shotgun scatter shells" + desc = "It has a picture of a gun and several warning symbols on the front.
    WARNING: Live ammunition. Misuse may result in serious injury or death." + icon = 'icons/obj/ammo_yw.dmi' + icon_state = "scattershot_box" + item_state_slots = list(slot_r_hand_str = "syringe_kit", slot_l_hand_str = "syringe_kit") + starts_with = list(/obj/item/ammo_casing/a12g/scatter = 8) + +/obj/item/weapon/storage/box/scattershot/large + starts_with = list(/obj/item/ammo_casing/a12g/scatter = 16) \ No newline at end of file diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index 76a6faf1d7..343384f290 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -4,10 +4,11 @@ name = "anti-materiel rifle" desc = "A portable anti-armour rifle fitted with a scope, the HI PTR-7 Rifle was originally designed to used against armoured exosuits. It is capable of punching through windows and non-reinforced walls with ease. Fires armor piercing 14.5mm shells." icon_state = "heavysniper" - item_state_slots = list(slot_r_hand_str = "l6closed-empty", slot_l_hand_str = "l6closed-empty") // placeholder + wielded_item_state = "heavysniper-wielded" w_class = ITEMSIZE_HUGE // So it can't fit in a backpack. force = 10 slot_flags = SLOT_BACK + action_button_name = "Use Scope" origin_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 2, TECH_ILLEGAL = 8) caliber = "14.5mm" recoil = 5 //extra kickback @@ -18,7 +19,8 @@ projectile_type = /obj/item/projectile/bullet/rifle/a145 accuracy = -75 scoped_accuracy = 75 -// one_handed_penalty = 90 + ignore_visor_zoom_restriction = TRUE // Ignore the restriction on vision modifiers when using this gun's scope. + one_handed_penalty = 90 var/bolt_open = 0 /obj/item/weapon/gun/projectile/heavysniper/update_icon() @@ -32,21 +34,21 @@ bolt_open = !bolt_open if(bolt_open) if(chambered) - user << "You work the bolt open, ejecting [chambered]!" + to_chat(user, "You work the bolt open, ejecting [chambered]!") chambered.loc = get_turf(src) loaded -= chambered chambered = null else - user << "You work the bolt open." + to_chat(user, "You work the bolt open.") else - user << "You work the bolt closed." + to_chat(user, "You work the bolt closed.") bolt_open = 0 add_fingerprint(user) update_icon() /obj/item/weapon/gun/projectile/heavysniper/special_check(mob/user) if(bolt_open) - user << "You can't fire [src] while the bolt is open!" + to_chat(user, "You can't fire [src] while the bolt is open!") return 0 return ..() @@ -60,6 +62,9 @@ return ..() +/obj/item/weapon/gun/projectile/heavysniper/ui_action_click() + scope() + /obj/item/weapon/gun/projectile/heavysniper/verb/scope() set category = "Object" set name = "Use Scope" @@ -74,16 +79,17 @@ desc = "The SVD, also known as the Dragunov, is mass produced with an Optical Sniper Sight so simple that even Ivan can use it. Too bad for you that the inscriptions are written in Russian. Uses 7.62mm rounds." icon_state = "SVD" item_state = "SVD" + wielded_item_state = "heavysniper-wielded" //Placeholder w_class = ITEMSIZE_HUGE // So it can't fit in a backpack. force = 10 slot_flags = SLOT_BACK // Needs a sprite. + action_button_name = "Use Scope" origin_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 2, TECH_ILLEGAL = 8) caliber = "7.62mm" load_method = MAGAZINE accuracy = -45 //shooting at the hip scoped_accuracy = 0 -// requires_two_hands = 1 -// one_handed_penalty = 60 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand. + one_handed_penalty = 60 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand. fire_sound = 'sound/weapons/Gunshot_SVD.ogg' // Has a very unique sound. magazine_type = /obj/item/ammo_magazine/m762svd allowed_magazines = list(/obj/item/ammo_magazine/m762svd) @@ -95,6 +101,9 @@ else icon_state = "SVD-empty" +/obj/item/weapon/gun/projectile/SVD/ui_action_click() + scope() + /obj/item/weapon/gun/projectile/SVD/verb/scope() set category = "Object" set name = "Use Scope" diff --git a/code/modules/projectiles/guns/projectile/sniper_vr.dm b/code/modules/projectiles/guns/projectile/sniper_vr.dm deleted file mode 100644 index 649fc4ba3d..0000000000 --- a/code/modules/projectiles/guns/projectile/sniper_vr.dm +++ /dev/null @@ -1,36 +0,0 @@ -////////////// Dragunov Sniper Rifle ////////////// - -/obj/item/weapon/gun/projectile/SVD - name = "\improper Dragunov" - desc = "The SVD, also known as the Dragunov, was mass produced with an Optical Sniper Sight so simple that even Ivan can figure out how it works. Too bad for you that it's written in Russian. Uses 7.62mm rounds." - icon_state = "SVD" - item_state = "SVD" - w_class = ITEMSIZE_HUGE // So it can't fit in a backpack. - force = 10 - slot_flags = SLOT_BACK // Needs a sprite. - origin_tech = list(TECH_COMBAT = 8, TECH_MATERIAL = 2, TECH_ILLEGAL = 8) - recoil = 2 //extra kickback - caliber = "a762" - load_method = MAGAZINE - accuracy = -45 //shooting at the hip - scoped_accuracy = 0 - one_handed_penalty = 60 // The weapon itself is heavy, and the long barrel makes it hard to hold steady with just one hand. - fire_sound = 'sound/weapons/SVD_shot.ogg' - magazine_type = /obj/item/ammo_magazine/m762 - allowed_magazines = list(/obj/item/ammo_magazine/m762) - -/obj/item/weapon/gun/projectile/SVD/update_icon() - ..() -// if(istype(ammo_magazine,/obj/item/ammo_magazine/m762) -// icon_state = "SVD-bigmag" //No icon for this exists yet. - if(ammo_magazine) - icon_state = "SVD" - else - icon_state = "SVD-empty" - -/obj/item/weapon/gun/projectile/SVD/verb/scope() - set category = "Object" - set name = "Use Scope" - set popup_menu = 1 - - toggle_scope(2.0) \ No newline at end of file diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index b3b70aa7af..3ed6564229 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -10,6 +10,8 @@ unacidable = TRUE pass_flags = PASSTABLE mouse_opacity = 0 + hitsound = 'sound/weapons/pierce.ogg' + var/hitsound_wall = null // Played when something hits a wall, or anything else that isn't a mob. ////TG PROJECTILE SYTSEM //Projectile stuff @@ -95,7 +97,7 @@ var/spread_submunition_damage = FALSE // Do we assign damage to our sub projectiles based on our main projectile damage? var/damage = 10 - var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID are the only things that should be in here + var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID, SEARING are the only things that should be in here var/SA_bonus_damage = 0 // Some bullets inflict extra damage on simple animals. var/SA_vulnerability = null // What kind of simple animal the above bonus damage should be applied to. Set to null to apply to all SAs. var/nodamage = 0 //Determines if the projectile will skip any damage inflictions @@ -104,7 +106,7 @@ var/projectile_type = /obj/item/projectile var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate() //Effects - var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech + var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 for intense fire. - Mech var/flammability = 0 //Amount of fire stacks to add for the above. var/combustion = TRUE //Does this set off flammable objects on fire/hit? var/stun = 0 @@ -128,12 +130,18 @@ var/temporary_unstoppable_movement = FALSE + // When a non-hitscan projectile hits something, a visual effect can be spawned. + // This is distinct from the hitscan's "impact_type" var. + var/impact_effect_type = null + /obj/item/projectile/proc/Range() range-- if(range <= 0 && loc) on_range() /obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range + impact_sounds(loc) + impact_visuals(loc) // So it does a little 'burst' effect, but not actually do anything (unless overrided). qdel(src) /obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight. @@ -219,12 +227,8 @@ Range() /obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it. - //VOREStation Edit begin: SHADEKIN - var/mob/SK = AM - if(istype(SK)) - if(SK.shadekin_phasing_check()) - return - //VOREStation Edit end: SHADEKIN + if(AM.is_incorporeal()) + return ..() if(isliving(AM) && !(pass_flags & PASSMOB)) var/mob/living/L = AM @@ -448,10 +452,14 @@ qdel(beam_index) /obj/item/projectile/proc/vol_by_damage() - if(damage) - return CLAMP((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100 + if(damage || agony) + var/value_to_use = damage > agony ? damage : agony + // Multiply projectile damage by 1.2, then CLAMP the value between 30 and 100. + // This was 0.67 but in practice it made all projectiles that did 45 or less damage play at 30, + // which is hard to hear over the gunshots, and is rather rare for a projectile to do that much. + return CLAMP((value_to_use) * 1.2, 30, 100) else - return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume. + return 50 //if the projectile doesn't do damage or agony, play its hitsound at 50% volume. /obj/item/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE) if(trajectory && beam_index) @@ -592,6 +600,9 @@ //called when the projectile stops flying because it Bump'd with something /obj/item/projectile/proc/on_impact(atom/A) + impact_sounds(A) + impact_visuals(A) + if(damage && damage_type == BURN) var/turf/T = get_turf(A) if(T) @@ -633,16 +644,27 @@ def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part result = target_mob.bullet_act(src, def_zone) + if(!istype(target_mob)) + return FALSE // Mob deleted itself or something. + if(result == PROJECTILE_FORCE_MISS) if(!silenced) - visible_message("\The [src] misses [target_mob] narrowly!") + target_mob.visible_message("\The [src] misses \the [target_mob] narrowly!") + playsound(target_mob, "bullet_miss", 75, 1) return FALSE //hit messages if(silenced) - to_chat(target_mob, "You've been hit in the [parse_zone(def_zone)] by \the [src]!") + playsound(target_mob, hitsound, 5, 1, -1) + to_chat(target_mob, span("critical", "You've been hit in the [parse_zone(def_zone)] by \the [src]!")) else - visible_message("\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter + var/volume = vol_by_damage() + playsound(target_mob, hitsound, volume, 1, -1) + // X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter + target_mob.visible_message( + span("danger", "\The [target_mob] was hit in the [parse_zone(def_zone)] by \the [src]!"), + span("critical", "You've been hit in the [parse_zone(def_zone)] by \the [src]!") + ) //admin logs if(!no_attack_log) @@ -747,3 +769,28 @@ preparePixelProjectile(target, get_turf(src), params, forced_spread) return fire(angle_override, direct_target) + +// Makes a brief effect sprite appear when the projectile hits something solid. +/obj/item/projectile/proc/impact_visuals(atom/A, hit_x, hit_y) + if(impact_effect_type && !hitscan) // Hitscan things have their own impact sprite. + if(isnull(hit_x) && isnull(hit_y)) + if(trajectory) + // Effect goes where the projectile 'stopped'. + hit_x = A.pixel_x + trajectory.return_px() + hit_y = A.pixel_y + trajectory.return_py() + else if(A == original) + // Otherwise it goes where the person who fired clicked. + hit_x = A.pixel_x + p_x - 16 + hit_y = A.pixel_y + p_y - 16 + else + // Otherwise it'll be random. + hit_x = A.pixel_x + rand(-8, 8) + hit_y = A.pixel_y + rand(-8, 8) + new impact_effect_type(get_turf(A), src, hit_x, hit_y) + +/obj/item/projectile/proc/impact_sounds(atom/A) + if(hitsound_wall && !ismob(A)) // Mob sounds are handled in attack_mob(). + var/volume = CLAMP(vol_by_damage() + 20, 0, 100) + if(silenced) + volume = 5 + playsound(get_turf(A), hitsound_wall, volume, 1, -1) diff --git a/code/modules/projectiles/projectile/arc.dm b/code/modules/projectiles/projectile/arc.dm index 1f19dc0242..12f135a9bb 100644 --- a/code/modules/projectiles/projectile/arc.dm +++ b/code/modules/projectiles/projectile/arc.dm @@ -49,7 +49,6 @@ on_impact(loc) return ..() - /obj/item/projectile/arc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) fired_dir = get_dir(user, target) // Used to determine if the projectile should turn in the air. distance_to_fly = calculate_initial_pixel_distance(user, target) // Calculates how many pixels to travel before hitting the ground. @@ -168,3 +167,34 @@ /obj/item/projectile/arc/radioactive/on_impact(turf/T) SSradiation.radiate(T, rad_power) + +// Blob mortar +/obj/item/projectile/arc/spore + name = "spore" + icon_state = "declone" + damage = 20 + damage_type = BIOACID + armor_penetration = 30 + fire_sound = 'sound/effects/slime_squish.ogg' + +/obj/item/projectile/arc/spore/on_impact(turf/T) + for(var/mob/living/L in T) + attack_mob(L) + + spawn() + T.visible_message("\The [src] covers \the [T] in a corrosive paste!") + for(var/turf/simulated/floor/F in view(2, T)) + spawn() + var/obj/effect/effect/water/splash = new(T) + splash.create_reagents(15) + splash.reagents.add_reagent("stomacid", 5) + splash.reagents.add_reagent("blood", 10,list("blood_colour" = "#ec4940")) + splash.set_color() + + splash.set_up(F, 2, 3) + + var/obj/effect/decal/cleanable/chemcoating/acid = locate() in T + if(!istype(acid)) + acid = new(T) + acid.reagents.add_reagent("stomacid", 5) + acid.update_icon() diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index b00fa8221a..41430bea38 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -14,6 +14,8 @@ light_range = 2 light_power = 0.5 light_color = "#FF0D00" + hitsound = 'sound/weapons/sear.ogg' + hitsound_wall = 'sound/weapons/effects/searwall.ogg' muzzle_type = /obj/effect/projectile/muzzle/laser tracer_type = /obj/effect/projectile/tracer/laser @@ -211,6 +213,7 @@ agony = 40 damage_type = HALLOSS light_color = "#FFFFFF" + hitsound = 'sound/weapons/zapbang.ogg' combustion = FALSE @@ -257,3 +260,4 @@ damage = 30 agony = 15 eyeblur = 2 + hitsound = 'sound/weapons/zapbang.ogg' diff --git a/code/modules/projectiles/projectile/blob.dm b/code/modules/projectiles/projectile/blob.dm index 7ebc5cdfad..f73fe9d008 100644 --- a/code/modules/projectiles/projectile/blob.dm +++ b/code/modules/projectiles/projectile/blob.dm @@ -28,9 +28,9 @@ /obj/item/projectile/energy/blob/on_impact(var/atom/A) if(splatter) var/turf/location = get_turf(src) - var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem + var/datum/effect/effect/system/smoke_spread/chem/blob/S = new /datum/effect/effect/system/smoke_spread/chem/blob S.attach(location) - S.set_up(reagents, splatter_volume, 0, location) + S.set_up(reagents, rand(1, splatter_volume), 0, location) playsound(location, 'sound/effects/slime_squish.ogg', 30, 1, -3) spawn(0) S.start() @@ -70,3 +70,12 @@ /obj/item/projectile/energy/blob/freezing/splattering splatter = TRUE + +/obj/item/projectile/bullet/thorn + name = "spike" + icon_state = "SpearFlight" + damage = 20 + damage_type = BIOACID + armor_penetration = 20 + penetrating = 3 + fire_sound = 'sound/effects/slime_squish.ogg' diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 8220e90eee..e05739d194 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -8,6 +8,8 @@ check_armour = "bullet" embed_chance = 20 //Modified in the actual embed process, but this should keep embed chance about the same sharp = 1 + hitsound_wall = "ricochet" + impact_effect_type = /obj/effect/temp_visual/impact_effect var/mob_passthrough_check = 0 muzzle_type = /obj/effect/projectile/muzzle/bullet @@ -245,7 +247,7 @@ icon_state = "bullet_alt" damage = 15 damage_type = BURN - incendiary = 1 + incendiary = 0.5 flammability = 2 /obj/item/projectile/bullet/incendiary/flamethrower @@ -261,9 +263,20 @@ vacuum_traversal = 0 /obj/item/projectile/bullet/incendiary/flamethrower/large - damage = 15 + damage = 5 + incendiary = 3 + flammability = 2 range = 6 +/obj/item/projectile/bullet/incendiary/flamethrower/tiny + damage = 2 + incendiary = 0 + flammability = 2 + modifier_type_to_apply = /datum/modifier/fire/stack_managed/weak + modifier_duration = 20 SECONDS + range = 6 + agony = 0 + /* Practice rounds and blanks */ /obj/item/projectile/bullet/practice diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index 9c30436930..d2cb729644 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -92,10 +92,10 @@ else new_mob.key = M.key - new_mob << "Your form morphs into that of \a [lowertext(randomize)]." + to_chat(new_mob, "Your form morphs into that of \a [lowertext(randomize)].") qdel(M) return else - M << "Your form morphs into that of \a [lowertext(randomize)]." + to_chat(M, "Your form morphs into that of \a [lowertext(randomize)].") return \ No newline at end of file diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index fcbc10f109..132ac6ccf4 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -4,6 +4,11 @@ damage = 0 damage_type = BURN check_armour = "energy" + + impact_effect_type = /obj/effect/temp_visual/impact_effect + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + hitsound = 'sound/weapons/zapbang.ogg' + var/flash_strength = 10 //releases a burst of light on impact or after travelling a distance @@ -11,6 +16,7 @@ name = "chemical shell" icon_state = "bullet" fire_sound = 'sound/weapons/gunshot_pathetic.ogg' + hitsound_wall = null damage = 5 range = 15 //if the shell hasn't hit anything after travelling this far it just explodes. var/flash_range = 0 @@ -91,6 +97,7 @@ light_range = 2 light_power = 0.5 light_color = "#33CC00" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser combustion = FALSE @@ -157,6 +164,7 @@ light_range = 2 light_power = 0.5 light_color = "#33CC00" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser combustion = FALSE @@ -209,10 +217,11 @@ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE damage_type = BURN check_armour = "energy" - light_color = "#0000FF" + light_color = "#00AAFF" embed_chance = 0 muzzle_type = /obj/effect/projectile/muzzle/pulse + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/phase name = "phase wave" diff --git a/code/modules/projectiles/projectile/energy_yw.dm b/code/modules/projectiles/projectile/energy_yw.dm index 4e6c5eb923..0d13d1eec8 100644 --- a/code/modules/projectiles/projectile/energy_yw.dm +++ b/code/modules/projectiles/projectile/energy_yw.dm @@ -2,7 +2,7 @@ //Gauss beam for gaussguns -RadiantFlash// /obj/item/projectile/energy/gauss name = "gauss bolt" - icon = 'icons/obj/projectiles_yw.dmi' + icon = 'icons/obj/projectiles.dmi' icon_state = "gauss" damage = 35 stun = 0 @@ -14,7 +14,7 @@ /obj/item/projectile/energy/gaussweak. name = "gauss bolt" - icon = 'icons/obj/projectiles_yw.dmi' + icon = 'icons/obj/projectiles.dmi' icon_state = "gauss" damage = 25 stun = 0 @@ -26,7 +26,7 @@ /obj/item/projectile/energy/gaussrifle name = "gauss bolt" - icon = 'icons/obj/projectiles_yw.dmi' + icon = 'icons/obj/projectiles.dmi' icon_state = "gauss" damage = 60 stun = 2 @@ -56,4 +56,4 @@ damage = 25 damage_type = BURN pass_flags = PASSGRILLE - check_armour = "laser" \ No newline at end of file + check_armour = "laser" diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm index 550aab6113..d03de505b5 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/force.dm @@ -7,6 +7,9 @@ combustion = FALSE + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + /obj/item/projectile/forcebolt/strong name = "force bolt" diff --git a/code/modules/projectiles/projectile/magnetic.dm b/code/modules/projectiles/projectile/magnetic.dm index 9e76c3551f..4c36476ddb 100644 --- a/code/modules/projectiles/projectile/magnetic.dm +++ b/code/modules/projectiles/projectile/magnetic.dm @@ -36,13 +36,16 @@ damage = 30 damage_type = SEARING embed_chance = 0 + armor_penetration = 40 + penetrating = 1 /obj/item/projectile/bullet/magnetic/heated/weak icon_state = "gauss_silenced" damage = 15 agony = 5 embed_chance = 0 - armor_penetration = 50 + armor_penetration = 30 + penetrating = 0 /obj/item/projectile/bullet/magnetic/fuelrod name = "fuel rod" diff --git a/code/modules/projectiles/projectile/scatter.dm b/code/modules/projectiles/projectile/scatter.dm index 0aa6ad5719..4b0511d0b4 100644 --- a/code/modules/projectiles/projectile/scatter.dm +++ b/code/modules/projectiles/projectile/scatter.dm @@ -60,3 +60,13 @@ submunitions = list( /obj/item/projectile/bullet/shotgun/ion = 3 ) + +/obj/item/projectile/scatter/flamethrower + damage = 5 + submunition_spread_max = 100 + submunition_spread_min = 30 + force_max_submunition_spread = TRUE + + submunitions = list( + /obj/item/projectile/bullet/incendiary/flamethrower/tiny = 7 + ) diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index 78e3f4f46b..8f81ed16cd 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -11,6 +11,9 @@ light_color = "#55AAFF" combustion = FALSE + impact_effect_type = /obj/effect/temp_visual/impact_effect/ion + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + hitsound = 'sound/weapons/ionrifle.ogg' var/sev1_range = 0 var/sev2_range = 1 @@ -18,8 +21,8 @@ var/sev4_range = 1 /obj/item/projectile/ion/on_impact(var/atom/target) - empulse(target, sev1_range, sev2_range, sev3_range, sev4_range) - return 1 + empulse(target, sev1_range, sev2_range, sev3_range, sev4_range) + ..() /obj/item/projectile/ion/small sev1_range = -1 @@ -58,6 +61,7 @@ light_range = 2 light_power = 0.5 light_color = "#55AAFF" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser combustion = FALSE @@ -133,6 +137,7 @@ light_range = 2 light_power = 0.5 light_color = "#33CC00" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser combustion = FALSE @@ -189,6 +194,7 @@ light_range = 2 light_power = 0.5 light_color = "#FFFFFF" + impact_effect_type = /obj/effect/temp_visual/impact_effect/monochrome_laser /obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) var/mob/M = target @@ -211,6 +217,7 @@ if(ishuman(target)) var/mob/living/carbon/human/M = target M.Confuse(rand(5,8)) + ..() /obj/item/projectile/chameleon name = "bullet" diff --git a/code/modules/projectiles/targeting/targeting_overlay.dm b/code/modules/projectiles/targeting/targeting_overlay.dm index 2c7f75cc96..9bce28831f 100644 --- a/code/modules/projectiles/targeting/targeting_overlay.dm +++ b/code/modules/projectiles/targeting/targeting_overlay.dm @@ -72,7 +72,7 @@ else return - owner << "[aiming_at ? "\The [aiming_at] is" : "Your targets are"] [message]." + to_chat(owner, "[aiming_at ? "The [aiming_at] is" : "Your targets are"] [message].") if(aiming_at) to_chat(aiming_at, "You are [message].") diff --git a/code/modules/projectiles/targeting/targeting_triggers.dm b/code/modules/projectiles/targeting/targeting_triggers.dm index 188440dcf6..3563727fc1 100644 --- a/code/modules/projectiles/targeting/targeting_triggers.dm +++ b/code/modules/projectiles/targeting/targeting_triggers.dm @@ -21,7 +21,7 @@ return owner.setClickCooldown(5) // Spam prevention, essentially. if(owner.a_intent == I_HELP && owner.is_preference_enabled(/datum/client_preference/safefiring)) - owner << "You refrain from firing \the [aiming_with] as your intent is set to help." + to_chat(owner, "You refrain from firing \the [aiming_with] as your intent is set to help.") return owner.visible_message("\The [owner] pulls the trigger reflexively!") var/obj/item/weapon/gun/G = aiming_with diff --git a/code/modules/random_map/automata/diona.dm b/code/modules/random_map/automata/diona.dm index e9e58a03a4..d6bee1c0ff 100644 --- a/code/modules/random_map/automata/diona.dm +++ b/code/modules/random_map/automata/diona.dm @@ -1,5 +1,5 @@ -/turf/simulated/wall/diona/New(var/newloc) - ..(newloc,"biomass") +/turf/simulated/wall/diona/Initialize(mapload) + ..(mapload, "biomass") /turf/simulated/wall/diona/attack_generic(var/mob/user, var/damage, var/attack_message) if(istype(user, /mob/living/carbon/alien/diona)) diff --git a/code/modules/random_map/drop/droppod.dm b/code/modules/random_map/drop/droppod.dm index 1d5fcb8d8f..454eb1cc04 100644 --- a/code/modules/random_map/drop/droppod.dm +++ b/code/modules/random_map/drop/droppod.dm @@ -177,7 +177,7 @@ candidates |= player if(!candidates.len) - usr << "There are no candidates for a drop pod launch." + to_chat(usr, "There are no candidates for a drop pod launch.") return // Get a player and a mob type. diff --git a/code/modules/random_map/drop/droppod_doors.dm b/code/modules/random_map/drop/droppod_doors.dm index 7009a5885b..7c7075f45f 100644 --- a/code/modules/random_map/drop/droppod_doors.dm +++ b/code/modules/random_map/drop/droppod_doors.dm @@ -10,11 +10,10 @@ var/deploying var/deployed -/obj/structure/droppod_door/New(var/newloc, var/autoopen) - ..(newloc) +/obj/structure/droppod_door/Initialize(var/mapload, var/autoopen) + . = ..() if(autoopen) - spawn(100) - deploy() + addtimer(CALLBACK(src, .proc/deploy), 10 SECONDS) /obj/structure/droppod_door/attack_ai(var/mob/user) if(!user.Adjacent(src)) @@ -26,7 +25,7 @@ /obj/structure/droppod_door/attack_hand(var/mob/user) if(deploying) return - user << "You prime the explosive bolts. Better get clear!" + to_chat(user, "You prime the explosive bolts. Better get clear!") sleep(30) deploy() diff --git a/code/modules/random_map/random_map.dm b/code/modules/random_map/random_map.dm index 72d52ae4d0..d36e7a80a1 100644 --- a/code/modules/random_map/random_map.dm +++ b/code/modules/random_map/random_map.dm @@ -105,7 +105,7 @@ var/global/list/map_count = list() if(current_cell) dat += get_map_char(map[current_cell]) dat += "
    " - user << "[dat]+------+" + to_chat(user, "[dat]+------+") /datum/random_map/proc/set_map_size() map = list() diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index 9cbf00ddc6..230bfb4bab 100644 --- a/code/modules/reagents/Chemistry-Machinery.dm +++ b/code/modules/reagents/Chemistry-Machinery.dm @@ -16,9 +16,9 @@ icon = 'icons/obj/chemical.dmi' icon_state = "mixer0" circuit = /obj/item/weapon/circuitboard/chem_master - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 20 - var/beaker = null + var/obj/item/weapon/reagent_containers/beaker = null var/obj/item/weapon/storage/pill_bottle/loaded_pill_bottle = null var/mode = 0 var/condi = 0 @@ -105,7 +105,7 @@ data["pillBottle"] = null if(beaker) - var/datum/reagents/R = beaker:reagents + var/datum/reagents/R = beaker.reagents var/ui_reagent_beaker_list[0] for(var/datum/reagent/G in R.reagent_list) ui_reagent_beaker_list[++ui_reagent_beaker_list.len] = list("name" = G.name, "volume" = G.volume, "description" = G.description, "id" = G.id) @@ -160,11 +160,15 @@ if (href_list["ejectp"]) if(loaded_pill_bottle) - loaded_pill_bottle.loc = src.loc + loaded_pill_bottle.forceMove(get_turf(src)) + + if(Adjacent(usr)) + usr.put_in_hands(loaded_pill_bottle) + loaded_pill_bottle = null if(beaker) - var/datum/reagents/R = beaker:reagents + var/datum/reagents/R = beaker.reagents if (tab == "analyze") analyze_data["name"] = href_list["name"] analyze_data["desc"] = href_list["desc"] @@ -216,7 +220,11 @@ else if (href_list["eject"]) if(beaker) - beaker:loc = src.loc + beaker.forceMove(get_turf(src)) + + if(Adjacent(usr)) // So the AI doesn't get a beaker somehow. + usr.put_in_hands(beaker) + beaker = null reagents.clear_reagents() icon_state = "mixer0" @@ -333,7 +341,7 @@ icon_state = "juicer1" density = 0 anchored = 0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 5 active_power_usage = 100 circuit = /obj/item/weapon/circuitboard/grinder diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm index 1e46a5870a..b7880163d1 100644 --- a/code/modules/reagents/Chemistry-Reagents.dm +++ b/code/modules/reagents/Chemistry-Reagents.dm @@ -125,7 +125,8 @@ affect_ingest(M, alien, removed * ingest_abs_mult) if(CHEM_TOUCH) affect_touch(M, alien, removed) - if(overdose && (volume > overdose) && (active_metab.metabolism_class != CHEM_TOUCH && !can_overdose_touch)) + if(overdose && (volume > overdose * M.species.chemOD_threshold) && (active_metab.metabolism_class != CHEM_TOUCH && !can_overdose_touch)) //YW EDIT + overdose(M, alien, removed) remove_self(removed) return @@ -190,4 +191,3 @@ /datum/reagent/proc/reaction_mob(var/mob/target) touch_mob(target) - diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm index 633b3828c3..2d565c9692 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm @@ -175,10 +175,10 @@ S.visible_message("[S]'s flesh sizzles where the water touches it!", "Your flesh burns in the water!") // Then extinguish people on fire. - var/needed = L.fire_stacks * 5 + var/needed = max(0,L.fire_stacks) * 5 if(amount > needed) L.ExtinguishMob() - L.adjust_fire_stacks(-(amount / 5)) + L.water_act(amount / 25) // Div by 25, as water_act multiplies it by 5 in order to calculate firestack modification. remove_self(needed) //YWedit start, readds promethean damage that was removed by vorestation. /datum/reagent/water/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm index 19347d82a0..da26bb38aa 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Dispenser.dm @@ -94,7 +94,7 @@ /datum/reagent/ethanol/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) //This used to do just toxin. That's boring. Let's make this FUN. if(issmall(M)) removed *= 2 - var/strength_mod = 3 //Alcohol is 3x stronger when injected into the veins. + var/strength_mod = 3 * M.species.alcohol_tolerance //YW EDIT: multiply by inherent tolerance first, I don't want to strip out the following block yet if(alien == IS_SKRELL) strength_mod *= 5 if(alien == IS_TAJARA) @@ -139,7 +139,7 @@ /datum/reagent/ethanol/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) if(issmall(M)) removed *= 2 M.nutrition += nutriment_factor * removed - var/strength_mod = 1 + var/strength_mod = 1 * M.species.alcohol_tolerance //YW EDIT: multiply by inherent tolerance first, I don't want to strip out the following block yet if(alien == IS_SKRELL) strength_mod *= 5 if(alien == IS_TAJARA) @@ -184,17 +184,17 @@ if(istype(O, /obj/item/weapon/paper)) var/obj/item/weapon/paper/paperaffected = O paperaffected.clearpaper() - usr << "The solution dissolves the ink on the paper." + to_chat(usr, "The solution dissolves the ink on the paper.") return if(istype(O, /obj/item/weapon/book)) if(volume < 5) return if(istype(O, /obj/item/weapon/book/tome)) - usr << "The solution does nothing. Whatever this is, it isn't normal ink." + to_chat(usr, "The solution does nothing. Whatever this is, it isn't normal ink.") return var/obj/item/weapon/book/affectedbook = O affectedbook.dat = null - usr << "The solution dissolves the ink on the book." + to_chat(usr, "The solution dissolves the ink on the book.") return /datum/reagent/fluorine @@ -356,11 +356,11 @@ var/mob/living/carbon/human/H = M if(H.head) if(H.head.unacidable) - H << "Your [H.head] protects you from the acid." + to_chat(H, "Your [H.head] protects you from the acid.") remove_self(volume) return else if(removed > meltdose) - H << "Your [H.head] melts away!" + to_chat(H, "Your [H.head] melts away!") qdel(H.head) H.update_inv_head(1) H.update_hair(1) @@ -370,11 +370,11 @@ if(H.wear_mask) if(H.wear_mask.unacidable) - H << "Your [H.wear_mask] protects you from the acid." + to_chat(H, "Your [H.wear_mask] protects you from the acid.") remove_self(volume) return else if(removed > meltdose) - H << "Your [H.wear_mask] melts away!" + to_chat(H, "Your [H.wear_mask] melts away!") qdel(H.wear_mask) H.update_inv_wear_mask(1) H.update_hair(1) @@ -384,10 +384,10 @@ if(H.glasses) if(H.glasses.unacidable) - H << "Your [H.glasses] partially protect you from the acid!" + to_chat(H, "Your [H.glasses] partially protect you from the acid!") removed /= 2 else if(removed > meltdose) - H << "Your [H.glasses] melt away!" + to_chat(H, "Your [H.glasses] melt away!") qdel(H.glasses) H.update_inv_glasses(1) removed -= meltdose / 2 @@ -418,7 +418,7 @@ var/obj/effect/decal/cleanable/molten_item/I = new/obj/effect/decal/cleanable/molten_item(O.loc) I.desc = "Looks like this was \an [O] some time ago." for(var/mob/M in viewers(5, O)) - M << "\The [O] melts." + to_chat(M, "\The [O] melts.") qdel(O) remove_self(meltdose) // 10 units of acid will not melt EVERYTHING on the tile diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm index 3fd9bc0598..f50a791550 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm @@ -134,6 +134,14 @@ nutriment_factor = 10 color = "#FFFFFF" +/datum/reagent/nutriment/yeast + name = "Yeast" + id = "yeast" + description = "For making bread rise!" + taste_description = "yeast" + nutriment_factor = 1 + color = "#D3AF70" + /datum/reagent/nutriment/flour name = "Flour" id = "flour" @@ -332,7 +340,7 @@ /datum/reagent/nutriment/durian/touch_mob(var/mob/M, var/amount) if(iscarbon(M) && !M.isSynthetic()) var/message = pick("Oh god, it smells disgusting here.", "What is that stench?", "That's an awful odor.") - to_chat(M,"[message]") + to_chat(M, "[message]") if(prob(CLAMP(amount, 5, 90))) var/mob/living/L = M L.vomit() @@ -476,7 +484,7 @@ return if(dose < 5 && (dose == metabolism || prob(5))) - M << "Your insides feel uncomfortably hot!" + to_chat(M, "Your insides feel uncomfortably hot!") if(dose >= 5) M.apply_effect(2, AGONY, 0) if(prob(5)) @@ -620,7 +628,7 @@ if(!H.can_feel_pain()) return if(dose == metabolism) - M << "You feel like your insides are burning!" + to_chat(M, "You feel like your insides are burning!") else M.apply_effect(4, AGONY, 0) if(prob(5)) @@ -1823,6 +1831,17 @@ glass_desc = "A fruity punch!" glass_special = list(DRINK_FIZZ) +/datum/reagent/drink/mintapplesparkle + name = "Mint Apple Sparkle" + id = "mintapplesparkle" + description = "Delicious appleade with a touch of mint." + taste_description = "minty apples" + color = "#FDDA98" + + glass_name = "mint apple sparkle" + glass_desc = "Delicious appleade with a touch of mint." + glass_special = list(DRINK_FIZZ) + /datum/reagent/drink/berrycordial name = "Berry Cordial" id = "berrycordial" diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks_vr.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks_vr.dm index 537aa3854d..28fd9dbaf4 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks_vr.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks_vr.dm @@ -92,7 +92,7 @@ H.feral -= removed * 3 // should calm them down quick, provided they're actually in a state to STAY calm. if (H.feral <=0) //check if they're unferalled H.feral = 0 - H << "Your mind starts to clear, soothed into a state of clarity as your senses return." + to_chat(H, "Your mind starts to clear, soothed into a state of clarity as your senses return.") log_and_message_admins("is no longer feral.", H) /datum/reagent/ethanol/monstertamer/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) @@ -104,10 +104,8 @@ M.nutrition += (alt_nutriment_factor * removed) -/* Yawnstation edit start - Removes gnome sounds on ingestion. /datum/reagent/nutriment/magicdust/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) ..() playsound(M.loc, 'sound/items/hooh.ogg', 50, 1, -1) if(prob(5)) to_chat(M, "You feel like you've been gnomed...") - Yawnstation edit end. */ diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm index a3239678a3..1d04cac590 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm @@ -51,7 +51,7 @@ scannable = 1 /datum/reagent/bicaridine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 if(alien != IS_DIONA) @@ -88,7 +88,7 @@ can_overdose_touch = TRUE /datum/reagent/bicaridine/topical/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 if(alien != IS_DIONA) @@ -96,7 +96,7 @@ M.adjustToxLoss(2 * removed) /datum/reagent/bicaridine/topical/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 if(alien != IS_DIONA) @@ -132,7 +132,7 @@ scannable = 1 /datum/reagent/kelotane/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.5 M.adjustBruteLoss(2 * removed) //Mends burns, but has negative effects with a Promethean's skeletal structure. @@ -151,7 +151,7 @@ scannable = 1 /datum/reagent/dermaline/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 if(alien != IS_DIONA) @@ -171,7 +171,7 @@ can_overdose_touch = TRUE /datum/reagent/dermaline/topical/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 if(alien != IS_DIONA) @@ -179,7 +179,7 @@ M.adjustToxLoss(2 * removed) /datum/reagent/dermaline/topical/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 if(alien != IS_DIONA) @@ -195,7 +195,7 @@ scannable = 1 /datum/reagent/dylovene/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.66 if(dose >= 15) @@ -291,10 +291,17 @@ reagent_state = LIQUID color = "#8040FF" scannable = 1 + //YW ADDITIONS START + overdose = REAGENTS_OVERDOSE * 4 //120 overdose +/datum/reagent/tricordrazine/overdose(var/mob/living/carbon/M, var/alien) + ..() + M.druggy = max(M.druggy, 5) + M.Confuse(5) + //YW ADDITIONS END /datum/reagent/tricordrazine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien != IS_DIONA) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.5 M.adjustOxyLoss(-3 * removed * chem_effective) @@ -317,7 +324,7 @@ /datum/reagent/tricorlidaze/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) if(alien != IS_DIONA) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.5 M.adjustOxyLoss(-2 * removed * chem_effective) @@ -354,7 +361,7 @@ /datum/reagent/cryoxadone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(M.bodytemperature < 170) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.25 to_chat(M, "It's cold. Something causes your cellular mass to harden occasionally, resulting in vibration.") @@ -379,7 +386,7 @@ /datum/reagent/clonexadone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(M.bodytemperature < 170) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) if(prob(10)) to_chat(M, "It's so cold. Something causes your cellular mass to harden sporadically, resulting in seizure-like twitching.") @@ -413,7 +420,7 @@ /datum/reagent/necroxadone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(M.bodytemperature < 170 || (M.stat == DEAD && M.has_modifier_of_type(/datum/modifier/bloodpump_corpse))) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) if(prob(10)) to_chat(M, "It's so cold. Something causes your cellular mass to harden sporadically, resulting in seizure-like twitching.") @@ -441,7 +448,7 @@ mrate_static = TRUE /datum/reagent/paracetamol/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 M.add_chemical_effect(CE_PAINKILLER, 25 * chem_effective) @@ -465,7 +472,7 @@ mrate_static = TRUE /datum/reagent/tramadol/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.8 M.add_chemical_effect(CE_SLOWDOWN, 1) @@ -488,7 +495,7 @@ mrate_static = TRUE /datum/reagent/oxycodone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 M.stuttering = min(50, max(0, M.stuttering + 5)) //If you can't feel yourself, and your main mode of speech is resonation, there's a problem. @@ -515,7 +522,7 @@ scannable = 1 /datum/reagent/synaptizine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_DIONA) return if(alien == IS_SLIME) @@ -568,7 +575,7 @@ /datum/reagent/alkysine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) return - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.25 if(M.brainloss >= 10) @@ -692,7 +699,7 @@ scannable = 1 /datum/reagent/respirodaxon/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/repair_strength = 1 + var/repair_strength = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) repair_strength = 0.6 if(ishuman(M)) @@ -723,7 +730,7 @@ scannable = 1 /datum/reagent/gastirodaxon/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/repair_strength = 1 + var/repair_strength = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) repair_strength = 0.6 if(ishuman(M)) @@ -754,7 +761,7 @@ scannable = 1 /datum/reagent/hepanephrodaxon/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/repair_strength = 1 + var/repair_strength = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) repair_strength = 0.4 if(ishuman(M)) @@ -769,7 +776,7 @@ if(prob(5)) H.vomit(1) else if(prob(5)) - to_chat(H,"Something churns inside you.") + to_chat(H, "Something churns inside you.") H.adjustToxLoss(10 * removed) H.vomit(0, 1) else @@ -787,7 +794,7 @@ scannable = 1 /datum/reagent/cordradaxon/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/repair_strength = 1 + var/repair_strength = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) repair_strength = 0.6 if(ishuman(M)) @@ -814,7 +821,7 @@ scannable = 1 /datum/reagent/immunosuprizine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/strength_mod = 1 + var/strength_mod = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_DIONA) // It's a tree. strength_mod = 0.25 @@ -871,7 +878,7 @@ scannable = 1 /datum/reagent/skrellimmuno/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/strength_mod = 0.5 + var/strength_mod = 0.5 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SKRELL) strength_mod = 1 @@ -977,7 +984,7 @@ color = "#605048" overdose = REAGENTS_OVERDOSE -/datum/reagent/ethylredoxrazine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) +/datum/reagent/ethylredoxrazine/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) return M.dizziness = 0 @@ -988,6 +995,14 @@ for(var/datum/reagent/R in M.ingested.reagent_list) if(istype(R, /datum/reagent/ethanol)) R.remove_self(removed * 30) + +/datum/reagent/ethylredoxrazine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + if(alien == IS_DIONA) + return + M.dizziness = 0 + M.drowsyness = 0 + M.stuttering = 0 + M.SetConfused(0) if(M.bloodstr) for(var/datum/reagent/R in M.bloodstr.reagent_list) if(istype(R, /datum/reagent/ethanol)) @@ -1300,11 +1315,11 @@ return if(volume <= 0.1 && data != -1) data = -1 - M << "You lose focus..." + to_chat(M, "You lose focus...") else if(world.time > data + ANTIDEPRESSANT_MESSAGE_DELAY) data = world.time - M << "Your mind feels focused and undivided." + to_chat(M, "Your mind feels focused and undivided.") /datum/reagent/citalopram name = "Citalopram" @@ -1323,11 +1338,11 @@ return if(volume <= 0.1 && data != -1) data = -1 - M << "Your mind feels a little less stable..." + to_chat(M, "Your mind feels a little less stable...") else if(world.time > data + ANTIDEPRESSANT_MESSAGE_DELAY) data = world.time - M << "Your mind feels stable... a little stable." + to_chat(M, "Your mind feels stable... a little stable.") /datum/reagent/paroxetine name = "Paroxetine" @@ -1346,14 +1361,14 @@ return if(volume <= 0.1 && data != -1) data = -1 - M << "Your mind feels much less stable..." + to_chat(M, "Your mind feels much less stable...") else if(world.time > data + ANTIDEPRESSANT_MESSAGE_DELAY) data = world.time if(prob(90)) - M << "Your mind feels much more stable." + to_chat(M, "Your mind feels much more stable.") else - M << "Your mind breaks apart..." + to_chat(M, "Your mind breaks apart...") M.hallucination += 200 /datum/reagent/qerr_quem @@ -1395,3 +1410,14 @@ M.adjustOxyLoss(-4 * removed) M.adjustToxLoss(-2 * removed) M.adjustCloneLoss(-2 * removed) + +/datum/reagent/menthol + name = "Menthol" + id = "menthol" + description = "Tastes naturally minty, and imparts a very mild numbing sensation." + taste_description = "mint" + reagent_state = LIQUID + color = "#80af9c" + metabolism = REM * 0.002 + overdose = REAGENTS_OVERDOSE * 0.25 + scannable = 1 diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine_vr.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine_vr.dm index 63f8033636..a1497dfa7b 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine_vr.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine_vr.dm @@ -72,7 +72,7 @@ scannable = 1 /datum/reagent/vermicetol/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/chem_effective = 1 + var/chem_effective = 1 * M.species.chem_strength_heal //YW EDIT if(alien == IS_SLIME) chem_effective = 0.75 if(alien != IS_DIONA) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm index d9a7483ec1..cd2e6767b4 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Modifiers.dm @@ -12,12 +12,13 @@ metabolism = REM var/modifier_to_add = /datum/modifier/berserk - var/modifier_duration = 2 SECONDS // How long, per unit dose, will this last? + var/modifier_duration = 3 SECONDS // How long, per unit dose, will this last? + // 2 SECONDS is the resolution of life code, and the modifier will expire before chemical processing tries to re-add it /datum/reagent/modapplying/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) return - M.add_modifier(modifier_to_add, dose * modifier_duration) + M.add_modifier(modifier_to_add, modifier_duration, suppress_failure = TRUE) /datum/reagent/modapplying/cryofluid name = "cryogenic slurry" diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index 971a54bff2..9171691d58 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -14,6 +14,7 @@ var/skin_danger = 0.2 // The multiplier for how effective the toxin is when making skin contact. /datum/reagent/toxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) + strength *= M.species.chem_strength_tox // YW ADD (e.g. basic toxin and 1.5 = 6 damage/u, but amatox or carptox are 15/u) if(strength && alien != IS_DIONA) if(issmall(M)) removed *= 2 // Small bodymass, more effect from lower volume. if(alien == IS_SLIME) @@ -450,11 +451,11 @@ if(alien == IS_DIONA) return if(prob(10)) - to_chat(M,"Your veins feel like they're on fire!") + to_chat(M, "Your veins feel like they're on fire!") M.adjust_fire_stacks(0.1) else if(prob(5)) M.IgniteMob() - to_chat(M,"Some of your veins rupture, the exposed blood igniting!") + to_chat(M, "Some of your veins rupture, the exposed blood igniting!") /datum/reagent/condensedcapsaicin/venom name = "Irritant toxin" @@ -472,7 +473,7 @@ if(prob(50)) M.apply_effect(4, AGONY, 0) if(prob(20)) - to_chat(M,"You feel like your insides are burning!") + to_chat(M, "You feel like your insides are burning!") else if(prob(20)) M.visible_message("[M] [pick("dry heaves!","coughs!","splutters!","rubs at their eyes!")]") else @@ -572,7 +573,7 @@ M.UpdateAppearance() if(prob(removed * 40)) //Additionally, let's make it so there's an 8% chance per tick for a random cosmetic/not guranteed good/bad mutation. randmuti(M)//This should equate to 4 random cosmetic mutations per 10 injected/20 ingested/30 touching units - M << "You feel odd!" + to_chat(M, "You feel odd!") M.apply_effect(10 * removed, IRRADIATE, 0) /datum/reagent/slimejelly @@ -595,7 +596,7 @@ M.add_chemical_effect(CE_PAINKILLER, 60) else if(prob(10)) - M << "Your insides are burning!" + to_chat(M, "Your insides are burning!") M.adjustToxLoss(rand(100, 300) * removed) else if(prob(40)) M.heal_organ_damage(25 * removed, 0) @@ -615,7 +616,7 @@ if(alien == IS_DIONA) return - var/threshold = 1 + var/threshold = 1 * M.species.chem_strength_tox // YW ADD if(alien == IS_SKRELL) threshold = 1.2 @@ -663,7 +664,7 @@ if(alien == IS_DIONA) return - var/threshold = 1 + var/threshold = 1 * M.species.chem_strength_tox // YW ADD if(alien == IS_SKRELL) threshold = 1.2 @@ -726,7 +727,7 @@ if(alien == IS_DIONA) return - var/drug_strength = 15 + var/drug_strength = 15 * M.species.chem_strength_tox // YW ADD if(alien == IS_SKRELL) drug_strength = drug_strength * 0.8 @@ -949,7 +950,7 @@ datum/reagent/talum_quem/affect_blood(var/mob/living/carbon/M, var/alien, var/re M.UpdateAppearance() if(prob(removed * 40)) randmuti(M) - M << "You feel odd!" + to_chat(M, "You feel odd!") M.apply_effect(16 * removed, IRRADIATE, 0) /datum/reagent/aslimetoxin @@ -979,7 +980,7 @@ datum/reagent/talum_quem/affect_blood(var/mob/living/carbon/M, var/alien, var/re M.UpdateAppearance() if(prob(removed * 40)) randmuti(M) - M << "You feel odd!" + to_chat(M, "You feel odd!") M.apply_effect(6 * removed, IRRADIATE, 0) /* diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index d709134b98..d76b731ee7 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -118,7 +118,7 @@ var/turf/T = get_turf(container) var/list/seen = viewers(4, T) for(var/mob/M in seen) - M.show_message("\icon[container] [mix_message]", 1) + M.show_message("[bicon(container)] [mix_message]", 1) playsound(T, reaction_sound, 80, 1) //obtains any special data that will be provided to the reaction products @@ -897,7 +897,7 @@ var/location = get_turf(holder.my_atom) for(var/mob/M in viewers(5, location)) - M << "The solution spews out foam!" + to_chat(M, "The solution spews out foam!") var/datum/effect/effect/system/foam_spread/s = new() s.set_up(created_volume, location, holder, 0) @@ -919,7 +919,7 @@ var/location = get_turf(holder.my_atom) for(var/mob/M in viewers(5, location)) - M << "The solution spews out a metalic foam!" + to_chat(M, "The solution spews out a metalic foam!") var/datum/effect/effect/system/foam_spread/s = new() s.set_up(created_volume, location, holder, 1) @@ -937,7 +937,7 @@ var/location = get_turf(holder.my_atom) for(var/mob/M in viewers(5, location)) - M << "The solution spews out a metalic foam!" + to_chat(M, "The solution spews out a metalic foam!") var/datum/effect/effect/system/foam_spread/s = new() s.set_up(created_volume, location, holder, 2) diff --git a/code/modules/reagents/Chemistry-Recipes_vr.dm b/code/modules/reagents/Chemistry-Recipes_vr.dm index 97782e61a2..fede57cab9 100644 --- a/code/modules/reagents/Chemistry-Recipes_vr.dm +++ b/code/modules/reagents/Chemistry-Recipes_vr.dm @@ -205,7 +205,20 @@ O.show_message(text("The slime core fizzles disappointingly."), 1) return - var/blocked = list(/obj/item/stack/material, /obj/item/stack/material/cyborg, /obj/item/stack/material/cyborg/plastic, /obj/item/stack/material/cyborg/plasteel, /obj/item/stack/material/cyborg/glass/reinforced, /obj/item/stack/material/cyborg/wood, /obj/item/stack/material/animalhide/human, /obj/item/stack/material/animalhide/corgi, /obj/item/stack/material/animalhide/cat, /obj/item/stack/material/animalhide/monkey, /obj/item/stack/material/animalhide/lizard , /obj/item/stack/material/animalhide/xeno, /obj/item/stack/material/cyborg, /obj/item/stack/material/cyborg/glass/reinforced) + var/blocked = list( + /obj/item/stack/material, //Technical stacks + /obj/item/stack/material/hairlesshide, //Useless leather production steps + /obj/item/stack/material/wetleather, + /obj/item/stack/material/algae/ten) //Why is this one even a separate thing + blocked += typesof(/obj/item/stack/material/cyborg) //Borg matter synths, should only exist in borgs + blocked += typesof(/obj/item/stack/material/animalhide) //Hides which are only used for leather production anyway + + var/rare_types = list( + /obj/item/stack/material/morphium, //Complex materials requiring Particle Smasher to create + /obj/item/stack/material/morphium/hull, + /obj/item/stack/material/valhollide, + /obj/item/stack/material/supermatter) + var/list/material = typesof(/obj/item/stack/material) - blocked playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) @@ -216,6 +229,8 @@ */ var/spawn_amount = rand(1,50) var/chosen = pick(material) + if(chosen in rare_types) + spawn_amount = rand(1,15) var/obj/item/stack/material/C = new chosen C.amount = spawn_amount C.loc = get_turf(holder.my_atom) @@ -258,7 +273,7 @@ playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) for(var/mob/living/M in range (get_turf(holder.my_atom), 7)) M.bodytemperature -= 140 - M << " You suddenly feel a chill!" + to_chat(M, " You suddenly feel a chill!") @@ -307,7 +322,7 @@ required_reagents = list("phoron" = 10, "bicaridine" = 10, "kelotane" = 10, "inaprovaline" = 10, "slimejelly" = 10) on_reaction(var/datum/reagents/holder, var/created_volume) for (var/mob/living/carbon/C in viewers(get_turf(holder.my_atom), null)) - C << "A wave of energy suddenly invigorates you." + to_chat(C, "A wave of energy suddenly invigorates you.") C.adjustBruteLoss(-25) C.adjustFireLoss(-25) C.adjustToxLoss(-25) @@ -323,10 +338,6 @@ required_reagents = list("phoron" = 20, "sugar" = 50, "lithium" = 50) //In case a xenobiologist is impatient and is willing to drain their dispenser resources, along with plasma! result_amount = 5 - - - -/* //VORESTATION AI TEMPORARY REMOVAL /datum/chemical_reaction/slimevore name = "Slime Vore" // Hostile vore mobs only id = "m_tele" @@ -335,12 +346,44 @@ result_amount = 1 on_reaction(var/datum/reagents/holder) var/mob_path = /mob/living/simple_mob - var/blocked = list( - /mob/living/simple_mob/hostile/mimic, - /mob/living/simple_mob/animal/space/alien/queen, - /mob/living/simple_mob/shadekin + var/blocked = list( //List of things we do NOT want to spawn + /mob/living/simple_mob, //Technical parent mobs + /mob/living/simple_mob/animal, + /mob/living/simple_mob/animal/passive, + /mob/living/simple_mob/animal/space, + /mob/living/simple_mob/blob, + /mob/living/simple_mob/mechanical, + /mob/living/simple_mob/mechanical/mecha, + /mob/living/simple_mob/slime, + /mob/living/simple_mob/vore, + /mob/living/simple_mob/vore/aggressive, + /mob/living/simple_mob/illusion, //Other technical mobs + /mob/living/simple_mob/animal/passive/crab/Coffee, //Unique pets/named mobs + /mob/living/simple_mob/animal/passive/cat/runtime, + /mob/living/simple_mob/animal/passive/cat/bones, + /mob/living/simple_mob/animal/passive/cat/tabiranth, + /mob/living/simple_mob/animal/passive/dog/corgi/puppy/Bockscar, + /mob/living/simple_mob/animal/passive/dog/corgi/Ian, + /mob/living/simple_mob/animal/passive/dog/corgi/Lisa, + /mob/living/simple_mob/animal/passive/dog/tamaskan/Spice, + /mob/living/simple_mob/animal/passive/fox/renault, + /mob/living/simple_mob/animal/passive/bird/azure_tit/tweeter, + /mob/living/simple_mob/animal/passive/bird/parrot/poly, + /mob/living/simple_mob/animal/sif/fluffy, + /mob/living/simple_mob/animal/sif/fluffy/silky, + /mob/living/simple_mob/animal/passive/snake/noodle, + /mob/living/simple_mob/slime/xenobio/rainbow/kendrick, + /mob/living/simple_mob/animal/space/space_worm, //Space Worm parts that aren't proper heads + /mob/living/simple_mob/animal/space/space_worm/head/severed, + /mob/living/simple_mob/animal/borer, //Event/player-control-only mobs + /mob/living/simple_mob/vore/hostile/morph )//exclusion list for things you don't want the reaction to create. - var/list/voremobs = typesof(mob_path) - mob_path - blocked // list of possible hostile mobs + blocked += typesof(/mob/living/simple_mob/mechanical/ward) //Wards that should be created with ward items, are mobs mostly on technicalities + blocked += typesof(/mob/living/simple_mob/construct) //Should only exist + blocked += typesof(/mob/living/simple_mob/vore/demon) //as player-controlled + blocked += typesof(/mob/living/simple_mob/shadekin) //and/or event things + blocked += typesof(/mob/living/simple_mob/horror) + var/list/voremobs = typesof(mob_path) - blocked // list of possible hostile mobs playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) /* Removed at some point, unsure what to replace with @@ -351,13 +394,13 @@ var/spawn_count = rand(1,3) for(var/i = 1, i <= spawn_count, i++) var/chosen = pick(voremobs) - var/mob/living/simple_mob/hostile/C = new chosen + var/mob/living/simple_mob/C = new chosen C.faction = "slimesummon" C.loc = get_turf(holder.my_atom) if(prob(50)) for(var/j = 1, j <= rand(1, 3), j++) step(C, pick(NORTH,SOUTH,EAST,WEST)) -*/ + /datum/chemical_reaction/food/syntiflesh required_reagents = list("blood" = 5, "clonexadone" = 1) diff --git a/code/modules/reagents/dispenser/cartridge.dm b/code/modules/reagents/dispenser/cartridge.dm index ddcd173cb7..6a8d01a240 100644 --- a/code/modules/reagents/dispenser/cartridge.dm +++ b/code/modules/reagents/dispenser/cartridge.dm @@ -23,13 +23,13 @@ /obj/item/weapon/reagent_containers/chem_disp_cartridge/examine(mob/user) ..() - user << "It has a capacity of [volume] units." + to_chat(user, "It has a capacity of [volume] units.") if(reagents.total_volume <= 0) - user << "It is empty." + to_chat(user, "It is empty.") else - user << "It contains [reagents.total_volume] units of liquid." + to_chat(user, "It contains [reagents.total_volume] units of liquid.") if(!is_open_container()) - user << "The cap is sealed." + to_chat(user, "The cap is sealed.") /obj/item/weapon/reagent_containers/chem_disp_cartridge/verb/verb_set_label(L as text) set name = "Set Cartridge Label" @@ -41,23 +41,23 @@ /obj/item/weapon/reagent_containers/chem_disp_cartridge/proc/setLabel(L, mob/user = null) if(L) if(user) - user << "You set the label on \the [src] to '[L]'." + to_chat(user, "You set the label on \the [src] to '[L]'.") label = L name = "[initial(name)] - '[L]'" else if(user) - user << "You clear the label on \the [src]." + to_chat(user, "You clear the label on \the [src].") label = "" name = initial(name) /obj/item/weapon/reagent_containers/chem_disp_cartridge/attack_self() ..() if (is_open_container()) - usr << "You put the cap on \the [src]." + to_chat(usr, "You put the cap on \the [src].") flags ^= OPENCONTAINER else - usr << "You take the cap off \the [src]." + to_chat(usr, "You take the cap off \the [src].") flags |= OPENCONTAINER /obj/item/weapon/reagent_containers/chem_disp_cartridge/afterattack(obj/target, mob/user , flag) @@ -68,28 +68,28 @@ target.add_fingerprint(user) if(!target.reagents.total_volume && target.reagents) - user << "\The [target] is empty." + to_chat(user, "\The [target] is empty.") return if(reagents.total_volume >= reagents.maximum_volume) - user << "\The [src] is full." + to_chat(user, "\The [src] is full.") return var/trans = target.reagents.trans_to(src, target:amount_per_transfer_from_this) - user << "You fill \the [src] with [trans] units of the contents of \the [target]." + to_chat(user, "You fill \the [src] with [trans] units of the contents of \the [target].") else if(target.is_open_container() && target.reagents) //Something like a glass. Player probably wants to transfer TO it. if(!reagents.total_volume) - user << "\The [src] is empty." + to_chat(user, "\The [src] is empty.") return if(target.reagents.total_volume >= target.reagents.maximum_volume) - user << "\The [target] is full." + to_chat(user, "\The [target] is full.") return var/trans = src.reagents.trans_to(target, amount_per_transfer_from_this) - user << "You transfer [trans] units of the solution to \the [target]." + to_chat(user, "You transfer [trans] units of the solution to \the [target].") else return ..() diff --git a/code/modules/reagents/dispenser/cartridge_presets_vr.dm b/code/modules/reagents/dispenser/cartridge_presets_vr.dm index 37d7514f03..ebec2278a0 100644 --- a/code/modules/reagents/dispenser/cartridge_presets_vr.dm +++ b/code/modules/reagents/dispenser/cartridge_presets_vr.dm @@ -3,4 +3,8 @@ ammonia spawn_reagent = "ammonia" diethylamine spawn_reagent = "diethylamine" plantbgone spawn_reagent = "plantbgone" - mutagen spawn_reagent = "mutagen" \ No newline at end of file + mutagen spawn_reagent = "mutagen" + + //Biochem + nutriment spawn_reagent = "nutriment" + protein spawn_reagent = "protein" \ No newline at end of file diff --git a/code/modules/reagents/dispenser/dispenser2.dm b/code/modules/reagents/dispenser/dispenser2.dm index b09c55d95b..391659762f 100644 --- a/code/modules/reagents/dispenser/dispenser2.dm +++ b/code/modules/reagents/dispenser/dispenser2.dm @@ -3,6 +3,7 @@ desc = "Automagically fabricates chemicals from electricity." icon = 'icons/obj/chemical.dmi' icon_state = "dispenser" + clicksound = "switch" var/list/spawn_cartridges = null // Set to a list of types to spawn one of each on New() @@ -14,7 +15,7 @@ var/accept_drinking = 0 var/amount = 30 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 100 anchored = 1 @@ -159,12 +160,16 @@ var/label = href_list["dispense"] if(cartridges[label] && container && container.is_open_container()) var/obj/item/weapon/reagent_containers/chem_disp_cartridge/C = cartridges[label] + playsound(src.loc, 'sound/machines/reagent_dispense.ogg', 25, 1) C.reagents.trans_to(container, amount) else if(href_list["ejectBeaker"]) if(container) - var/obj/item/weapon/reagent_containers/B = container - B.loc = loc + container.forceMove(get_turf(src)) + + if(Adjacent(usr)) // So the AI doesn't get a beaker somehow. + usr.put_in_hands(container) + container = null add_fingerprint(usr) diff --git a/code/modules/reagents/dispenser/dispenser_presets_vr.dm b/code/modules/reagents/dispenser/dispenser_presets_vr.dm index be95daa438..bf7f85688d 100644 --- a/code/modules/reagents/dispenser/dispenser_presets_vr.dm +++ b/code/modules/reagents/dispenser/dispenser_presets_vr.dm @@ -1,5 +1,6 @@ /obj/machinery/chemical_dispenser/xenoflora name = "xenoflora chem dispenser" + ui_title = "Xenoflora Chemical Dispenser" dispense_reagents = list( "water", "sugar", "ethanol", "radium", "ammonia", "diethylamine", "plantbgone", "mutagen", "calcium" ) @@ -15,4 +16,18 @@ /obj/item/weapon/reagent_containers/chem_disp_cartridge/plantbgone, /obj/item/weapon/reagent_containers/chem_disp_cartridge/mutagen, /obj/item/weapon/reagent_containers/chem_disp_cartridge/calcium + ) + +/obj/machinery/chemical_dispenser/biochemistry + name = "bioproduct dispenser" + ui_title = "Bioproduct Dispenser" + dispense_reagents = list( + "nutriment", "protein", "milk" + ) + +/obj/machinery/chemical_dispenser/biochemistry/full + spawn_cartridges = list( + /obj/item/weapon/reagent_containers/chem_disp_cartridge/nutriment, + /obj/item/weapon/reagent_containers/chem_disp_cartridge/protein, + /obj/item/weapon/reagent_containers/chem_disp_cartridge/milk ) \ No newline at end of file diff --git a/code/modules/reagents/distilling/distilling.dm b/code/modules/reagents/distilling/distilling.dm index b86bcc7435..78780a3543 100644 --- a/code/modules/reagents/distilling/distilling.dm +++ b/code/modules/reagents/distilling/distilling.dm @@ -6,7 +6,7 @@ /obj/machinery/portable_atmospherics/powered/reagent_distillery name = "chemical distillery" desc = "A complex machine utilizing state-of-the-art components to mix chemicals at different temperatures." - use_power = 1 + use_power = USE_POWER_IDLE icon = 'icons/obj/machines/reagent.dmi' icon_state = "distiller" @@ -108,6 +108,57 @@ ..() +/obj/machinery/portable_atmospherics/powered/reagent_distillery/examine(mob/user) + ..() + if(get_dist(user, src) < 3) + to_chat(user, "\The [src] is powered [on ? "on" : "off"].") + + to_chat(user, "\The [src]'s gauges read:") + if(!use_atmos) + to_chat(user, "- Target Temperature: [target_temp]") + to_chat(user, "- Temperature: [current_temp]") + + if(InputBeaker) + if(InputBeaker.reagents.reagent_list.len) + to_chat(user, "\The [src]'s input beaker holds [InputBeaker.reagents.total_volume] units of liquid.") + else + to_chat(user, "\The [src]'s input beaker is empty!") + + if(Reservoir.reagents.reagent_list.len) + to_chat(user, "\The [src]'s internal buffer holds [Reservoir.reagents.total_volume] units of liquid.") + else + to_chat(user, "\The [src]'s internal buffer is empty!") + + if(OutputBeaker) + if(OutputBeaker.reagents.reagent_list.len) + to_chat(user, "\The [src]'s output beaker holds [OutputBeaker.reagents.total_volume] units of liquid.") + else + to_chat(user, "\The [src]'s output beaker is empty!") + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/verb/toggle_power(mob/user = usr) + set name = "Toggle Distillery Heating" + set category = "Object" + set src in view(1) + + if(powered()) + on = !on + to_chat(user, "You turn \the [src] [on ? "on" : "off"].") + else + to_chat(user, " Nothing happens.") + +/obj/machinery/portable_atmospherics/powered/reagent_distillery/verb/toggle_mixing(mob/user = usr) + set name = "Start Distillery Mixing" + set category = "Object" + set src in view(1) + + to_chat(user, "You press \the [src]'s chamber agitator button.") + if(on) + visible_message("\The [src] rattles to life.") + Reservoir.reagents.handle_reactions() + else + spawn(1 SECOND) + to_chat(user, "Nothing happens..") + /obj/machinery/portable_atmospherics/powered/reagent_distillery/attack_hand(mob/user) var/list/options = list() options["examine"] = radial_examine @@ -138,9 +189,7 @@ examine(user) if("use") - if(powered()) - on = !on - to_chat(user, "You turn \the [src] [on ? "on" : "off"].") + toggle_power(user) if("inspect gauges") to_chat(user, "\The [src]'s gauges read:") @@ -149,13 +198,7 @@ to_chat(user, "- Temperature: [current_temp]") if("pulse agitator") - to_chat(user, "You press \the [src]'s chamber agitator button.") - if(on) - visible_message("\The [src] rattles to life.") - Reservoir.reagents.handle_reactions() - else - spawn(1 SECOND) - to_chat(user, "Nothing happens..") + toggle_mixing(user) if("eject input") if(InputBeaker) @@ -252,19 +295,36 @@ if(!powered()) on = FALSE - if(!on || (use_atmos && (!connected_port || avg_pressure < 1000))) + if(!on || (use_atmos && (!connected_port || (avg_pressure / avg_temp) < (1000 / T20C)))) // This mostly respects gas laws by ignoring volume but it should make it usable at low temps current_temp = round((current_temp + T20C) / 2) else if(on) if(!use_atmos) if(current_temp != round(target_temp)) - var/shift_mod = 0 - if(current_temp < target_temp) - shift_mod = 1 - else if(current_temp > target_temp) - shift_mod = -1 - current_temp = CLAMP(round((current_temp + 1 * shift_mod) + (rand(-5, 5) / 10)), min_temp, max_temp) + // Some horrible bastardized attempt at approximating the values of a logistic function, bounded by (max_temp, target_temp, min_temp) + // So we can attempt to estimate the change in temperature for this process() step + + // Apply inverse of the logistic function to fetch our x value + var/x = -1 * log((current_temp < target_temp ? (target_temp - min_temp) / (current_temp - min_temp) : (max_temp - target_temp) / (max_temp - current_temp)) - 1) + if(!x) + x = 0 // Keep null from propagating into the temp + + // Apply the derivative of the logistic function to get the slope + var/dy = (NUM_E ** (-1 * x)) / ((1 + (NUM_E ** (-1 * x))) ** 2) + + // Compute temperature diff, being farther from the target should result in larger steps + // IMPORTANT: If you want to tweak how quickly this changes, tweak this *10! + // As of initial testing, a *10 gives ~5-6 minutes to go from room temp to 500C (+/-0.5C) + var/temp_diff = (current_temp < target_temp ? dy * 10 * target_temp / current_temp : dy * -10 * current_temp / target_temp) + + current_temp = CLAMP(round((current_temp + temp_diff), 0.01), min_temp, max_temp) use_power(power_rating * CELLRATE) + + if(target_temp == round(current_temp, 1.0)) + current_temp = target_temp // Hard set it so we don't need to worry about exact decimals any more, after we've been keeping track of it all this time + playsound(src, 'sound/machines/ping.ogg', 50, 0) + src.visible_message("\The [src] pings as it reaches the target temperature.") + else if(connected_port && avg_pressure > 1000) current_temp = round((current_temp + avg_temp) / 2) else if(!run_pump) diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 8f6be28b80..7a6927f49d 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -38,15 +38,15 @@ return 0 if(!target.reagents || !target.reagents.total_volume) - user << "[target] is empty." + to_chat(user, "[target] is empty.") return 1 if(reagents && !reagents.get_free_space()) - user << "[src] is full." + to_chat(user, "[src] is full.") return 1 var/trans = target.reagents.trans_to_obj(src, target:amount_per_transfer_from_this) - user << "You fill [src] with [trans] units of the contents of [target]." + to_chat(user, "You fill [src] with [trans] units of the contents of [target].") return 1 /obj/item/weapon/reagent_containers/proc/standard_splash_mob(var/mob/user, var/mob/target) // This goes into afterattack @@ -54,11 +54,11 @@ return if(!reagents || !reagents.total_volume) - user << "[src] is empty." + to_chat(user, "[src] is empty.") return 1 if(target.reagents && !target.reagents.get_free_space()) - user << "[target] is full." + to_chat(user, "[target] is full.") return 1 var/contained = reagentlist() @@ -68,7 +68,7 @@ return 1 /obj/item/weapon/reagent_containers/proc/self_feed_message(var/mob/user) - user << "You eat \the [src]" + to_chat(user, "You eat \the [src]") /obj/item/weapon/reagent_containers/proc/other_feed_message_start(var/mob/user, var/mob/target) user.visible_message("[user] is trying to feed [target] \the [src]!") @@ -84,18 +84,18 @@ return 0 if(!reagents || !reagents.total_volume) - user << "\The [src] is empty." + to_chat(user, "\The [src] is empty.") return 1 if(target == user) if(istype(user, /mob/living/carbon/human)) var/mob/living/carbon/human/H = user if(!H.check_has_mouth()) - user << "Where do you intend to put \the [src]? You don't have a mouth!" + to_chat(user, "Where do you intend to put \the [src]? You don't have a mouth!") return var/obj/item/blocked = H.check_mouth_coverage() if(blocked) - user << "\The [blocked] is in the way!" + to_chat(user, "\The [blocked] is in the way!") return user.setClickCooldown(user.get_attack_speed(src)) //puts a limit on how fast people can eat/drink things @@ -104,14 +104,14 @@ feed_sound(user) return 1 else - if(istype(user, /mob/living/carbon/human)) + if(istype(target, /mob/living/carbon/human)) var/mob/living/carbon/human/H = target if(!H.check_has_mouth()) - user << "Where do you intend to put \the [src]? \The [H] doesn't have a mouth!" + to_chat(user, "Where do you intend to put \the [src]? \The [H] doesn't have a mouth!") return var/obj/item/blocked = H.check_mouth_coverage() if(blocked) - user << "\The [blocked] is in the way!" + to_chat(user, "\The [blocked] is in the way!") return other_feed_message_start(user, target) @@ -133,13 +133,13 @@ return 0 if(!reagents || !reagents.total_volume) - user << "[src] is empty." + to_chat(user, "[src] is empty.") return 1 if(!target.reagents.get_free_space()) - user << "[target] is full." + to_chat(user, "[target] is full.") return 1 var/trans = reagents.trans_to(target, amount_per_transfer_from_this) - user << "You transfer [trans] units of the solution to [target]." + to_chat(user, "You transfer [trans] units of the solution to [target].") return 1 diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index fe6ddb87dc..9611460f21 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -21,6 +21,7 @@ icon = 'icons/obj/bloodpack.dmi' icon_state = "empty" item_state = "bloodpack_empty" + drop_sound = 'sound/items/drop/food.ogg' volume = 200 var/label_text = "" diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index f8c51eef9e..ee081e16f6 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -70,29 +70,29 @@ return if(!reagent_volumes[reagent_ids[mode]]) - user << "The injector is empty." + to_chat(user, "The injector is empty.") return var/mob/living/carbon/human/H = M if(istype(H)) var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting) if(!affected) - user << "\The [H] is missing that limb!" + to_chat(user, "\The [H] is missing that limb!") return else if(affected.robotic >= ORGAN_ROBOT) - user << "You cannot inject a robotic limb." + to_chat(user, "You cannot inject a robotic limb.") return if(M.can_inject(user, 1, ignore_thickness = bypass_protection)) - user << "You inject [M] with the injector." - M << "You feel a tiny prick!" + to_chat(user, "You inject [M] with the injector.") + to_chat(M, "You feel a tiny prick!") if(M.reagents) var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]]) M.reagents.add_reagent(reagent_ids[mode], t) reagent_volumes[reagent_ids[mode]] -= t add_attack_logs(user, M, "Borg injected with [reagent_ids[mode]]") - user << "[t] units injected. [reagent_volumes[reagent_ids[mode]]] units remaining." + to_chat(user, "[t] units injected. [reagent_volumes[reagent_ids[mode]]] units remaining.") return /obj/item/weapon/reagent_containers/borghypo/attack_self(mob/user as mob) //Change the mode @@ -105,7 +105,7 @@ else t += "[reagent_names[i]]" t = "Available reagents: [t]." - user << t + to_chat(user,t) return @@ -116,7 +116,7 @@ playsound(loc, 'sound/effects/pop.ogg', 50, 0) mode = t var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]] - usr << "Synthesizer is now producing '[R.name]'." + to_chat(usr, "Synthesizer is now producing '[R.name]'.") /obj/item/weapon/reagent_containers/borghypo/examine(mob/user) if(!..(user, 2)) @@ -124,7 +124,7 @@ var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]] - user << "It is currently producing [R.name] and has [reagent_volumes[reagent_ids[mode]]] out of [volume] units left." + to_chat(user, "It is currently producing [R.name] and has [reagent_volumes[reagent_ids[mode]]] out of [volume] units left.") /obj/item/weapon/reagent_containers/borghypo/service name = "cyborg drink synthesizer" @@ -148,15 +148,15 @@ return if(!reagent_volumes[reagent_ids[mode]]) - user << "[src] is out of this reagent, give it some time to refill." + to_chat(user, "[src] is out of this reagent, give it some time to refill.") return if(!target.reagents.get_free_space()) - user << "[target] is full." + to_chat(user, "[target] is full.") return var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]]) target.reagents.add_reagent(reagent_ids[mode], t) reagent_volumes[reagent_ids[mode]] -= t - user << "You transfer [t] units of the solution to [target]." + to_chat(user, "You transfer [t] units of the solution to [target].") return diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm index ee551696cf..da0205f9ea 100644 --- a/code/modules/reagents/reagent_containers/dropper.dm +++ b/code/modules/reagents/reagent_containers/dropper.dm @@ -11,6 +11,7 @@ w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS volume = 5 + drop_sound = 'sound/items/drop/glass.ogg' /obj/item/weapon/reagent_containers/dropper/examine(var/mob/user) if(!..(user, 2)) diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index a22257a8e2..4d320cc86c 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -35,6 +35,7 @@ /mob/living/bot/medbot, /obj/item/weapon/storage/secure/safe, /obj/machinery/iv_drip, + /obj/structure/medical_stand, //VOREStation Add, /obj/machinery/disease2/incubator, /obj/machinery/disposal, /mob/living/simple_mob/animal/passive/cow, @@ -152,6 +153,7 @@ icon = 'icons/obj/chemical.dmi' icon_state = "beaker" item_state = "beaker" + center_of_mass = list("x" = 15,"y" = 11) matter = list("glass" = 500) /obj/item/weapon/reagent_containers/glass/beaker/Initialize() @@ -200,6 +202,7 @@ name = "large beaker" desc = "A large beaker." icon_state = "beakerlarge" + center_of_mass = list("x" = 16,"y" = 11) matter = list("glass" = 5000) volume = 120 amount_per_transfer_from_this = 10 @@ -210,6 +213,7 @@ name = "cryostasis beaker" desc = "A cryostasis beaker that allows for chemical storage without reactions." icon_state = "beakernoreact" + center_of_mass = list("x" = 16,"y" = 13) matter = list("glass" = 500) volume = 60 amount_per_transfer_from_this = 10 @@ -219,6 +223,7 @@ name = "bluespace beaker" desc = "A bluespace beaker, powered by experimental bluespace technology." icon_state = "beakerbluespace" + center_of_mass = list("x" = 16,"y" = 11) matter = list("glass" = 5000) volume = 300 amount_per_transfer_from_this = 10 @@ -229,6 +234,7 @@ name = "vial" desc = "A small glass vial." icon_state = "vial" + center_of_mass = list("x" = 15,"y" = 9) matter = list("glass" = 250) volume = 30 w_class = ITEMSIZE_TINY @@ -237,6 +243,7 @@ flags = OPENCONTAINER /obj/item/weapon/reagent_containers/glass/beaker/cryoxadone + name = "beaker (cryoxadone)" prefill = list("cryoxadone" = 30) /obj/item/weapon/reagent_containers/glass/beaker/sulphuric @@ -248,6 +255,7 @@ icon = 'icons/obj/janitor.dmi' icon_state = "bucket" item_state = "bucket" + center_of_mass = list("x" = 16,"y" = 10) matter = list(DEFAULT_WALL_MATERIAL = 200) w_class = ITEMSIZE_NORMAL amount_per_transfer_from_this = 20 @@ -258,7 +266,7 @@ /obj/item/weapon/reagent_containers/glass/bucket/attackby(var/obj/item/D, mob/user as mob) if(isprox(D)) - user << "You add [D] to [src]." + to_chat(user, "You add [D] to [src].") qdel(D) user.put_in_hands(new /obj/item/weapon/bucket_sensor) user.drop_from_inventory(src) @@ -304,6 +312,7 @@ obj/item/weapon/reagent_containers/glass/bucket/wood icon = 'icons/obj/janitor.dmi' icon_state = "woodbucket" item_state = "woodbucket" + center_of_mass = list("x" = 16,"y" = 8) matter = list("wood" = 50) w_class = ITEMSIZE_LARGE amount_per_transfer_from_this = 20 @@ -314,7 +323,7 @@ obj/item/weapon/reagent_containers/glass/bucket/wood /obj/item/weapon/reagent_containers/glass/bucket/wood/attackby(var/obj/D, mob/user as mob) if(isprox(D)) - user << "This wooden bucket doesn't play well with electronics." + to_chat(user, "This wooden bucket doesn't play well with electronics.") return else if(istype(D, /obj/item/weapon/material/knife/machete/hatchet)) to_chat(user, "You cut a big hole in \the [src] with \the [D]. It's kinda useless as a bucket now.") diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 6b7910fab3..aaa8cec486 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -103,7 +103,7 @@ loaded_vial.update_icon() user.put_in_hands(loaded_vial) loaded_vial = null - user << "You remove the vial from the [src]." + to_chat(user, "You remove the vial from the [src].") update_icon() playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1) return @@ -129,7 +129,7 @@ update_icon() playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) else - user << "\The [src] already has a vial." + to_chat(user, "\The [src] already has a vial.") else ..() diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index 5ca47a6d3b..82a6f7b97a 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -7,6 +7,7 @@ icon = 'icons/obj/chemical.dmi' icon_state = null item_state = "pill" + drop_sound = 'sound/items/drop/food.ogg' var/base_state = "pill" diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index e0926f47b3..3e94bedd59 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/janitor.dmi' icon_state = "cleaner" item_state = "cleaner" + center_of_mass = list("x" = 16,"y" = 10) flags = OPENCONTAINER|NOBLUDGEON //TFF 24/12/19 - Let people print more spray bottles if needed. matter = list("glass" = 300, DEFAULT_WALL_MATERIAL = 300) @@ -35,7 +36,7 @@ return if(reagents.total_volume < amount_per_transfer_from_this) - user << "\The [src] is empty!" + to_chat(user, "\The [src] is empty!") return Spray_at(A, user, proximity) @@ -75,11 +76,11 @@ return amount_per_transfer_from_this = next_in_list(amount_per_transfer_from_this, possible_transfer_amounts) spray_size = next_in_list(spray_size, spray_sizes) - user << "You adjusted the pressure nozzle. You'll now use [amount_per_transfer_from_this] units per spray." + to_chat(user, "You adjusted the pressure nozzle. You'll now use [amount_per_transfer_from_this] units per spray.") /obj/item/weapon/reagent_containers/spray/examine(mob/user) if(..(user, 0) && loc == user) - user << "[round(reagents.total_volume)] units left." + to_chat(user, "[round(reagents.total_volume)] units left.") return /obj/item/weapon/reagent_containers/spray/verb/empty() @@ -91,7 +92,7 @@ if (alert(usr, "Are you sure you want to empty that?", "Empty Bottle:", "Yes", "No") != "Yes") return if(isturf(usr.loc)) - usr << "You empty \the [src] onto the floor." + to_chat(usr, "You empty \the [src] onto the floor.") reagents.splash(usr.loc, reagents.total_volume) //space cleaner @@ -122,6 +123,7 @@ icon = 'icons/obj/weapons.dmi' icon_state = "pepperspray" item_state = "pepperspray" + center_of_mass = list("x" = 16,"y" = 16) possible_transfer_amounts = null volume = 40 var/safety = TRUE @@ -132,15 +134,15 @@ /obj/item/weapon/reagent_containers/spray/pepper/examine(mob/user) if(..(user, 1)) - user << "The safety is [safety ? "on" : "off"]." + to_chat(user, "The safety is [safety ? "on" : "off"].") /obj/item/weapon/reagent_containers/spray/pepper/attack_self(var/mob/user) safety = !safety - usr << "You switch the safety [safety ? "on" : "off"]." + to_chat(usr, "You switch the safety [safety ? "on" : "off"].") /obj/item/weapon/reagent_containers/spray/pepper/Spray_at(atom/A as mob|obj) if(safety) - usr << "The safety is on!" + to_chat(usr, "The safety is on!") return . = ..() @@ -164,6 +166,7 @@ icon = 'icons/obj/gun.dmi' icon_state = "chemsprayer" item_state = "chemsprayer" + center_of_mass = list("x" = 16,"y" = 16) throwforce = 3 w_class = ITEMSIZE_NORMAL possible_transfer_amounts = null diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 66c5f652f9..6d9becfbc6 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -11,6 +11,7 @@ icon = 'icons/obj/syringe.dmi' item_state = "syringe_0" icon_state = "0" + center_of_mass = list("x" = 16,"y" = 14) matter = list("glass" = 150) amount_per_transfer_from_this = 5 possible_transfer_amounts = null @@ -24,6 +25,7 @@ var/visible_name = "a syringe" var/time = 30 var/drawing = 0 + drop_sound = 'sound/items/drop/glass.ogg' /obj/item/weapon/reagent_containers/syringe/on_reagent_change() update_icon() diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index c2fae37751..17af8d3b88 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -331,6 +331,7 @@ return /obj/structure/reagent_dispensers/water_cooler/update_icon() + /* VOREStation Lazy Fix for Right Now icon_state = "water_cooler" overlays.Cut() var/image/I @@ -343,6 +344,7 @@ if(cups) I = image(icon, "water_cooler_cups") overlays += I + */ return /obj/structure/reagent_dispensers/beerkeg diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 8a2e9796cd..f3dd0fa384 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -29,23 +29,24 @@ // create a new disposal // find the attached trunk (if present) and init gas resvr. -/obj/machinery/disposal/New() - ..() - spawn(5) - trunk = locate() in src.loc - if(!trunk) - mode = 0 - flush = 0 - else - trunk.linked = src // link the pipe trunk to self +/obj/machinery/disposal/Initialize() + . = ..() + + trunk = locate() in loc + if(!trunk) + mode = 0 + flush = 0 + else + trunk.linked = src // link the pipe trunk to self - air_contents = new/datum/gas_mixture(PRESSURE_TANK_VOLUME) - update() + air_contents = new/datum/gas_mixture(PRESSURE_TANK_VOLUME) + update() /obj/machinery/disposal/Destroy() eject() if(trunk) trunk.linked = null + trunk = null return ..() // attack by item places it in to disposal @@ -359,7 +360,7 @@ // charge the gas reservoir and perform flush if ready /obj/machinery/disposal/process() if(!air_contents || (stat & BROKEN)) // nothing can happen if broken - update_use_power(0) + update_use_power(USE_POWER_OFF) return flush_count++ @@ -377,7 +378,7 @@ flush() if(mode != 1) //if off or ready, no need to charge - update_use_power(1) + update_use_power(USE_POWER_IDLE) else if(air_contents.return_pressure() >= SEND_PRESSURE) mode = 2 //if full enough, switch to ready mode update() @@ -386,7 +387,7 @@ /obj/machinery/disposal/proc/pressurize() if(stat & NOPOWER) // won't charge if no power - update_use_power(0) + update_use_power(USE_POWER_OFF) return var/atom/L = loc // recharging from loc turf @@ -1308,46 +1309,39 @@ icon_state = "pipe-t" var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet -/obj/structure/disposalpipe/trunk/New() - ..() +/obj/structure/disposalpipe/trunk/Initialize() + ..() //Lateload below dpdir = dir - spawn(1) - getlinked() + return INITIALIZE_HINT_LATELOAD +/obj/structure/disposalpipe/trunk/LateInitialize() + if(!linked) + getlinked() update() - return + +/obj/structure/disposalpipe/trunk/Destroy() + if(linked) + if(istype(linked, /obj/machinery/disposal)) + + var/obj/machinery/disposal/D = linked + D.trunk = null + linked = null + return ..() /obj/structure/disposalpipe/trunk/proc/getlinked() linked = null - var/obj/machinery/disposal/D = locate() in src.loc + var/obj/machinery/disposal/D = locate() in loc if(D) linked = D - if (!D.trunk) + if(!D.trunk) D.trunk = src - var/obj/structure/disposaloutlet/O = locate() in src.loc + var/obj/structure/disposaloutlet/O = locate() in loc if(O) linked = O - update() - return - - // Override attackby so we disallow trunkremoval when somethings ontop +// Override attackby so we disallow trunkremoval when somethings ontop /obj/structure/disposalpipe/trunk/attackby(var/obj/item/I, var/mob/user) - - //Disposal bins or chutes - /* - These shouldn't be required - var/obj/machinery/disposal/D = locate() in src.loc - if(D && D.anchored) - return - - //Disposal outlet - var/obj/structure/disposaloutlet/O = locate() in src.loc - if(O && O.anchored) - return - */ - //Disposal constructors var/obj/structure/disposalconstruct/C = locate() in src.loc if(C && C.anchored) @@ -1439,72 +1433,70 @@ var/turf/target // this will be where the output objects are 'thrown' to. var/mode = 0 - New() - ..() +/obj/structure/disposaloutlet/Initialize() + . = ..() - spawn(1) - target = get_ranged_target_turf(src, dir, 10) + target = get_ranged_target_turf(src, dir, 10) - var/obj/structure/disposalpipe/trunk/trunk = locate() in src.loc - if(trunk) - trunk.linked = src // link the pipe trunk to self + var/obj/structure/disposalpipe/trunk/trunk = locate() in loc + if(trunk) + trunk.linked = src // link the pipe trunk to self // expel the contents of the holder object, then delete it // called when the holder exits the outlet - proc/expel(var/obj/structure/disposalholder/H) +/obj/structure/disposaloutlet/proc/expel(var/obj/structure/disposalholder/H) + flick("outlet-open", src) + playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) + sleep(20) //wait until correct animation frame + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - flick("outlet-open", src) - playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) - sleep(20) //wait until correct animation frame - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + AM.forceMove(src.loc) + AM.pipe_eject(dir) + if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z + spawn(5) + AM.throw_at(target, 3, 1) + H.vent_gas(src.loc) + qdel(H) - if(H) - for(var/atom/movable/AM in H) - AM.forceMove(src.loc) - AM.pipe_eject(dir) - if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z - spawn(5) - AM.throw_at(target, 3, 1) - H.vent_gas(src.loc) - qdel(H) + return +/obj/structure/disposaloutlet/attackby(var/obj/item/I, var/mob/user) + if(!I || !user) return - - attackby(var/obj/item/I, var/mob/user) - if(!I || !user) + src.add_fingerprint(user) + if(I.is_screwdriver()) + if(mode==0) + mode=1 + to_chat(user, "You remove the screws around the power connection.") + playsound(src, I.usesound, 50, 1) + return + else if(mode==1) + mode=0 + to_chat(user, "You attach the screws around the power connection.") + playsound(src, I.usesound, 50, 1) + return + else if(istype(I, /obj/item/weapon/weldingtool) && mode==1) + var/obj/item/weapon/weldingtool/W = I + if(W.remove_fuel(0,user)) + playsound(src, W.usesound, 100, 1) + to_chat(user, "You start slicing the floorweld off the disposal outlet.") + if(do_after(user,20 * W.toolspeed)) + if(!src || !W.isOn()) return + to_chat(user, "You sliced the floorweld off the disposal outlet.") + var/obj/structure/disposalconstruct/C = new (src.loc) + src.transfer_fingerprints_to(C) + C.ptype = 7 // 7 = outlet + C.update() + C.anchored = 1 + C.density = 1 + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") return - src.add_fingerprint(user) - if(I.is_screwdriver()) - if(mode==0) - mode=1 - to_chat(user, "You remove the screws around the power connection.") - playsound(src, I.usesound, 50, 1) - return - else if(mode==1) - mode=0 - to_chat(user, "You attach the screws around the power connection.") - playsound(src, I.usesound, 50, 1) - return - else if(istype(I, /obj/item/weapon/weldingtool) && mode==1) - var/obj/item/weapon/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src, W.usesound, 100, 1) - to_chat(user, "You start slicing the floorweld off the disposal outlet.") - if(do_after(user,20 * W.toolspeed)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the disposal outlet.") - var/obj/structure/disposalconstruct/C = new (src.loc) - src.transfer_fingerprints_to(C) - C.ptype = 7 // 7 = outlet - C.update() - C.anchored = 1 - C.density = 1 - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return // called when movable is expelled from a disposal pipe or outlet // by default does nothing, override for special behaviour diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 47adce6373..ee4285330f 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -18,6 +18,7 @@ unwrap() proc/unwrap() + playsound(loc, 'sound/items/package_unwrap.ogg', 50, 1) // Destroy will drop our wrapped object on the turf, so let it. qdel(src) @@ -48,6 +49,7 @@ user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ "You title \the [src]: \"[str]\"",\ "You hear someone scribbling a note.") + playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20) name = "[name] ([str])" if(!examtext && !nameset) nameset = 1 @@ -67,6 +69,7 @@ user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ "You label \the [src]: \"[examtext]\"",\ "You hear someone scribbling a note.") + playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20) return update_icon() @@ -111,6 +114,7 @@ name = "small parcel" icon = 'icons/obj/storage_vr.dmi' //VOREStation Edit icon_state = "deliverycrate3" + drop_sound = 'sound/items/drop/box.ogg' var/obj/item/wrapped = null var/sortTag = null var/examtext = null @@ -155,6 +159,7 @@ user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ "You title \the [src]: \"[str]\"",\ "You hear someone scribbling a note.") + playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20) name = "[name] ([str])" if(!examtext && !nameset) nameset = 1 @@ -175,6 +180,7 @@ user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ "You label \the [src]: \"[examtext]\"",\ "You hear someone scribbling a note.") + playsound(src, pick('sound/bureaucracy/pen1.ogg','sound/bureaucracy/pen2.ogg'), 20) return update_icon() @@ -217,6 +223,7 @@ icon_state = "deliveryPaper" w_class = ITEMSIZE_NORMAL var/amount = 25.0 + drop_sound = 'sound/items/drop/wrapper.ogg' afterattack(var/obj/target as obj, mob/user as mob, proximity) @@ -267,6 +274,7 @@ user.visible_message("\The [user] wraps \a [target] with \a [src].",\ "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ "You hear someone taping paper around a small object.") + playsound(loc, 'sound/items/package_wrap.ogg', 50, 1) else if (istype(target, /obj/structure/closet/crate)) var/obj/structure/closet/crate/O = target if (src.amount > 3 && !O.opened) @@ -278,6 +286,7 @@ user.visible_message("\The [user] wraps \a [target] with \a [src].",\ "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ "You hear someone taping paper around a large object.") + playsound(loc, 'sound/items/package_wrap.ogg', 50, 1) else if(src.amount < 3) to_chat(user, "You need more paper.") else if (istype (target, /obj/structure/closet)) @@ -291,6 +300,7 @@ user.visible_message("\The [user] wraps \a [target] with \a [src].",\ "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ "You hear someone taping paper around a large object.") + playsound(loc, 'sound/items/package_wrap.ogg', 50, 1) else if(src.amount < 3) to_chat(user, "You need more paper.") else diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm index 7f9fcb565f..f9ff3adb51 100644 --- a/code/modules/research/circuitprinter.dm +++ b/code/modules/research/circuitprinter.dm @@ -16,11 +16,11 @@ using metal and glass, it uses glass and reagents (usually sulphuric acid). var/mat_efficiency = 1 var/speed = 1 - materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, MAT_PLASTEEL = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, MAT_LEAD = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0) + materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, MAT_PLASTEEL = 0, "plastic" = 0, MAT_GRAPHITE, "gold" = 0, "silver" = 0, "osmium" = 0, MAT_LEAD = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0) - hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER) + hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_GRAPHITE, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER) - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 2500 diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index f55bd71d77..3eed6bdff3 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -31,6 +31,7 @@ other types of metals and chemistry for reagents). var/time = 10 //How many ticks it requires to build var/category = null //Primarily used for Mech Fabricators, but can be used for anything. var/sort_string = "ZZZZZ" //Sorting order + var/maxstack = 1 // yw edit, used by autolathe, says how many stacks a item can have or the limit of how many you can spawn at once /datum/design/New() ..() diff --git a/code/modules/research/designs/circuits/circuits.dm b/code/modules/research/designs/circuits/circuits.dm index 63150b8967..4717e14bdc 100644 --- a/code/modules/research/designs/circuits/circuits.dm +++ b/code/modules/research/designs/circuits/circuits.dm @@ -570,36 +570,6 @@ CIRCUITS BELOW build_path = /obj/item/weapon/circuitboard/telecomms/exonet_node sort_string = "PAAAH" -/datum/design/circuit/shield - req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3) - materials = list("glass" = 2000, "gold" = 1000) - -/datum/design/circuit/shield/AssembleDesignName() - name = "Shield generator circuit design ([name])" -/datum/design/circuit/shield/AssembleDesignDesc() - if(!desc) - desc = "Allows for the construction of \a [name] shield generator." - -/datum/design/circuit/shield/bubble - name = "bubble" - id = "shield_gen" - build_path = /obj/item/weapon/circuitboard/shield_gen - sort_string = "VAAAZ" // Duplicate string, really need to redo this whole thing - -/datum/design/circuit/shield/hull - name = "hull" - id = "shield_gen_ex" - build_path = /obj/item/weapon/circuitboard/shield_gen_ex - sort_string = "VAAAB" - -/datum/design/circuit/shield/capacitor - name = "capacitor" - desc = "Allows for the construction of a shield capacitor circuit board." - id = "shield_cap" - req_tech = list(TECH_MAGNET = 3, TECH_POWER = 4) - build_path = /obj/item/weapon/circuitboard/shield_cap - sort_string = "VAAAC" - /datum/design/circuit/ntnet_relay name = "NTNet Quantum Relay" id = "ntnet_relay" @@ -621,12 +591,30 @@ CIRCUITS BELOW build_path = /obj/item/weapon/circuitboard/microwave/advanced sort_string = "MAAAC" +/datum/design/circuit/shield_generator + name = "shield generator" + id = "shield_generator" + req_tech = list(TECH_MAGNET = 3, TECH_POWER = 4, TECH_BLUESPACE = 2, TECH_ENGINEERING = 3) + build_path = /obj/item/weapon/circuitboard/shield_generator + sort_string = "OAAAA" -/* I have no idea how this was even running before, but it doesn't seem to be necessary. -/////////////////////////////////// -/////////Shield Generators///////// -/////////////////////////////////// -/datum/design/circuit/shield - req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3) - materials = list("$glass" = 2000, "sacid" = 20, "$phoron" = 10000, "$diamond" = 5000, "$gold" = 10000) -*/ +/datum/design/circuit/shield_diffuser + name = "shield diffuser" + id = "shield_diffuser" + req_tech = list(TECH_MAGNET = 4, TECH_POWER = 2, TECH_ENGINEERING = 5) + build_path = /obj/item/weapon/circuitboard/shield_diffuser + sort_string = "OAAAB" + +/datum/design/circuit/pointdefense + name = "point defense battery" + id = "pointdefense" + req_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 3, TECH_COMBAT = 4) + build_path = /obj/item/weapon/circuitboard/pointdefense + sort_string = "OAABA" + +/datum/design/circuit/pointdefense_control + name = "deluxe microwave" + id = "pointdefense_control" + req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 3, TECH_COMBAT = 2) + build_path = /obj/item/weapon/circuitboard/pointdefense_control + sort_string = "OAABB" diff --git a/code/modules/research/designs/circuits/disks.dm b/code/modules/research/designs/circuits/disks.dm new file mode 100644 index 0000000000..2a3c2df83f --- /dev/null +++ b/code/modules/research/designs/circuits/disks.dm @@ -0,0 +1,46 @@ + +/datum/design/circuit/disk + build_type = IMPRINTER + req_tech = list(TECH_DATA = 3) + materials = list(MAT_PLASTIC = 2000, MAT_GLASS = 1000) + chemicals = list("pacid" = 10) + time = 5 + +/datum/design/circuit/disk/AssembleDesignName() + ..() + if(build_path) + var/obj/item/weapon/disk/D = build_path + if(istype(D, /obj/item/weapon/disk/species)) + name = "Species Prosthetic design ([item_name])" + else if(istype(D, /obj/item/weapon/disk/limb)) + name = "Transtellar Prosthetic design ([item_name])" + else + name = "Disk design ([item_name])" + +/datum/design/circuit/disk/skrellprint + name = SPECIES_SKRELL + id = "prosthetic_skrell" + req_tech = list(TECH_DATA = 3, TECH_BIO = 3) + build_path = /obj/item/weapon/disk/species/skrell + sort_string = "DBAAA" + +/datum/design/circuit/disk/tajprint + name = SPECIES_TAJ + id = "prosthetic_tajaran" + req_tech = list(TECH_DATA = 3, TECH_BIO = 3) + build_path = /obj/item/weapon/disk/species/tajaran + sort_string = "DBAAB" + +/datum/design/circuit/disk/unathiprint + name = SPECIES_UNATHI + id = "prosthetic_unathi" + req_tech = list(TECH_DATA = 3, TECH_BIO = 4) + build_path = /obj/item/weapon/disk/species/unathi + sort_string = "DBAAC" + +/datum/design/circuit/disk/teshariprint + name = SPECIES_TESHARI + id = "prosthetic_teshari" + req_tech = list(TECH_DATA = 3, TECH_BIO = 4) + build_path = /obj/item/weapon/disk/species/teshari + sort_string = "DBAAD" diff --git a/code/modules/research/designs/weapons.dm b/code/modules/research/designs/weapons.dm index 81a2bd57d9..13d6036edd 100644 --- a/code/modules/research/designs/weapons.dm +++ b/code/modules/research/designs/weapons.dm @@ -206,6 +206,13 @@ build_path = /obj/item/ammo_casing/chemdart sort_string = "MADBD" +/datum/design/item/weapon/ammo/flechette + id = "magnetic_ammo" + req_tech = list(TECH_COMBAT = 4, TECH_MATERIAL = 4, TECH_MAGNET = 4) + materials = list(DEFAULT_WALL_MATERIAL = 500, "gold" = 300, "glass" = 150, MAT_PHORON = 100) + build_path = /obj/item/weapon/magnetic_ammo + sort_string = "MADBE" + // Melee weapons /datum/design/item/weapon/melee/AssembleDesignName() diff --git a/code/modules/research/designs/weapons_vr.dm b/code/modules/research/designs/weapons_vr.dm index 2b81ce34ce..c31db780d0 100644 --- a/code/modules/research/designs/weapons_vr.dm +++ b/code/modules/research/designs/weapons_vr.dm @@ -14,7 +14,7 @@ id = "protector" req_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2) materials = list(DEFAULT_WALL_MATERIAL = 4000, "glass" = 2000, "silver" = 1000) - build_path = /obj/item/weapon/gun/energy/protector + build_path = /obj/item/weapon/gun/energy/gun/protector sort_string = "MAAVA" /datum/design/item/weapon/energy/sickshot diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 3fbb141f7e..a17bf370bf 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -12,7 +12,7 @@ Note: Must be placed within 3 tiles of the R&D Console var/obj/item/weapon/loaded_item = null var/decon_mod = 0 circuit = /obj/item/weapon/circuitboard/destructive_analyzer - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 2500 @@ -40,10 +40,10 @@ Note: Must be placed within 3 tiles of the R&D Console /obj/machinery/r_n_d/destructive_analyzer/attackby(var/obj/item/O as obj, var/mob/user as mob) if(busy) - user << "\The [src] is busy right now." + to_chat(user, "\The [src] is busy right now.") return if(loaded_item) - user << "There is something already loaded into \the [src]." + to_chat(user, "There is something already loaded into \the [src].") return 1 if(default_deconstruction_screwdriver(user, O)) if(linked_console) @@ -55,25 +55,25 @@ Note: Must be placed within 3 tiles of the R&D Console if(default_part_replacement(user, O)) return if(panel_open) - user << "You can't load \the [src] while it's opened." + to_chat(user, "You can't load \the [src] while it's opened.") return 1 if(!linked_console) - user << "\The [src] must be linked to an R&D console first." + to_chat(user, "\The [src] must be linked to an R&D console first.") return if(!loaded_item) if(isrobot(user)) //Don't put your module items in there! return if(!O.origin_tech) - user << "This doesn't seem to have a tech origin." + to_chat(user, "This doesn't seem to have a tech origin.") return if(O.origin_tech.len == 0) - user << "You cannot deconstruct this item." + to_chat(user, "You cannot deconstruct this item.") return busy = 1 loaded_item = O user.drop_item() O.loc = src - user << "You add \the [O] to \the [src]." + to_chat(user, "You add \the [O] to \the [src].") flick("d_analyzer_la", src) spawn(10) update_icon() diff --git a/code/modules/research/mechfab_designs.dm b/code/modules/research/mechfab_designs.dm index 0fdd3df5be..125de3db6c 100644 --- a/code/modules/research/mechfab_designs.dm +++ b/code/modules/research/mechfab_designs.dm @@ -538,6 +538,13 @@ req_tech = list(TECH_BLUESPACE = 10, TECH_MAGNET = 5) build_path = /obj/item/mecha_parts/mecha_equipment/teleporter +/datum/design/item/mecha/teleporter + name = "Cloaking Device" + desc = "A device that renders the exosuit invisible to the naked eye, though not to thermal detection. Uses large amounts of energy." + id = "mech_cloaking" + req_tech = list(TECH_BLUESPACE = 10, TECH_MAGNET = 5) + build_path = /obj/item/mecha_parts/mecha_equipment/cloak + /datum/design/item/mecha/rcd name = "RCD" desc = "An exosuit-mounted rapid construction device." @@ -570,6 +577,14 @@ materials = list(DEFAULT_WALL_MATERIAL = 8000, "gold" = 2000, "silver" = 3000, "phoron" = 5000, "glass" = 3750) build_path = /obj/item/mecha_parts/mecha_equipment/combat_shield +/datum/design/item/mecha/omni_shield + name = "Omni Shield" + desc = "Integral shield projector. Can only protect the exosuit, but has no weak angles." + id = "mech_shield_omni" + req_tech = list(TECH_PHORON = 3, TECH_MAGNET = 6, TECH_ILLEGAL = 4) + materials = list(DEFAULT_WALL_MATERIAL = 8000, "gold" = 2000, "silver" = 3000, "phoron" = 5000, "glass" = 3750) + build_path = /obj/item/mecha_parts/mecha_equipment/omni_shield + /datum/design/item/mecha/crisis_drone name = "Crisis Drone" desc = "Deploys a small medical drone capable of patching small wounds in order to stabilize nearby patients." @@ -680,6 +695,46 @@ materials = list(DEFAULT_WALL_MATERIAL = 4000, "osmium" = 3000, "glass" = 1000) build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/prybar +/datum/design/item/mecha/powercutters + name = "hydraulic cable cutter" + desc = "A large, hydraulic cablecutter." + id = "mech_wirecutter" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "osmium" = 3000, "glass" = 1000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/cutter + +/datum/design/item/mecha/powerscrewdriver + name = "hydraulic screwdriver" + desc = "A large, hydraulic screwdriver." + id = "mech_screwdriver" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, "osmium" = 3000, "glass" = 1000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/screwdriver + +/datum/design/item/mecha/powerwelder + name = "welding laser" + desc = "A large welding laser." + id = "mech_welder" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 4000, MAT_PHORON = 3000, "glass" = 1000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/welding + +/datum/design/item/mecha/inflatables + name = "inflatables deployer" + desc = "A large pneumatic inflatable deployer." + id = "mech_inflatables" + req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_POWER = 2) + materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_PLASTIC = 4000, "glass" = 1000) + build_path = /obj/item/mecha_parts/mecha_equipment/tool/powertool/inflatables + +/datum/design/item/mecha/hardpoint_clamp + name = "hardpoint actuator" + desc = "A complex device used to commandeer equipment from the ground." + id = "mech_hardpoint_clamp" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_POWER = 5, TECH_COMBAT = 2, TECH_MAGNET = 4) + materials = list(MAT_PLASTEEL = 2500, MAT_PLASTIC = 3000, MAT_OSMIUM = 1500, MAT_SILVER = 2000) + build_path = /obj/item/mecha_parts/mecha_equipment/hardpoint_actuator + /datum/design/item/mecha/generator_nuclear name = "Nuclear Reactor" desc = "Exosuit-held nuclear reactor. Converts uranium and everyone's health to energy." @@ -729,3 +784,249 @@ req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 6, TECH_MAGNET = 3, TECH_POWER = 2) materials = list(DEFAULT_WALL_MATERIAL = 15000, "silver" = 3000, "plastic" = 3000, "osmium" = 1000) build_path = /obj/item/weapon/vehicle_assembly/quadbike + +/* + * Rigsuits + */ + +/datum/design/item/mechfab/rigsuit + category = "Rigsuit" + req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 5, TECH_PHORON = 3, TECH_MAGNET = 4, TECH_POWER = 6) + +/datum/design/item/mechfab/rigsuit/basic_belt + name = "Advanced Suit Control Belt" + desc = "A belt holding a compressed space-suit." + id = "rigmodule_belt_basic" + materials = list(MAT_PLASTEEL = 12000, MAT_GOLD = 3000, MAT_GRAPHITE = 3000, MAT_OSMIUM = 1000, MAT_PLASTIC = 5000) + build_path = /obj/item/weapon/rig/robotics + +/datum/design/item/mechfab/rigsuit/jetpack + name = "hardsuit maneuvering jets" + desc = "A compact gas thruster system for a hardsuit." + id = "rig_thrusters" + materials = list(MAT_PLASTEEL = 1000, MAT_GOLD = 1000, MAT_GRAPHITE = 1000, MAT_PLASTIC = 500) + build_path = /obj/item/rig_module/maneuvering_jets + +/datum/design/item/mechfab/rigsuit/powersink + name = "hardsuit power siphon" + desc = "A complex device used to pull power from machines." + id = "rig_siphon" + req_tech = list(TECH_MATERIAL = 7, TECH_ENGINEERING = 5, TECH_PHORON = 4, TECH_MAGNET = 5, TECH_POWER = 6, TECH_ILLEGAL = 3) + materials = list(MAT_PLASTEEL = 3000, MAT_METALHYDROGEN = 1000, MAT_GRAPHITE = 1000, MAT_PLASTIC = 5000, MAT_PHORON = 2000, MAT_VERDANTIUM = 1500) + build_path = /obj/item/rig_module/power_sink + +/datum/design/item/mechfab/rigsuit/flash + name = "hardsuit mounted flash" + desc = "A suit-mounted flash." + id = "rig_device_flash" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_MAGNET = 4, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 2000, MAT_PLASTIC = 3000, MAT_METALHYDROGEN = 200, MAT_GRAPHITE = 500) + build_path = /obj/item/rig_module/device/flash + +/datum/design/item/mechfab/rigsuit/plasmacutter + name = "hardsuit mounted plasmacutter" + desc = "A suit-mounted plasmacutter." + id = "rig_device_plasmacutter" + req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 3, TECH_MAGNET = 4, TECH_PHORON = 3, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 3000, MAT_PLASTIC = 3000, MAT_PHORON = 2500, MAT_GRAPHITE = 500) + build_path = /obj/item/rig_module/device/plasmacutter + +/datum/design/item/mechfab/rigsuit/healthanalyzer + name = "hardsuit health analyzer" + desc = "A hardsuit mounted health analyzer." + id = "rig_device_healthanalyzer" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3, TECH_BIO = 4, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 1000, MAT_SILVER = 1000, MAT_GRAPHITE = 1000, MAT_PLASTIC = 500) + build_path = /obj/item/rig_module/device/healthscanner + +/datum/design/item/mechfab/rigsuit/drill + name = "hardsuit mounted drill" + desc = "A hardsuit mounted drill." + id = "rig_device_drill" + req_tech = list(TECH_MATERIAL = 7, TECH_ENGINEERING = 5, TECH_MAGNET = 5, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 1500, MAT_DIAMOND = 2500, MAT_GRAPHITE = 1000, MAT_PLASTIC = 500) + build_path = /obj/item/rig_module/device/drill + +/datum/design/item/mechfab/rigsuit/excdrill + name = "hardsuit mounted excavation drill" + desc = "A hardsuit mounted excavation drill." + id = "rig_device_excdrill" + req_tech = list(TECH_MATERIAL = 7, TECH_ENGINEERING = 6, TECH_MAGNET = 5, TECH_POWER = 5, TECH_ARCANE = 1) + materials = list(MAT_PLASTEEL = 1500, MAT_DIAMOND = 2000, MAT_GRAPHITE = 1500, MAT_PLASTIC = 1000) + build_path = /obj/item/rig_module/device/arch_drill + +/datum/design/item/mechfab/rigsuit/anomscanner + name = "hardsuit mounted anomaly scanner" + desc = "A suit-mounted anomaly scanner." + id = "rig_device_anomscanner" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_MAGNET = 4, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 2000, MAT_PLASTIC = 3000, MAT_METALHYDROGEN = 200, MAT_GRAPHITE = 500) + build_path = /obj/item/rig_module/device/anomaly_scanner + +/datum/design/item/mechfab/rigsuit/orescanner + name = "hardsuit mounted ore scanner" + desc = "A suit-mounted ore scanner." + id = "rig_device_orescanner" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3, TECH_MAGNET = 3, TECH_POWER = 3) + materials = list(MAT_PLASTEEL = 2000, MAT_PLASTIC = 2000, MAT_GRAPHITE = 500) + build_path = /obj/item/rig_module/device/orescanner + +/datum/design/item/mechfab/rigsuit/rcd + name = "hardsuit mounted rcd" + desc = "A suit-mounted rcd." + id = "rig_device_rcd" + req_tech = list(TECH_MATERIAL = 7, TECH_ENGINEERING = 3, TECH_MAGNET = 4, TECH_PHORON = 3, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 5000, MAT_URANIUM = 3000, MAT_PHORON = 2000, MAT_GRAPHITE = 1500) + build_path = /obj/item/rig_module/device/rcd + +/datum/design/item/mechfab/rigsuit/paperdispenser + name = "hardsuit mounted paper dispenser" + desc = "A suit-mounted paper dispenser." + id = "rig_device_paperdispenser" + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2, TECH_MAGNET = 2, TECH_BIO = 2, TECH_POWER = 2) + materials = list(MAT_PLASTEEL = 1000, MAT_PLASTIC = 500, MAT_PHORON = 500, MAT_GRAPHITE = 100) + build_path = /obj/item/rig_module/device/paperdispenser + +/datum/design/item/mechfab/rigsuit/pen + name = "hardsuit mounted pen" + desc = "A suit-mounted pen." + id = "rig_device_pen" + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2, TECH_MAGNET = 2, TECH_BIO = 2, TECH_POWER = 2) + materials = list(MAT_PLASTEEL = 1000, MAT_PLASTIC = 500, MAT_PHORON = 500, MAT_GRAPHITE = 100) + build_path = /obj/item/rig_module/device/pen + +/datum/design/item/mechfab/rigsuit/grenade_metalfoam + name = "hardsuit metalfoam-bomb launcher" + desc = "A compact metalfoam grenade system for a hardsuit." + id = "rig_grenade_metalfoam" + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_MAGNET = 2, TECH_POWER = 3) + materials = list(MAT_PLASTEEL = 2000, MAT_OSMIUM = 1000, MAT_GRAPHITE = 1500, MAT_PLASTIC = 500) + build_path = /obj/item/rig_module/grenade_launcher/metalfoam + +/datum/design/item/mechfab/rigsuit/grenade_flash + name = "hardsuit flashbang launcher" + desc = "A compact flashbang grenade system for a hardsuit." + id = "rig_grenade_flashbang" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_PHORON = 3, TECH_MAGNET = 4, TECH_POWER = 5, TECH_COMBAT = 4) + materials = list(MAT_PLASTEEL = 2000, MAT_OSMIUM = 1500, MAT_GRAPHITE = 1000, MAT_PLASTIC = 1000) + build_path = /obj/item/rig_module/grenade_launcher/flash + +/datum/design/item/mechfab/rigsuit/grenade_cleanfoam + name = "hardsuit cleaning-foam-bomb launcher" + desc = "A compact cleaning-foam grenade system for a hardsuit." + id = "rig_grenade_cleanfoam" + req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_BIO = 2, TECH_POWER = 2) + materials = list(MAT_PLASTEEL = 2000, MAT_GLASS = 1000, MAT_GRAPHITE = 1500, MAT_PLASTIC = 750) + build_path = /obj/item/rig_module/cleaner_launcher + +/datum/design/item/mechfab/rigsuit/taser + name = "hardsuit taser" + desc = "A compact taser system for a hardsuit." + id = "rig_gun_taser" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3, TECH_MAGNET = 2, TECH_POWER = 3, TECH_COMBAT = 2) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_PLASTIC = 500) + build_path = /obj/item/rig_module/mounted/taser + +/datum/design/item/mechfab/rigsuit/egun + name = "hardsuit egun" + desc = "A compact egun system for a hardsuit." + id = "rig_gun_egun" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4, TECH_MAGNET = 3, TECH_POWER = 4, TECH_COMBAT = 4) + materials = list(MAT_PLASTEEL = 2000, MAT_GOLD = 1250, MAT_GRAPHITE = 1500, MAT_PLASTIC = 500) + build_path = /obj/item/rig_module/mounted/egun + +/datum/design/item/mechfab/rigsuit/mop + name = "hardsuit intense cleaning device" + desc = "An advanced cleaning device." + id = "rig_gun_tempgun" + req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 4, TECH_MAGNET = 5, TECH_POWER = 4, TECH_COMBAT = 6) + materials = list(MAT_PLASTEEL = 2000, MAT_GOLD = 1750, MAT_URANIUM = 1500, MAT_GRAPHITE = 1500, MAT_PLASTIC = 1000) + build_path = /obj/item/rig_module/mounted/mop + +/datum/design/item/mechfab/rigsuit/sprinter + name = "hardsuit overclocker" + desc = "A compact overclocking system for a hardsuit." + id = "rig_component_sprinter" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_BIO = 4, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_PLASTIC = 500, MAT_VERDANTIUM = 1000) + build_path = /obj/item/rig_module/sprinter + +/datum/design/item/mechfab/rigsuit/meson + name = "hardsuit meson visor" + desc = "A compact meson visor for a hardsuit." + id = "rig_component_meson" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 5, TECH_MAGNET = 3, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_OSMIUM = 500) + build_path = /obj/item/rig_module/vision/meson + +/datum/design/item/mechfab/rigsuit/material + name = "hardsuit material visor" + desc = "A compact material visor for a hardsuit." + id = "rig_component_material" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 5, TECH_MAGNET = 3, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_OSMIUM = 500) + build_path = /obj/item/rig_module/vision/material + +/datum/design/item/mechfab/rigsuit/nvg + name = "hardsuit night-vision visor" + desc = "A compact night-vision visor for a hardsuit." + id = "rig_component_nvg" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 6, TECH_MAGNET = 4, TECH_POWER = 5) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_OSMIUM = 500, MAT_URANIUM = 1000) + build_path = /obj/item/rig_module/vision/nvg + +/datum/design/item/mechfab/rigsuit/sechud + name = "hardsuit security visor" + desc = "A compact security visor for a hardsuit." + id = "rig_component_sechud" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3, TECH_MAGNET = 3, TECH_POWER = 2) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_PLASTIC = 500, MAT_SILVER = 500) + build_path = /obj/item/rig_module/vision/sechud + +/datum/design/item/mechfab/rigsuit/medhud + name = "hardsuit medical visor" + desc = "A compact medical visor for a hardsuit." + id = "rig_component_medhud" + req_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3, TECH_MAGNET = 3, TECH_BIO = 2) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_PLASTIC = 500, MAT_SILVER = 500) + build_path = /obj/item/rig_module/vision/medhud + +/datum/design/item/mechfab/rigsuit/voice + name = "hardsuit voice-changer" + desc = "A compact voice-changer for a hardsuit." + id = "rig_component_voice" + req_tech = list(TECH_MATERIAL = 6, TECH_ENGINEERING = 4, TECH_MAGNET = 4, TECH_BIO = 4, TECH_ILLEGAL = 3) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_PLASTIC = 1000, MAT_SILVER = 500, MAT_PHORON = 1000) + build_path = /obj/item/rig_module/voice + +/datum/design/item/mechfab/rigsuit/aicontainer + name = "hardsuit intelligence storage system" + desc = "A compact AI network system for a hardsuit." + id = "rig_component_aicontainer" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_BIO = 4, TECH_POWER = 4) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_DIAMOND = 1000, MAT_GOLD = 500, MAT_SILVER = 750, MAT_VERDANTIUM = 1000) + build_path = /obj/item/rig_module/ai_container + +/datum/design/item/mechfab/rigsuit/datajack + name = "hardsuit datajack" + desc = "A compact datajack for a hardsuit." + id = "rig_component_datajack" + req_tech = list(TECH_MATERIAL = 7, TECH_ENGINEERING = 5, TECH_MAGNET = 5, TECH_POWER = 5) + materials = list(MAT_PLASTEEL = 2000, MAT_GRAPHITE = 1500, MAT_METALHYDROGEN = 1000, MAT_GOLD = 500, MAT_SILVER = 750, MAT_VERDANTIUM = 1000) + build_path = /obj/item/rig_module/datajack + +/datum/design/item/mechfab/rigsuit/cheminjector + name = "hardsuit chemical injector" + desc = "A compact chemical injector network for a hardsuit." + id = "rig_component_chemicals" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_MAGNET = 5, TECH_BIO = 4) + materials = list(MAT_PLASTEEL = 3000, MAT_GRAPHITE = 2000, MAT_PLASTIC = 3500, MAT_SILVER = 1750, MAT_GOLD = 1250) + build_path = /obj/item/rig_module/chem_dispenser/injector/advanced/empty + +/datum/design/item/mechfab/rigsuit/teleporter + name = "hardsuit teleporter system" + desc = "An enigmatic teleporter system for a hardsuit." + id = "rig_component_teleport" + req_tech = list(TECH_MATERIAL = 7, TECH_ENGINEERING = 5, TECH_MAGNET = 5, TECH_POWER = 6, TECH_ILLEGAL = 3, TECH_BLUESPACE = 4, TECH_ARCANE = 2, TECH_PRECURSOR = 3) + materials = list(MAT_DURASTEEL = 5000, MAT_GRAPHITE = 3000, MAT_MORPHIUM = 1500, MAT_OSMIUM = 1500, MAT_PHORON = 1750, MAT_VERDANTIUM = 3000, MAT_SUPERMATTER = 2000) + build_path = /obj/item/rig_module/teleporter diff --git a/code/modules/research/mechfab_designs_vr.dm b/code/modules/research/mechfab_designs_vr.dm new file mode 100644 index 0000000000..1de0cc2dda --- /dev/null +++ b/code/modules/research/mechfab_designs_vr.dm @@ -0,0 +1,7 @@ +/datum/design/item/mechfab/rigsuit/rescuepharm + name = "hardsuit mounted rescue pharmacy" + desc = "A suit mounted rescue drug dispenser." + id = "rig_component_rescuepharm" + req_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 5, TECH_MAGNET = 5, TECH_BIO = 4) + materials = list(MAT_PLASTEEL = 3000, MAT_GRAPHITE = 2000, MAT_PLASTIC = 3500, MAT_SILVER = 1750, MAT_GOLD = 1250) + build_path = /obj/item/rig_module/rescue_pharm diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index a23193581e..86004b6130 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -54,7 +54,7 @@ var/global/list/obj/machinery/message_server/message_servers = list() desc = "Facilitates both PDA messages and request console functions." density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 100 @@ -131,19 +131,19 @@ var/global/list/obj/machinery/message_server/message_servers = list() if(2) if(!Console.silent) playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message(text("\icon[Console] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5) + Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5) Console.message_log += "High Priority message from [sender]
    [authmsg]" else if(!Console.silent) playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message(text("\icon[Console] *The Requests Console beeps: 'Message from [sender]'"),,4) + Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4) Console.message_log += "Message from [sender]
    [authmsg]" Console.set_light(2) /obj/machinery/message_server/attack_hand(user as mob) -// user << "There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentCom delays." - user << "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]" +// to_chat(user, "There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentCom delays.") + to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") active = !active update_icon() @@ -155,7 +155,7 @@ var/global/list/obj/machinery/message_server/message_servers = list() spamfilter_limit += round(MESSAGE_SERVER_DEFAULT_SPAM_LIMIT / 2) user.drop_item() qdel(O) - user << "You install additional memory and processors into message server. Its filtering capabilities been enhanced." + to_chat(user, "You install additional memory and processors into message server. Its filtering capabilities been enhanced.") else ..(O, user) @@ -240,7 +240,7 @@ var/obj/machinery/blackbox_recorder/blackbox desc = "Records all radio communications, as well as various other information in case of the worst." density = 1 anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 100 var/list/messages = list() //Stores messages of non-standard frequencies diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm index 50a5d6db0d..da9cc05509 100644 --- a/code/modules/research/prosfab_designs.dm +++ b/code/modules/research/prosfab_designs.dm @@ -11,20 +11,21 @@ if(istype(fabricator, /obj/machinery/pros_fabricator)) var/obj/machinery/pros_fabricator/prosfab = fabricator var/obj/item/organ/O = new build_path(newloc) - //VOREStation Edit - Suggesting a species - var/newspecies = "Human" if(prosfab.manufacturer) var/datum/robolimb/manf = all_robolimbs[prosfab.manufacturer] - newspecies = manf.suggested_species - O.species = GLOB.all_species[newspecies] - if(istype(O,/obj/item/organ/external)) - var/obj/item/organ/external/EO = O - if(EO.species.base_color) - var/r_skin = hex2num(copytext(EO.species.base_color,2,4)) - var/g_skin = hex2num(copytext(EO.species.base_color,4,6)) - var/b_skin = hex2num(copytext(EO.species.base_color,6,8)) - EO.s_col = list(r_skin, g_skin, b_skin) - //VOREStation Edit End + + if(!(O.organ_tag in manf.parts)) // Make sure we're using an actually present icon. + manf = all_robolimbs["Unbranded"] + + if(prosfab.species in manf.species_alternates) // If the prosthetics fab is set to say, Unbranded, and species set to 'Tajaran', it will make the Taj variant of Unbranded, if it exists. + manf = manf.species_alternates[prosfab.species] + + if(!prosfab.species || (prosfab.species in manf.species_cannot_use)) // Fabricator ensures the manufacturer can make parts for the species we're set to. + O.species = GLOB.all_species["[manf.suggested_species]"] + else + O.species = GLOB.all_species[prosfab.species] + else + O.species = GLOB.all_species["Human"] O.robotize(prosfab.manufacturer) O.dna = new/datum/dna() //Uuughhhh... why do I have to do this? O.dna.ResetUI() @@ -38,14 +39,21 @@ /datum/design/item/prosfab/pros/torso/Fabricate(var/newloc, var/fabricator) if(istype(fabricator, /obj/machinery/pros_fabricator)) var/obj/machinery/pros_fabricator/prosfab = fabricator - //VOREStation Edit - Suggesting a species var/newspecies = "Human" - if(prosfab.manufacturer) - var/datum/robolimb/manf = all_robolimbs[prosfab.manufacturer] - newspecies = manf.suggested_species + + var/datum/robolimb/manf = all_robolimbs[prosfab.manufacturer] + + if(manf) + if(prosfab.species in manf.species_alternates) // If the prosthetics fab is set to say, Unbranded, and species set to 'Tajaran', it will make the Taj variant of Unbranded, if it exists. + manf = manf.species_alternates[prosfab.species] + + if(!prosfab.species || (prosfab.species in manf.species_cannot_use)) + newspecies = manf.suggested_species + else + newspecies = prosfab.species + var/mob/living/carbon/human/H = new(newloc,newspecies) - //VOREStation Edit End - H.stat = DEAD + H.set_stat(DEAD) H.gender = gender for(var/obj/item/organ/external/EO in H.organs) if(EO.organ_tag == BP_TORSO || EO.organ_tag == BP_GROIN) @@ -55,11 +63,26 @@ for(var/obj/item/organ/external/O in H.organs) O.species = GLOB.all_species[newspecies] - O.robotize(prosfab.manufacturer) + + if(!(O.organ_tag in manf.parts)) // Make sure we're using an actually present icon. + manf = all_robolimbs["Unbranded"] + + O.robotize(manf.company) O.dna = new/datum/dna() O.dna.ResetUI() O.dna.ResetSE() + // Skincolor weirdness. + O.s_col[1] = 0 + O.s_col[2] = 0 + O.s_col[3] = 0 + + // Resetting the UI does strange things for the skin of a non-human robot, which should be controlled by a whole different thing. + H.r_skin = 0 + H.g_skin = 0 + H.b_skin = 0 + H.dna.ResetUIFrom(H) + H.real_name = "Synthmorph #[rand(100,999)]" H.name = H.real_name H.dir = 2 diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm index dc3a0cf437..3837e13aae 100644 --- a/code/modules/research/protolathe.dm +++ b/code/modules/research/protolathe.dm @@ -3,7 +3,7 @@ icon_state = "protolathe" flags = OPENCONTAINER circuit = /obj/item/weapon/circuitboard/protolathe - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 30 active_power_usage = 5000 @@ -15,9 +15,9 @@ var/mat_efficiency = 1 var/speed = 1 - materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, MAT_PLASTEEL = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, MAT_LEAD = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0) + materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, MAT_PLASTEEL = 0, "plastic" = 0, MAT_GRAPHITE = 0, "gold" = 0, "silver" = 0, "osmium" = 0, MAT_LEAD = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0) - hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER) + hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_GRAPHITE, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER) /obj/machinery/r_n_d/protolathe/Initialize() . = ..() diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm index b7e9b78c56..5c33122815 100644 --- a/code/modules/research/rdmachines.dm +++ b/code/modules/research/rdmachines.dm @@ -7,7 +7,7 @@ icon = 'icons/obj/machines/research_vr.dmi' //VOREStation Edit - Replaced with Eris sprites density = 1 anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE var/busy = 0 var/obj/machinery/computer/rdconsole/linked_console diff --git a/code/modules/research/research.dm b/code/modules/research/research.dm index 5304f8c738..2c3f5fb6e9 100644 --- a/code/modules/research/research.dm +++ b/code/modules/research/research.dm @@ -222,27 +222,27 @@ research holder datum. /obj/item/weapon/disk/tech_disk name = "technology disk" desc = "A disk for storing technology data for further research." - icon = 'icons/obj/cloning.dmi' - icon_state = "datadisk2" + icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit + icon_state = "data-blue" //VOREStation Edit item_state = "card-id" + randpixel = 5 w_class = ITEMSIZE_SMALL matter = list(DEFAULT_WALL_MATERIAL = 30, "glass" = 10) var/datum/tech/stored /obj/item/weapon/disk/tech_disk/New() - pixel_x = rand(-5.0, 5) - pixel_y = rand(-5.0, 5) + randpixel_xy() /obj/item/weapon/disk/design_disk name = "component design disk" desc = "A disk for storing device design data for construction in lathes." - icon = 'icons/obj/cloning.dmi' - icon_state = "datadisk2" + icon = 'icons/obj/discs_vr.dmi' //VOREStation Edit + icon_state = "data-purple" //VOREStation Edit item_state = "card-id" + randpixel = 5 w_class = ITEMSIZE_SMALL matter = list(DEFAULT_WALL_MATERIAL = 30, "glass" = 10) var/datum/design/blueprint /obj/item/weapon/disk/design_disk/New() - pixel_x = rand(-5.0, 5) - pixel_y = rand(-5.0, 5) + randpixel_xy() diff --git a/code/modules/research/rigs_vr.dm b/code/modules/research/rigs_vr.dm deleted file mode 100644 index 24c02d3fdd..0000000000 --- a/code/modules/research/rigs_vr.dm +++ /dev/null @@ -1,123 +0,0 @@ -/* - O - rigsuit stuff - OA - rigs themselves - OB - rig modules - OBAA - general purpose - OBAB - mining - OBAC - medical - OBAD - sec/combat - OBAE - engineering/maintenance/cleaning -*/ - - -////// RIGSuit Stuff -/* -/datum/design/item/rig - req_tech = list(TECH_MATERIAL = 5, TECH_POWER = 5, TECH_MAGNET = 5) - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 6000, "uranium" = 4000) - -/datum/design/item/rig/AssembleDesignName() - ..() - name = "hardsuit prototype ([name])" - -/datum/design/item/rig/eva - name = "eva hardsuit (empty)" - id = "eva_hardsuit" - build_path = /obj/item/weapon/rig/eva - sort_string = "OAAAA" - -/datum/design/item/rig/mining - name = "industrial hardsuit (empty)" - id = "ind_hardsuit" - build_path = /obj/item/weapon/rig/industrial - sort_string = "OAAAB" - -/datum/design/item/rig/research - name = "ami hardsuit (empty)" - id = "ami_hardsuit" - build_path = /obj/item/weapon/rig/hazmat - sort_string = "OAAAC" - -/datum/design/item/rig/medical - name = "medical hardsuit (empty)" - id = "med_hardsuit" - build_path = /obj/item/weapon/rig/medical - sort_string = "OAAAD" -*/ - -/datum/design/item/rig_module - req_tech = list(TECH_MATERIAL = 5, TECH_POWER = 5, TECH_MAGNET = 5) - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000) - -/datum/design/item/rig_module/AssembleDesignName() - ..() - name = "rig module prototype ([name])" - -/datum/design/item/rig_module/maneuvering_jets - name = "maneuvering jets" - id = "rigmod_maneuveringjets" - build_path = /obj/item/rig_module/maneuvering_jets - sort_string = "OBAAA" - -/datum/design/item/rig_module/sprinter - name = "sprinter" - id = "rigmod_sprinter" - build_path = /obj/item/rig_module/sprinter - sort_string = "OBAAB" - -/datum/design/item/rig_module/plasma_cutter - name = "plasma cutter" - id = "rigmod_plasmacutter" - build_path = /obj/item/rig_module/device/plasmacutter - sort_string = "OBABA" - -/datum/design/item/rig_module/diamond_drill - name = "diamond drill" - id = "rigmod_diamonddrill" - build_path = /obj/item/rig_module/device/drill - sort_string = "OBABB" - -/datum/design/item/rig_module/anomaly_scanner - name = "anomaly scanner" - id = "rigmod_anomalyscanner" - build_path = /obj/item/rig_module/device/anomaly_scanner - sort_string = "OBABC" - -/datum/design/item/rig_module/orescanner - name = "ore scanner" - id = "rigmod_orescanner" - build_path = /obj/item/rig_module/device/orescanner - sort_string = "OBABD" - -/datum/design/item/rig_module/rescue_pharm - name = "rescue pharm" - id = "rigmod_rescue_pharm" - build_path = /obj/item/rig_module/rescue_pharm - sort_string = "OBACA" - -/datum/design/item/rig_module/lasercannon - name = "laser cannon" - id = "rigmod_lasercannon" - build_path = /obj/item/rig_module/mounted - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000, "diamond" = 2000) - sort_string = "OBADA" - -/datum/design/item/rig_module/egun - name = "energy gun" - id = "rigmod_egun" - build_path = /obj/item/rig_module/mounted/egun - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000, "diamond" = 1000) - sort_string = "OBADB" - -/datum/design/item/rig_module/taser - name = "taser" - id = "rigmod_taser" - build_path = /obj/item/rig_module/mounted/taser - sort_string = "OBADC" - -/datum/design/item/rig_module/rcd - name = "rcd" - id = "rigmod_rcd" - build_path = /obj/item/rig_module/device/rcd - materials = list(DEFAULT_WALL_MATERIAL = 6000, "glass" = 6000, "silver" = 4000, "uranium" = 2000, "diamond" = 2000) - sort_string = "OBAEA" \ No newline at end of file diff --git a/code/modules/research/server.dm b/code/modules/research/server.dm index 8963928b36..94762fc691 100644 --- a/code/modules/research/server.dm +++ b/code/modules/research/server.dm @@ -168,7 +168,7 @@ add_fingerprint(usr) usr.set_machine(src) if(!allowed(usr) && !emagged) - usr << "You do not have the required access level" + to_chat(usr, "You do not have the required access level") return if(href_list["main"]) @@ -296,7 +296,7 @@ if(!emagged) playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) emagged = 1 - user << "You you disable the security protocols." + to_chat(user, "You you disable the security protocols.") src.updateUsrDialog() return 1 diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm index 11739438b5..20457c98fc 100644 --- a/code/modules/resleeving/computers.dm +++ b/code/modules/resleeving/computers.dm @@ -71,12 +71,12 @@ pods += P P.connected = src P.name = "[initial(P.name)] #[pods.len]" - user << "You connect [P] to [src]." + to_chat(user, "You connect [P] to [src].") else if(istype(W, /obj/item/weapon/disk/transcore) && SStranscore && !SStranscore.core_dumped) user.unEquip(W) disk = W disk.forceMove(src) - user << "You insert \the [W] into \the [src]." + to_chat(user, "You insert \the [W] into \the [src].") if(istype(W, /obj/item/weapon/disk/body_record)) var/obj/item/weapon/disk/body_record/brDisk = W if(!brDisk.stored) @@ -391,7 +391,7 @@ /obj/item/weapon/cmo_disk_holder/attack_self(var/mob/attacker) playsound(src, 'sound/items/poster_ripped.ogg', 50) - attacker << "You tear open \the [name]." + to_chat(attacker, "You tear open \the [name].") attacker.unEquip(src) var/obj/item/weapon/disk/transcore/newdisk = new(get_turf(src)) attacker.put_in_any_hand_if_possible(newdisk) diff --git a/code/modules/resleeving/designer.dm b/code/modules/resleeving/designer.dm index bdb6b5872a..38feff922e 100644 --- a/code/modules/resleeving/designer.dm +++ b/code/modules/resleeving/designer.dm @@ -5,7 +5,7 @@ name = "body design console" catalogue_data = list(/datum/category_item/catalogue/information/organization/vey_med, /datum/category_item/catalogue/technology/resleeving) - icon = 'icons/obj/computer_vr.dmi' + icon = 'icons/obj/computer.dmi' icon_keyboard = "med_key" icon_screen = "explosive" light_color = "#315ab4" @@ -371,8 +371,8 @@ desc = "It has a small label: \n\ \"Portable Body Record Storage Disk. \n\ Insert into resleeving control console\"" - icon = 'icons/obj/cloning.dmi' - icon_state = "datadisk2" + icon = 'icons/obj/discs_vr.dmi' + icon_state = "data-green" item_state = "card-id" w_class = ITEMSIZE_SMALL var/datum/transhuman/body_record/stored = null diff --git a/code/modules/resleeving/implant.dm b/code/modules/resleeving/implant.dm index 0edcbd7654..535b53b028 100644 --- a/code/modules/resleeving/implant.dm +++ b/code/modules/resleeving/implant.dm @@ -69,14 +69,14 @@ return if(imps.len) - user << "You eject a backup implant." + to_chat(user, "You eject a backup implant.") var/obj/item/weapon/implant/backup/imp = imps[imps.len] imp.forceMove(get_turf(user)) imps -= imp user.put_in_any_hand_if_possible(imp) update() else - user << "\The [src] is empty." + to_chat(user, "\The [src] is empty.") return @@ -88,9 +88,9 @@ W.germ_level = 0 W.forceMove(src) update() - user << "You load \the [W] into \the [src]." + to_chat(user, "You load \the [W] into \the [src].") else - user << "\The [src] is already full!" + to_chat(user, "\The [src] is already full!") /obj/item/weapon/backup_implanter/attack(mob/M as mob, mob/user as mob) if (!istype(M, /mob/living/carbon)) diff --git a/code/modules/resleeving/infomorph.dm b/code/modules/resleeving/infomorph.dm index 9f5528d547..e734e19108 100644 --- a/code/modules/resleeving/infomorph.dm +++ b/code/modules/resleeving/infomorph.dm @@ -188,7 +188,7 @@ var/list/infomorph_emotions = list( medicalActive2 = null medical_cannotfind = 0 SSnanoui.update_uis(src) - usr << "You reset your record-viewing software." + to_chat(usr, "You reset your record-viewing software.") /* /mob/living/silicon/infomorph/proc/switchCamera(var/obj/machinery/camera/C) @@ -364,16 +364,16 @@ var/list/infomorph_emotions = list( switch(alert(user, "Do you wish to add access to [src] or remove access from [src]?",,"Add Access","Remove Access", "Cancel")) if("Add Access") idcard.access |= ID.access - user << "You add the access from the [W] to [src]." + to_chat(user, "You add the access from the [W] to [src].") return if("Remove Access") idcard.access = null - user << "You remove the access from [src]." + to_chat(user, "You remove the access from [src].") return if("Cancel") return else if (istype(W, /obj/item/weapon/card/id) && idaccessible == 0) - user << "[src] is not accepting access modifcations at this time." + to_chat(user, "[src] is not accepting access modifcations at this time.") return //////////////////// MISC VERBS @@ -419,7 +419,7 @@ var/list/infomorph_emotions = list( if(radio) radio.ui_interact(src,"main",null,1,conscious_state) else - to_chat(src,"You don't have a radio!") + to_chat(src, "You don't have a radio!") /mob/living/silicon/infomorph/say(var/msg) if(silence_time) @@ -448,7 +448,7 @@ var/global/list/default_infomorph_software = list() var/datum/infomorph_software/P = new type() if(infomorph_software_by_key[P.id]) var/datum/infomorph_software/O = infomorph_software_by_key[P.id] - world << "Infomorph software module [P.name] has the same key as [O.name]!" + to_world("Infomorph software module [P.name] has the same key as [O.name]!") r = 0 continue infomorph_software_by_key[P.id] = P @@ -567,7 +567,7 @@ var/global/list/default_infomorph_software = list() pose = addtext(pose,".") //Makes sure all emotes end with a period. msg += "\nIt is [pose]" - user << msg + to_chat(user,msg) /mob/living/silicon/infomorph/Life() //We're dead or EMP'd or something. diff --git a/code/modules/resleeving/infomorph_software.dm b/code/modules/resleeving/infomorph_software.dm index bb5b7fc870..2d2439b614 100644 --- a/code/modules/resleeving/infomorph_software.dm +++ b/code/modules/resleeving/infomorph_software.dm @@ -205,9 +205,9 @@ if(prob(20)) for(var/mob/living/silicon/ai/AI in player_list) if(T.loc) - AI << "Network Alert: Brute-force encryption crack in progress in [T.loc]." + to_chat(AI, "Network Alert: Brute-force encryption crack in progress in [T.loc].") else - AI << "Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location." + to_chat(AI, "Network Alert: Brute-force encryption crack in progress. Unable to pinpoint location.") var/obj/machinery/door/D = cable.machine if(!istype(D)) @@ -328,7 +328,7 @@ if(href_list["send"]) P.sradio.send_signal("ACTIVATE") for(var/mob/O in hearers(1, P.loc)) - O.show_message(text("\icon[] *beep* *beep*", P), 3, "*beep* *beep*", 2) + O.show_message("[bicon(P)] *beep* *beep*", 3, "*beep* *beep*", 2) return 1 else if(href_list["freq"]) diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm index a66029bc03..a3a794c4fd 100644 --- a/code/modules/resleeving/machines.dm +++ b/code/modules/resleeving/machines.dm @@ -340,13 +340,13 @@ /obj/machinery/transhuman/synthprinter/attack_hand(mob/user as mob) if((busy == 0) || (stat & NOPOWER)) return - user << "Current print cycle is [busy]% complete." + to_chat(user, "Current print cycle is [busy]% complete.") return /obj/machinery/transhuman/synthprinter/attackby(obj/item/W as obj, mob/user as mob) src.add_fingerprint(user) if(busy) - user << "\The [src] is busy. Please wait for completion of previous operation." + to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.") return if(default_deconstruction_screwdriver(user, W)) return @@ -355,15 +355,15 @@ if(default_part_replacement(user, W)) return if(panel_open) - user << "You can't load \the [src] while it's opened." + to_chat(user, "You can't load \the [src] while it's opened.") return if(!istype(W, /obj/item/stack/material)) - user << "You cannot insert this item into \the [src]!" + to_chat(user, "You cannot insert this item into \the [src]!") return var/obj/item/stack/material/S = W if(!(S.material.name in stored_material)) - user << "\the [src] doesn't accept [S.material]!" + to_chat(user, "\the [src] doesn't accept [S.material]!") return var/amnt = S.perunit @@ -374,9 +374,9 @@ stored_material[S.material.name] += amnt S.use(1) count++ - user << "You insert [count] [S.name] into \the [src]." + to_chat(user, "You insert [count] [S.name] into \the [src].") else - user << "\the [src] cannot hold more [S.name]." + to_chat(user, "\the [src] cannot hold more [S.name].") updateUsrDialog() return @@ -474,7 +474,7 @@ return for(var/mob/living/carbon/slime/M in range(1, G.affecting)) if(M.Victim == G.affecting) - usr << "[G.affecting:name] will not fit into the [src.name] because they have a slime latched onto their head." + to_chat(usr, "[G.affecting:name] will not fit into the [src.name] because they have a slime latched onto their head.") return var/mob/M = G.affecting if(put_mob(M)) @@ -487,7 +487,7 @@ C.removePersonality() qdel(C) sleevecards++ - to_chat(user,"You store \the [C] in \the [src].") + to_chat(user, "You store \the [C] in \the [src].") return return ..() @@ -541,7 +541,7 @@ //In case they already had a mind! if(occupant && occupant.mind) - occupant << "You feel your mind being overwritten..." + to_chat(occupant, "You feel your mind being overwritten...") log_and_message_admins("was resleeve-wiped from their body.",occupant.mind) occupant.ghostize() @@ -555,7 +555,7 @@ occupant.apply_vore_prefs() //Cheap hack for now to give them SOME bellies. if(MR.one_time) var/how_long = round((world.time - MR.last_update)/10/60) - to_chat(occupant,"Your mind backup was a 'one-time' backup. \ + to_chat(occupant, "Your mind backup was a 'one-time' backup. \ You will not be able to remember anything since the backup, [how_long] minutes ago.") //Re-supply a NIF if one was backed up with them. @@ -578,17 +578,23 @@ //Inform them and make them a little dizzy. if(confuse_amount + blur_amount <= 16) - occupant << "You feel a small pain in your head as you're given a new backup implant. Your new body feels comfortable already, however." + to_chat(occupant, "You feel a small pain in your head as you're given a new backup implant. Your new body feels comfortable already, however.") else - occupant << "You feel a small pain in your head as you're given a new backup implant. Oh, and a new body. It's disorienting, to say the least." + to_chat(occupant, "You feel a small pain in your head as you're given a new backup implant. Oh, and a new body. It's disorienting, to say the least.") occupant.confused = max(occupant.confused, confuse_amount) // Apply immedeate effects occupant.eye_blurry = max(occupant.eye_blurry, blur_amount) - if(!(occupant.mind.vore_death)) - occupant.add_modifier(/datum/modifier/resleeving_sickness, sickness_duration) // YW Edit + + // Vore deaths get a fake modifier labeled as such + if(!occupant.mind) + log_debug("[occupant] didn't have a mind to check for vore_death, which may be problematic.") + + if(occupant.mind?.vore_death) + occupant.add_modifier(/datum/modifier/resleeving_sickness, sickness_duration) //YW Edit: you git vored you still get the same debuff + occupant.mind.vore_death = FALSE + // Normal ones get a normal modifier to nerf charging into combat else - occupant.add_modifier(/datum/modifier/resleeving_sickness, sickness_duration) // Much more serious if it wasn't a death by vore though - occupant.mind.vore_death = FALSE // Reset our death type. Just in case + occupant.add_modifier(/datum/modifier/resleeving_sickness, sickness_duration) if(occupant.mind && occupant.original_player && ckey(occupant.mind.key) != occupant.original_player) log_and_message_admins("is now a cross-sleeved character. Body originally belonged to [occupant.real_name]. Mind is now [occupant.mind.name].",occupant) @@ -611,10 +617,10 @@ /obj/machinery/transhuman/resleever/proc/put_mob(mob/living/carbon/human/M as mob) if(!ishuman(M)) - usr << "\The [src] cannot hold this!" + to_chat(usr, "\The [src] cannot hold this!") return if(src.occupant) - usr << "\The [src] is already occupied!" + to_chat(usr, "\The [src] is already occupied!") return if(M.client) M.client.perspective = EYE_PERSPECTIVE diff --git a/code/modules/resleeving/resleeving_sickness.dm b/code/modules/resleeving/resleeving_sickness.dm index 93c09fa496..483bb3f98e 100644 --- a/code/modules/resleeving/resleeving_sickness.dm +++ b/code/modules/resleeving/resleeving_sickness.dm @@ -16,9 +16,32 @@ accuracy_dispersion = 20 // 20% less precise. /datum/modifier/faux_resleeving_sickness - name = "resleeving sickness" - desc = "You feel somewhat weak and unfocused, having been sleeved not so long ago." + name = "resleeving sickness (vore)" + desc = "You feel somewhat weak and unfocused, having been sleeved not so long ago. (OOC: No real penalty for vore-related deaths)" stacks = MODIFIER_STACK_EXTEND on_created_text = "You feel slightly weak and unfocused." - on_expired_text = "You feel your strength and focus return to you." \ No newline at end of file + on_expired_text = "You feel your strength and focus return to you." + +/datum/modifier/gory_devourment + name = "gory devourment" + desc = "You are being devoured! Dying right now would definitely be same as dying as food." + stacks = MODIFIER_STACK_EXTEND + hidden = TRUE + var/datum/mind/cached_mind = null + +/datum/modifier/gory_devourment/can_apply(var/mob/living/L) + if(L.stat == DEAD) + return FALSE + else + return TRUE + +/datum/modifier/gory_devourment/on_applied() + cached_mind = holder.mind + return ..() + +/datum/modifier/gory_devourment/on_expire() + if(holder.stat == DEAD) + cached_mind?.vore_death = TRUE + cached_mind = null //Don't keep a hardref + return ..() \ No newline at end of file diff --git a/code/modules/rogueminer_vr/asteroid.dm b/code/modules/rogueminer_vr/asteroid.dm index fec6c4aa67..d1e046cfb1 100644 --- a/code/modules/rogueminer_vr/asteroid.dm +++ b/code/modules/rogueminer_vr/asteroid.dm @@ -5,8 +5,8 @@ /datum/rogue/asteroid //Composition - var/type_wall = /turf/simulated/mineral //Type of turf used to generate the asteroid - var/type_under = /turf/simulated/mineral/floor //Type of turf that's under the normal one + var/type_wall = /turf/simulated/mineral/vacuum //Type of turf used to generate the asteroid + var/type_under = /turf/simulated/mineral/floor/vacuum //Type of turf that's under the normal one //Dimensions var/coresize = 3 //The size of the center square @@ -99,7 +99,7 @@ spot_add(1,3,type_wall) spot_add(2,1,type_wall) spot_add(2,2,type_under) //Center floor - spot_add(2,2,/obj/random/cargopod) //Loot! + spot_add(2,2,/obj/random/roguemineloot) //Loot! spot_add(2,3,type_wall) spot_add(3,1,type_wall) spot_add(3,2,type_wall) @@ -112,7 +112,7 @@ New() ..() - spot_add(2,2,/obj/random/cargopod) //EXTRA loot! + spot_add(2,2,/obj/random/roguemineloot) //EXTRA loot! spot_add(2,2,/mob/living/simple_mob/animal/space/alien) //GRRR //Longer cargo container for higher difficulties @@ -142,9 +142,9 @@ spot_add(3,3,type_under) //Mid floor spot_add(4,3,type_under) //Right floor - spot_add(2,3,/obj/random/cargopod) //Left loot - spot_add(3,3,/obj/random/cargopod) //Mid loot - spot_add(4,3,/obj/random/cargopod) //Right loot + spot_add(2,3,/obj/random/roguemineloot) //Left loot + spot_add(3,3,/obj/random/roguemineloot) //Mid loot + spot_add(4,3,/obj/random/roguemineloot) //Right loot if(prob(30)) - spot_add(3,3,/mob/living/simple_mob/animal/space/alien) //And maybe a friend. + spot_add(3,3,/mob/living/simple_mob/animal/space/alien/sentinel/praetorian) //And maybe a big friend for big loot. diff --git a/code/modules/rogueminer_vr/controller.dm b/code/modules/rogueminer_vr/controller.dm index 1481ed3258..8f7d883992 100644 --- a/code/modules/rogueminer_vr/controller.dm +++ b/code/modules/rogueminer_vr/controller.dm @@ -3,7 +3,7 @@ // Makes mining zones more difficult as you enter new ones // THIS IS THE FIRST UNIT INITIALIZED THAT STARTS EVERYTHING ////////////////////////////// -var/datum/controller/rogue/rm_controller = new() +var/datum/controller/rogue/rm_controller /datum/controller/rogue var/list/datum/rogue/zonemaster/all_zones = list() @@ -13,11 +13,19 @@ var/datum/controller/rogue/rm_controller = new() //So I don't have to do absurd list[list[thing]] over and over. var/list/diffstep_nums = list( 100, - 200, - 300, - 450, + 350, 600, - 800) + 900, + 1250, + 1700) + + var/list/diffstep_chances = list( + 10, + 20, + 30, + 45, + 60, + 80) var/list/diffstep_strs = list( "Low", @@ -33,13 +41,16 @@ var/datum/controller/rogue/rm_controller = new() //Info about our current step var/diffstep = 1 + //Difficulty cap + var/max_diffstep = 6 + //The current mining zone that the shuttle goes to and whatnot var/datum/rogue/zonemaster/current_zone = null var/datum/rogue/zonemaster/previous_zone = null // The world.time at which the scanner was last run (for cooldown) var/last_scan = 0 - var/scan_wait = 10 //In minutes + var/scan_wait = 5 //In minutes var/debugging = 0 @@ -47,29 +58,46 @@ var/datum/controller/rogue/rm_controller = new() var/prefabs = list( "tier1" = list(/datum/rogue/asteroid/predef/cargo), "tier2" = list(/datum/rogue/asteroid/predef/cargo,/datum/rogue/asteroid/predef/cargo/angry), - "tier3" = list(/datum/rogue/asteroid/predef/cargo/angry,/datum/rogue/asteroid/predef/cargo_large), - "tier4" = list(/datum/rogue/asteroid/predef/cargo/angry,/datum/rogue/asteroid/predef/cargo_large), + "tier3" = list(/datum/rogue/asteroid/predef/cargo,/datum/rogue/asteroid/predef/cargo/angry), + "tier4" = list(/datum/rogue/asteroid/predef/cargo,/datum/rogue/asteroid/predef/cargo/angry,/datum/rogue/asteroid/predef/cargo_large), "tier5" = list(/datum/rogue/asteroid/predef/cargo/angry,/datum/rogue/asteroid/predef/cargo_large), "tier6" = list(/datum/rogue/asteroid/predef/cargo/angry,/datum/rogue/asteroid/predef/cargo_large) ) ///// Monster Lists ///// - var/mobs = list(/* - "tier1" = list(/mob/living/simple_mob/animal/space/carp, /mob/living/simple_mob/animal/space/goose), + var/mobs = list( + "tier1" = list( + /mob/living/simple_mob/animal/space/bats/roguemines = 3, + /mob/living/simple_mob/animal/space/carp/roguemines = 2, + /mob/living/simple_mob/animal/space/goose/roguemines = 1), - "tier2" = list(/mob/living/simple_mob/animal/space/carp, /mob/living/simple_mob/animal/space/goose), + "tier2" = list( + /mob/living/simple_mob/animal/space/bats/roguemines = 1, + /mob/living/simple_mob/animal/space/carp/roguemines = 2, + /mob/living/simple_mob/animal/space/goose/roguemines = 2, + /mob/living/simple_mob/animal/wolf/space/roguemines = 1), - "tier3" = list(/mob/living/simple_mob/animal/space/carp, /mob/living/simple_mob/animal/space/goose, - /mob/living/simple_mob/animal/space/bear, /mob/living/simple_mob/animal/space/carp/strong), + "tier3" = list( + /mob/living/simple_mob/animal/space/carp/roguemines = 1, + /mob/living/simple_mob/animal/space/goose/roguemines = 1, + /mob/living/simple_mob/animal/wolf/space/roguemines = 3, + /mob/living/simple_mob/animal/space/carp/large/roguemines = 2, + /mob/living/simple_mob/animal/space/bear/roguemines = 1), - "tier4" = list(/mob/living/simple_mob/animal/space/carp, /mob/living/simple_mob/animal/space/goose, /mob/living/simple_mob/animal/space/bear, - /mob/living/simple_mob/animal/space/carp/strong, /mob/living/simple_mob/animal/space/carp/pike/weak), + "tier4" = list( + /mob/living/simple_mob/animal/wolf/space/roguemines = 1, + /mob/living/simple_mob/animal/space/carp/large/roguemines = 4, + /mob/living/simple_mob/animal/space/bear/roguemines = 2), - "tier5" = list(/mob/living/simple_mob/animal/space/carp, /mob/living/simple_mob/animal/space/bear, /mob/living/simple_mob/animal/space/carp/pike/weak, - /mob/living/simple_mob/animal/space/carp/strong, /mob/living/simple_mob/animal/space/carp/pike), + "tier5" = list( + /mob/living/simple_mob/animal/space/carp/large/roguemines = 2, + /mob/living/simple_mob/animal/space/bear/roguemines = 4, + /mob/living/simple_mob/vore/aggressive/corrupthound/space/roguemines = 1), - "tier6" = list(/mob/living/simple_mob/animal/space/bear, /mob/living/simple_mob/animal/space/carp/strong, - /mob/living/simple_mob/animal/space/carp/pike, /mob/living/simple_mob/animal/space/carp/pike/weak)*/ //VORESTATION AI TEMPORARY REMOVAL + "tier6" = list( + /mob/living/simple_mob/animal/space/bear/roguemines = 6, + /mob/living/simple_mob/vore/aggressive/corrupthound/space/roguemines = 4, + /mob/living/simple_mob/animal/space/carp/large/huge/roguemines = 1) ) /datum/controller/rogue/New() @@ -79,7 +107,7 @@ var/datum/controller/rogue/rm_controller = new() //decay() //Decay removed for now, since people aren't getting high scores as it is. /datum/controller/rogue/proc/decay(var/manual = 0) - world.log << "RM(stats): DECAY on controller from [difficulty] to [difficulty+(RM_DIFF_DECAY_AMT)] min 100." //DEBUG code for playtest stats gathering. + to_world_log("RM(stats): DECAY on controller from [difficulty] to [difficulty+(RM_DIFF_DECAY_AMT)] min 100.") //DEBUG code for playtest stats gathering. adjust_difficulty(RM_DIFF_DECAY_AMT) if(!manual) //If it was called manually somehow, then don't start the timer, just decay now. @@ -90,7 +118,7 @@ var/datum/controller/rogue/rm_controller = new() /datum/controller/rogue/proc/dbg(var/message) ASSERT(message) //I want a stack trace if there's no message if(debugging) - world.log << "[message]" + to_world_log("[message]") /datum/controller/rogue/proc/adjust_difficulty(var/amt) ASSERT(amt) @@ -99,8 +127,9 @@ var/datum/controller/rogue/rm_controller = new() if(difficulty < diffstep_nums[diffstep]) diffstep-- - else if(difficulty >= diffstep_nums[diffstep+1]) - diffstep++ + else if(diffstep < max_diffstep) + if(difficulty >= diffstep_nums[diffstep+1]) + diffstep++ /datum/controller/rogue/proc/get_oldest_zone() var/oldest_time = world.time @@ -156,7 +185,7 @@ var/datum/controller/rogue/rm_controller = new() ZM_target = pick(clean_zones) if(ZM_target) - world.log << "RM(stats): SCORING [ready_zones.len] zones (if unscored)." //DEBUG code for playtest stats gathering. + to_world_log("RM(stats): SCORING [ready_zones.len] zones (if unscored).") //DEBUG code for playtest stats gathering. for(var/datum/rogue/zonemaster/ZM_toscore in ready_zones) //Score all the zones first. if(ZM_toscore.scored) continue ZM_toscore.score_zone() diff --git a/code/modules/rogueminer_vr/roguemines_mobs.dm b/code/modules/rogueminer_vr/roguemines_mobs.dm new file mode 100644 index 0000000000..a296c6d811 --- /dev/null +++ b/code/modules/rogueminer_vr/roguemines_mobs.dm @@ -0,0 +1,23 @@ +/mob/living/simple_mob/animal/space/bats/roguemines + faction = "roguemines" + +/mob/living/simple_mob/animal/space/carp/roguemines + faction = "roguemines" + +/mob/living/simple_mob/animal/space/goose/roguemines + faction = "roguemines" + +/mob/living/simple_mob/animal/wolf/space/roguemines + faction = "roguemines" + +/mob/living/simple_mob/animal/space/carp/large/roguemines + faction = "roguemines" + +/mob/living/simple_mob/animal/space/bear/roguemines + faction = "roguemines" + +/mob/living/simple_mob/vore/aggressive/corrupthound/space/roguemines + faction = "roguemines" + +/mob/living/simple_mob/animal/space/carp/large/huge/roguemines + faction = "roguemines" \ No newline at end of file diff --git a/code/modules/rogueminer_vr/wrappers.dm b/code/modules/rogueminer_vr/wrappers.dm index 0e19a5cb00..2aae52d123 100644 --- a/code/modules/rogueminer_vr/wrappers.dm +++ b/code/modules/rogueminer_vr/wrappers.dm @@ -1,6 +1,31 @@ +/obj/effect/step_trigger/teleporter/roguemine_loop/north/New() + ..() + teleport_x = x + teleport_y = 16 + teleport_z = z + +/obj/effect/step_trigger/teleporter/roguemine_loop/south/New() + ..() + teleport_x = x + teleport_y = world.maxy - 16 + teleport_z = z + +/obj/effect/step_trigger/teleporter/roguemine_loop/west/New() + ..() + teleport_x = world.maxx - 16 + teleport_y = y + teleport_z = z + +/obj/effect/step_trigger/teleporter/roguemine_loop/east/New() + ..() + teleport_x = 16 + teleport_y = y + teleport_z = z + +/* /obj/effect/step_trigger/teleporter/random/rogue - teleport_z = 7 - teleport_z_offset = 7 + teleport_z = 15 + teleport_z_offset = 0 /obj/effect/step_trigger/teleporter/random/rogue/fourbyfour var/margin = 15 //How far from the side should we teleport? Make sure to place the opposite @@ -8,7 +33,7 @@ //else people will get stuck in the margins //Important for auto-determining the size, as well. - var/mapsize = 300 //I don't know of a way to obtain map size sorry. + var/mapsize = 140 //I don't know of a way to obtain map size sorry. //Important infos! var/quad_L @@ -84,7 +109,7 @@ teleport_x_offset = quad_R - margin teleport_y = quad_U - margin teleport_y_offset = quad_U - margin - +*/ //Sure, I could probably do this with math. But I'm tired. /* S1 300 diff --git a/code/modules/rogueminer_vr/zone_console.dm b/code/modules/rogueminer_vr/zone_console.dm index b009d3a1e0..748b508dfb 100644 --- a/code/modules/rogueminer_vr/zone_console.dm +++ b/code/modules/rogueminer_vr/zone_console.dm @@ -2,9 +2,9 @@ // The zone control console, fluffed ingame as // a scanner console for the asteroid belt ////////////////////////////// -#define OUTPOST_Z 6 +#define OUTPOST_Z 2 #define TRANSIT_Z 3 -#define BELT_Z 8 +#define BELT_Z 10 /obj/machinery/computer/roguezones name = "asteroid belt scanning computer" @@ -12,7 +12,7 @@ icon_keyboard = "tech_key" icon_screen = "request" light_color = "#315ab4" - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 250 active_power_usage = 500 circuit = /obj/item/weapon/circuitboard/roguezones @@ -26,6 +26,11 @@ /obj/machinery/computer/roguezones/Initialize() . = ..() shuttle_control = locate(/obj/machinery/computer/shuttle_control/belter) + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/computer/roguezones/LateInitialize() + if(!rm_controller) + rm_controller = new /datum/controller/rogue() /obj/machinery/computer/roguezones/attack_ai(mob/user as mob) return attack_hand(user) @@ -56,19 +61,19 @@ if(!shuttle_control) data["shuttle_location"] = "Unknown" data["shuttle_at_station"] = 0 - else if(shuttle_control.z == OUTPOST_Z) + else if(shuttle_control.z in using_map.belter_docked_z) data["shuttle_location"] = "Landed" data["shuttle_at_station"] = 1 - else if(shuttle_control.z == TRANSIT_Z) + else if(shuttle_control.z == using_map.belter_transit_z) data["shuttle_location"] = "In-transit" data["shuttle_at_station"] = 0 - else if(shuttle_control.z == BELT_Z) + else if(shuttle_control.z == using_map.belter_belt_z) data["shuttle_location"] = "Belt" data["shuttle_at_station"] = 0 var/can_scan = 0 if(chargePercent >= 100) //Keep having weird problems with these in one 'if' statement - if(shuttle_control && shuttle_control.z == OUTPOST_Z) //Even though I put them all in parens to avoid OoO problems... + if(shuttle_control && (shuttle_control.z in using_map.belter_docked_z)) //Even though I put them all in parens to avoid OoO problems... if(!curZoneOccupied) //Not sure why. if(!scanning) can_scan = 1 @@ -77,7 +82,7 @@ data["scan_ready"] = can_scan // Permit emergency recall of the shuttle if its stranded in a zone with just dead people. - data["can_recall_shuttle"] = (shuttle_control && shuttle_control.z == BELT_Z && !curZoneOccupied) + data["can_recall_shuttle"] = (shuttle_control && (shuttle_control.z in using_map.belter_belt_z) && !curZoneOccupied) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -117,8 +122,9 @@ var/datum/rogue/zonemaster/ZM_target = rm_controller.prepare_new_zone() //Update shuttle destination. - var/datum/shuttle/ferry/S = shuttle_controller.shuttles["Belter"] - S.area_offsite = ZM_target.myshuttle + var/datum/shuttle/autodock/ferry/S = SSshuttles.shuttles["Belter"] + S.landmark_offsite = ZM_target.myshuttle_landmark + S.next_location = S.get_location_waypoint(!S.location) //Re-enable shuttle. shuttle_control.shuttle_tag = "Belter" @@ -141,12 +147,12 @@ /obj/machinery/computer/roguezones/proc/failsafe_shuttle_recall() if(!shuttle_control) return // Shuttle computer has been destroyed - if (shuttle_control.z != BELT_Z) + if (!(shuttle_control.z in using_map.belter_belt_z)) return // Usable only when shuttle is away if(rm_controller.current_zone && rm_controller.current_zone.is_occupied()) return // Not usable if shuttle is in occupied zone // Okay do it - var/datum/shuttle/ferry/S = shuttle_controller.shuttles["Belter"] + var/datum/shuttle/autodock/ferry/S = SSshuttles.shuttles["Belter"] S.launch(usr) /obj/item/weapon/circuitboard/roguezones diff --git a/code/modules/rogueminer_vr/zonemaster.dm b/code/modules/rogueminer_vr/zonemaster.dm index c2775f9ce7..2c0839fc95 100644 --- a/code/modules/rogueminer_vr/zonemaster.dm +++ b/code/modules/rogueminer_vr/zonemaster.dm @@ -6,7 +6,8 @@ /datum/rogue/zonemaster //our area var/area/asteroid/rogue/myarea - var/area/shuttle/belter/myshuttle + // var/area/shuttle/belter/myshuttle + var/obj/effect/shuttle_landmark/myshuttle_landmark //world.time var/prepared_at = 0 @@ -32,7 +33,9 @@ /datum/rogue/zonemaster/New(var/area/A) ASSERT(A) myarea = A - myshuttle = locate(myarea.shuttle_area) + myshuttle_landmark = locate(/obj/effect/shuttle_landmark) in myarea + if(!istype(myshuttle_landmark)) + warning("Zonemaster cannot find a shuttle landmark in its area '[A]'") spawn(10) //This is called from controller New() and freaks out if this calls back too fast. rm_controller.mark_clean(src) @@ -46,7 +49,7 @@ if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it. continue var/area/A = get_area(H) - if((A == myarea) || (A == myshuttle)) //The loc of a turf is the area it is in. + if(A == myarea) //The loc of a turf is the area it is in. humans++ return humans @@ -151,7 +154,7 @@ rm_controller.dbg("ZM(pa): Replacing [P.type] with [T].") var/turf/newturf = P.ChangeTurf(T) switch(newturf.type) - if(/turf/simulated/mineral) + if(/turf/simulated/mineral/vacuum) place_resources(newturf) newturf.update_icon(1) @@ -167,7 +170,7 @@ #define ARTIFACTSPAWNNUM_LOWER 6 #define ARTIFACTSPAWNNUM_UPPER 12 //Replace with difficulty-based ones. - if(!M.mineral && prob(rm_controller.diffstep_nums[rm_controller.diffstep]/10)) //Difficulty translates directly into ore chance + if(!M.mineral && prob(rm_controller.diffstep_chances[rm_controller.diffstep])) //Difficulty translates directly into ore chance rm_controller.dbg("ZM(par): Adding mineral to [M.x],[M.y].") M.make_ore(rm_controller.diffstep >= 3 ? 1 : 0) mineral_rocks += M @@ -293,7 +296,7 @@ break if(SP) rm_controller.dbg("ZM(p): Got a mob spawnpoint, so picking a type.") - var/mobchoice = pick(rm_controller.mobs["tier[rm_controller.diffstep]"]) + var/mobchoice = pickweight(rm_controller.mobs["tier[rm_controller.diffstep]"]) rm_controller.dbg("ZM(p): Picked [mobchoice] to spawn.") var/mob/living/newmob = new mobchoice(get_turf(SP)) newmob.faction = "asteroid_belt" @@ -302,7 +305,7 @@ sleep(delay) rm_controller.dbg("ZM(p): Zone generation done.") - world.log << "RM(stats): PREP [myarea] at [world.time] with [spawned_mobs.len] mobs, [mineral_rocks.len] minrocks, total of [rockspawns.len] rockspawns, [mobspawns.len] mobspawns." //DEBUG code for playtest stats gathering. + to_world_log("RM(stats): PREP [myarea] at [world.time] with [spawned_mobs.len] mobs, [mineral_rocks.len] minrocks, total of [rockspawns.len] rockspawns, [mobspawns.len] mobspawns.") //DEBUG code for playtest stats gathering. prepared_at = world.time rm_controller.mark_ready(src) return myarea @@ -321,7 +324,7 @@ mobspawns.Cut() rm_controller.dbg("ZM(rs): Now [mobspawns.len] mobspawns.") for(var/obj/rogue_mobspawner/SP in myarea.mob_spawns) - if(prob(rm_controller.diffstep_nums[rm_controller.diffstep]/10)) + if(prob(rm_controller.diffstep_chances[rm_controller.diffstep])) mobspawns += SP original_mobs++ rm_controller.dbg("ZM(rs): Picked [mobspawns.len] new mobspawns with [chance]% chance.") @@ -362,13 +365,13 @@ rm_controller.adjust_difficulty(tally) rm_controller.dbg("ZM(sz): Finished scoring and adjusted by [tally].") - world.log << "RM(stats): SCORE [myarea] for [tally]." //DEBUG code for playtest stats gathering. + to_world_log("RM(stats): SCORE [myarea] for [tally].") //DEBUG code for playtest stats gathering. return tally //Overall 'destroy' proc (marks as unready) /datum/rogue/zonemaster/proc/clean_zone(var/delay = 1) rm_controller.dbg("ZM(cz): Cleaning zone with area [myarea].") - world.log << "RM(stats): CLEAN start [myarea] at [world.time] prepared at [prepared_at]." //DEBUG code for playtest stats gathering. + to_world_log("RM(stats): CLEAN start [myarea] at [world.time] prepared at [prepared_at].") //DEBUG code for playtest stats gathering. rm_controller.unmark_ready(src) //Cut these lists so qdel can dereference the things properly @@ -380,15 +383,18 @@ var/ignored = list( /obj/asteroid_spawner, /obj/rogue_mobspawner, - /obj/effect/step_trigger/teleporter/random/rogue/fourbyfour/onleft, - /obj/effect/step_trigger/teleporter/random/rogue/fourbyfour/onright, - /obj/effect/step_trigger/teleporter/random/rogue/fourbyfour/ontop, - /obj/effect/step_trigger/teleporter/random/rogue/fourbyfour/onbottom) + /obj/effect/shuttle_landmark, + /obj/effect/step_trigger/teleporter/roguemine_loop/north, + /obj/effect/step_trigger/teleporter/roguemine_loop/south, + /obj/effect/step_trigger/teleporter/roguemine_loop/east, + /obj/effect/step_trigger/teleporter/roguemine_loop/west) for(var/atom/I in myarea.contents) if(I.type == /turf/space) I.overlays.Cut() continue + else if(!I.simulated) + continue else if(I.type in ignored) continue qdel(I) @@ -396,7 +402,12 @@ //A deletion so nice that I give it twice for(var/atom/I in myarea.contents) - if(I.type in ignored) + if(I.type == /turf/space) + I.overlays.Cut() + continue + else if(!I.simulated) + continue + else if(I.type in ignored) continue qdel(I) sleep(delay) @@ -406,7 +417,7 @@ original_mobs = 0 prepared_at = 0 - world.log << "RM(stats): CLEAN done [myarea] at [world.time]." //DEBUG code for playtest stats gathering. + to_world_log("RM(stats): CLEAN done [myarea] at [world.time].") //DEBUG code for playtest stats gathering. rm_controller.dbg("ZM(cz): Finished cleaning up zone area [myarea].") rm_controller.mark_clean(src) diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security levels/keycard authentication.dm index 7696c92a51..707bd3ccd9 100644 --- a/code/modules/security levels/keycard authentication.dm +++ b/code/modules/security levels/keycard authentication.dm @@ -16,7 +16,7 @@ //1 = select event //2 = authenticate anchored = 1.0 - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 2 active_power_usage = 6 power_channel = ENVIRON @@ -188,13 +188,13 @@ var/global/maint_all_access = 0 /proc/make_maint_all_access() maint_all_access = 1 - world << "Attention!" - world << "The maintenance access requirement has been revoked on all airlocks." + to_world("Attention!") + to_world("The maintenance access requirement has been revoked on all airlocks.") /proc/revoke_maint_all_access() maint_all_access = 0 - world << "Attention!" - world << "The maintenance access requirement has been readded on all maintenance airlocks." + to_world("Attention!") + to_world("The maintenance access requirement has been readded on all maintenance airlocks.") /obj/machinery/door/airlock/allowed(mob/M) if(maint_all_access && src.check_access_list(list(access_maint_tunnels))) diff --git a/code/modules/security levels/security levels.dm b/code/modules/security levels/security levels.dm index ff5f96bdde..8224b72e22 100644 --- a/code/modules/security levels/security levels.dm +++ b/code/modules/security levels/security levels.dm @@ -78,8 +78,7 @@ FA.set_security_level(newlevel) for(var/obj/machinery/status_display/FA in machines) if(FA.z in using_map.contact_levels) - FA.display_alert(newlevel) - FA.mode = 3 + FA.on_alert_changed(newlevel) if(level >= SEC_LEVEL_RED) atc.reroute_traffic(yes = 1) // Tell them fuck off we're busy. diff --git a/code/modules/shieldgen/directional_shield.dm b/code/modules/shieldgen/directional_shield.dm index 9cc0454620..00c416895e 100644 --- a/code/modules/shieldgen/directional_shield.dm +++ b/code/modules/shieldgen/directional_shield.dm @@ -125,6 +125,7 @@ for(var/obj/effect/directional_shield/S in active_shields) active_shields -= S qdel(S) + set_light(0) active = FALSE /obj/item/shield_projector/proc/update_shield_positions() @@ -179,13 +180,18 @@ if(always_on) to_chat(user, "You can't seem to deactivate \the [src].") return - - destroy_shields() + set_on(FALSE) else set_dir(user.dir) // Needed for linear shields. - create_shields() + set_on(TRUE) visible_message("\The [user] [!active ? "de":""]activates \the [src].") +/obj/item/shield_projector/proc/set_on(var/on) + if(isnull(on)) + return + + on ? create_shields() : destroy_shields() // Harmless if called when in the wrong state. + /obj/item/shield_projector/process() if(shield_health < max_shield_health && ( (last_damaged_time + shield_regen_delay) < world.time) ) adjust_health(shield_regen_amount) diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index e764e0b516..923d1ec327 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -134,7 +134,7 @@ var/is_open = 0 //Whether or not the wires are exposed var/locked = 0 var/check_delay = 60 //periodically recheck if we need to rebuild a shield - use_power = 0 + use_power = USE_POWER_OFF idle_power_usage = 0 var/global/list/blockedturfs = list( /turf/space, @@ -156,7 +156,7 @@ idle_power_usage = 0 for(var/obj/machinery/shield/shield_tile in deployed_shields) idle_power_usage += shield_tile.shield_idle_power - update_use_power(1) + update_use_power(USE_POWER_IDLE) /obj/machinery/shieldgen/proc/shields_down() if(!active) return 0 //If it's already off, how did this get called? @@ -166,7 +166,7 @@ collapse_shields() - update_use_power(0) + update_use_power(USE_POWER_OFF) /obj/machinery/shieldgen/proc/create_shields() for(var/turf/target_tile in range(2, src)) @@ -258,14 +258,14 @@ return if (src.active) - user.visible_message("\icon[src] [user] deactivated the shield generator.", \ - "\icon[src] You deactivate the shield generator.", \ + user.visible_message("[bicon(src)] [user] deactivated the shield generator.", \ + "[bicon(src)] You deactivate the shield generator.", \ "You hear heavy droning fade out.") src.shields_down() else if(anchored) - user.visible_message("\icon[src] [user] activated the shield generator.", \ - "\icon[src] You activate the shield generator.", \ + user.visible_message("[bicon(src)] [user] activated the shield generator.", \ + "[bicon(src)] You activate the shield generator.", \ "You hear heavy droning.") src.shields_up() else diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm index 5b7d7f2118..87d07cceaf 100644 --- a/code/modules/shieldgen/energy_field.dm +++ b/code/modules/shieldgen/energy_field.dm @@ -31,8 +31,14 @@ /obj/effect/energy_field/Destroy() update_nearby_tiles() - my_gen.field.Remove(src) - my_gen = null + if(my_gen) + if(istype(my_gen)) + my_gen.field.Remove(src) + my_gen = null + else if(istype(my_gen, /datum/artifact_effect/forcefield)) + var/datum/artifact_effect/forcefield/AE = my_gen + AE.created_field.Remove(src) + my_gen = null var/turf/current_loc = get_turf(src) . = ..() for(var/direction in cardinal) diff --git a/code/modules/shieldgen/energy_shield.dm b/code/modules/shieldgen/energy_shield.dm new file mode 100644 index 0000000000..d6218eac8f --- /dev/null +++ b/code/modules/shieldgen/energy_shield.dm @@ -0,0 +1,357 @@ +// +// This is the shield effect object for the supercool shield gens. +// +/obj/effect/shield + name = "energy shield" + desc = "An impenetrable field of energy, capable of blocking anything as long as it's active." + icon = 'icons/obj/machines/shielding.dmi' + icon_state = "shield" + anchored = 1 + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + density = 1 + invisibility = 0 + var/obj/machinery/power/shield_generator/gen = null // Owning generator + var/disabled_for = 0 + var/diffused_for = 0 + can_atmos_pass = ATMOS_PASS_YES + var/enabled_icon_state + +/obj/effect/shield/proc/update_visuals() + update_iconstate() + update_color() + update_glow() + update_opacity() + +/obj/effect/shield/proc/update_iconstate() + if(!enabled_icon_state) + enabled_icon_state = icon_state + + if(disabled_for || diffused_for) + icon_state = "shield_broken" + overlays.Cut() //NOT ssoverlays + else + icon_state = enabled_icon_state + flags |= OVERLAY_QUEUED //Trick SSoverlays + SSoverlays.queue += src + +/obj/effect/shield/proc/update_color() + if(disabled_for || diffused_for) + color = "#FFA500" + else if(gen?.check_flag(MODEFLAG_OVERCHARGE)) + color = "#FE6666" + else + color = "#00AAFF" + +/obj/effect/shield/proc/update_glow() + if(density) + set_light(3, 3, "#66FFFF") + else + set_light(0) + +/obj/effect/shield/proc/update_opacity() + if(gen?.check_flag(MODEFLAG_PHOTONIC) && !disabled_for && !diffused_for) + set_opacity(1) + else + set_opacity(0) + +// Prevents singularities and pretty much everything else from moving the field segments away. +// The only thing that is allowed to move us is the Destroy() proc. +/obj/effect/shield/forceMove() + if(QDELING(src)) + return ..() + return 0 + +/obj/effect/shield/Destroy() + if(can_atmos_pass != ATMOS_PASS_YES) + update_nearby_tiles() //Force ZAS update + . = ..() + if(gen) + if(src in gen.field_segments) + gen.field_segments -= src + if(src in gen.damaged_segments) + gen.damaged_segments -= src + gen = null + +// Temporarily collapses this shield segment. +/obj/effect/shield/proc/fail(var/duration) + if(duration <= 0) + return + + if(gen) + gen.damaged_segments |= src + disabled_for += duration + + set_density(0) + update_visuals() + update_nearby_tiles() //Force ZAS update + update_explosion_resistance() + +// Regenerates this shield segment. +/obj/effect/shield/proc/regenerate() + if(!gen) + return + + disabled_for = max(0, disabled_for - 1) + diffused_for = max(0, diffused_for - 1) + + if(!disabled_for && !diffused_for) + set_density(1) + update_visuals() + update_nearby_tiles() //Force ZAS update + update_explosion_resistance() + gen.damaged_segments -= src + +/obj/effect/shield/proc/diffuse(var/duration) + // The shield is trying to counter diffusers. Cause lasting stress on the shield. + if(gen?.check_flag(MODEFLAG_BYPASS) && !disabled_for) + take_damage(duration * rand(8, 12), SHIELD_DAMTYPE_EM) + return + + diffused_for = max(duration, 0) + gen?.damaged_segments |= src + + set_density(0) + update_visuals() + update_nearby_tiles() //Force ZAS update + update_explosion_resistance() + +/obj/effect/shield/attack_generic(var/source, var/damage, var/emote) + take_damage(damage, SHIELD_DAMTYPE_PHYSICAL) + if(gen.check_flag(MODEFLAG_OVERCHARGE) && istype(source, /mob/living/)) + overcharge_shock(source) + ..(source, damage, emote) + + +// Fails shield segments in specific range. Range of 1 affects the shielded turf only. +/obj/effect/shield/proc/fail_adjacent_segments(var/range, var/hitby = null) + if(hitby) + visible_message("\The [src] flashes a bit as \the [hitby] collides with it, eventually fading out in a rain of sparks!") + else + visible_message("\The [src] flashes a bit as it eventually fades out in a rain of sparks!") + fail(range * 2) + + for(var/obj/effect/shield/S in range(range, src)) + // Don't affect shields owned by other shield generators + if(S.gen != src.gen) + continue + // The closer we are to impact site, the longer it takes for shield to come back up. + S.fail(-(-range + get_dist(src, S)) * 2) + +// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment, and spreads to nearby shields. +/obj/effect/shield/proc/flash_adjacent_segments(var/range) + range = between(1, range, 10) // Sanity check + for(var/obj/effect/shield/S in range(range, src)) + // Don't affect shields owned by other shield generators + if(S.gen != src.gen || S == src) + continue + // Note: Range is a non-exact aproximation of the spread effect. If it doesn't look good + // we'll need to switch to actually walking along the shields to get exact number of steps away. + addtimer(CALLBACK(S, .proc/impact_flash), get_dist(src, S) * 2) + impact_flash() + +// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment +/obj/effect/shield/proc/impact_flash() + alpha = 100 + animate(src, alpha = initial(alpha), time = 1 SECOND) + +// Just for fun +/obj/effect/shield/attack_hand(var/user) + flash_adjacent_segments(3) + +/obj/effect/shield/take_damage(var/damage, var/damtype, var/hitby) + if(!gen) + qdel(src) + return + + if(!damtype) + crash_with("CANARY: shield.take_damage() callled without damtype.") + + if(!damage) + return + + damage = round(damage) + + new /obj/effect/temp_visual/shield_impact_effect(get_turf(src)) + + switch(gen.deal_shield_damage(damage, damtype)) + if(SHIELD_ABSORBED) + flash_adjacent_segments(round(damage/10)) // Nice visual effect only. + return + if(SHIELD_BREACHED_MINOR) + fail_adjacent_segments(rand(1, 3), hitby) + return + if(SHIELD_BREACHED_MAJOR) + fail_adjacent_segments(rand(2, 5), hitby) + return + if(SHIELD_BREACHED_CRITICAL) + fail_adjacent_segments(rand(4, 8), hitby) + return + if(SHIELD_BREACHED_FAILURE) + fail_adjacent_segments(rand(8, 16), hitby) + return + + +// As we have various shield modes, this handles whether specific things can pass or not. +/obj/effect/shield/CanPass(var/atom/movable/mover, var/turf/target) + // Somehow we don't have a generator. This shouldn't happen. Delete the shield. + if(!gen) + qdel(src) + return 1 + + if(disabled_for || diffused_for) + return 1 + + if(mover) + return mover.can_pass_shield(gen) + return 1 + +/obj/effect/shield/proc/set_can_atmos_pass(var/new_value) + if(new_value == can_atmos_pass) + return + can_atmos_pass = new_value + update_nearby_tiles() //Force ZAS update + + +// EMP. It may seem weak but keep in mind that multiple shield segments are likely to be affected. +/obj/effect/shield/emp_act(var/severity) + if(!disabled_for) + take_damage(rand(30,60) / severity, SHIELD_DAMTYPE_EM) + + +// Explosions +/obj/effect/shield/ex_act(var/severity) + if(!disabled_for) + take_damage(rand(10,15) / severity, SHIELD_DAMTYPE_PHYSICAL) + + +// Fire +/obj/effect/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) + if(!disabled_for) + take_damage(rand(5,10), SHIELD_DAMTYPE_HEAT) + + +// Projectiles +/obj/effect/shield/bullet_act(var/obj/item/projectile/proj) + if(proj.damage_type == BURN) + take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_HEAT) + else if (proj.damage_type == BRUTE) + take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_PHYSICAL) + else //TODO - This will never happen because of get_structure_damage() only returning values for BRUTE and BURN damage types + take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_EM) + + +// Attacks with hand tools. Blocked by Hyperkinetic flag. +/obj/effect/shield/attackby(var/obj/item/weapon/I as obj, var/mob/user as mob) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(src) + + if(gen.check_flag(MODEFLAG_HYPERKINETIC)) + user.visible_message("\The [user] hits \the [src] with \the [I]!") + if(I.damtype == BURN) + take_damage(I.force, SHIELD_DAMTYPE_HEAT) + else if (I.damtype == BRUTE) + take_damage(I.force, SHIELD_DAMTYPE_PHYSICAL) + else + take_damage(I.force, SHIELD_DAMTYPE_EM) + else + user.visible_message("\The [user] tries to attack \the [src] with \the [I], but it passes through!") + + +// Special treatment for meteors because they would otherwise penetrate right through the shield. +/obj/effect/shield/Bumped(var/atom/movable/mover) + if(!gen) + qdel(src) + return 0 + mover.shield_impact(src) + return ..() + +// Meteors call this instad of Bumped for some reason +/obj/effect/shield/handle_meteor_impact(var/obj/effect/meteor/meteor) + meteor.shield_impact(src) + return !QDELETED(meteor) // If it was stopped it will have been deleted + +/obj/effect/shield/proc/overcharge_shock(var/mob/living/M) + M.adjustFireLoss(rand(20, 40)) + M.Weaken(5) + to_chat(M, "As you come into contact with \the [src] a surge of energy paralyses you!") + take_damage(10, SHIELD_DAMTYPE_EM) + +// Called when a flag is toggled. Can be used to add on-toggle behavior, such as visual changes. +/obj/effect/shield/proc/flags_updated() + if(!gen) + qdel(src) + return + + // Update airflow - If atmospheric we block air as long as we're enabled (density works for this) + set_can_atmos_pass(gen.check_flag(MODEFLAG_ATMOSPHERIC) ? ATMOS_PASS_DENSITY : ATMOS_PASS_YES) + update_visuals() + update_explosion_resistance() + +/obj/effect/shield/proc/update_explosion_resistance() + if(gen && gen.check_flag(MODEFLAG_HYPERKINETIC)) + explosion_resistance = INFINITY + else + explosion_resistance = 0 + +// +// Visual effect of shield taking impact +// +/obj/effect/temp_visual/shield_impact_effect + name = "shield impact" + icon = 'icons/obj/machines/shielding.dmi' + icon_state = "shield_impact" + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER + duration = 2 SECONDS + randomdir = FALSE + +// +// Shield collision checks below +// + +// Called only if shield is active/not destroyed etc. +/atom/movable/proc/can_pass_shield(var/obj/machinery/power/shield_generator/gen) + return 1 + + +// Other mobs +/mob/living/can_pass_shield(var/obj/machinery/power/shield_generator/gen) + return !gen.check_flag(MODEFLAG_NONHUMANS) + +// Human mobs +/mob/living/carbon/human/can_pass_shield(var/obj/machinery/power/shield_generator/gen) + if(isSynthetic()) + return !gen.check_flag(MODEFLAG_ANORGANIC) + return !gen.check_flag(MODEFLAG_HUMANOIDS) + +// Silicon mobs +/mob/living/silicon/can_pass_shield(var/obj/machinery/power/shield_generator/gen) + return !gen.check_flag(MODEFLAG_ANORGANIC) + + +// Generic objects. Also applies to bullets and meteors. +/obj/can_pass_shield(var/obj/machinery/power/shield_generator/gen) + return !gen.check_flag(MODEFLAG_HYPERKINETIC) + +// Beams +/obj/item/projectile/beam/can_pass_shield(var/obj/machinery/power/shield_generator/gen) + return !gen.check_flag(MODEFLAG_PHOTONIC) + + +// Shield on-impact logic here. This is called only if the object is actually blocked by the field (can_pass_shield applies first) +/atom/movable/proc/shield_impact(var/obj/effect/shield/S) + return + +/mob/living/shield_impact(var/obj/effect/shield/S) + if(!S.gen.check_flag(MODEFLAG_OVERCHARGE)) + return + S.overcharge_shock(src) + +/obj/effect/meteor/shield_impact(var/obj/effect/shield/S) + if(!S.gen.check_flag(MODEFLAG_HYPERKINETIC)) + return + S.take_damage(get_shield_damage(), SHIELD_DAMTYPE_PHYSICAL, src) + visible_message("\The [src] breaks into dust!") + make_debris() + qdel(src) diff --git a/code/modules/shieldgen/handheld_defuser.dm b/code/modules/shieldgen/handheld_defuser.dm index 7b4fdd786b..21eca0587f 100644 --- a/code/modules/shieldgen/handheld_defuser.dm +++ b/code/modules/shieldgen/handheld_defuser.dm @@ -1,20 +1,20 @@ /obj/item/weapon/shield_diffuser name = "portable shield diffuser" - desc = "A small handheld device designed to disrupt energy barriers" + desc = "A small handheld device designed to disrupt energy barriers." description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern), in a similar way the floor mounted variant does. It is, however, portable and run by an internal battery. Can be recharged with a regular recharger." icon = 'icons/obj/machines/shielding.dmi' icon_state = "hdiffuser_off" + origin_tech = list(TECH_MAGNET = 5, TECH_POWER = 5, TECH_ILLEGAL = 2) var/obj/item/weapon/cell/device/cell var/enabled = 0 -/obj/item/weapon/shield_diffuser/New() +/obj/item/weapon/shield_diffuser/Initialize() + . = ..() cell = new(src) - ..() /obj/item/weapon/shield_diffuser/Destroy() - qdel(cell) - cell = null + QDEL_NULL(cell) if(enabled) STOP_PROCESSING(SSobj, src) . = ..() @@ -24,10 +24,15 @@ /obj/item/weapon/shield_diffuser/process() if(!enabled) - return + return PROCESS_KILL for(var/direction in cardinal) var/turf/simulated/shielded_tile = get_step(get_turf(src), direction) + for(var/obj/effect/shield/S in shielded_tile) + // 10kJ per pulse, but gap in the shield lasts for longer than regular diffusers. + if(istype(S) && !S.diffused_for && !S.disabled_for && cell.checked_use(10 KILOWATTS * CELLRATE)) + S.diffuse(20) + // Legacy shield support for(var/obj/effect/energy_field/S in shielded_tile) if(istype(S) && cell.checked_use(10 KILOWATTS * CELLRATE)) qdel(S) @@ -38,16 +43,16 @@ else icon_state = "hdiffuser_off" -/obj/item/weapon/shield_diffuser/attack_self() +/obj/item/weapon/shield_diffuser/attack_self(mob/user) enabled = !enabled update_icon() if(enabled) START_PROCESSING(SSobj, src) else STOP_PROCESSING(SSobj, src) - to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].") + to_chat(user, "You turn \the [src] [enabled ? "on" : "off"].") -/obj/item/weapon/shield_diffuser/examine() +/obj/item/weapon/shield_diffuser/examine(mob/user) . = ..() - to_chat(usr, "The charge meter reads [cell ? cell.percent() : 0]%") - to_chat(usr, "It is [enabled ? "enabled" : "disabled"].") \ No newline at end of file + to_chat(user, "The charge meter reads [cell ? cell.percent() : 0]%") + to_chat(user, "It is [enabled ? "enabled" : "disabled"].") \ No newline at end of file diff --git a/code/modules/shieldgen/sheldwallgen.dm b/code/modules/shieldgen/sheldwallgen.dm index e6e9bc11d7..2a6a859c43 100644 --- a/code/modules/shieldgen/sheldwallgen.dm +++ b/code/modules/shieldgen/sheldwallgen.dm @@ -23,17 +23,17 @@ //There have to be at least two posts, so these are effectively doubled var/power_draw = 30000 //30 kW. How much power is drawn from powernet. Increase this to allow the generator to sustain longer shields, at the cost of more power draw. var/max_stored_power = 50000 //50 kW - use_power = 0 //Draws directly from power net. Does not use APC power. + use_power = USE_POWER_OFF //Draws directly from power net. Does not use APC power. /obj/machinery/shieldwallgen/attack_hand(mob/user as mob) if(state != 1) - user << "The shield generator needs to be firmly secured to the floor first." + to_chat(user, "The shield generator needs to be firmly secured to the floor first.") return 1 if(src.locked && !istype(user, /mob/living/silicon)) - user << "The controls are locked!" + to_chat(user, "The controls are locked!") return 1 if(power != 1) - user << "The shield generator needs to be powered by wire underneath." + to_chat(user, "The shield generator needs to be powered by wire underneath.") return 1 if(src.active >= 1) @@ -159,29 +159,29 @@ /obj/machinery/shieldwallgen/attackby(obj/item/W, mob/user) if(W.is_wrench()) if(active) - user << "Turn off the field generator first." + to_chat(user, "Turn off the field generator first.") return else if(state == 0) state = 1 playsound(src, W.usesound, 75, 1) - user << "You secure the external reinforcing bolts to the floor." + to_chat(user, "You secure the external reinforcing bolts to the floor.") src.anchored = 1 return else if(state == 1) state = 0 playsound(src, W.usesound, 75, 1) - user << "You undo the external reinforcing bolts." + to_chat(user, "You undo the external reinforcing bolts.") src.anchored = 0 return if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) if (src.allowed(user)) src.locked = !src.locked - user << "Controls are now [src.locked ? "locked." : "unlocked."]" + to_chat(user, "Controls are now [src.locked ? "locked." : "unlocked."]") else - user << "Access denied." + to_chat(user, "Access denied.") else src.add_fingerprint(user) diff --git a/code/modules/shieldgen/shield_capacitor.dm b/code/modules/shieldgen/shield_capacitor.dm index 4006a1cedf..2ef3f17a85 100644 --- a/code/modules/shieldgen/shield_capacitor.dm +++ b/code/modules/shieldgen/shield_capacitor.dm @@ -15,7 +15,7 @@ var/max_charge = 8e6 //8 MJ var/max_charge_rate = 400000 //400 kW var/locked = 0 - use_power = 0 //doesn't use APC power + use_power = USE_POWER_OFF //doesn't use APC power var/charge_rate = 100000 //100 kW var/obj/machinery/shield_gen/owned_gen @@ -48,7 +48,7 @@ else if(W.is_wrench()) src.anchored = !src.anchored playsound(src, W.usesound, 75, 1) - src.visible_message("\icon[src] [src] has been [anchored ? "bolted to the floor" : "unbolted from the floor"] by [user].") + src.visible_message("[bicon(src)] [src] has been [anchored ? "bolted to the floor" : "unbolted from the floor"] by [user].") if(anchored) spawn(0) diff --git a/code/modules/shieldgen/shield_diffuser.dm b/code/modules/shieldgen/shield_diffuser.dm index c30f8a7351..52c57db020 100644 --- a/code/modules/shieldgen/shield_diffuser.dm +++ b/code/modules/shieldgen/shield_diffuser.dm @@ -4,7 +4,8 @@ description_info = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern). They are commonly installed around exterior airlocks to prevent shields from blocking EVA access." icon = 'icons/obj/machines/shielding.dmi' icon_state = "fdiffuser_on" - use_power = 2 + circuit = /obj/item/weapon/circuitboard/shield_diffuser + use_power = USE_POWER_ACTIVE idle_power_usage = 25 // Previously 100. active_power_usage = 500 // Previously 2000 anchored = 1 @@ -13,12 +14,17 @@ var/alarm = 0 var/enabled = 1 -/obj/machinery/shield_diffuser/New() - ..() +/obj/machinery/shield_diffuser/Initialize() + . = ..() + // TODO - Remove this bit once machines are converted to Initialize + if(ispath(circuit)) + circuit = new circuit(src) + default_apply_parts() + var/turf/T = get_turf(src) hide(!T.is_plating()) -//If underfloor, hide the cable +//If underfloor, hide the cable^H^H diffuser /obj/machinery/shield_diffuser/hide(var/i) if(istype(loc, /turf)) invisibility = i ? 101 : 0 @@ -38,6 +44,9 @@ return for(var/direction in cardinal) var/turf/simulated/shielded_tile = get_step(get_turf(src), direction) + for(var/obj/effect/shield/S in shielded_tile) + S.diffuse(5) + // Legacy shield support for(var/obj/effect/energy_field/S in shielded_tile) qdel(S) @@ -50,16 +59,27 @@ else icon_state = "fdiffuser_on" -/obj/machinery/shield_diffuser/attack_hand() +/obj/machinery/shield_diffuser/attack_hand(mob/user as mob) + if((. = ..())) + return if(alarm) - to_chat(usr, "You press an override button on \the [src], re-enabling it.") + to_chat(user, "You press an override button on \the [src], re-enabling it.") alarm = 0 update_icon() return enabled = !enabled - use_power = enabled + 1 + update_use_power(enabled ? USE_POWER_ACTIVE : USE_POWER_IDLE) update_icon() - to_chat(usr, "You turn \the [src] [enabled ? "on" : "off"].") + to_chat(user, "You turn \the [src] [enabled ? "on" : "off"].") + +/obj/machinery/shield_diffuser/attackby(var/obj/item/W, var/mob/user) + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_part_replacement(user, W)) + return + return ..() /obj/machinery/shield_diffuser/proc/meteor_alarm(var/duration) if(!duration) @@ -71,4 +91,4 @@ . = ..() to_chat(user, "It is [enabled ? "enabled" : "disabled"].") if(alarm) - to_chat(user, "A red LED labeled \"Proximity Alarm\" is blinking on the control panel.") \ No newline at end of file + to_chat(user, "A red LED labeled \"Proximity Alarm\" is blinking on the control panel.") diff --git a/code/modules/shieldgen/shield_gen.dm b/code/modules/shieldgen/shield_gen.dm index 0562bbd9ff..cdd16d2429 100644 --- a/code/modules/shieldgen/shield_gen.dm +++ b/code/modules/shieldgen/shield_gen.dm @@ -22,7 +22,8 @@ var/time_since_fail = 100 var/energy_conversion_rate = 0.0006 //how many renwicks per watt? Higher numbers equals more effiency. var/z_range = 0 // How far 'up and or down' to extend the shield to, in z-levels. Only works on MultiZ supported z-levels. - use_power = 0 //doesn't use APC power + use_power = USE_POWER_OFF //doesn't use APC power + var/id //for button usage /obj/machinery/shield_gen/advanced name = "advanced bubble shield generator" @@ -48,7 +49,7 @@ /obj/machinery/shield_gen/emag_act(var/remaining_charges, var/mob/user) if(prob(75)) src.locked = !src.locked - user << "Controls are now [src.locked ? "locked." : "unlocked."]" + to_chat(user, "Controls are now [src.locked ? "locked." : "unlocked."]") . = 1 updateDialog() var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread @@ -60,14 +61,14 @@ var/obj/item/weapon/card/id/C = W if(access_captain in C.access || access_security in C.access || access_engine in C.access) src.locked = !src.locked - user << "Controls are now [src.locked ? "locked." : "unlocked."]" + to_chat(user, "Controls are now [src.locked ? "locked." : "unlocked."]") updateDialog() else - user << "Access denied." + to_chat(user, "Access denied.") else if(W.is_wrench()) src.anchored = !src.anchored playsound(src, W.usesound, 75, 1) - src.visible_message("\icon[src] [src] has been [anchored?"bolted to the floor":"unbolted from the floor"] by [user].") + src.visible_message("[bicon(src)] [src] has been [anchored?"bolted to the floor":"unbolted from the floor"] by [user].") if(active) toggle() @@ -213,7 +214,7 @@ return else if( href_list["toggle"] ) if (!active && !anchored) - usr << "The [src] needs to be firmly secured to the floor first." + to_chat(usr, "The [src] needs to be firmly secured to the floor first.") return toggle() else if( href_list["change_radius"] ) @@ -248,7 +249,7 @@ covered_turfs = null for(var/mob/M in view(5,src)) - M << "\icon[src] You hear heavy droning start up." + to_chat(M, "[bicon(src)] You hear heavy droning start up.") for(var/obj/effect/energy_field/E in field) // Update the icons here to ensure all the shields have been made already. E.update_icon() else @@ -258,7 +259,7 @@ qdel(D) for(var/mob/M in view(5,src)) - M << "\icon[src] You hear heavy droning fade out." + to_chat(M, "[bicon(src)] You hear heavy droning fade out.") /obj/machinery/shield_gen/update_icon() if(stat & BROKEN) diff --git a/code/modules/shieldgen/shield_generator.dm b/code/modules/shieldgen/shield_generator.dm new file mode 100644 index 0000000000..824eb86f66 --- /dev/null +++ b/code/modules/shieldgen/shield_generator.dm @@ -0,0 +1,721 @@ +// +// This generator is for the supercool big shields intended for ships that do nice stuff with overmaps. +// +/obj/machinery/power/shield_generator + name = "advanced shield generator" + desc = "A heavy-duty shield generator and capacitor, capable of generating energy shields at large distances." + icon = 'icons/obj/machines/shielding.dmi' + icon_state = "generator0" + circuit = /obj/item/weapon/circuitboard/shield_generator + density = 1 + var/datum/wires/shield_generator/wires = null + var/list/field_segments = list() // List of all shield segments owned by this generator. + var/list/damaged_segments = list() // List of shield segments that have failed and are currently regenerating. + var/shield_modes = 0 // Enabled shield mode flags + var/mitigation_em = 0 // Current EM mitigation + var/mitigation_physical = 0 // Current Physical mitigation + var/mitigation_heat = 0 // Current Burn mitigation + var/mitigation_max = 0 // Maximal mitigation reachable with this generator. Set by RefreshParts() + var/max_energy = 0 // Maximal stored energy. In joules. Depends on the type of used SMES coil when constructing this generator. + var/current_energy = 0 // Current stored energy. + var/field_radius = 1 // Current field radius. + var/target_radius = 1 // Desired field radius. + var/running = SHIELD_OFF // Whether the generator is enabled or not. + var/input_cap = 1 MEGAWATTS // Currently set input limit. Set to 0 to disable limits altogether. The shield will try to input this value per tick at most + var/upkeep_power_usage = 0 // Upkeep power usage last tick. + var/upkeep_multiplier = 1 // Multiplier of upkeep values. + var/power_coefficient = 1 // Multiplier of overall power usage (for mappers, subtypes, etc) + var/power_usage = 0 // Total power usage last tick. + var/overloaded = 0 // Whether the field has overloaded and shut down to regenerate. + var/hacked = 0 // Whether the generator has been hacked by cutting the safety wire. + var/offline_for = 0 // The generator will be inoperable for this duration in ticks. + var/input_cut = 0 // Whether the input wire is cut. + var/mode_changes_locked = 0 // Whether the control wire is cut, locking out changes. + var/ai_control_disabled = 0 // Whether the AI control is disabled. + var/list/mode_list = null // A list of shield_mode datums. + var/full_shield_strength = 0 // The amount of power shields need to be at full operating strength. + var/initial_shield_modes = MODEFLAG_HYPERKINETIC|MODEFLAG_EM|MODEFLAG_ATMOSPHERIC|MODEFLAG_HUMANOIDS + + var/idle_multiplier = 1 // Trades off cost vs. spin-up time from idle to running + var/idle_valid_values = list(1, 2, 5, 10) + var/spinup_delay = 20 + var/spinup_counter = 0 + +/obj/machinery/power/shield_generator/update_icon() + if(running) + icon_state = "generator1" + set_light(1, 2, "#66FFFF") + else + icon_state = "generator0" + set_light(0) + + +/obj/machinery/power/shield_generator/Initialize() + . = ..() + if(!wires) + wires = new(src) + // TODO - Remove this bit once machines are converted to Initialize + if(ispath(circuit)) + circuit = new circuit(src) + default_apply_parts() + connect_to_network() + + mode_list = list() + for(var/st in subtypesof(/datum/shield_mode)) + var/datum/shield_mode/SM = new st() + mode_list.Add(SM) + toggle_flag(initial_shield_modes) + +/obj/machinery/power/shield_generator/Destroy() + shutdown_field() + field_segments = null + damaged_segments = null + mode_list = null + . = ..() + + +/obj/machinery/power/shield_generator/RefreshParts() + max_energy = 0 + full_shield_strength = 0 + for(var/obj/item/weapon/smes_coil/S in component_parts) + full_shield_strength += (S.ChargeCapacity * 5) + max_energy = full_shield_strength * 20 + current_energy = between(0, current_energy, max_energy) + + mitigation_max = MAX_MITIGATION_BASE + MAX_MITIGATION_RESEARCH * total_component_rating_of_type(/obj/item/weapon/stock_parts/capacitor) + mitigation_em = between(0, mitigation_em, mitigation_max) + mitigation_physical = between(0, mitigation_physical, mitigation_max) + mitigation_heat = between(0, mitigation_heat, mitigation_max) + ..() + + +// Shuts down the shield, removing all shield segments and unlocking generator settings. +/obj/machinery/power/shield_generator/proc/shutdown_field() + for(var/obj/effect/shield/S in field_segments) + qdel(S) + + running = SHIELD_OFF + current_energy = 0 + mitigation_em = 0 + mitigation_physical = 0 + mitigation_heat = 0 + update_icon() + + +// Generates the field objects. Deletes existing field, if applicable. +/obj/machinery/power/shield_generator/proc/regenerate_field() + for(var/obj/effect/shield/S in field_segments) + qdel(S) + var/list/shielded_turfs + + if(check_flag(MODEFLAG_HULL)) + shielded_turfs = fieldtype_hull() + else + shielded_turfs = fieldtype_square() + + for(var/turf/T in shielded_turfs) + var/obj/effect/shield/S = new(T) + S.gen = src + S.flags_updated() + field_segments |= S + + //Hull shield chaos icon generation + if(check_flag(MODEFLAG_HULL)) + var/list/midsections = list() + var/list/startends = list() + var/list/corners = list() + var/list/horror = list() + + for(var/obj/effect/shield/SE in field_segments) + var/adjacent_fields = 0 + for(var/direction in cardinal) + var/turf/T = get_step(SE, direction) + var/obj/effect/shield/S = locate() in T + if(S) + adjacent_fields |= direction + + //What? + if(!adjacent_fields) + horror += SE + testing("Solo shield turf at [SE.x], [SE.y], [SE.z]") + continue + + //Middle section or corner (multiple bits set) + if((adjacent_fields & (adjacent_fields - 1)) != 0) + //'Impossible' directions + if(adjacent_fields == (NORTH|SOUTH) || adjacent_fields == (EAST|WEST)) + midsections[SE] = adjacent_fields + //It's a simple corner + else //if (adjacent_fields in cornerdirs) + corners[SE] = adjacent_fields + + //Not 0, not multiple bits, it's a start or an end + else + startends[SE] = adjacent_fields + + //Midsections go first + for(var/obj/effect/shield/SE in midsections) + var/adjacent = midsections[SE] + var/turf/L = get_step(SE, ~adjacent & (SOUTH|WEST)) + var/turf/R = get_step(SE, ~adjacent & (NORTH|EAST)) + if(!isspace(L) && !isspace(R)) // Squished in a single tile gap of space! + switch(adjacent) + if(NORTH|SOUTH) //Middle vertical section + if(SE.x < src.x) //Left of generator goes north + SE.set_dir(NORTH) + else + SE.set_dir(SOUTH) + if(EAST|WEST) //Middle horizontal section + if(SE.y < src.y) //South of generator goes left + SE.set_dir(WEST) + else + SE.set_dir(EAST) + else if(isspace(L)) + SE.set_dir(turn(~adjacent & (SOUTH|WEST), -90)) + else + SE.set_dir(turn(~adjacent & (NORTH|EAST), -90)) + + midsections -= SE + + //Some unhandled error state + for(var/obj/effect/shield/SE in midsections) + SE.enabled_icon_state = "arrow" //Error state/unhandled + + //Corners + for(var/obj/effect/shield/S in corners) + var/adjacent = corners[S] + if(adjacent in cornerdirs) + do_corner_shield(S, adjacent) //Dir is adjacent fields direction + else + // Okay first a quick hack. If only one nonshield... + var/nonshield = adjacent ^ (NORTH|SOUTH|EAST|WEST) + if((nonshield & (nonshield - 1)) == 0) + if(!isspace(get_step(S, nonshield))) + S.set_dir(turn(nonshield, 90)) // We're basically a normal midsection just with another touching. Ignore it. + //What's this mysterious 3rd shield touching us? + var/dir_to_them = turn(nonshield, 180) + var/turf/T = get_step(S, dir_to_them) + var/obj/effect/shield/SO = locate() in T + //They are a corner + if((SO.dir & (SO.dir - 1)) != 0) + continue + else if(dir_to_them & SO.dir) //They're facing away from us, so we're their start + S.add_overlay(image(icon, icon_state = "shield_start" , dir = SO.dir)) + else if(SO.dir & nonshield) //They're facing us (and the wall) + S.add_overlay(image(icon, icon_state = "shield_end" , dir = SO.dir)) + + else + var/list/touchnonshield = list() + for(var/direction in cornerdirs) + var/turf/T = get_step(S, direction) + if(!isspace(T)) + touchnonshield += T + if(touchnonshield.len == 1) + do_corner_shield(S, get_dir(S, touchnonshield[1])) + else + S.enabled_icon_state = "capacitor" + else + // Not actually a corner... It has MULTIPLE! + S.enabled_icon_state = "arrow" //Error state/unhandled + + for(var/obj/effect/shield/S in startends) + var/adjacent = startends[S] + log_debug("Processing startend [S] at [S?.x],[S?.y] adjacent=[adjacent]") + var/turf/T = get_step(S, adjacent) + var/obj/effect/shield/SO = locate() in T + S.set_dir(SO.dir) + if(S.dir == adjacent) //Flowing into them + S.enabled_icon_state = "shield_start" + else + S.enabled_icon_state = "shield_end" + else + var/turf/gen_turf = get_turf(src) + for(var/obj/effect/shield/SE in field_segments) + var/new_dir = 0 + if(SE.x == gen_turf.x - field_radius) + new_dir |= NORTH + else if(SE.x == gen_turf.x + field_radius) + new_dir |= SOUTH + if(SE.y == gen_turf.y + field_radius) + new_dir |= EAST + else if(SE.y == gen_turf.y - field_radius) + new_dir |= WEST + if((new_dir & (new_dir - 1)) == 0) + SE.set_dir(new_dir) // Only one bit set means we are an edge not corner. + else + do_corner_shield(SE, turn(new_dir, -90), TRUE) // All our corners are outside, don't check turf type. + + for(var/obj/effect/shield/SE in field_segments) + SE.update_visuals() + + //Phew, update our own icon + update_icon() + +/obj/machinery/power/shield_generator/proc/do_corner_shield(var/obj/effect/shield/S, var/new_dir, var/force_outside) + S.enabled_icon_state = "blank" + S.set_dir(new_dir) + var/inside = force_outside ? FALSE : isspace(get_step(S, new_dir)) + // TODO - Obviously this can be more elegant + if(inside) + switch(new_dir) + if(NORTHEAST) + S.add_overlay(image(S.icon, icon_state = "shield_end", dir = SOUTH)) + S.add_overlay(image(S.icon, icon_state = "shield_start", dir = EAST)) + if(NORTHWEST) + S.add_overlay(image(S.icon, icon_state = "shield_end", dir = EAST)) + S.add_overlay(image(S.icon, icon_state = "shield_start", dir = NORTH)) + if(SOUTHEAST) + S.add_overlay(image(S.icon, icon_state = "shield_end", dir = WEST)) + S.add_overlay(image(S.icon, icon_state = "shield_start", dir = SOUTH)) + if(SOUTHWEST) + S.add_overlay(image(S.icon, icon_state = "shield_end", dir = NORTH)) + S.add_overlay(image(S.icon, icon_state = "shield_start", dir = WEST)) + else + switch(new_dir) + if(NORTHEAST) + S.add_overlay(image(S.icon, icon_state = "shield_end", dir = WEST)) + S.add_overlay(image(S.icon, icon_state = "shield_start", dir = NORTH)) + if(NORTHWEST) + S.add_overlay(image(S.icon, icon_state = "shield_end", dir = SOUTH)) + S.add_overlay(image(S.icon, icon_state = "shield_start", dir = WEST)) + if(SOUTHEAST) + S.add_overlay(image(S.icon, icon_state = "shield_end", dir = NORTH)) + S.add_overlay(image(S.icon, icon_state = "shield_start", dir = EAST)) + if(SOUTHWEST) + S.add_overlay(image(S.icon, icon_state = "shield_end", dir = EAST)) + S.add_overlay(image(S.icon, icon_state = "shield_start", dir = SOUTH)) + + +// Recalculates and updates the upkeep multiplier +/obj/machinery/power/shield_generator/proc/update_upkeep_multiplier() + var/new_upkeep = 1.0 + for(var/datum/shield_mode/SM in mode_list) + if(check_flag(SM.mode_flag)) + new_upkeep *= SM.multiplier + + upkeep_multiplier = new_upkeep * power_coefficient + + +/obj/machinery/power/shield_generator/process() + upkeep_power_usage = 0 + power_usage = 0 + + if(offline_for) + offline_for = max(0, offline_for - 1) + // We're turned off. + if(running == SHIELD_OFF) + return + + if(target_radius != field_radius && running != SHIELD_RUNNING) // Do not recalculate the field while it's running; that's extremely laggy. + field_radius += (target_radius > field_radius) ? 1 : -1 + + // We are shutting down, therefore our stored energy disperses faster than usual. + else if(running == SHIELD_DISCHARGING) + current_energy -= SHIELD_SHUTDOWN_DISPERSION_RATE + else if(running == SHIELD_SPINNING_UP) + spinup_counter-- + if(spinup_counter <= 0) + running = SHIELD_RUNNING + regenerate_field() + + mitigation_em = between(0, mitigation_em - MITIGATION_LOSS_PASSIVE, mitigation_max) + mitigation_heat = between(0, mitigation_heat - MITIGATION_LOSS_PASSIVE, mitigation_max) + mitigation_physical = between(0, mitigation_physical - MITIGATION_LOSS_PASSIVE, mitigation_max) + + if(running == SHIELD_RUNNING) + upkeep_power_usage = round((field_segments.len - damaged_segments.len) * ENERGY_UPKEEP_PER_TILE * upkeep_multiplier) + else if(running > SHIELD_RUNNING) + upkeep_power_usage = round(ENERGY_UPKEEP_IDLE * idle_multiplier * (field_radius * 8) * upkeep_multiplier) // Approximates number of turfs. + + if(powernet && (running >= SHIELD_RUNNING) && !input_cut) + var/energy_buffer = 0 + energy_buffer = draw_power(min(upkeep_power_usage, input_cap)) + power_usage += round(energy_buffer) + + if(energy_buffer < upkeep_power_usage) + current_energy -= round(upkeep_power_usage - energy_buffer) // If we don't have enough energy from the grid, take it from the internal battery instead. + + // Now try to recharge our internal energy. + var/energy_to_demand + if(input_cap) + energy_to_demand = between(0, max_energy - current_energy, input_cap - energy_buffer) + else + energy_to_demand = max(0, max_energy - current_energy) + energy_buffer = draw_power(energy_to_demand) + power_usage += energy_buffer + current_energy += round(energy_buffer) + else + current_energy -= round(upkeep_power_usage) // We are shutting down, or we lack external power connection. Use energy from internal source instead. + + if(current_energy <= 0) + energy_failure() + + if(!overloaded) + for(var/obj/effect/shield/S in damaged_segments) + S.regenerate() + else if (field_integrity() > 25) + overloaded = 0 + +/obj/machinery/power/shield_generator/attackby(obj/item/O as obj, mob/user as mob) + if(panel_open && (O?.is_multitool() || O?.is_wirecutter())) + wires.Interact(user) + return TRUE + if(default_deconstruction_screwdriver(user, O)) + return + if(O?.is_crowbar() || O?.is_wrench() || istype(O, /obj/item/weapon/storage/part_replacer)) + if(offline_for) + to_chat(user, "Wait until \the [src] cools down from emergency shutdown first!") + return + if(running) + to_chat(user, "Turn off \the [src] first!") + return + if(default_deconstruction_crowbar(user, O)) + return + if(default_part_replacement(user, O)) + return + if(default_unfasten_wrench(user, O, 40)) + return + return ..() + +/obj/machinery/power/shield_generator/proc/energy_failure() + if(running == SHIELD_DISCHARGING) + shutdown_field() + else + current_energy = 0 + overloaded = 1 + for(var/obj/effect/shield/S in field_segments) + S.fail(1) + +/obj/machinery/power/shield_generator/proc/set_idle(var/new_state) + if(new_state) + if(running == SHIELD_IDLE) + return + running = SHIELD_IDLE + for(var/obj/effect/shield/S in field_segments) + qdel(S) + else + if(running != SHIELD_IDLE) + return + running = SHIELD_SPINNING_UP + spinup_counter = round(spinup_delay / idle_multiplier) + update_icon() + +/obj/machinery/power/shield_generator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + + data["running"] = running + data["modes"] = get_flag_descriptions() + data["overloaded"] = overloaded + data["mitigation_max"] = mitigation_max + data["mitigation_physical"] = round(mitigation_physical, 0.1) + data["mitigation_em"] = round(mitigation_em, 0.1) + data["mitigation_heat"] = round(mitigation_heat, 0.1) + data["field_integrity"] = field_integrity() + data["max_energy"] = round(max_energy / 1000000, 0.1) + data["current_energy"] = round(current_energy / 1000000, 0.1) + data["percentage_energy"] = round(data["current_energy"] / data["max_energy"] * 100) + data["total_segments"] = field_segments ? field_segments.len : 0 + data["functional_segments"] = damaged_segments ? data["total_segments"] - damaged_segments.len : data["total_segments"] + data["field_radius"] = field_radius + data["target_radius"] = target_radius + data["input_cap_kw"] = round(input_cap / 1000) + data["upkeep_power_usage"] = round(upkeep_power_usage / 1000, 0.1) + data["power_usage"] = round(power_usage / 1000) + data["hacked"] = hacked + data["offline_for"] = offline_for * 2 + data["idle_multiplier"] = idle_multiplier + data["idle_valid_values"] = idle_valid_values + data["spinup_counter"] = spinup_counter + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "shield_generator.tmpl", src.name, 500, 800) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/power/shield_generator/attack_hand(mob/user) + if((. = ..())) + return + if(panel_open && Adjacent(user)) + wires.Interact(user) + return + if(CanUseTopic(user, global.default_state) > STATUS_CLOSE) + ui_interact(user) + return TRUE + +/obj/machinery/power/shield_generator/CanUseTopic(var/mob/user) + if(issilicon(user) && !Adjacent(user) && ai_control_disabled) + return STATUS_UPDATE + if(panel_open) + return min(..(), STATUS_DISABLED) + return ..() + +/obj/machinery/power/shield_generator/Topic(href, href_list, datum/topic_state/state = default_state) + if((. = ..())) + return + + if(href_list["begin_shutdown"]) + if(running < SHIELD_RUNNING) // Discharging or off + return + var/alert = alert(usr, "Are you sure you wish to do this? It will drain the power inside the internal storage rapidly.", "Are you sure?", "Yes", "No") + if(!CanInteract(usr, state)) + return + if(running < SHIELD_RUNNING) + return + if(alert == "Yes") + set_idle(TRUE) // do this first to clear the field + running = SHIELD_DISCHARGING + return TOPIC_REFRESH + + if(href_list["start_generator"]) + if(offline_for) + return + set_idle(TRUE) + return TOPIC_REFRESH + + if(href_list["toggle_idle"]) + if(running < SHIELD_RUNNING) + return TOPIC_HANDLED + set_idle(text2num(href_list["toggle_idle"])) + return TOPIC_REFRESH + + // Instantly drops the shield, but causes a cooldown before it may be started again. Also carries a risk of EMP at high charge. + if(href_list["emergency_shutdown"]) + if(!running) + return TOPIC_HANDLED + + var/choice = input(usr, "Are you sure that you want to initiate an emergency shield shutdown? This will instantly drop the shield, and may result in unstable release of stored electromagnetic energy. Proceed at your own risk.") in list("Yes", "No") + if((choice != "Yes") || !running) + return TOPIC_HANDLED + + // If the shield would take 5 minutes to disperse and shut down using regular methods, it will take x1.5 (7 minutes and 30 seconds) of this time to cool down after emergency shutdown + offline_for = round(current_energy / (SHIELD_SHUTDOWN_DISPERSION_RATE / 1.5)) + var/old_energy = current_energy + shutdown_field() + log_and_message_admins("has triggered \the [src]'s emergency shutdown!", usr) + spawn() + empulse(src, old_energy / 60000000, old_energy / 32000000, 1) // If shields are charged at 450 MJ, the EMP will be 7.5, 14.0625. 90 MJ, 1.5, 2.8125 + old_energy = 0 + + return TOPIC_REFRESH + + if(mode_changes_locked) + return TOPIC_REFRESH + + if(href_list["set_range"]) + var/new_range = input(usr, "Enter new field range (1-[world.maxx]). Leave blank to cancel.", "Field Radius Control", field_radius) as num + if(!new_range) + return TOPIC_HANDLED + target_radius = between(1, new_range, world.maxx) + return TOPIC_REFRESH + + if(href_list["set_input_cap"]) + var/new_cap = round(input(usr, "Enter new input cap (in kW). Enter 0 or nothing to disable input cap.", "Generator Power Control", round(input_cap / 1000)) as num) + if(!new_cap) + input_cap = 0 + return + input_cap = max(0, new_cap) * 1000 + return TOPIC_REFRESH + + if(href_list["toggle_mode"]) + // Toggling hacked-only modes requires the hacked var to be set to 1 + if((text2num(href_list["toggle_mode"]) & (MODEFLAG_BYPASS | MODEFLAG_OVERCHARGE)) && !hacked) + return TOPIC_HANDLED + + toggle_flag(text2num(href_list["toggle_mode"])) + return TOPIC_REFRESH + + if(href_list["switch_idle"]) + if(running == SHIELD_SPINNING_UP) + return TOPIC_REFRESH + var/new_idle = text2num(href_list["switch_idle"]) + if(new_idle in idle_valid_values) + idle_multiplier = new_idle + return TOPIC_REFRESH + +/obj/machinery/power/shield_generator/proc/field_integrity() + if(full_shield_strength) + return round(CLAMP01(current_energy / full_shield_strength) * 100) + return 0 + + +// Takes specific amount of damage +/obj/machinery/power/shield_generator/proc/deal_shield_damage(var/damage, var/shield_damtype) + var/energy_to_use = damage * ENERGY_PER_HP + if(check_flag(MODEFLAG_MODULATE)) + mitigation_em -= MITIGATION_HIT_LOSS + mitigation_heat -= MITIGATION_HIT_LOSS + mitigation_physical -= MITIGATION_HIT_LOSS + + switch(shield_damtype) + if(SHIELD_DAMTYPE_PHYSICAL) + mitigation_physical += MITIGATION_HIT_LOSS + MITIGATION_HIT_GAIN + energy_to_use *= 1 - (mitigation_physical / 100) + if(SHIELD_DAMTYPE_EM) + mitigation_em += MITIGATION_HIT_LOSS + MITIGATION_HIT_GAIN + energy_to_use *= 1 - (mitigation_em / 100) + if(SHIELD_DAMTYPE_HEAT) + mitigation_heat += MITIGATION_HIT_LOSS + MITIGATION_HIT_GAIN + energy_to_use *= 1 - (mitigation_heat / 100) + + mitigation_em = between(0, mitigation_em, mitigation_max) + mitigation_heat = between(0, mitigation_heat, mitigation_max) + mitigation_physical = between(0, mitigation_physical, mitigation_max) + + current_energy -= energy_to_use + + // Overload the shield, which will shut it down until we recharge above 25% again + if(current_energy < 0) + energy_failure() + return SHIELD_BREACHED_FAILURE + + if(prob(10 - field_integrity())) + return SHIELD_BREACHED_CRITICAL + if(prob(20 - field_integrity())) + return SHIELD_BREACHED_MAJOR + if(prob(35 - field_integrity())) + return SHIELD_BREACHED_MINOR + return SHIELD_ABSORBED + + +// Checks whether specific flags are enabled +/obj/machinery/power/shield_generator/proc/check_flag(var/flag) + return (shield_modes & flag) + + +/obj/machinery/power/shield_generator/proc/toggle_flag(var/flag) + shield_modes ^= flag + update_upkeep_multiplier() + for(var/obj/effect/shield/S in field_segments) + S.flags_updated() + + if((flag & (MODEFLAG_HULL|MODEFLAG_MULTIZ)) && (running == SHIELD_RUNNING)) + regenerate_field() + + if(flag & MODEFLAG_MODULATE) + mitigation_em = 0 + mitigation_physical = 0 + mitigation_heat = 0 + + +/obj/machinery/power/shield_generator/proc/get_flag_descriptions() + var/list/all_flags = list() + for(var/datum/shield_mode/SM in mode_list) + if(SM.hacked_only && !hacked) + continue + all_flags.Add(list(list( + "name" = SM.mode_name, + "desc" = SM.mode_desc, + "flag" = SM.mode_flag, + "status" = check_flag(SM.mode_flag), + "hacked" = SM.hacked_only, + "multiplier" = SM.multiplier + ))) + return all_flags + + +// These two procs determine tiles that should be shielded given the field range. They are quite CPU intensive and may trigger BYOND infinite loop checks, therefore they are set +// as background procs to prevent locking up the server. They are only called when the field is generated, or when hull mode is toggled on/off. +/obj/machinery/power/shield_generator/proc/fieldtype_square() + set background = 1 + var/list/out = list() + var/list/base_turfs = get_base_turfs() + + for(var/turf/gen_turf in base_turfs) + var/turf/T + for (var/x_offset = -field_radius; x_offset <= field_radius; x_offset++) + T = locate(gen_turf.x + x_offset, gen_turf.y - field_radius, gen_turf.z) + if(T) + out += T + T = locate(gen_turf.x + x_offset, gen_turf.y + field_radius, gen_turf.z) + if(T) + out += T + + for (var/y_offset = -field_radius+1; y_offset < field_radius; y_offset++) + T = locate(gen_turf.x - field_radius, gen_turf.y + y_offset, gen_turf.z) + if(T) + out += T + T = locate(gen_turf.x + field_radius, gen_turf.y + y_offset, gen_turf.z) + if(T) + out += T + return out + + +/obj/machinery/power/shield_generator/proc/fieldtype_hull() + set background = 1 + . = list() + var/list/base_turfs = get_base_turfs() + + // Old code found all space turfs and added them if it had a non-space neighbor. + // This one finds all non-space turfs and adds all its non-space neighbors. + for(var/turf/gen_turf in base_turfs) + var/area/TA = null // Variable for area checking. Defining it here so memory does not have to be allocated repeatedly. + for(var/turf/T in trange(field_radius, gen_turf)) + // Don't expand to space or on shuttle areas. + if(istype(T, /turf/space) || istype(T, /turf/simulated/open)) + continue + + // Find adjacent space/shuttle tiles and cover them. Shuttles won't be blocked if shield diffuser is mapped in and turned on. + for(var/turf/TN in orange(1, T)) + TA = get_area(TN) + //if ((istype(TN, /turf/space) || (istype(TN, /turf/simulated/open) && (istype(TA, /area/space) || TA.area_flags & AREA_FLAG_EXTERNAL)))) + if((istype(TN, /turf/space) || (istype(TN, /turf/simulated/open) && (istype(TA, /area/space))))) + . |= TN + continue + +// Returns a list of turfs from which a field will propagate. If multi-Z mode is enabled, this will return a "column" of turfs above and below the generator. +/obj/machinery/power/shield_generator/proc/get_base_turfs() + var/list/turfs = list() + var/turf/T = get_turf(src) + + if(!istype(T)) + return + + turfs.Add(T) + + // Multi-Z mode is disabled + if(!check_flag(MODEFLAG_MULTIZ)) + return turfs + + while(HasAbove(T.z)) + T = GetAbove(T) + if(istype(T)) + turfs.Add(T) + + T = get_turf(src) + + while(HasBelow(T.z)) + T = GetBelow(T) + if(istype(T)) + turfs.Add(T) + + return turfs + + +// Starts fully charged +/obj/machinery/power/shield_generator/charged/Initialize() + . = ..() + current_energy = max_energy + +// Starts with the best SMES coil and capacitor (and fully charged) +/obj/machinery/power/shield_generator/upgraded/Initialize() + . = ..() + for(var/obj/item/weapon/smes_coil/sc in component_parts) + component_parts -= sc + qdel(sc) + + for(var/obj/item/weapon/stock_parts/capacitor/cap in component_parts) + component_parts -= cap + qdel(cap) + + component_parts += new /obj/item/weapon/stock_parts/capacitor/omni(src) + component_parts += new /obj/item/weapon/smes_coil/super_capacity(src) + RefreshParts() + current_energy = max_energy + +// Only uses 20% as much power (and starts upgraded and charged and hacked) +/obj/machinery/power/shield_generator/upgraded/admin + name = "experimental shield generator" + power_coefficient = 0.2 + hacked = TRUE diff --git a/code/modules/shieldgen/shield_modes.dm b/code/modules/shieldgen/shield_modes.dm new file mode 100644 index 0000000000..5a889b4c08 --- /dev/null +++ b/code/modules/shieldgen/shield_modes.dm @@ -0,0 +1,83 @@ +// Definitions for shield modes. Names, descriptions and power usage multipliers can be changed here. +// Do not change the mode_flag variables without a good reason! + +/datum/shield_mode + var/mode_name // User-friendly name of this mode. + var/mode_desc // A short description of what the mode does. + var/mode_flag // Mode bitflag. See defines file. + var/multiplier // Energy usage multiplier. Each enabled mode multiplies upkeep power usage by this number. Values between 1-2 are good balance-wise. Hacked modes can go up to 3-4 + var/hacked_only = 0 // Set to 1 to allow usage of this shield mode only on hacked generators. + +/datum/shield_mode/hyperkinetic + mode_name = "Hyperkinetic Projectiles" + mode_desc = "This mode blocks various fast moving physical objects, such as bullets, blunt weapons, meteors and other." + mode_flag = MODEFLAG_HYPERKINETIC + multiplier = 1.2 + +/datum/shield_mode/photonic + mode_name = "Photonic Dispersion" + mode_desc = "This mode blocks majority of light. This includes beam weaponry and most of the visible light spectrum." + mode_flag = MODEFLAG_PHOTONIC + multiplier = 1.3 + +/datum/shield_mode/em + mode_name = "Electro-Magnetic Shielding" + mode_desc = "This mode blocks various high-power emissions like electrical storms." + mode_flag = MODEFLAG_EM + multiplier = 1.3 + +/datum/shield_mode/humanoids + mode_name = "Humanoid Lifeforms" + mode_desc = "This mode blocks various humanoid lifeforms. Does not affect fully synthetic humanoids." + mode_flag = MODEFLAG_HUMANOIDS + multiplier = 1.5 + +/datum/shield_mode/silicon + mode_name = "Silicon Lifeforms" + mode_desc = "This mode blocks various silicon based lifeforms." + mode_flag = MODEFLAG_ANORGANIC + multiplier = 1.5 + +/datum/shield_mode/mobs + mode_name = "Unknown Lifeforms" + mode_desc = "This mode blocks various other non-humanoid and non-silicon lifeforms. Typical uses include blocking carps." + mode_flag = MODEFLAG_NONHUMANS + multiplier = 1.5 + +/datum/shield_mode/atmosphere + mode_name = "Atmospheric Containment" + mode_desc = "This mode blocks air flow and acts as atmosphere containment." + mode_flag = MODEFLAG_ATMOSPHERIC + multiplier = 1.3 + +/datum/shield_mode/hull + mode_name = "Hull Shielding" + mode_desc = "This mode recalibrates the field to cover surface of the installation instead of projecting a bubble shaped field." + mode_flag = MODEFLAG_HULL + multiplier = 1 + +/datum/shield_mode/adaptive + mode_name = "Adaptive Field Harmonics" + mode_desc = "This mode modulates the shield harmonic frequencies, allowing the field to adapt to various damage types." + mode_flag = MODEFLAG_MODULATE + multiplier = 2 + +/datum/shield_mode/bypass + mode_name = "Diffuser Bypass" + mode_desc = "This mode disables the built-in safeties which allows the generator to counter effect of various shield diffusers. This tends to create a very large strain on the generator. Does not work with enabled safety protocols." + mode_flag = MODEFLAG_BYPASS + multiplier = 3 + hacked_only = 1 + +/datum/shield_mode/overcharge + mode_name = "Field Overcharge" + mode_desc = "This mode polarises the field, causing damage on contact. Does not work with enabled safety protocols." + mode_flag = MODEFLAG_OVERCHARGE + multiplier = 3 + hacked_only = 1 + +/datum/shield_mode/multiz + mode_name = "Multi-Dimensional Field Warp" + mode_desc = "Recalibrates the field projection array to increase the vertical height of the field, allowing it's usage on multi-deck stations or ships." + mode_flag = MODEFLAG_MULTIZ + multiplier = 1 diff --git a/code/modules/shuttles/_defines.dm b/code/modules/shuttles/_defines.dm deleted file mode 100644 index ad8c39466b..0000000000 --- a/code/modules/shuttles/_defines.dm +++ /dev/null @@ -1,4 +0,0 @@ -#define SHUTTLE_FLAGS_NONE 0 -#define SHUTTLE_FLAGS_PROCESS 1 -#define SHUTTLE_FLAGS_SUPPLY 2 -#define SHUTTLE_FLAGS_ALL (~SHUTTLE_FLAGS_NONE) \ No newline at end of file diff --git a/code/modules/shuttles/crashes.dm b/code/modules/shuttles/crashes.dm index eaf7e06019..6f6c66e177 100644 --- a/code/modules/shuttles/crashes.dm +++ b/code/modules/shuttles/crashes.dm @@ -3,30 +3,34 @@ // /datum/shuttle - var/list/crash_areas = null + var/list/crash_locations = null var/crash_message = "Oops. The shuttle blew up." // Announcement made when shuttle crashes /datum/shuttle/New() - if(crash_areas) - for(var/i in 1 to crash_areas.len) - crash_areas[i] = locate(crash_areas[i]) + if(crash_locations) + var/crash_location_ids = crash_locations + crash_locations = list() + for(var/location_tag in crash_location_ids) + var/obj/effect/shuttle_landmark/L = SSshuttles.get_landmark(location_tag) + if(L) + crash_locations += L ..() // Return 0 to let the jump continue, 1 to abort the jump. // Default implementation checks if the shuttle should crash and if so crashes it. -/datum/shuttle/proc/process_longjump(var/area/origin, var/area/intended_destination, var/direction) - if(should_crash()) - do_crash(origin) +/datum/shuttle/proc/process_longjump(var/obj/effect/shuttle_landmark/intended_destination) + if(should_crash(intended_destination)) + do_crash(intended_destination) return 1 // Decide if this is the time we crash. Return true for yes -/datum/shuttle/proc/should_crash(var/area/origin, var/area/intended_destination, var/direction) +/datum/shuttle/proc/should_crash(var/obj/effect/shuttle_landmark/intended_destination) return FALSE // Actually crash the shuttle -/datum/shuttle/proc/do_crash(var/area/source) +/datum/shuttle/proc/do_crash(var/obj/effect/shuttle_landmark/intended_destination) // Choose the target - var/area/target = pick(crash_areas) + var/obj/effect/shuttle_landmark/target = pick(crash_locations) ASSERT(istype(target)) // Blow up the target area? @@ -34,16 +38,18 @@ //What people are we dealing with here var/list/victims = list() - for(var/mob/living/L in source) - victims += L - spawn(0) - shake_camera(L,2 SECONDS,4) + for(var/area/A in shuttle_area) + for(var/mob/living/L in A) + victims += L + spawn(0) + shake_camera(L,2 SECONDS,4) //SHAKA SHAKA SHAKA sleep(2 SECONDS) // Move the shuttle - move(source, target) + if (!attempt_move(target)) + return // Lucky! // Hide people for(var/living in victims) @@ -54,9 +60,11 @@ L.loc = null // Blow up the shuttle - var/list/area_turfs = get_area_turfs(target) - var/turf/epicenter = pick(area_turfs) - var/boomsize = area_turfs.len / 10 // Bigger shuttle = bigger boom + var/list/shuttle_turfs = list() + for(var/area/A in shuttle_area) + shuttle_turfs += get_area_turfs(A) + var/turf/epicenter = pick(shuttle_turfs) + var/boomsize = shuttle_turfs.len / 10 // Bigger shuttle = bigger boom explosion(epicenter, 0, boomsize, boomsize*2, boomsize*3) moving_status = SHUTTLE_CRASHED command_announcement.Announce("[crash_message]", "Shuttle Alert") diff --git a/code/modules/shuttles/escape_pods.dm b/code/modules/shuttles/escape_pods.dm index 60c1e4bd0d..245b96e82e 100644 --- a/code/modules/shuttles/escape_pods.dm +++ b/code/modules/shuttles/escape_pods.dm @@ -1,50 +1,55 @@ -/datum/shuttle/ferry/escape_pod - var/datum/computer/file/embedded_program/docking/simple/escape_pod/arming_controller - category = /datum/shuttle/ferry/escape_pod +/datum/shuttle/autodock/ferry/escape_pod + var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/arming_controller + category = /datum/shuttle/autodock/ferry/escape_pod -/datum/shuttle/ferry/escape_pod/New() +/datum/shuttle/autodock/ferry/escape_pod/New() move_time = move_time + rand(-30, 60) if(name in emergency_shuttle.escape_pods) CRASH("An escape pod with the name '[name]' has already been defined.") emergency_shuttle.escape_pods[name] = src + ..() -/datum/shuttle/ferry/escape_pod/init_docking_controllers() - ..() - arming_controller = locate(dock_target_station) + //find the arming controller (berth) - If not configured directly, try to read it from current location landmark + var/arming_controller_tag = arming_controller + if(!arming_controller && active_docking_controller) + arming_controller_tag = active_docking_controller.id_tag + arming_controller = SSshuttles.docking_registry[arming_controller_tag] if(!istype(arming_controller)) - warning("warning: escape pod with station dock tag [dock_target_station] could not find it's dock target!") + CRASH("Could not find arming controller for escape pod \"[name]\", tag was '[arming_controller_tag]'.") - if(docking_controller) - var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/controller_master = docking_controller.master - if(!istype(controller_master)) - warning("warning: escape pod with docking tag [docking_controller_tag] could not find it's controller master!") - else - controller_master.pod = src + //find the pod's own controller + var/datum/computer/file/embedded_program/docking/simple/prog = SSshuttles.docking_registry[docking_controller_tag] + var/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/controller_master = prog.master + if(!istype(controller_master)) + CRASH("Escape pod \"[name]\" could not find it's controller master! docking_controller_tag=[docking_controller_tag]") + controller_master.pod = src -/datum/shuttle/ferry/escape_pod/can_launch() +/datum/shuttle/autodock/ferry/escape_pod/can_launch() if(arming_controller && !arming_controller.armed) //must be armed return 0 if(location) return 0 //it's a one-way trip. return ..() -/datum/shuttle/ferry/escape_pod/can_force() +/datum/shuttle/autodock/ferry/escape_pod/can_force() if (arming_controller.eject_time && world.time < arming_controller.eject_time + 50) return 0 //dont allow force launching until 5 seconds after the arming controller has reached it's countdown return ..() -/datum/shuttle/ferry/escape_pod/can_cancel() +/datum/shuttle/autodock/ferry/escape_pod/can_cancel() return 0 //This controller goes on the escape pod itself /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod name = "escape pod controller" - var/datum/shuttle/ferry/escape_pod/pod + program = /datum/computer/file/embedded_program/docking/simple + var/datum/shuttle/autodock/ferry/escape_pod/pod /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type data = list( "docking_status" = docking_program.get_docking_status(), @@ -64,17 +69,18 @@ ui.set_auto_update(1) /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod/Topic(href, href_list) - if(..()) - return 1 + if((. = ..())) + return if("manual_arm") pod.arming_controller.arm() + return TOPIC_REFRESH if("force_launch") if (pod.can_force()) pod.force_launch(src) else if (emergency_shuttle.departed && pod.can_launch()) //allow players to manually launch ahead of time if the shuttle leaves pod.launch(src) - + return TOPIC_REFRESH return 0 @@ -82,18 +88,15 @@ //This controller is for the escape pod berth (station side) /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth name = "escape pod berth controller" - -/obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/Initialize() - . = ..() - docking_program = new/datum/computer/file/embedded_program/docking/simple/escape_pod(src) - program = docking_program + program = /datum/computer/file/embedded_program/docking/simple/escape_pod_berth /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] + var/datum/computer/file/embedded_program/docking/simple/docking_program = program // Cast to proper type var/armed = null - if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod)) - var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program + if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod_berth)) + var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/P = docking_program armed = P.armed data = list( @@ -112,46 +115,46 @@ /obj/machinery/embedded_controller/radio/simple_docking_controller/escape_pod_berth/emag_act(var/remaining_charges, var/mob/user) if (!emagged) - user << "You emag the [src], arming the escape pod!" + to_chat(user, "You emag the [src], arming the escape pod!") emagged = 1 - if (istype(docking_program, /datum/computer/file/embedded_program/docking/simple/escape_pod)) - var/datum/computer/file/embedded_program/docking/simple/escape_pod/P = docking_program + if (istype(program, /datum/computer/file/embedded_program/docking/simple/escape_pod_berth)) + var/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/P = program if (!P.armed) P.arm() return 1 //A docking controller program for a simple door based docking port -/datum/computer/file/embedded_program/docking/simple/escape_pod +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth var/armed = 0 var/eject_delay = 10 //give latecomers some time to get out of the way if they don't make it onto the pod var/eject_time = null var/closing = 0 -/datum/computer/file/embedded_program/docking/simple/escape_pod/proc/arm() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/proc/arm() if(!armed) armed = 1 open_door() -/datum/computer/file/embedded_program/docking/simple/escape_pod/receive_user_command(command) +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/receive_user_command(command) if (!armed) - return - ..(command) + return TRUE // Eat all commands. + return ..(command) -/datum/computer/file/embedded_program/docking/simple/escape_pod/process() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/process() ..() if (eject_time && world.time >= eject_time && !closing) close_door() closing = 1 -/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_docking() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/prepare_for_docking() return -/datum/computer/file/embedded_program/docking/simple/escape_pod/ready_for_docking() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/ready_for_docking() return 1 -/datum/computer/file/embedded_program/docking/simple/escape_pod/finish_docking() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/finish_docking() return //don't do anything - the doors only open when the pod is armed. -/datum/computer/file/embedded_program/docking/simple/escape_pod/prepare_for_undocking() +/datum/computer/file/embedded_program/docking/simple/escape_pod_berth/prepare_for_undocking() eject_time = world.time + eject_delay*10 diff --git a/code/modules/shuttles/landmarks.dm b/code/modules/shuttles/landmarks.dm new file mode 100644 index 0000000000..38f2d41f25 --- /dev/null +++ b/code/modules/shuttles/landmarks.dm @@ -0,0 +1,194 @@ +//making this separate from /obj/effect/landmark until that mess can be dealt with +/obj/effect/shuttle_landmark + name = "Nav Point" + icon = 'icons/effects/effects.dmi' + icon_state = "energynet" + anchored = 1 + unacidable = 1 + simulated = 0 + invisibility = 101 + flags = SLANDMARK_FLAG_AUTOSET // We generally want to use current area/turf as base. + + //ID of the landmark + var/landmark_tag + //ID of the controller on the dock side (intialize to id_tag, becomes reference) + var/datum/computer/file/embedded_program/docking/docking_controller + //Map of shuttle names to ID of controller used for this landmark for shuttles with multiple ones. + var/list/special_dock_targets + + //When the shuttle leaves this landmark, it will leave behind the base area + //also used to determine if the shuttle can arrive here without obstruction + var/area/base_area + //Will also leave this type of turf behind if set. + var/turf/base_turf + //Name of the shuttle, null for generic waypoint + var/shuttle_restricted + +/obj/effect/shuttle_landmark/Initialize() + . = ..() + if(docking_controller) + . = INITIALIZE_HINT_LATELOAD + + // Even if this flag is set, hardcoded values take precedence. + if(flags & SLANDMARK_FLAG_AUTOSET) + if(ispath(base_area)) + var/area/A = locate(base_area) + if(!istype(A)) + CRASH("Shuttle landmark \"[landmark_tag]\" couldn't locate area [base_area].") + base_area = A + else + base_area = get_area(src) + var/turf/T = get_turf(src) + if(T && !base_turf) + base_turf = T.type + else + base_area = locate(base_area || world.area) + + name = (name + " ([x],[y])") + SSshuttles.register_landmark(landmark_tag, src) + +/obj/effect/shuttle_landmark/LateInitialize() + if(!docking_controller) + return + var/docking_tag = docking_controller + docking_controller = SSshuttles.docking_registry[docking_tag] + if(!istype(docking_controller)) + log_error("Could not find docking controller for shuttle waypoint '[name]', docking tag was '[docking_tag]'.") + if(using_map.use_overmap) + var/obj/effect/overmap/visitable/location = get_overmap_sector(z) + if(location && location.docking_codes) + docking_controller.docking_codes = location.docking_codes + +/obj/effect/shuttle_landmark/forceMove() + var/obj/effect/overmap/visitable/map_origin = get_overmap_sector(z) + . = ..() + var/obj/effect/overmap/visitable/map_destination = get_overmap_sector(z) + if(map_origin != map_destination) + if(map_origin) + map_origin.remove_landmark(src, shuttle_restricted) + if(map_destination) + map_destination.add_landmark(src, shuttle_restricted) + +//Called when the landmark is added to an overmap sector. +/obj/effect/shuttle_landmark/proc/sector_set(var/obj/effect/overmap/visitable/O, shuttle_name) + shuttle_restricted = shuttle_name + +/obj/effect/shuttle_landmark/proc/is_valid(var/datum/shuttle/shuttle) + if(shuttle.current_location == src) + return FALSE + for(var/area/A in shuttle.shuttle_area) + var/list/translation = get_turf_translation(get_turf(shuttle.current_location), get_turf(src), A.contents) + if(check_collision(base_area, list_values(translation))) + return FALSE + var/conn = GetConnectedZlevels(z) + for(var/w in (z - shuttle.multiz) to z) + if(!(w in conn)) + return FALSE + return TRUE + +// This creates a graphical warning to where the shuttle is about to land in approximately five seconds. +/obj/effect/shuttle_landmark/proc/create_warning_effect(var/datum/shuttle/shuttle) + if(shuttle.current_location == src) + return // TOO LATE! + for(var/area/A in shuttle.shuttle_area) + var/list/translation = get_turf_translation(get_turf(shuttle.current_location), get_turf(src), A.contents) + for(var/T in list_values(translation)) + new /obj/effect/temporary_effect/shuttle_landing(T) // It'll delete itself when needed. + return + +// Should return a readable description of why not if it can't depart. +/obj/effect/shuttle_landmark/proc/cannot_depart(datum/shuttle/shuttle) + return FALSE + +/obj/effect/shuttle_landmark/proc/shuttle_departed(datum/shuttle/shuttle) + return + +/obj/effect/shuttle_landmark/proc/shuttle_arrived(datum/shuttle/shuttle) + return + +/proc/check_collision(area/target_area, list/target_turfs) + for(var/target_turf in target_turfs) + var/turf/target = target_turf + if(!target) + return TRUE //collides with edge of map + if(target.loc != target_area) + return TRUE //collides with another area + if(target.density) + return TRUE //dense turf + return FALSE + +// +//Self-naming/numbering ones. +// +/obj/effect/shuttle_landmark/automatic + name = "Navpoint" + landmark_tag = "navpoint" + flags = SLANDMARK_FLAG_AUTOSET + +/obj/effect/shuttle_landmark/automatic/Initialize() + landmark_tag += "-[x]-[y]-[z]-[random_id("landmarks",1,9999)]" + return ..() + +/obj/effect/shuttle_landmark/automatic/sector_set(var/obj/effect/overmap/visitable/O) + ..() + name = ("[O.name] - [initial(name)] ([x],[y])") + +//Subtype that calls explosion on init to clear space for shuttles +/obj/effect/shuttle_landmark/automatic/clearing + var/radius = 10 + +/obj/effect/shuttle_landmark/automatic/clearing/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/effect/shuttle_landmark/automatic/clearing/LateInitialize() + ..() + for(var/turf/T in range(radius, src)) + if(T.density) + T.ChangeTurf(get_base_turf_by_area(T)) + + +// Subtype that also queues a shuttle datum (for shuttles starting on maps loaded at runtime) +/obj/effect/shuttle_landmark/shuttle_initializer + var/datum/shuttle/shuttle_type + +/obj/effect/shuttle_landmark/shuttle_initializer/Initialize() + . = ..() + LAZYADD(SSshuttles.shuttles_to_initialize, shuttle_type) // queue up for init. + +// +// Bluespace flare landmark beacon +// +/obj/item/device/spaceflare + name = "bluespace flare" + desc = "Burst transmitter used to broadcast all needed information for shuttle navigation systems. Has a flare attached for marking the spot where you probably shouldn't be standing." + icon_state = "bluflare" + light_color = "#3728ff" + var/active + +/obj/item/device/spaceflare/attack_self(var/mob/user) + if(!active) + visible_message("[user] pulls the cord, activating the [src].") + activate() + +/obj/item/device/spaceflare/proc/activate() + if(active) + return + var/turf/T = get_turf(src) + var/mob/M = loc + if(istype(M) && !M.unEquip(src, T)) + return + + active = 1 + anchored = 1 + + var/obj/effect/shuttle_landmark/automatic/mark = new(T) + mark.name = ("Beacon signal ([T.x],[T.y])") + T.hotspot_expose(1500, 5) + update_icon() + +/obj/item/device/spaceflare/update_icon() + . = ..() + if(active) + icon_state = "bluflare_on" + set_light(0.3, 0.1, 6, 2, "85d1ff") diff --git a/code/modules/shuttles/shuttle.dm b/code/modules/shuttles/shuttle.dm index 4ce211ee72..808a1f55e7 100644 --- a/code/modules/shuttles/shuttle.dm +++ b/code/modules/shuttles/shuttle.dm @@ -1,6 +1,3 @@ -//These lists are populated in /datum/controller/subsystem/shuttles/proc/setup_shuttle_docks() -//Shuttle subsystem is instantiated in shuttles.dm. - //shuttle moving state defines are in setup.dm /datum/shuttle @@ -8,48 +5,88 @@ var/warmup_time = 0 var/moving_status = SHUTTLE_IDLE - var/docking_controller_tag //tag of the controller used to coordinate docking - var/datum/computer/file/embedded_program/docking/docking_controller //the controller itself. (micro-controller, not game controller) + var/list/shuttle_area // Initial value can be either a single area type or a list of area types + var/obj/effect/shuttle_landmark/current_location //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. - var/arrive_time = 0 //the time at which the shuttle arrives when long jumping - var/depart_time = 0 //Similar to above, set when the shuttle leaves when long jumping, to compare against arrive time. - var/flags = SHUTTLE_FLAGS_PROCESS + var/tmp/arrive_time = 0 //the time at which the shuttle arrives when long jumping + var/flags = SHUTTLE_FLAGS_NONE + var/process_state = IDLE_STATE // Used with SHUTTLE_FLAGS_PROCESS, as well as to store current state. var/category = /datum/shuttle + var/multiz = 0 //how many multiz levels, starts at 0 TODO Leshana - Are we porting this? - var/ceiling_type = /turf/unsimulated/floor/shuttle_ceiling + var/ceiling_type // Type path of turf to roof over the shuttle when at multi-z landmarks. Ignored if null. -/datum/shuttle/New() + var/sound_takeoff = 'sound/effects/shuttles/shuttle_takeoff.ogg' + var/sound_landing = 'sound/effects/shuttles/shuttle_landing.ogg' + + var/knockdown = 1 //whether shuttle downs non-buckled people when it moves + + var/defer_initialisation = FALSE //If this this shuttle should be initialised automatically. + //If set to true, you are responsible for initialzing the shuttle manually. + //Useful for shuttles that are initialized by map_template loading, or shuttles that are created in-game or not used. + + var/mothershuttle //tag of mothershuttle + var/motherdock //tag of mothershuttle landmark, defaults to starting location + + var/tmp/depart_time = 0 //Similar to above, set when the shuttle leaves when long jumping. Used for progress bars. + + // Future Thoughts: Baystation put "docking" stuff in a subtype, leaving base type pure and free of docking stuff. Is this best? + +/datum/shuttle/New(_name, var/obj/effect/shuttle_landmark/initial_location) ..() - if(src.name in shuttle_controller.shuttles) + if(_name) + src.name = _name + + var/list/areas = list() + if(!islist(shuttle_area)) + shuttle_area = list(shuttle_area) + for(var/T in shuttle_area) + var/area/A = locate(T) + if(!istype(A)) + CRASH("Shuttle \"[name]\" couldn't locate area [T].") + areas += A + shuttle_area = areas + + if(initial_location) + current_location = initial_location + else + current_location = SSshuttles.get_landmark(current_location) + if(!istype(current_location)) + log_debug("UM whoops, no initial? [src]") + CRASH("Shuttle '[name]' could not find its starting location landmark [current_location].") + + if(src.name in SSshuttles.shuttles) CRASH("A shuttle with the name '[name]' is already defined.") - shuttle_controller.shuttles[src.name] = src + SSshuttles.shuttles[src.name] = src if(flags & SHUTTLE_FLAGS_PROCESS) - shuttle_controller.process_shuttles += src + SSshuttles.process_shuttles += src if(flags & SHUTTLE_FLAGS_SUPPLY) - if(supply_controller.shuttle) + if(SSsupply.shuttle) CRASH("A supply shuttle is already defined.") - supply_controller.shuttle = src + SSsupply.shuttle = src /datum/shuttle/Destroy() - shuttle_controller.shuttles -= src.name - shuttle_controller.process_shuttles -= src - if(supply_controller.shuttle == src) - supply_controller.shuttle = null + current_location = null + SSshuttles.shuttles -= src.name + SSshuttles.process_shuttles -= src + SSshuttles.shuttle_logs -= src + if(SSsupply.shuttle == src) + SSsupply.shuttle = null . = ..() -/datum/shuttle/process() +// This is called after all shuttles have been initialized by SSshuttles, but before sectors have been initialized. +// Importantly for subtypes, all shuttles will have been initialized and mothershuttles hooked up by the time this is called. +/datum/shuttle/proc/populate_shuttle_objects() + // Scan for shuttle consoles on them needing auto-config. + for(var/area/A in find_childfree_areas()) // Let sub-shuttles handle their areas, only do our own. + for(var/obj/machinery/computer/shuttle_control/SC in A) + if(!SC.shuttle_tag) + SC.set_shuttle_tag(src.name) return -/datum/shuttle/proc/init_docking_controllers() - if(docking_controller_tag) - docking_controller = locate(docking_controller_tag) - if(!istype(docking_controller)) - world << "warning: shuttle with docking tag [docking_controller_tag] could not find it's controller!" - // This creates a graphical warning to where the shuttle is about to land, in approximately five seconds. -/datum/shuttle/proc/create_warning_effect(area/landing_area) - for(var/turf/T in landing_area) - new /obj/effect/temporary_effect/shuttle_landing(T) // It'll delete itself when needed. +/datum/shuttle/proc/create_warning_effect(var/obj/effect/shuttle_landmark/destination) + destination.create_warning_effect(src) // Return false to abort a jump, before the 'warmup' phase. /datum/shuttle/proc/pre_warmup_checks() @@ -60,197 +97,283 @@ return TRUE // If you need an event to occur when the shuttle jumps in short or long jump, override this. -/datum/shuttle/proc/on_shuttle_departure(var/area/origin) - origin.shuttle_departed() +// Keep in mind that destination is the intended destination, the shuttle may or may not actually reach it.s +/datum/shuttle/proc/on_shuttle_departure(var/obj/effect/shuttle_landmark/origin, var/obj/effect/shuttle_landmark/destination) return // Similar to above, but when it finishes moving to the target. Short jump generally makes this occur immediately after the above proc. -/datum/shuttle/proc/on_shuttle_arrival(var/area/destination) - destination.shuttle_arrived() +// Keep in mind we might not actually have gotten to destination. Check current_location to be sure where we ended up. +/datum/shuttle/proc/on_shuttle_arrival(var/obj/effect/shuttle_landmark/origin, var/obj/effect/shuttle_landmark/destination) return -/datum/shuttle/proc/short_jump(var/area/origin,var/area/destination) +/datum/shuttle/proc/short_jump(var/obj/effect/shuttle_landmark/destination) if(moving_status != SHUTTLE_IDLE) return if(!pre_warmup_checks()) return + var/obj/effect/shuttle_landmark/start_location = current_location + // TODO - Figure out exactly when to play sounds. Before warmup_time delay? Should there be a sleep for waiting for sounds? or no? moving_status = SHUTTLE_WARMUP spawn(warmup_time*10) - make_sounds(origin, HYPERSPACE_WARMUP) + make_sounds(HYPERSPACE_WARMUP) create_warning_effect(destination) sleep(5 SECONDS) // so the sound finishes. if(!post_warmup_checks()) - moving_status = SHUTTLE_IDLE + cancel_launch(null) + + if(!fuel_check()) //fuel error (probably out of fuel) occured, so cancel the launch + cancel_launch(null) if (moving_status == SHUTTLE_IDLE) - make_sounds(origin, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) return //someone cancelled the launch - on_shuttle_departure(origin) - moving_status = SHUTTLE_INTRANSIT //shouldn't matter but just to be safe - move(origin, destination) + on_shuttle_departure(start_location, destination) + + attempt_move(destination) + moving_status = SHUTTLE_IDLE + on_shuttle_arrival(start_location, destination) - on_shuttle_arrival(destination) + make_sounds(HYPERSPACE_END) - make_sounds(destination, HYPERSPACE_END) - -/datum/shuttle/proc/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) - //world << "shuttle/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]" +// TODO - Far Future - Would be great if this was driven by process too. +/datum/shuttle/proc/long_jump(var/obj/effect/shuttle_landmark/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time) + //to_world("shuttle/long_jump: current_location=[current_location], destination=[destination], interim=[interim], travel_time=[travel_time]") if(moving_status != SHUTTLE_IDLE) return if(!pre_warmup_checks()) return - //it would be cool to play a sound here + var/obj/effect/shuttle_landmark/start_location = current_location + // TODO - Figure out exactly when to play sounds. Before warmup_time delay? Should there be a sleep for waiting for sounds? or no? moving_status = SHUTTLE_WARMUP spawn(warmup_time*10) - make_sounds(departing, HYPERSPACE_WARMUP) + make_sounds(HYPERSPACE_WARMUP) create_warning_effect(interim) // Really doubt someone is gonna get crushed in the interim area but for completeness's sake we'll make the warning. sleep(5 SECONDS) // so the sound finishes. if(!post_warmup_checks()) - moving_status = SHUTTLE_IDLE + cancel_launch(null) if (moving_status == SHUTTLE_IDLE) - make_sounds(departing, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) return //someone cancelled the launch arrive_time = world.time + travel_time*10 - depart_time = world.time moving_status = SHUTTLE_INTRANSIT + on_shuttle_departure(start_location, destination) - on_shuttle_departure(departing) + if(attempt_move(interim, TRUE)) + interim.shuttle_arrived() - move(departing, interim, direction) - interim.shuttle_arrived() + if(process_longjump(current_location, destination)) //VOREStation Edit - To hook custom shuttle code in + return //VOREStation Edit - It handled it for us (shuttle crash or such) - if(process_longjump(departing, destination)) //VOREStation Edit - To hook custom shuttle code in - return //VOREStation Edit - It handled it for us (shuttle crash or such) + var/last_progress_sound = 0 + var/made_warning = FALSE + while (world.time < arrive_time) + // Make the shuttle make sounds every four seconds, since the sound file is five seconds. + if(last_progress_sound + 4 SECONDS < world.time) + make_sounds(HYPERSPACE_PROGRESS) + last_progress_sound = world.time - var/last_progress_sound = 0 - var/made_warning = FALSE - while (world.time < arrive_time) - // Make the shuttle make sounds every four seconds, since the sound file is five seconds. - if(last_progress_sound + 4 SECONDS < world.time) - make_sounds(interim, HYPERSPACE_PROGRESS) - last_progress_sound = world.time + if(arrive_time - world.time <= 5 SECONDS && !made_warning) + made_warning = TRUE + create_warning_effect(destination) + sleep(5) - if(arrive_time - world.time <= 5 SECONDS && !made_warning) - made_warning = TRUE - create_warning_effect(destination) - sleep(5) + if(!attempt_move(destination)) + attempt_move(start_location) //try to go back to where we started. If that fails, I guess we're stuck in the interim location - interim.shuttle_departed() - move(interim, destination, direction) moving_status = SHUTTLE_IDLE + on_shuttle_arrival(start_location, destination) + make_sounds(HYPERSPACE_END) - on_shuttle_arrival(destination) - make_sounds(destination, HYPERSPACE_END) +////////////////////////////// +// Forward declarations of public procs. They do nothing because this is not auto-dock. +/datum/shuttle/proc/fuel_check() + return 1 //fuel check should always pass in non-overmap shuttles (they have magic engines) + +/datum/shuttle/proc/cancel_launch(var/user) + // If we are past warming up its too late to cancel. + if (moving_status == SHUTTLE_WARMUP) + moving_status = SHUTTLE_IDLE +/* + Docking stuff +*/ /datum/shuttle/proc/dock() - if (!docking_controller) - return - - var/dock_target = current_dock_target() - if (!dock_target) - return - - docking_controller.initiate_docking(dock_target) + return /datum/shuttle/proc/undock() - if (!docking_controller) - return - docking_controller.initiate_undocking() + return -/datum/shuttle/proc/current_dock_target() - return null +/datum/shuttle/proc/force_undock() + return -/datum/shuttle/proc/skip_docking_checks() - if (!docking_controller || !current_dock_target()) - return 1 //shuttles without docking controllers or at locations without docking ports act like old-style shuttles - return 0 +// Check if we are docked (or never dock) and thus have properly arrived. +/datum/shuttle/proc/check_docked() + return TRUE -//just moves the shuttle from A to B, if it can be moved -//A note to anyone overriding move in a subtype. move() must absolutely not, under any circumstances, fail to move the shuttle. +// Check if we are undocked and thus probably ready to depart. +/datum/shuttle/proc/check_undocked() + return TRUE + +/***************** +* Shuttle Moved Handling * (Observer Pattern Implementation: Shuttle Moved) +* Shuttle Pre Move Handling * (Observer Pattern Implementation: Shuttle Pre Move) +*****************/ + +// Move the shuttle to destination if possible. +// Returns TRUE if we actually moved, otherwise FALSE. +/datum/shuttle/proc/attempt_move(var/obj/effect/shuttle_landmark/destination, var/interim = FALSE) + if(current_location == destination) + log_shuttle("Shuttle [src] attempted to move to [destination] but is already there!") + return FALSE + + if(!destination.is_valid(src)) + log_shuttle("Shuttle [src] aborting attempt_move() because destination=[destination] is not valid") + return FALSE + if(current_location.cannot_depart(src)) + log_shuttle("Shuttle [src] aborting attempt_move() because current_location=[current_location] refuses.") + return FALSE + + log_shuttle("[src] moving to [destination]. Areas are [english_list(shuttle_area)]") + var/list/translation = list() + for(var/area/A in shuttle_area) + log_shuttle("Translating [A]") + translation += get_turf_translation(get_turf(current_location), get_turf(destination), A.contents) + var/old_location = current_location + + // Observer pattern pre-move + GLOB.shuttle_pre_move_event.raise_event(src, old_location, destination) + current_location.shuttle_departed(src) + + // Actually do it! (This never fails) + perform_shuttle_move(destination, translation) + + // Observer pattern post-move + destination.shuttle_arrived(src) + GLOB.shuttle_moved_event.raise_event(src, old_location, destination) + + return TRUE + + +//just moves the shuttle from A to B +//A note to anyone overriding move in a subtype. perform_shuttle_move() must absolutely not, under any circumstances, fail to move the shuttle. //If you want to conditionally cancel shuttle launches, that logic must go in short_jump() or long_jump() -/datum/shuttle/proc/move(var/area/origin, var/area/destination, var/direction=null) +/datum/shuttle/proc/perform_shuttle_move(var/obj/effect/shuttle_landmark/destination, var/list/turf_translation) + log_shuttle("perform_shuttle_move() current=[current_location] destination=[destination]") + //to_world("move_shuttle() called for [name] leaving [origin] en route to [destination].") - //world << "move_shuttle() called for [name] leaving [origin] en route to [destination]." + //to_world("area_coming_from: [origin]") + //to_world("destination: [destination]") + ASSERT(current_location != destination) - //world << "area_coming_from: [origin]" - //world << "destination: [destination]" + // If shuttle has no internal gravity, update our gravity with destination gravity + if((flags & SHUTTLE_FLAGS_ZERO_G)) + var/new_grav = 1 + if(destination.flags & SLANDMARK_FLAG_ZERO_G) + var/area/new_area = get_area(destination) + new_grav = new_area.has_gravity + for(var/area/our_area in shuttle_area) + if(our_area.has_gravity != new_grav) + our_area.gravitychange(new_grav) - if(origin == destination) - //world << "cancelling move, shuttle will overlap." - return + // TODO - Old code used to throw stuff out of the way instead of squashing. Should we? - if (docking_controller && !docking_controller.undocked()) - docking_controller.force_undock() - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in destination) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - for(var/turf/T in dstturfs) - var/turf/D = locate(T.x, throwy - 1, T.z) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - - for(var/mob/living/carbon/bug in destination) - bug.gib() - - for(var/mob/living/simple_mob/pest in destination) - pest.gib() - - origin.move_contents_to(destination, direction=direction) - - for(var/mob/M in destination) - if(M.client) - spawn(0) - if(M.buckled) - M << "Sudden acceleration presses you into \the [M.buckled]!" - shake_camera(M, 3, 1) + // Move, gib, or delete everything in our way! + for(var/turf/src_turf in turf_translation) + var/turf/dst_turf = turf_translation[src_turf] + if(src_turf.is_solid_structure()) // in case someone put a hole in the shuttle and you were lucky enough to be under it + for(var/atom/movable/AM in dst_turf) + //if(AM.movable_flags & MOVABLE_FLAG_DEL_SHUTTLE) + // qdel(AM) + // continue + if(!AM.simulated) + continue + if(isliving(AM)) + var/mob/living/bug = AM + bug.gib() else - M << "The floor lurches beneath you!" - shake_camera(M, 10, 1) - if(istype(M, /mob/living/carbon)) - if(!M.buckled) - M.Weaken(3) + qdel(AM) //it just gets atomized I guess? TODO throw it into space somewhere, prevents people from using shuttles as an atom-smasher + + var/list/powernets = list() + for(var/area/A in shuttle_area) + // If there was a zlevel above our origin and we own the ceiling, erase our ceiling now we're leaving + if(ceiling_type && HasAbove(current_location.z)) + for(var/turf/TO in A.contents) + var/turf/TA = GetAbove(TO) + if(istype(TA, ceiling_type)) + TA.ChangeTurf(get_base_turf_by_area(TA), 1, 1) + if(knockdown) + for(var/mob/living/M in A) + spawn(0) + if(M.buckled) + to_chat(M, "Sudden acceleration presses you into \the [M.buckled]!") + shake_camera(M, 3, 1) + else + to_chat(M, "The floor lurches beneath you!") + shake_camera(M, 10, 1) + // TODO - tossing? + //M.visible_message("[M.name] is tossed around by the sudden acceleration!") + //M.throw_at_random(FALSE, 4, 1) + if(istype(M, /mob/living/carbon)) + M.Weaken(3) + // We only need to rebuild powernets for our cables. No need to check machines because they are on top of cables. + for(var/obj/structure/cable/C in A) + powernets |= C.powernet + + // Actually do the movement of everything - This replaces origin.move_contents_to(destination) + translate_turfs(turf_translation, current_location.base_area, current_location.base_turf) + current_location = destination + + // If there's a zlevel above our destination, paint in a ceiling on it so we retain our air + if(ceiling_type && HasAbove(current_location.z)) + for(var/area/A in shuttle_area) + for(var/turf/TD in A.contents) + var/turf/TA = GetAbove(TD) + if(istype(TA, get_base_turf_by_area(TA)) || isopenspace(TA)) + if(get_area(TA) in shuttle_area) + continue + TA.ChangeTurf(ceiling_type, TRUE, TRUE, TRUE) // Power-related checks. If shuttle contains power related machinery, update powernets. - var/update_power = 0 - for(var/obj/machinery/power/P in destination) - update_power = 1 - break + // Note: Old way was to rebuild ALL powernets: if(powernets.len) SSmachines.makepowernets() + // New way only rebuilds the powernets we have to + var/list/cables = list() + for(var/datum/powernet/P in powernets) + cables |= P.cables + qdel(P) + SSmachines.setup_powernets_for_cables(cables) - for(var/obj/structure/cable/C in destination) - update_power = 1 - break + // Adjust areas of mothershuttle so it doesn't try and bring us with it if it jumps while we aren't on it. + if(mothershuttle) + var/datum/shuttle/MS = SSshuttles.shuttles[mothershuttle] + if(MS) + if(current_location.landmark_tag == motherdock) + MS.shuttle_area |= shuttle_area // We are now on mothershuttle! Bring us along! + else + MS.shuttle_area -= shuttle_area // We have left mothershuttle! Don't bring us along! - if(update_power) - SSmachines.makepowernets() return //returns 1 if the shuttle has a valid arrive time /datum/shuttle/proc/has_arrive_time() return (moving_status == SHUTTLE_INTRANSIT) -/datum/shuttle/proc/make_sounds(var/area/A, var/sound_type) +/datum/shuttle/proc/make_sounds(var/sound_type) var/sound_to_play = null switch(sound_type) if(HYPERSPACE_WARMUP) @@ -259,9 +382,29 @@ sound_to_play = 'sound/effects/shuttles/hyperspace_progress.ogg' if(HYPERSPACE_END) sound_to_play = 'sound/effects/shuttles/hyperspace_end.ogg' - for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice - playsound(E, sound_to_play, 50, FALSE) + for(var/area/A in shuttle_area) + for(var/obj/machinery/door/E in A) //dumb, I know, but playing it on the engines doesn't do it justice + playsound(E, sound_to_play, 50, FALSE) -/datum/shuttle/proc/message_passengers(area/A, var/message) - for(var/mob/M in A) - M.show_message(message, 2) +/datum/shuttle/proc/message_passengers(var/message) + for(var/area/A in shuttle_area) + for(var/mob/M in A) + M.show_message(message, 2) + +/datum/shuttle/proc/find_children() + . = list() + for(var/shuttle_name in SSshuttles.shuttles) + var/datum/shuttle/shuttle = SSshuttles.shuttles[shuttle_name] + if(shuttle.mothershuttle == name) + . += shuttle + +//Returns the areas in shuttle_area that are not actually child shuttles. +/datum/shuttle/proc/find_childfree_areas() + . = shuttle_area.Copy() + for(var/datum/shuttle/child in find_children()) + . -= child.shuttle_area + +/datum/shuttle/proc/get_location_name() + if(moving_status == SHUTTLE_INTRANSIT) + return "In transit" + return current_location.name diff --git a/code/modules/shuttles/shuttle_arrivals.dm b/code/modules/shuttles/shuttle_arrivals.dm index 115d4476d2..a2d71c3fcc 100644 --- a/code/modules/shuttles/shuttle_arrivals.dm +++ b/code/modules/shuttles/shuttle_arrivals.dm @@ -1,15 +1,18 @@ // The new arrivals shuttle. -/datum/shuttle/ferry/arrivals +/datum/shuttle/autodock/ferry/arrivals + category = /datum/shuttle/autodock/ferry/arrivals + name = "Arrivals" - location = 1 + location = FERRY_LOCATION_OFFSITE warmup_time = 25 // Warmup takes 5 seconds, so 30 total. always_process = TRUE var/launch_delay = 3 - area_offsite = /area/shuttle/arrival/pre_game // not really 'pre game' but this area is already defined and unused - area_station = /area/shuttle/arrival/station - docking_controller_tag = "arrivals_shuttle" - dock_target_station = "arrivals_dock" + // Maps must implement their own subtype for their arrivals shuttle, and define at least: + // shuttle_area + // landmark_station (Which should define its dock target) + // landmark_offsite + // docking_controller_tag // For debugging. /obj/machinery/computer/shuttle_control/arrivals @@ -18,36 +21,42 @@ shuttle_tag = "Arrivals" // Unlike most shuttles, the arrivals shuttle is completely automated, so we need to put some additional code here. - +// Process the arrivals shuttle even when idle. +/obj/machinery/computer/shuttle_control/arrivals/process() + var/datum/shuttle/autodock/ferry/arrivals/shuttle = SSshuttles.shuttles[shuttle_tag] + if(shuttle && shuttle.process_state == IDLE_STATE) + shuttle.process() + ..() // This proc checks if anyone is on the shuttle. -/datum/shuttle/ferry/arrivals/proc/check_for_passengers(area/A) - for(var/mob/living/L in A) - return TRUE +/datum/shuttle/autodock/ferry/arrivals/proc/check_for_passengers() + for(var/area/A in shuttle_area) + for(var/mob/living/L in A) + return TRUE return FALSE // This is to stop the shuttle if someone tries to stow away when its leaving. -/datum/shuttle/ferry/arrivals/post_warmup_checks() +/datum/shuttle/autodock/ferry/arrivals/post_warmup_checks() if(!location) // If we're at station. - if(check_for_passengers(area_station)) + if(check_for_passengers()) return FALSE return TRUE -/datum/shuttle/ferry/arrivals/process() +/datum/shuttle/autodock/ferry/arrivals/process() if(process_state == IDLE_STATE) if(location) // If we're off-station (space). - if(check_for_passengers(area_offsite)) // No point arriving with an empty shuttle. + if(check_for_passengers()) // No point arriving with an empty shuttle. warmup_time = initial(warmup_time) launch() - message_passengers(area_offsite, "Arriving at [using_map.station_name] in thirty seconds...") + message_passengers("Arriving at [using_map.station_name] in thirty seconds...") spawn(10 SECONDS) - message_passengers(area_offsite, "Arriving at [using_map.station_name] in twenty seconds.") + message_passengers("Arriving at [using_map.station_name] in twenty seconds.") spawn(10 SECONDS) - message_passengers(area_offsite, "Arriving at [using_map.station_name] in ten seconds. Please buckle up.") + message_passengers("Arriving at [using_map.station_name] in ten seconds. Please buckle up.") else // We are at the station. - if(!check_for_passengers(area_station)) // Don't leave with anyone. + if(!check_for_passengers()) // Don't leave with anyone. if(launch_delay) // Give some time to get on the docks so people don't get trapped inbetween the dock airlocks. launch_delay-- else @@ -58,7 +67,7 @@ ..() // Do everything else /* -/datum/shuttle/ferry/arrivals/current_dock_target() +/datum/shuttle/autodock/ferry/arrivals/current_dock_target() if(location) // If we're off station. return null // Nothing to dock to in space. return ..() diff --git a/code/modules/shuttles/shuttle_autodock.dm b/code/modules/shuttles/shuttle_autodock.dm new file mode 100644 index 0000000000..4181af5b15 --- /dev/null +++ b/code/modules/shuttles/shuttle_autodock.dm @@ -0,0 +1,220 @@ +#define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up. + +// Subtype of shuttle that handles docking with docking controllers +// Consists of code pulled down from the old /datum/shuttle and up from /datum/shuttle/ferry +// Note: Since all known shuttles extend this type, this really could just be built into /datum/shuttle +// Why isn't it you ask? Eh, baystation did it this way and its convenient to keep the files smaller I guess. +/datum/shuttle/autodock + var/in_use = null // Tells the controller whether this shuttle needs processing, also attempts to prevent double-use + var/last_dock_attempt_time = 0 + + var/docking_controller_tag = null // ID of the controller on the shuttle (If multiple, this is the default one) + var/datum/computer/file/embedded_program/docking/shuttle_docking_controller // Controller on the shuttle (the one in use) + var/docking_codes + + var/tmp/obj/effect/shuttle_landmark/next_location //This is only used internally. + var/datum/computer/file/embedded_program/docking/active_docking_controller // Controller we are docked with (or trying to) + + var/obj/effect/shuttle_landmark/landmark_transition //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. + var/move_time = 240 //the time spent in the transition area + + category = /datum/shuttle/autodock + flags = SHUTTLE_FLAGS_PROCESS | SHUTTLE_FLAGS_ZERO_G + +/datum/shuttle/autodock/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint) + ..(_name, start_waypoint) + + //Initial dock + active_docking_controller = current_location.docking_controller + update_docking_target(current_location) + if(active_docking_controller) + set_docking_codes(active_docking_controller.docking_codes) + else if(global.using_map.use_overmap) + var/obj/effect/overmap/visitable/location = get_overmap_sector(get_z(current_location)) + if(location && location.docking_codes) + set_docking_codes(location.docking_codes) + dock() + + //Optional transition area + if(landmark_transition) + landmark_transition = SSshuttles.get_landmark(landmark_transition) + +/datum/shuttle/autodock/Destroy() + in_use = null + next_location = null + active_docking_controller = null + landmark_transition = null + + return ..() + +/datum/shuttle/autodock/proc/set_docking_codes(var/code) + docking_codes = code + if(shuttle_docking_controller) + shuttle_docking_controller.docking_codes = code + +/datum/shuttle/autodock/perform_shuttle_move() + force_undock() //bye! + ..() + +// Despite the name this actually updates the SHUTTLE docking conroller, not the active. +/datum/shuttle/autodock/proc/update_docking_target(var/obj/effect/shuttle_landmark/location) + var/current_dock_target + if(location && location.special_dock_targets && location.special_dock_targets[name]) + current_dock_target = location.special_dock_targets[name] + else + current_dock_target = docking_controller_tag + shuttle_docking_controller = SSshuttles.docking_registry[current_dock_target] + if(current_dock_target && !shuttle_docking_controller) + to_world("warning: shuttle [src] can't find its controller with tag [current_dock_target]!") +/* + Docking stuff +*/ +/datum/shuttle/autodock/dock() + if(active_docking_controller && shuttle_docking_controller) + shuttle_docking_controller.initiate_docking(active_docking_controller.id_tag) + last_dock_attempt_time = world.time + +/datum/shuttle/autodock/undock() + if(shuttle_docking_controller) + shuttle_docking_controller.initiate_undocking() + +/datum/shuttle/autodock/force_undock() + if(shuttle_docking_controller) + shuttle_docking_controller.force_undock() + +/datum/shuttle/autodock/check_docked() + if(shuttle_docking_controller) + return shuttle_docking_controller.docked() + return TRUE + +/datum/shuttle/autodock/check_undocked() + if(shuttle_docking_controller) + return shuttle_docking_controller.can_launch() + return TRUE + +// You also could just directly reference active_docking_controller +/datum/shuttle/autodock/proc/current_dock_target() + if(active_docking_controller) + return active_docking_controller.id_tag + return null + +// These checks are built into the check_docked() and check_undocked() procs +/datum/shuttle/autodock/proc/skip_docking_checks() + if (!shuttle_docking_controller || !current_dock_target()) + return TRUE //shuttles without docking controllers or at locations without docking ports act like old-style shuttles + return FALSE + + +/* + Please ensure that long_jump() and short_jump() are only called from here. This applies to subtypes as well. + Doing so will ensure that multiple jumps cannot be initiated in parallel. +*/ +/datum/shuttle/autodock/process() + switch(process_state) + if (WAIT_LAUNCH) + if(check_undocked()) + //*** ready to go + process_launch() + + if (FORCE_LAUNCH) + process_launch() + + if (WAIT_ARRIVE) + if (moving_status == SHUTTLE_IDLE) + //*** we made it to the destination, update stuff + process_arrived() + process_state = WAIT_FINISH + + if (WAIT_FINISH) + if (world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT || check_docked()) + //*** all done here + process_state = IDLE_STATE + arrived() + +//not to be confused with the arrived() proc +/datum/shuttle/autodock/proc/process_arrived() + active_docking_controller = next_location.docking_controller + update_docking_target(next_location) + dock() + + next_location = null + in_use = null //release lock + +/datum/shuttle/autodock/proc/get_travel_time() + return move_time + +/datum/shuttle/autodock/proc/process_launch() + if(!next_location || !next_location.is_valid(src) || current_location.cannot_depart(src)) + process_state = IDLE_STATE + in_use = null + return + if (get_travel_time() && landmark_transition) + . = long_jump(next_location, landmark_transition, get_travel_time()) + else + . = short_jump(next_location) + process_state = WAIT_ARRIVE + +/* + Guards - (These don't take docking status into account, just the state machine and move safety) +*/ +/datum/shuttle/autodock/proc/can_launch() + return (next_location && next_location.is_valid(src) && !current_location.cannot_depart(src) && moving_status == SHUTTLE_IDLE && !in_use) + +/datum/shuttle/autodock/proc/can_force() + return (next_location && next_location.is_valid(src) && !current_location.cannot_depart(src) && moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH) + +/datum/shuttle/autodock/proc/can_cancel() + return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH) + +/* + "Public" procs +*/ +// Queue shuttle for undock and launch by shuttle subsystem. +/datum/shuttle/autodock/proc/launch(var/user) + if (!can_launch()) return + + in_use = user //obtain an exclusive lock on the shuttle + + process_state = WAIT_LAUNCH + undock() + +// Queue shuttle for forced undock and launch by shuttle subsystem. +/datum/shuttle/autodock/proc/force_launch(var/user) + if (!can_force()) return + + in_use = user //obtain an exclusive lock on the shuttle + + process_state = FORCE_LAUNCH + +// Cancel queued launch. +/datum/shuttle/autodock/cancel_launch(var/user) + if (!can_cancel()) return + + moving_status = SHUTTLE_IDLE + process_state = WAIT_FINISH + in_use = null + + //whatever we were doing with docking: stop it, then redock + force_undock() + spawn(1 SECOND) + dock() + +//returns 1 if the shuttle is getting ready to move, but is not in transit yet +/datum/shuttle/autodock/proc/is_launching() + return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH) + +// /datum/shuttle/autodock/get_location_name() defined in shuttle.dm + +/datum/shuttle/autodock/proc/get_destination_name() + if(!next_location) + return "None" + return next_location.name + +//This gets called when the shuttle finishes arriving at it's destination +//This can be used by subtypes to do things when the shuttle arrives. +//Note that this is called when the shuttle leaves the WAIT_FINISHED state, the proc name is a little misleading +/datum/shuttle/autodock/proc/arrived() + return //do nothing for now + +/obj/effect/shuttle_landmark/transit + flags = SLANDMARK_FLAG_ZERO_G|SLANDMARK_FLAG_AUTOSET diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm index 5055eb7543..6d277edec1 100644 --- a/code/modules/shuttles/shuttle_console.dm +++ b/code/modules/shuttles/shuttle_console.dm @@ -8,23 +8,20 @@ var/shuttle_tag // Used to coordinate data in shuttle controller. var/hacked = 0 // Has been emagged, no access restrictions. + var/ui_template = "shuttle_control_console.tmpl" + /obj/machinery/computer/shuttle_control/attack_hand(user as mob) if(..(user)) return //src.add_fingerprint(user) //shouldn't need fingerprints just for looking at it. if(!allowed(user)) - user << "Access Denied." + to_chat(user, "Access Denied.") return 1 ui_interact(user) -/obj/machinery/computer/shuttle_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - if (!istype(shuttle)) - return - +/obj/machinery/computer/shuttle_control/proc/get_ui_data(var/datum/shuttle/autodock/shuttle) var/shuttle_state switch(shuttle.moving_status) if(SHUTTLE_IDLE) shuttle_state = "idle" @@ -34,62 +31,114 @@ var/shuttle_status switch (shuttle.process_state) if(IDLE_STATE) + var/cannot_depart = shuttle.current_location.cannot_depart(shuttle) if (shuttle.in_use) shuttle_status = "Busy." - else if (!shuttle.location) - shuttle_status = "Standing-by at station." + else if(cannot_depart) + shuttle_status = cannot_depart else - shuttle_status = "Standing-by at offsite location." + shuttle_status = "Standing-by at \the [shuttle.get_location_name()]." + if(WAIT_LAUNCH, FORCE_LAUNCH) shuttle_status = "Shuttle has received command and will depart shortly." if(WAIT_ARRIVE) - shuttle_status = "Proceeding to destination." + shuttle_status = "Proceeding to \the [shuttle.get_destination_name()]." if(WAIT_FINISH) shuttle_status = "Arriving at destination now." - data = list( + return list( "shuttle_status" = shuttle_status, "shuttle_state" = shuttle_state, - "has_docking" = shuttle.docking_controller? 1 : 0, - "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, - "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, + "has_docking" = shuttle.shuttle_docking_controller ? 1 : 0, + "docking_status" = shuttle.shuttle_docking_controller?.get_docking_status(), + "docking_override" = shuttle.shuttle_docking_controller?.override_enabled, "can_launch" = shuttle.can_launch(), "can_cancel" = shuttle.can_cancel(), "can_force" = shuttle.can_force(), + "docking_codes" = shuttle.docking_codes ) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - - if (!ui) - ui = new(user, src, ui_key, "shuttle_control_console.tmpl", "[shuttle_tag] Shuttle Control", 470, 310) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) +// This is a subset of the actual checks; contains those that give messages to the user. +// This enables us to give nice error messages as well as not even bother proceeding if we can't. +/obj/machinery/computer/shuttle_control/proc/can_move(var/datum/shuttle/autodock/shuttle, var/user) + var/cannot_depart = shuttle.current_location.cannot_depart(shuttle) + if(cannot_depart) + to_chat(user, "[cannot_depart]") + log_shuttle("Shuttle [shuttle] cannot depart [shuttle.current_location] because: [cannot_depart].") + return FALSE + if(!shuttle.next_location.is_valid(shuttle)) + to_chat(user, "Destination zone is invalid or obstructed.") + log_shuttle("Shuttle [shuttle] destination [shuttle.next_location] is invalid.") + return FALSE + return TRUE /obj/machinery/computer/shuttle_control/Topic(href, href_list) - if(..()) - return 1 + if((. = ..())) + return usr.set_machine(src) src.add_fingerprint(usr) - var/datum/shuttle/ferry/shuttle = shuttle_controller.shuttles[shuttle_tag] - if (!istype(shuttle)) - return + var/datum/shuttle/autodock/shuttle = SSshuttles.shuttles[shuttle_tag] + if(!shuttle) + to_chat(usr, "Unable to establish link with the shuttle.") + return handle_topic_href(shuttle, href_list, usr) + +/obj/machinery/computer/shuttle_control/proc/handle_topic_href(var/datum/shuttle/autodock/shuttle, var/list/href_list, var/user) + if(!istype(shuttle)) + return TOPIC_NOACTION if(href_list["move"]) - shuttle.launch(src) + if(can_move(shuttle, user)) + shuttle.launch(src) + return TOPIC_REFRESH + return TOPIC_HANDLED + if(href_list["force"]) - shuttle.force_launch(src) - else if(href_list["cancel"]) + if(can_move(shuttle, user)) + shuttle.force_launch(src) + return TOPIC_REFRESH + return TOPIC_HANDLED + + if(href_list["cancel"]) shuttle.cancel_launch(src) + return TOPIC_REFRESH + + if(href_list["set_codes"]) + var/newcode = input("Input new docking codes", "Docking codes", shuttle.docking_codes) as text|null + if (newcode && CanInteract(usr, global.default_state)) + shuttle.set_docking_codes(uppertext(newcode)) + return TOPIC_REFRESH + +// We delegate populating data to another proc to make it easier for overriding types to add their data. +/obj/machinery/computer/shuttle_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/datum/shuttle/autodock/shuttle = SSshuttles.shuttles[shuttle_tag] + if (!istype(shuttle)) + to_chat(user, "Unable to establish link with the shuttle.") + return + + var/list/data = get_ui_data(shuttle) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, ui_template, "[shuttle_tag] Shuttle Control", 470, 360) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +// Call to set the linked shuttle tag; override to add behaviour to shuttle tag changes +/obj/machinery/computer/shuttle_control/proc/set_shuttle_tag(var/new_shuttle_tag) + if(shuttle_tag == new_shuttle_tag) + return FALSE + shuttle_tag = new_shuttle_tag + return TRUE /obj/machinery/computer/shuttle_control/emag_act(var/remaining_charges, var/mob/user) if (!hacked) req_access = list() req_one_access = list() hacked = 1 - user << "You short out the console's ID checking system. It's now available to everyone!" + to_chat(user, "You short out the console's ID checking system. It's now available to everyone!") return 1 /obj/machinery/computer/shuttle_control/bullet_act(var/obj/item/projectile/Proj) @@ -100,3 +149,30 @@ /obj/machinery/computer/shuttle_control/emp_act() return + + +GLOBAL_LIST_BOILERPLATE(papers_dockingcode, /obj/item/weapon/paper/dockingcodes) +/hook/roundstart/proc/populate_dockingcodes() + for(var/paper in global.papers_dockingcode) + var/obj/item/weapon/paper/dockingcodes/dcp = paper + dcp.populate_info() + return TRUE + +/obj/item/weapon/paper/dockingcodes + name = "Docking Codes" + var/codes_from_z = null //So you can put codes from the station other places to give to antags or whatever + +/obj/item/weapon/paper/dockingcodes/proc/populate_info() + var/dockingcodes = null + var/z_to_check = codes_from_z ? codes_from_z : z + if(using_map.use_overmap) + var/obj/effect/overmap/visitable/location = get_overmap_sector(z_to_check) + if(location && location.docking_codes) + dockingcodes = location.docking_codes + + if(!dockingcodes) + info = "

    Daily Docking Codes


    The docking security system is down for maintenance. Please exercise caution when shuttles dock and depart." + else + info = "

    Daily Docking Codes


    The docking codes for this shift are '[dockingcodes]'.
    These codes are secret, as they will allow hostile shuttles to dock with impunity if discovered.
    " + info_links = info + icon_state = "paper_words" diff --git a/code/modules/shuttles/shuttle_console_multi.dm b/code/modules/shuttles/shuttle_console_multi.dm new file mode 100644 index 0000000000..9a724a9ab7 --- /dev/null +++ b/code/modules/shuttles/shuttle_console_multi.dm @@ -0,0 +1,35 @@ +/obj/machinery/computer/shuttle_control/multi + circuit = /obj/item/weapon/circuitboard/shuttle_console/multi + ui_template = "shuttle_control_console_multi.tmpl" + +/obj/machinery/computer/shuttle_control/multi/get_ui_data(var/datum/shuttle/autodock/multi/shuttle) + . = ..() + if(istype(shuttle)) + . += list( + "destination_name" = shuttle.next_location ? shuttle.next_location.name : "No destination set.", + "can_pick" = shuttle.moving_status == SHUTTLE_IDLE, + "can_cloak" = shuttle.can_cloak ? 1 : 0, + "cloaked" = shuttle.cloaked ? 1 : 0, + "legit" = shuttle.legit ? 1 : 0, + // "engines_charging" = ((shuttle.last_move + (shuttle.cooldown SECONDS)) > world.time), // Replaced by longer warmup_time + ) + +/obj/machinery/computer/shuttle_control/multi/handle_topic_href(var/datum/shuttle/autodock/multi/shuttle, var/list/href_list) + if((. = ..()) != null) + return + + if(href_list["pick"]) + var/dest_key = input("Choose shuttle destination", "Shuttle Destination") as null|anything in shuttle.get_destinations() + if(dest_key && CanInteract(usr, global.default_state)) + shuttle.set_destination(dest_key, usr) + return TOPIC_REFRESH + + if(href_list["toggle_cloaked"]) + if(!shuttle.can_cloak) + return TOPIC_HANDLED + shuttle.cloaked = !shuttle.cloaked + if(shuttle.legit) + to_chat(usr, "Ship ATC inhibitor systems have been [(shuttle.cloaked ? "activated. The station will not" : "deactivated. The station will")] be notified of our arrival.") + else + to_chat(usr, "Ship stealth systems have been [(shuttle.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival.") + return TOPIC_REFRESH diff --git a/code/modules/shuttles/shuttle_emergency.dm b/code/modules/shuttles/shuttle_emergency.dm index 86b8e29809..4310b9e6ce 100644 --- a/code/modules/shuttles/shuttle_emergency.dm +++ b/code/modules/shuttles/shuttle_emergency.dm @@ -1,21 +1,22 @@ -/datum/shuttle/ferry/emergency - category = /datum/shuttle/ferry/emergency +// Formerly /datum/shuttle/ferry/emergency +/datum/shuttle/autodock/ferry/emergency + category = /datum/shuttle/autodock/ferry/emergency -/datum/shuttle/ferry/emergency/New() +/datum/shuttle/autodock/ferry/emergency/New() + ..() if(emergency_shuttle.shuttle) CRASH("An emergency shuttle has already been defined.") emergency_shuttle.shuttle = src - ..() -/datum/shuttle/ferry/emergency/arrived() +/datum/shuttle/autodock/ferry/emergency/arrived() + . = ..() if (istype(in_use, /obj/machinery/computer/shuttle_control/emergency)) var/obj/machinery/computer/shuttle_control/emergency/C = in_use C.reset_authorization() emergency_shuttle.shuttle_arrived() -/datum/shuttle/ferry/emergency/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) - //world << "shuttle/ferry/emergency/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]" +/datum/shuttle/autodock/ferry/emergency/long_jump(var/destination, var/interim, var/travel_time) if (!location) travel_time = SHUTTLE_TRANSIT_DURATION_RETURN else @@ -25,28 +26,28 @@ move_time = travel_time emergency_shuttle.launch_time = world.time + ..(destination, interim, travel_time, direction) + +/datum/shuttle/autodock/ferry/emergency/perform_shuttle_move() + if (current_location == landmark_station) //leaving the station + spawn(0) + emergency_shuttle.departed = 1 + var/estimated_time = round(emergency_shuttle.estimate_arrival_time()/60,1) + + if (emergency_shuttle.evac) + priority_announcement.Announce(replacetext(replacetext(using_map.emergency_shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s")) + else + priority_announcement.Announce(replacetext(replacetext(using_map.shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s")) ..() -/datum/shuttle/ferry/emergency/move(var/area/origin,var/area/destination) - ..(origin, destination) - - if (origin == area_station) //leaving the station - emergency_shuttle.departed = 1 - var/estimated_time = round(emergency_shuttle.estimate_arrival_time()/60,1) - - if (emergency_shuttle.evac) - priority_announcement.Announce(replacetext(replacetext(using_map.emergency_shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s")) - else - priority_announcement.Announce(replacetext(replacetext(using_map.shuttle_leaving_dock, "%dock_name%", "[using_map.dock_name]"), "%ETA%", "[estimated_time] minute\s")) - -/datum/shuttle/ferry/emergency/can_launch(var/user) +/datum/shuttle/autodock/ferry/emergency/can_launch(var/user) if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) var/obj/machinery/computer/shuttle_control/emergency/C = user if (!C.has_authorization()) return 0 return ..() -/datum/shuttle/ferry/emergency/can_force(var/user) +/datum/shuttle/autodock/ferry/emergency/can_force(var/user) if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) var/obj/machinery/computer/shuttle_control/emergency/C = user @@ -56,14 +57,14 @@ return 0 return ..() -/datum/shuttle/ferry/emergency/can_cancel(var/user) +/datum/shuttle/autodock/ferry/emergency/can_cancel(var/user) if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) var/obj/machinery/computer/shuttle_control/emergency/C = user if (!C.has_authorization()) return 0 return ..() -/datum/shuttle/ferry/emergency/launch(var/user) +/datum/shuttle/autodock/ferry/emergency/launch(var/user) if (!can_launch(user)) return if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console @@ -77,7 +78,7 @@ ..(user) -/datum/shuttle/ferry/emergency/force_launch(var/user) +/datum/shuttle/autodock/ferry/emergency/force_launch(var/user) if (!can_force(user)) return if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console @@ -91,7 +92,7 @@ ..(user) -/datum/shuttle/ferry/emergency/cancel_launch(var/user) +/datum/shuttle/autodock/ferry/emergency/cancel_launch(var/user) if (!can_cancel(user)) return if (istype(user, /obj/machinery/computer/shuttle_control/emergency)) //if we were given a command by an emergency shuttle console @@ -177,7 +178,7 @@ /obj/machinery/computer/shuttle_control/emergency/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] - var/datum/shuttle/ferry/emergency/shuttle = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/ferry/emergency/shuttle = SSshuttles.shuttles[shuttle_tag] if (!istype(shuttle)) return @@ -222,9 +223,9 @@ data = list( "shuttle_status" = shuttle_status, "shuttle_state" = shuttle_state, - "has_docking" = shuttle.docking_controller? 1 : 0, - "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, - "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, + "has_docking" = shuttle.active_docking_controller? 1 : 0, + "docking_status" = shuttle.active_docking_controller? shuttle.active_docking_controller.get_docking_status() : null, + "docking_override" = shuttle.active_docking_controller? shuttle.active_docking_controller.override_enabled : null, "can_launch" = shuttle.can_launch(src), "can_cancel" = shuttle.can_cancel(src), "can_force" = shuttle.can_force(src), diff --git a/code/modules/shuttles/shuttle_ferry.dm b/code/modules/shuttles/shuttle_ferry.dm index f279ce5d34..cad706c800 100644 --- a/code/modules/shuttles/shuttle_ferry.dm +++ b/code/modules/shuttles/shuttle_ferry.dm @@ -1,177 +1,56 @@ #define DOCK_ATTEMPT_TIMEOUT 200 //how long in ticks we wait before assuming the docking controller is broken or blown up. -/datum/shuttle/ferry - var/location = 0 //0 = at area_station, 1 = at area_offsite - var/direction = 0 //0 = going to station, 1 = going to offsite. - var/process_state = IDLE_STATE - var/always_process = FALSE +/datum/shuttle/autodock/ferry + var/location = FERRY_LOCATION_STATION //0 = at area_station, 1 = at area_offsite + var/direction = FERRY_GOING_TO_STATION //0 = going to station, 1 = going to offsite. - var/in_use = null //tells the controller whether this shuttle needs processing + var/always_process = FALSE // TODO -why should this exist? - var/area_transition - var/move_time = 0 //the time spent in the transition area - var/transit_direction = null //needed for area/move_contents_to() to properly handle shuttle corners - not exactly sure how it works. + var/obj/effect/shuttle_landmark/landmark_station //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. + var/obj/effect/shuttle_landmark/landmark_offsite //This variable is type-abused initially: specify the landmark_tag, not the actual landmark. - var/area/area_station - var/area/area_offsite - //TODO: change location to a string and use a mapping for area and dock targets. - var/dock_target_station - var/dock_target_offsite + category = /datum/shuttle/autodock/ferry - var/last_dock_attempt_time = 0 - category = /datum/shuttle/ferry +/datum/shuttle/autodock/ferry/New(var/_name) + if(landmark_station) + landmark_station = SSshuttles.get_landmark(landmark_station) + if(landmark_offsite) + landmark_offsite = SSshuttles.get_landmark(landmark_offsite) -/datum/shuttle/ferry/New() - area_offsite = locate(area_offsite) - area_station = locate(area_station) - if(area_transition) - area_transition = locate(area_transition) - ..() + ..(_name, get_location_waypoint(location)) -/datum/shuttle/ferry/short_jump(var/area/origin,var/area/destination) - if(isnull(location)) - return + next_location = get_location_waypoint(!location) - if(!destination) - destination = get_location_area(!location) - if(!origin) - origin = get_location_area(location) - direction = !location - ..(origin, destination) - -/datum/shuttle/ferry/long_jump(var/area/departing, var/area/destination, var/area/interim, var/travel_time, var/direction) - //world << "shuttle/ferry/long_jump: departing=[departing], destination=[destination], interim=[interim], travel_time=[travel_time]" - if(isnull(location)) - return - - if(!destination) - destination = get_location_area(!location) - if(!departing) - departing = get_location_area(location) - - direction = !location - ..(departing, destination, interim, travel_time, direction) - -/datum/shuttle/ferry/move(var/area/origin,var/area/destination) - ..(origin, destination) - - if (destination == area_station) location = 0 - if (destination == area_offsite) location = 1 - //if this is a long_jump retain the location we were last at until we get to the new one - -/datum/shuttle/ferry/dock() - ..() - last_dock_attempt_time = world.time - -/datum/shuttle/ferry/proc/get_location_area(location_id = null) +//Gets the shuttle landmark associated with the given location (defaults to current location) +/datum/shuttle/autodock/ferry/proc/get_location_waypoint(location_id = null) if (isnull(location_id)) location_id = location - if (!location_id) - return area_station - return area_offsite + if (location_id == FERRY_LOCATION_STATION) + return landmark_station + return landmark_offsite -/* - Please ensure that long_jump() and short_jump() are only called from here. This applies to subtypes as well. - Doing so will ensure that multiple jumps cannot be initiated in parallel. -*/ -/datum/shuttle/ferry/process() - switch(process_state) - if (WAIT_LAUNCH) - if (skip_docking_checks() || docking_controller.can_launch()) +/datum/shuttle/autodock/ferry/short_jump(var/destination) + direction = !location // Heading away from where we currently are + . = ..() - //world << "shuttle/ferry/process: area_transition=[area_transition], travel_time=[travel_time]" - if (move_time && area_transition) - long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction) - else - short_jump() +/datum/shuttle/autodock/ferry/long_jump(var/destination, var/obj/effect/shuttle_landmark/interim, var/travel_time) + direction = !location // Heading away from where we currently are + . = ..() - process_state = WAIT_ARRIVE +/datum/shuttle/autodock/ferry/perform_shuttle_move() + ..() + if (current_location == landmark_station) location = FERRY_LOCATION_STATION + if (current_location == landmark_offsite) location = FERRY_LOCATION_OFFSITE - if (FORCE_LAUNCH) - if (move_time && area_transition) - long_jump(interim=area_transition, travel_time=move_time, direction=transit_direction) - else - short_jump() - - process_state = WAIT_ARRIVE - - if (WAIT_ARRIVE) - if (moving_status == SHUTTLE_IDLE) - dock() - in_use = null //release lock - process_state = WAIT_FINISH - - if (WAIT_FINISH) - if (skip_docking_checks() || docking_controller.docked() || world.time > last_dock_attempt_time + DOCK_ATTEMPT_TIMEOUT) - process_state = IDLE_STATE - arrived() - -/datum/shuttle/ferry/current_dock_target() - var/dock_target - if (!location) //station - dock_target = dock_target_station - else - dock_target = dock_target_offsite - return dock_target - - -/datum/shuttle/ferry/proc/launch(var/user) - if (!can_launch()) return - - in_use = user //obtain an exclusive lock on the shuttle - - process_state = WAIT_LAUNCH - undock() - -/datum/shuttle/ferry/proc/force_launch(var/user) - if (!can_force()) return - - in_use = user //obtain an exclusive lock on the shuttle - - process_state = FORCE_LAUNCH - -/datum/shuttle/ferry/proc/cancel_launch(var/user) - if (!can_cancel()) return - - moving_status = SHUTTLE_IDLE - process_state = WAIT_FINISH - in_use = null - - if (docking_controller && !docking_controller.undocked()) - docking_controller.force_undock() - - spawn(10) - dock() - - return - -/datum/shuttle/ferry/proc/can_launch() - if (moving_status != SHUTTLE_IDLE) - return 0 - - if (in_use) - return 0 - - return 1 - -/datum/shuttle/ferry/proc/can_force() - if (moving_status == SHUTTLE_IDLE && process_state == WAIT_LAUNCH) - return 1 - return 0 - -/datum/shuttle/ferry/proc/can_cancel() - if (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH) - return 1 - return 0 - -//returns 1 if the shuttle is getting ready to move, but is not in transit yet -/datum/shuttle/ferry/proc/is_launching() - return (moving_status == SHUTTLE_WARMUP || process_state == WAIT_LAUNCH || process_state == FORCE_LAUNCH) - -//This gets called when the shuttle finishes arriving at it's destination -//This can be used by subtypes to do things when the shuttle arrives. -/datum/shuttle/ferry/proc/arrived() - return //do nothing for now +// Once we have arrived where we are going, plot a course back! +/datum/shuttle/autodock/ferry/process_arrived() + ..() + next_location = get_location_waypoint(!location) +// Ferry shuttles should generally always be able to dock. So read the docking codes off of the target. +/datum/shuttle/autodock/ferry/update_docking_target(var/obj/effect/shuttle_landmark/location) + ..() + if(active_docking_controller && active_docking_controller.docking_codes) + set_docking_codes(active_docking_controller.docking_codes) diff --git a/code/modules/shuttles/shuttle_specops.dm b/code/modules/shuttles/shuttle_specops.dm index 47c3aca5ab..0dd88a58e8 100644 --- a/code/modules/shuttles/shuttle_specops.dm +++ b/code/modules/shuttles/shuttle_specops.dm @@ -4,39 +4,11 @@ req_access = list(access_cent_specops) /obj/machinery/computer/shuttle_control/specops/attack_ai(user as mob) - user << "Access Denied." + to_chat(user, "Access Denied.") return 1 -//for shuttles that may use a different docking port at each location -/datum/shuttle/ferry/multidock - var/docking_controller_tag_station - var/docking_controller_tag_offsite - var/datum/computer/file/embedded_program/docking/docking_controller_station - var/datum/computer/file/embedded_program/docking/docking_controller_offsite - category = /datum/shuttle/ferry/multidock - -/datum/shuttle/ferry/multidock/init_docking_controllers() - if(docking_controller_tag_station) - docking_controller_station = locate(docking_controller_tag_station) - if(!istype(docking_controller_station)) - warning("warning: shuttle with docking tag [docking_controller_station] could not find it's controller!") - if(docking_controller_tag_offsite) - docking_controller_offsite = locate(docking_controller_tag_offsite) - if(!istype(docking_controller_offsite)) - warning("warning: shuttle with docking tag [docking_controller_offsite] could not find it's controller!") - if (!location) - docking_controller = docking_controller_station - else - docking_controller = docking_controller_offsite - -/datum/shuttle/ferry/multidock/move(var/area/origin,var/area/destination) - ..(origin, destination) - if (!location) - docking_controller = docking_controller_station - else - docking_controller = docking_controller_offsite - -/datum/shuttle/ferry/multidock/specops +// Formerly /datum/shuttle/ferry/multidock/specops +/datum/shuttle/autodock/ferry/specops var/specops_return_delay = 6000 //After moving, the amount of time that must pass before the shuttle may move again var/specops_countdown_time = 600 //Length of the countdown when moving the shuttle @@ -44,19 +16,19 @@ var/reset_time = 0 //the world.time at which the shuttle will be ready to move again. var/launch_prep = 0 var/cancel_countdown = 0 - category = /datum/shuttle/ferry/multidock/specops + category = /datum/shuttle/autodock/ferry/specops -/datum/shuttle/ferry/multidock/specops/New() +/datum/shuttle/autodock/ferry/specops/New() ..() announcer = new /obj/item/device/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. announcer.config(list("Response Team" = 0)) -/datum/shuttle/ferry/multidock/specops/proc/radio_announce(var/message) +/datum/shuttle/autodock/ferry/specops/proc/radio_announce(var/message) if(announcer) announcer.autosay(message, "A.L.I.C.E.", "Response Team") -/datum/shuttle/ferry/multidock/specops/launch(var/user) +/datum/shuttle/autodock/ferry/specops/launch(var/user) if (!can_launch()) return @@ -64,14 +36,14 @@ var/obj/machinery/computer/C = user if(world.time <= reset_time) - C.visible_message("[using_map.boss_name] will not allow the Special Operations shuttle to launch yet.") + C.visible_message("[global.using_map.boss_name] will not allow the Special Operations shuttle to launch yet.") if (((world.time - reset_time)/10) > 60) - C.visible_message("[-((world.time - reset_time)/10)/60] minutes remain!") + C.visible_message("[-((world.time - reset_time)/10)/60] minutes remain!") else - C.visible_message("[-(world.time - reset_time)/10] seconds remain!") + C.visible_message("[-(world.time - reset_time)/10] seconds remain!") return - C.visible_message("The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.") + C.visible_message("The Special Operations shuttle will depart in [(specops_countdown_time/10)] seconds.") if (location) //returning radio_announce("THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN") @@ -81,31 +53,31 @@ sleep_until_launch() if (location) - var/obj/machinery/light/small/readylight/light = locate() in get_location_area() + var/obj/machinery/light/small/readylight/light = locate() in shuttle_area if(light) light.set_state(0) //launch radio_announce("ALERT: INITIATING LAUNCH SEQUENCE") ..(user) -/datum/shuttle/ferry/multidock/specops/move(var/area/origin,var/area/destination) - ..(origin, destination) +/datum/shuttle/autodock/ferry/specops/perform_shuttle_move() + ..() - spawn(20) + spawn(2 SECONDS) if (!location) //just arrived home - for(var/turf/T in get_area_turfs(destination)) + for(var/turf/T in get_area_turfs(shuttle_area)) var/mob/M = locate(/mob) in T - M << "You have arrived at [using_map.boss_name]. Operation has ended!" + to_chat(M, "You have arrived at [using_map.boss_name]. Operation has ended!") else //just left for the station launch_mauraders() - for(var/turf/T in get_area_turfs(destination)) + for(var/turf/T in get_area_turfs(shuttle_area)) var/mob/M = locate(/mob) in T - M << "You have arrived at [station_name()]. Commence operation!" + to_chat(M, "You have arrived at [station_name()]. Commence operation!") var/obj/machinery/light/small/readylight/light = locate() in T if(light) light.set_state(1) -/datum/shuttle/ferry/multidock/specops/cancel_launch() +/datum/shuttle/autodock/ferry/specops/cancel_launch() if (!can_cancel()) return @@ -113,27 +85,26 @@ radio_announce("ALERT: LAUNCH SEQUENCE ABORTED") if (istype(in_use, /obj/machinery/computer)) var/obj/machinery/computer/C = in_use - C.visible_message("Launch sequence aborted.") - + C.visible_message("Launch sequence aborted.") ..() -/datum/shuttle/ferry/multidock/specops/can_launch() +/datum/shuttle/autodock/ferry/specops/can_launch() if(launch_prep) return 0 return ..() //should be fine to allow forcing. process_state only becomes WAIT_LAUNCH after the countdown is over. -///datum/shuttle/ferry/multidock/specops/can_force() +///datum/shuttle/autodock/ferry/specops/can_force() // return 0 -/datum/shuttle/ferry/multidock/specops/can_cancel() +/datum/shuttle/autodock/ferry/specops/can_cancel() if(launch_prep) return 1 return ..() -/datum/shuttle/ferry/multidock/specops/proc/sleep_until_launch() +/datum/shuttle/autodock/ferry/specops/proc/sleep_until_launch() var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. var/launch_time = world.time + specops_countdown_time diff --git a/code/modules/shuttles/shuttle_supply.dm b/code/modules/shuttles/shuttle_supply.dm index 7565d7f466..a51fe50d56 100644 --- a/code/modules/shuttles/shuttle_supply.dm +++ b/code/modules/shuttles/shuttle_supply.dm @@ -1,82 +1,91 @@ -/datum/shuttle/ferry/supply - var/away_location = 1 //the location to hide at while pretending to be in-transit +// Formerly /datum/shuttle/ferry/supply +/datum/shuttle/autodock/ferry/supply + var/away_location = FERRY_LOCATION_OFFSITE //the location to hide at while pretending to be in-transit var/late_chance = 80 var/max_late_time = 300 - category = /datum/shuttle/ferry/supply + flags = SHUTTLE_FLAGS_PROCESS|SHUTTLE_FLAGS_SUPPLY + category = /datum/shuttle/autodock/ferry/supply -/datum/shuttle/ferry/supply/short_jump(var/area/origin,var/area/destination) +/datum/shuttle/autodock/ferry/supply/short_jump(var/obj/effect/shuttle_landmark/destination) if(moving_status != SHUTTLE_IDLE) return if(isnull(location)) return - if(!destination) - destination = get_location_area(!location) - if(!origin) - origin = get_location_area(location) + // Set the supply shuttle displays to read out the ETA + var/datum/signal/S = new() + S.source = src + S.data = list("command" = "supply") + var/datum/radio_frequency/F = radio_controller.return_frequency(1435) + F.post_signal(src, S) //it would be cool to play a sound here moving_status = SHUTTLE_WARMUP spawn(warmup_time*10) - make_sounds(origin, HYPERSPACE_WARMUP) + make_sounds(HYPERSPACE_WARMUP) sleep(5 SECONDS) // so the sound finishes. if (moving_status == SHUTTLE_IDLE) - make_sounds(origin, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) return //someone cancelled the launch if (at_station() && forbidden_atoms_check()) //cancel the launch because of forbidden atoms. announce over supply channel? moving_status = SHUTTLE_IDLE - make_sounds(origin, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) return if (!at_station()) //at centcom - supply_controller.buy() + SSsupply.buy() //We pretend it's a long_jump by making the shuttle stay at centcom for the "in-transit" period. - var/area/away_area = get_location_area(away_location) + var/obj/effect/shuttle_landmark/away_waypoint = get_location_waypoint(away_location) moving_status = SHUTTLE_INTRANSIT - //If we are at the away_area then we are just pretending to move, otherwise actually do the move - if (origin != away_area) - move(origin, away_area) + //If we are at the away_landmark then we are just pretending to move, otherwise actually do the move + if (next_location == away_waypoint) + attempt_move(away_waypoint) //wait ETA here. - arrive_time = world.time + supply_controller.movetime + arrive_time = world.time + SSsupply.movetime while (world.time <= arrive_time) sleep(5) - if (destination != away_area) + if (next_location != away_waypoint) //late if (prob(late_chance)) sleep(rand(0,max_late_time)) - move(away_area, destination) + attempt_move(destination) moving_status = SHUTTLE_IDLE - make_sounds(destination, HYPERSPACE_END) + make_sounds(HYPERSPACE_END) if (!at_station()) //at centcom - supply_controller.sell() + SSsupply.sell() // returns 1 if the supply shuttle should be prevented from moving because it contains forbidden atoms -/datum/shuttle/ferry/supply/proc/forbidden_atoms_check() +/datum/shuttle/autodock/ferry/supply/proc/forbidden_atoms_check() if (!at_station()) return 0 //if badmins want to send mobs or a nuke on the supply shuttle from centcom we don't care - return supply_controller.forbidden_atoms_check(get_location_area()) + for(var/area/A in shuttle_area) + if(SSsupply.forbidden_atoms_check(A)) + return 1 -/datum/shuttle/ferry/supply/proc/at_station() +/datum/shuttle/autodock/ferry/supply/proc/at_station() return (!location) //returns 1 if the shuttle is idle and we can still mess with the cargo shopping list -/datum/shuttle/ferry/supply/proc/idle() +/datum/shuttle/autodock/ferry/supply/proc/idle() return (moving_status == SHUTTLE_IDLE) //returns the ETA in minutes -/datum/shuttle/ferry/supply/proc/eta_minutes() - var/ticksleft = arrive_time - world.time - return round(ticksleft/600,1) +/datum/shuttle/autodock/ferry/supply/proc/eta_minutes() + return round((arrive_time - world.time) / (1 MINUTE), 1) // Floor, so it's an actual timer + +// returns the ETA in seconds +/datum/shuttle/autodock/ferry/supply/proc/eta_seconds() + return round((arrive_time - world.time) / (1 SECOND)) // Floor, so it's an actual timer diff --git a/code/modules/shuttles/shuttles_multi.dm b/code/modules/shuttles/shuttles_multi.dm index 73e08a719f..562a21f4d4 100644 --- a/code/modules/shuttles/shuttles_multi.dm +++ b/code/modules/shuttles/shuttles_multi.dm @@ -1,13 +1,15 @@ //This is a holder for things like the Skipjack and Nuke shuttle. -/datum/shuttle/multi_shuttle +// Formerly /datum/shuttle/multi_shuttle +/datum/shuttle/autodock/multi + var/list/destination_tags + var/list/destinations_cache = list() + var/last_cache_rebuild_time = 0 + category = /datum/shuttle/autodock/multi - flags = SHUTTLE_FLAGS_NONE var/cloaked = FALSE var/can_cloak = FALSE + var/at_origin = 1 - var/returned_home = 0 -// var/move_time = 240 - var/move_time = 60 var/cooldown = 20 var/last_move = 0 //the time at which we last moved @@ -15,276 +17,46 @@ var/arrival_message var/departure_message - var/area/interim - var/area/last_departed var/start_location var/last_location - var/list/destinations - var/list/destination_dock_controller_tags = list() //optional, in case the shuttle has multiple docking ports like the ERT shuttle (even though that isn't a multi_shuttle) - var/list/destination_dock_controllers = list() - var/list/destination_dock_targets = list() - var/area/origin var/return_warning = 0 - var/legit = 0 //VOREStation Add - Whether or not a shuttle is a legit NT shuttle. - category = /datum/shuttle/multi_shuttle + var/legit = FALSE -/datum/shuttle/multi_shuttle/New() - origin = locate(origin) - interim = locate(interim) - for(var/destination in destinations) - destinations[destination] = locate(destinations[destination]) +/datum/shuttle/autodock/multi/New() ..() + start_location = current_location + last_location = current_location -/datum/shuttle/multi_shuttle/init_docking_controllers() - ..() - for(var/destination in destinations) - var/controller_tag = destination_dock_controller_tags[destination] - if(!controller_tag) - destination_dock_controllers[destination] = docking_controller - else - var/datum/computer/file/embedded_program/docking/C = locate(controller_tag) +/datum/shuttle/autodock/multi/proc/set_destination(var/destination_key, mob/user) + if(moving_status != SHUTTLE_IDLE) + return + next_location = destinations_cache[destination_key] + if(!next_location) + warning("Shuttle [src] set to destination we can't find: [destination_key]") - if(!istype(C)) - warning("warning: shuttle with docking tag [controller_tag] could not find it's controller!") - else - destination_dock_controllers[destination] = C +/datum/shuttle/autodock/multi/proc/get_destinations() + if (last_cache_rebuild_time < SSshuttles.last_landmark_registration_time) + build_destinations_cache() + return destinations_cache - //might as well set this up here. - if(origin) last_departed = origin - last_location = start_location - //VOREStation Add - Set up origin dock controller - if(!(start_location in destination_dock_controller_tags)) - destination_dock_controllers[start_location] = docking_controller - //VOREStation Add End -/datum/shuttle/multi_shuttle/current_dock_target() - return destination_dock_targets[last_location] +/datum/shuttle/autodock/multi/proc/build_destinations_cache() + last_cache_rebuild_time = world.time + destinations_cache.Cut() + for(var/destination_tag in destination_tags) + var/obj/effect/shuttle_landmark/landmark = SSshuttles.get_landmark(destination_tag) + if (istype(landmark)) + destinations_cache["[landmark.name]"] = landmark -/datum/shuttle/multi_shuttle/move(var/area/origin, var/area/destination) +/datum/shuttle/autodock/multi/perform_shuttle_move() ..() last_move = world.time - if (destination == src.origin) - returned_home = 1 - docking_controller = destination_dock_controllers[last_location] - -/datum/shuttle/multi_shuttle/proc/announce_departure() +/datum/shuttle/autodock/multi/proc/announce_departure() if(cloaked || isnull(departure_message)) return + command_announcement.Announce(departure_message, (announcer ? announcer : "[using_map.boss_name]")) - command_announcement.Announce(departure_message,(announcer ? announcer : "[using_map.boss_name]")) - -/datum/shuttle/multi_shuttle/proc/announce_arrival() - +/datum/shuttle/autodock/multi/proc/announce_arrival() if(cloaked || isnull(arrival_message)) return - - command_announcement.Announce(arrival_message,(announcer ? announcer : "[using_map.boss_name]")) - - -/obj/machinery/computer/shuttle_control/multi - icon_keyboard = "syndie_key" - icon_screen = "syndishuttle" - -/obj/machinery/computer/shuttle_control/multi/attack_hand(user as mob) - - if(..(user)) - return - src.add_fingerprint(user) - - var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag] - if(!istype(MS)) return - - var/dat - dat = "
    [shuttle_tag] Ship Control
    " - - - if(MS.moving_status != SHUTTLE_IDLE) - dat += "Location: Moving
    " - else - var/area/areacheck = get_area(src) - dat += "Location: [areacheck.name]
    " - - if((MS.last_move + MS.cooldown*10) > world.time) - dat += "Engines charging.
    " - else - dat += "Engines ready.
    " - - //dat += "
    [MS.legit ? "Inhibit ATC":"Toggle cloak"]
    " //VOREStation Edit - Adds legit shuttles. //SYNC Edit: Below deals with cloak. - if(MS.can_cloak) - dat += "
    Toggle cloaking field
    " - dat += "Move ship
    " - dat += "Return to base
    " - - //Docking - dat += "


    " - if(MS.skip_docking_checks()) - dat += "Docking Status: Not in use." - else - var/override_en = MS.docking_controller.override_enabled - var/docking_status = MS.docking_controller.get_docking_status() - - dat += "Docking Status: " - switch(docking_status) - if("undocked") - dat += "Undocked" - if("docking") - dat += "Docking" - if("undocking") - dat += "Undocking" - if("docked") - dat += "Docked" - - if(override_en) dat += " (Override Enabled)" - - dat += ". \[Refresh\]

    " - - switch(docking_status) - if("undocked") - dat += "Dock" - if("docked") - dat += "Undock" - dat += "
    " - - user << browse("[dat]", "window=[shuttle_tag]shuttlecontrol;size=300x600") - -//check if we're undocked, give option to force launch -/obj/machinery/computer/shuttle_control/proc/check_docking(datum/shuttle/multi_shuttle/MS) - if(MS.skip_docking_checks() || MS.docking_controller.can_launch()) - return 1 - - var/choice = alert("The shuttle is currently docked! Please undock before continuing.","Error","Cancel","Force Launch") - if(choice == "Cancel") - return 0 - - choice = alert("Forcing a shuttle launch while docked may result in severe injury, death and/or damage to property. Are you sure you wish to continue?", "Force Launch", "Force Launch", "Cancel") - if(choice == "Cancel") - return 0 - - return 1 - -/obj/machinery/computer/shuttle_control/multi/Topic(href, href_list) - if(..()) - return 1 - - usr.set_machine(src) - src.add_fingerprint(usr) - - var/datum/shuttle/multi_shuttle/MS = shuttle_controller.shuttles[shuttle_tag] - if(!istype(MS)) return - - //world << "multi_shuttle: last_departed=[MS.last_departed], origin=[MS.origin], interim=[MS.interim], travel_time=[MS.move_time]" - - if(href_list["refresh"]) - updateUsrDialog() - return - - if (MS.moving_status != SHUTTLE_IDLE) - to_chat(usr, "[shuttle_tag] vessel is moving.") - return - - if(href_list["dock_command"]) - MS.dock() - return - - if(href_list["undock_command"]) - MS.undock() - return - - if(href_list["start"]) - if(MS.at_origin) - to_chat(usr, "You are already at the home base.") - return - - if((MS.last_move + MS.cooldown*10) > world.time) - to_chat(usr, "The ship's drive is inoperable while the engines are charging.") - return - - if(!check_docking(MS)) - updateUsrDialog() - return - - if(!MS.return_warning && !MS.legit) //VOREStation Add - Criminals only! - usr << "Returning to your home base will end your mission. If you are sure, press the button again." - //TODO: Actually end the mission. - MS.return_warning = 1 - return - - //VOREStation Add - Only long-jump if that's a thing you do - if(MS.interim) - MS.long_jump(MS.last_departed, MS.origin, MS.interim, MS.move_time) - else - MS.short_jump(MS.last_departed, MS.origin) - //VOREStation Add End - // No point giving a warning if it does literally nothing. -// if(!MS.return_warning) -// usr << "Returning to your home base will end your mission. If you are sure, press the button again." -// //TODO: Actually end the mission. -// MS.return_warning = 1 -// return - - //MS.long_jump(MS.last_departed, MS.origin, MS.interim, MS.move_time) //VOREStation Edit - MS.last_departed = MS.origin - MS.last_location = MS.start_location - MS.at_origin = 1 - - if(href_list["toggle_cloak"]) - if(!MS.can_cloak) - return - MS.cloaked = !MS.cloaked - usr << " Ship [MS.legit ? "ATC inhibitor":"stealth"] systems have been [(MS.cloaked ? "activated. The station will not" : "deactivated. The station will")] be [MS.legit ? "notified":"warned"] of our arrival." //VOREStation Edit - Adds legit shuttles. - //to_chat(usr, "Ship stealth systems have been [(MS.cloaked ? "activated. The station will not" : "deactivated. The station will")] be warned of our arrival.") //VOREStation Edit. - - if(href_list["move_multi"]) - if((MS.last_move + MS.cooldown*10) > world.time) - to_chat(usr, "The ship's drive is inoperable while the engines are charging.") - return - - if(!check_docking(MS)) - updateUsrDialog() - return - - var/choice = input("Select a destination.") as null|anything in MS.destinations - if(!choice) return - - to_chat(usr, "[shuttle_tag] main computer received message.") - - if(MS.at_origin) - MS.announce_arrival() - MS.last_departed = MS.origin - MS.at_origin = 0 - - - //VOREStation Add - Only long-jump if that's a thing you do - if(MS.interim) - MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time) - else - MS.short_jump(MS.last_departed, MS.destinations[choice]) - //VOREStation Add End - MS.last_departed = MS.destinations[choice] - MS.last_location = choice - return - - else if(choice == MS.origin) - - MS.announce_departure() - - //VOREStation Add - Only long-jump if that's a thing you do - if(MS.interim) - MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time) - else - MS.short_jump(MS.last_departed, MS.destinations[choice]) - return - //VOREStation Add End - - //VOREStation Add - Only long-jump if that's a thing you do - if(MS.interim) - MS.long_jump(MS.last_departed, MS.destinations[choice], MS.interim, MS.move_time) - else - MS.short_jump(MS.last_departed, MS.destinations[choice]) - //VOREStation Add End - - //MS.short_jump(MS.last_departed, MS.destinations[choice]) //VOREStation Removal - MS.last_departed = MS.destinations[choice] - MS.last_location = choice - - updateUsrDialog() + command_announcement.Announce(arrival_message, (announcer ? announcer : "[using_map.boss_name]")) diff --git a/code/modules/shuttles/shuttles_vr.dm b/code/modules/shuttles/shuttles_vr.dm index 7462fcd188..bc643d3838 100644 --- a/code/modules/shuttles/shuttles_vr.dm +++ b/code/modules/shuttles/shuttles_vr.dm @@ -10,8 +10,8 @@ /obj/machinery/computer/shuttle_control/belter name = "belter control console" - req_one_access = list(access_research, access_mining) //Allows xenoarch AND miners to use it. - // shuttle_tag = "Belter" //The scanning console needs to enable/disable this at will. + req_one_access = list(access_mining, access_medical_equip) //Allows xenoarch, miners AND doctors to use it. + shuttle_tag = "Belter" //The scanning console needs to enable/disable this at will. /obj/machinery/computer/shuttle_control/mining name = "mining elevator control console" diff --git a/code/modules/shuttles/shuttles_web.dm b/code/modules/shuttles/shuttles_web.dm index d24f47b1d7..0aebd2bf42 100644 --- a/code/modules/shuttles/shuttles_web.dm +++ b/code/modules/shuttles/shuttles_web.dm @@ -1,12 +1,11 @@ //This shuttle traverses a "web" of route_datums to have a wider range of places to go and make flying feel like movement is actually occuring. -/datum/shuttle/web_shuttle - flags = SHUTTLE_FLAGS_NONE +/datum/shuttle/autodock/web_shuttle + flags = SHUTTLE_FLAGS_ZERO_G var/visible_name = null // The pretty name shown to people in announcements, since the regular name var is used internally for other things. var/cloaked = FALSE var/can_cloak = FALSE var/cooldown = 0 var/last_move = 0 //the time at which we last moved - var/area/current_area = null var/datum/shuttle_web_master/web_master = null var/web_master_type = null var/flight_time_modifier = 1.0 @@ -15,15 +14,15 @@ var/autopilot_delay = 60 // How many ticks to not do anything when not following an autopath. Should equal two minutes. var/autopilot_first_delay = null // If your want your shuttle to stay for a different amount of time for the first time, set this. var/can_rename = TRUE // Lets the pilot rename the shuttle. Only available once. - category = /datum/shuttle/web_shuttle + category = /datum/shuttle/autodock/web_shuttle var/list/obj/item/clothing/head/pilot/helmets -/datum/shuttle/web_shuttle/New() - current_area = locate(current_area) +/datum/shuttle/autodock/web_shuttle/New() web_master = new web_master_type(src) build_destinations() if(autopilot) flags |= SHUTTLE_FLAGS_PROCESS + process_state = DO_AUTOPILOT if(autopilot_first_delay) autopilot_delay = autopilot_first_delay if(!visible_name) @@ -31,42 +30,45 @@ helmets = list() ..() -/datum/shuttle/web_shuttle/Destroy() - qdel(web_master) +/datum/shuttle/autodock/web_shuttle/Destroy() + QDEL_NULL(web_master) helmets.Cut() return ..() +/datum/shuttle/autodock/web_shuttle/current_dock_target() + // TODO - Probably don't even need to override this right? Debug testing code below will check! + . = web_master?.get_current_destination()?.my_landmark?.docking_controller?.id_tag + if (. != ..()) + warning("Web shuttle [src] had current_dock_target()=[.] but autodock.current_dock_target() = [..()]") -/datum/shuttle/web_shuttle/current_dock_target() - if(web_master) - return web_master.current_dock_target() - -/datum/shuttle/web_shuttle/move(var/area/origin, var/area/destination) +/datum/shuttle/autodock/web_shuttle/perform_shuttle_move() ..() last_move = world.time -/datum/shuttle/web_shuttle/short_jump() +/datum/shuttle/autodock/web_shuttle/short_jump() . = ..() update_helmets() -/datum/shuttle/web_shuttle/long_jump() +/datum/shuttle/autodock/web_shuttle/long_jump() . = ..() update_helmets() -/datum/shuttle/web_shuttle/on_shuttle_departure() +/datum/shuttle/autodock/web_shuttle/on_shuttle_departure() . = ..() web_master.on_shuttle_departure() update_helmets() -/datum/shuttle/web_shuttle/on_shuttle_arrival() +/datum/shuttle/autodock/web_shuttle/on_shuttle_arrival() . = ..() + active_docking_controller = current_location.docking_controller + update_docking_target(current_location) web_master.on_shuttle_arrival() update_helmets() -/datum/shuttle/web_shuttle/proc/build_destinations() +/datum/shuttle/autodock/web_shuttle/proc/build_destinations() return -/datum/shuttle/web_shuttle/process() +/datum/shuttle/autodock/web_shuttle/process() update_helmets() if(moving_status == SHUTTLE_IDLE) @@ -76,8 +78,8 @@ else // Otherwise we are about to start one or just finished one. if(autopilot_delay > 0) // Wait for awhile so people can get on and off. - if(docking_controller && !skip_docking_checks()) // Dock to the destination if possible. - var/docking_status = docking_controller.get_docking_status() + if(active_docking_controller && shuttle_docking_controller) // Dock to the destination if possible. + var/docking_status = shuttle_docking_controller.get_docking_status() if(docking_status == "undocked") dock() autopilot_say("Docking.") @@ -96,8 +98,8 @@ autopilot_delay-- else // Time to go. - if(docking_controller && !skip_docking_checks()) // Undock if possible. - var/docking_status = docking_controller.get_docking_status() + if(active_docking_controller && shuttle_docking_controller) // Undock if possible. + var/docking_status = shuttle_docking_controller.get_docking_status() if(docking_status == "docked") undock() autopilot_say("Undocking.") @@ -109,13 +111,13 @@ autopilot_say("Taking off.") web_master.process_autopath() -/datum/shuttle/web_shuttle/proc/update_helmets() +/datum/shuttle/autodock/web_shuttle/proc/update_helmets() for(var/helm in helmets) - if(!helm) - helmets -= helm - continue var/obj/item/clothing/head/pilot/H = helm - if(!H.shuttle_comp || get_area(H.shuttle_comp) != get_area(H)) + if(QDELETED(H)) + helmets -= H + continue + if(!H.shuttle_comp || !(get_area(H) in shuttle_area)) H.shuttle_comp = null H.audible_message("\The [H] pings as it loses it's connection with the ship.") H.update_hud("discon") @@ -123,24 +125,28 @@ else H.update_hud(moving_status) -/datum/shuttle/web_shuttle/proc/adjust_autopilot(on) +/datum/shuttle/autodock/web_shuttle/proc/adjust_autopilot(on) if(on) if(autopilot) return autopilot = TRUE autopilot_delay = initial(autopilot_delay) - shuttle_controller.process_shuttles += src + shuttle_controller.process_shuttles |= src + if(process_state == IDLE_STATE) + process_state = DO_AUTOPILOT else if(!autopilot) return autopilot = FALSE shuttle_controller.process_shuttles -= src + if (process_state == DO_AUTOPILOT) + process_state = initial(process_state) -/datum/shuttle/web_shuttle/proc/autopilot_say(message) // Makes the autopilot 'talk' to the passengers. +/datum/shuttle/autodock/web_shuttle/proc/autopilot_say(message) // Makes the autopilot 'talk' to the passengers. var/padded_message = "shuttle autopilot states, \"[message]\"" - message_passengers(current_area, padded_message) + message_passengers(padded_message) -/datum/shuttle/web_shuttle/proc/rename_shuttle(mob/user) +/datum/shuttle/autodock/web_shuttle/proc/rename_shuttle(mob/user) if(!can_rename) to_chat(user, "You can't rename this vessel.") return @@ -162,6 +168,8 @@ var/list/my_doors //Should be list("id_tag" = "Pretty Door Name", ...) var/list/my_sensors //Should be list("id_tag" = "Pretty Sensor Name", ...) +// Note - Searching own area for doors/sensors is fine for legacy web shuttles as they are single-area. +// However if this code is copied to future multi-area shuttles, should search in all shuttle areas /obj/machinery/computer/shuttle_control/web/Initialize() . = ..() var/area/my_area = get_area(src) @@ -186,12 +194,12 @@ log_debug("[my_area] shuttle computer couldn't find [lost] sensor!") /obj/machinery/computer/shuttle_control/web/attackby(obj/I, mob/user) - var/datum/shuttle/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag] if(shuttle && istype(I,/obj/item/clothing/head/pilot)) var/obj/item/clothing/head/pilot/H = I H.shuttle_comp = src shuttle.helmets |= I - to_chat(user,"You register the helmet with the ship's console.") + to_chat(user, "You register the helmet with the ship's console.") shuttle.update_helmets() return @@ -208,7 +216,7 @@ /* // If nanoUI falls over and you want a non-nanoUI UI, feel free to uncomment this section. - var/datum/shuttle/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag] if(!istype(WS)) message_admins("ERROR: Shuttle computer ([src]) ([shuttle_tag]) could not find their shuttle in the shuttles list.") return @@ -272,8 +280,9 @@ /obj/machinery/computer/shuttle_control/web/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] var/list/routes[0] - var/datum/shuttle/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/web_shuttle/shuttle = SSshuttles.shuttles[shuttle_tag] if(!istype(shuttle)) + to_chat(user, "Unable to establish link with the shuttle.") return var/list/R = shuttle.web_master.get_available_routes() @@ -333,11 +342,11 @@ "future_location" = future_location, "shuttle_state" = shuttle_state, "routes" = routes, - "has_docking" = shuttle.docking_controller? 1 : 0, + "has_docking" = shuttle.shuttle_docking_controller? 1 : 0, "skip_docking" = shuttle.skip_docking_checks(), "is_moving" = shuttle.moving_status != SHUTTLE_IDLE, - "docking_status" = shuttle.docking_controller? shuttle.docking_controller.get_docking_status() : null, - "docking_override" = shuttle.docking_controller? shuttle.docking_controller.override_enabled : null, + "docking_status" = shuttle.shuttle_docking_controller? shuttle.shuttle_docking_controller.get_docking_status() : null, + "docking_override" = shuttle.shuttle_docking_controller? shuttle.shuttle_docking_controller.override_enabled : null, "is_in_transit" = shuttle.has_arrive_time(), "travel_progress" = between(0, percent_finished, 100), "time_left" = round( (total_time - elapsed_time) / 10), @@ -360,13 +369,13 @@ /obj/machinery/computer/shuttle_control/web/Topic(href, href_list) - if(..()) - return 1 + if((. = ..())) + return usr.set_machine(src) src.add_fingerprint(usr) - var/datum/shuttle/web_shuttle/WS = shuttle_controller.shuttles[shuttle_tag] + var/datum/shuttle/autodock/web_shuttle/WS = SSshuttles.shuttles[shuttle_tag] if(!istype(WS)) message_admins("ERROR: Shuttle computer ([src]) ([shuttle_tag]) could not find their shuttle in the shuttles list.") return @@ -375,7 +384,7 @@ ui_interact(usr) if (WS.moving_status != SHUTTLE_IDLE) - usr << "[WS.visible_name] is busy moving." + to_chat(usr, "[WS.visible_name] is busy moving.") return if(href_list["rename_command"]) @@ -417,7 +426,7 @@ return if((WS.last_move + WS.cooldown) > world.time) - usr << "The ship's drive is inoperable while the engines are charging." + to_chat(usr, "The ship's drive is inoperable while the engines are charging.") return var/index = text2num(href_list["traverse"]) @@ -436,19 +445,38 @@ message_admins("ERROR: Shuttle computer was asked to travel to a nonexistant destination.") return + WS.next_location = target_destination.my_landmark + if(!can_move(WS, usr)) + return + WS.web_master.future_destination = target_destination to_chat(usr, "[WS.visible_name] flight computer received command.") WS.web_master.reset_autopath() // Deviating from the path will almost certainly confuse the autopilot, so lets just reset its memory. var/travel_time = new_route.travel_time * WS.flight_time_modifier - + // TODO - Leshana - Change this to use proccess stuff of autodock! if(new_route.interim && new_route.travel_time) - WS.long_jump(WS.current_area, target_destination.my_area, new_route.interim, travel_time / 10) + WS.long_jump(target_destination.my_landmark, new_route.interim, travel_time / 10) else - WS.short_jump(WS.current_area, target_destination.my_area) + WS.short_jump(target_destination.my_landmark) ui_interact(usr) +//check if we're undocked, give option to force launch +/obj/machinery/computer/shuttle_control/web/proc/check_docking(datum/shuttle/autodock/MS) + if(MS.skip_docking_checks() || MS.check_undocked()) + return 1 + + var/choice = alert("The shuttle is currently docked! Please undock before continuing.","Error","Cancel","Force Launch") + if(choice == "Cancel") + return 0 + + choice = alert("Forcing a shuttle launch while docked may result in severe injury, death and/or damage to property. Are you sure you wish to continue?", "Force Launch", "Force Launch", "Cancel") + if(choice == "Cancel") + return 0 + + return 1 + // Props, for now. /obj/structure/flight_left name = "flight computer meters" @@ -474,11 +502,18 @@ /obj/shuttle_connector/Initialize() . = ..() - SSshuttles.OnDocksInitialized(CALLBACK(src, .proc/setup_routes)) + GLOB.shuttle_added.register_global(src, .proc/setup_routes) -/obj/shuttle_connector/proc/setup_routes() - if(destinations && shuttle_name) - var/datum/shuttle/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name] +/obj/shuttle_connector/Destroy() + GLOB.shuttle_added.unregister_global(src, .proc/setup_routes) + . = ..() + +// This is called whenever a shuttle is initialized. If its our shuttle, do our thing! +/obj/shuttle_connector/proc/setup_routes(var/new_shuttle) + var/datum/shuttle/autodock/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name] + if(ES != new_shuttle) + return // Its not our shuttle! Ignore! + if(destinations && istype(ES)) var/datum/shuttle_web_master/WM = ES.web_master for(var/new_dest in destinations) @@ -487,7 +522,9 @@ for(var/type_to_link in D.routes_to_make) var/travel_delay = D.routes_to_make[type_to_link] - D.link_destinations(WM.get_destination_by_type(type_to_link), D.preferred_interim_area, travel_delay) + D.link_destinations(WM.get_destination_by_type(type_to_link), D.preferred_interim_tag, travel_delay) + else + warning("[log_info_line()]'s shuttle [global.log_info_line(ES)] initialized but destinations:[destinations]") qdel(src) diff --git a/code/modules/shuttles/upgrade_guide.md b/code/modules/shuttles/upgrade_guide.md new file mode 100644 index 0000000000..7c2c73f422 --- /dev/null +++ b/code/modules/shuttles/upgrade_guide.md @@ -0,0 +1,124 @@ +# "Landmark" Shuttles Conversion +This guide helps with updating maps and shuttle datums from the old "area" based system to the "landmark" based system. + +## Summary +The old shuttle datum worked with areas (`/area`). You are probably familiar with every shuttle having a few cookie-cutter shaped areas it travels between. +When the shuttle "moved" it was translated from its current area to the destination area. The areas had to be _exactly_ the same shape so +that each turf in the origin area got translated to the equivalent place at the target.\ +Since _all possible_ destinations had to have a dedicated area (and areas in BYOND can't overlap) this means it is impossible for two shuttles to travel to the same spot, even at different times. + +In the new system shuttle destinations are represented by "landmark" objects (`/obj/effect/shuttle_landmark`). +When a shuttle is "moved" it is translated from its current landmark to the destination landmark, with each turf keeping its same position _relative_ to the landmarks. +In other words, whatever a turf's x/y/z offsets are from the origin landmark, it will be moved to the same x/y/z offset from the destination landmark. + +## Landmark Objects +Shuttle destinations are represented by `/obj/effect/shuttle_landmark` objects on the map. + +* `name` - Pretty name of the nav point, used on overmap and in messages and console UI. +* `landmark_tag` - Globally unique ID, used by everything else to refer to this landmark. +* `docking_controller` - ID of the controller on the dock side (initialize to id_tag, becomes reference). Leave null if not applicable. +* `base_area` - Type path of the `/area` that should be here when a shuttle is *not* present. +* `base_turf` - Type path of the `/turf` that should be here when a shuttle is *not* present. +* `shuttle_restricted` - If not null, only the named shuttle is allowed to use this landmark. (TODO: Overmap functionality) +* `flags` - Bitfield - defaults to `SLANDMARK_FLAG_AUTOSET`, can be any combination of: + * `SLANDMARK_FLAG_AUTOSET` (1) - If set, will initialize base_area and base_turf to same as where it was spawned at. + * `SLANDMARK_FLAG_ZERO_G` (2) - If set, Zero-G shuttles moved here will lose gravity unless the area has ambient gravity. +* `special_dock_targets` - Used to configure shuttles with multiple docking controllers on the shuttle. Map of shuttle `name` -> `id_tag` of the docking controller it should use for this landmark. (Think of a shuttle with airlocks on both sides, each with their own controller. This would tell it which side to use.) + + +## Shuttle Types + + +### Ferry Shuttles +These shuttles go back and forth between two locations (normally called "station" and "offsite"). +Examples: Mining shuttle, Arrivals Shuttle, etc. + +Old Type Path: `/datum/shuttle/ferry`\ +New Type Path: `/datum/shuttle/autodock/ferry` + +##### New Vars: + +Name|Type|Required?|Info +---|---|---|--- +shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths. + +##### Replaced vars: + +Old|New|Required?|Info +:---:|:---:|:---:|--- +area_station |landmark_station |Yes|Tag of the landmark for the "station" location. +area_offsite |landmark_offsite |Yes|Tag of the landmark for the "offsite" location. +area_transition |landmark_transition |No|Tag of the landmark for the "transition" location used during long_jump() +dock_target_station |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the landmark_station landmark obj. +dock_target_offsite |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the landmark_offsite landmark obj. + + + + +### Multi Shuttles +These shuttles go between a list of configured locations, one of which is its starting location. +Examples: Skipjack, Syndicate Shuttle + +Old Type Path: `/datum/shuttle/multi_shuttle`\ +New Type Path: `/datum/shuttle/autodock/multi` + +##### New Vars: + +Name|Type|Required?|Info +---|---|---|--- +shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths. + +##### Replaced vars: + +Old|New|Required?|Info +:---:|:---:|:---:|--- +origin |current_location |Yes|Tag of the landmark where the shuttle is at startup. +interim |landmark_transition |No|Tag of the landmark for the "transition" location used during long_jump() +start_location |N/A |No|No longer necessary, automatically determined from the value of `origin` +destinations |destination_tags |Yes|List of destinations the shuttle can travel to. Used to be associative list of *name* -> *area typepath*, now is normal list of landmark tag ids. Name is now read from the landmark obj. +destination_dock_targets|On landmarks |No|Used to be associative list of *name* -> *id_tag* for which docking controller *on the dock* to use at each destination. This is now specified by the `docking_controller` var on each landmark obj. + + + +### Web Shuttles +These shuttles travel along a network of locations connected by routes. Instead of being able to travel to any of its destinations, it can only travel to destinations connected by a route to its current location. Added by Polaris as an upgrade to Multi Shuttles. +Note: While cool, it is likely that the upcoming "overmap" shuttles will be even cooler, and may eventually replace some web shuttles. +Examples: Southern Cross' Ninja Shuttle, Tether's Excursion Shuttle + +Old Type Path: `/datum/shuttle/web_shuttle`\ +New Type Path: `/datum/shuttle/autodock/web_shuttle` + +##### New Vars: + +Name|Type|Required?|Info +---|---|---|--- +shuttle_area |`/area` typepath(s)|Yes| Can be a single path or list of paths. + +##### Replaced vars: + +Old|New|Required?|Info +:---:|:---:|:---:|--- +current_area |current_location |Yes|Tag of the landmark where the shuttle is at startup. + +#### Web Destination Configuration (`/datum/shuttle_destination`) +The network of routes for each web shuttle is configured by defining datums. These are mostly unchanged but use landmarks instead of areas now. + +##### Replaced vars: + +Old|New|Required?|Info +:---:|:---:|:---:|--- +my_area |my_landmark |Yes|Tag of the landmark associated with this destination. +preferred_interim_area |preferred_interim_tag |No|Tag of the landmark for the "transition" location used during long_jump() +dock_target |On landmark |No|`id_tag` docking controller *on the dock* has been moved to the `docking_controller` var on the my_landmark landmark obj. + +### Misc Shuttle Types +Other shuttle types that are either unused or unchanged in particular. + +#### Escape Pods +Special case of ferry shuttles that use escape pod berth controllers. +Type path changed from `/datum/shuttle/ferry/escape_pod` to `/datum/shuttle/autodock/ferry/escape_pod` +Follow same instructions as for other ferry shuttles. + +#### Multidock Ferry Shuttles +`/datum/shuttle/ferry/multidock` was a variant of ferry shuttles that could use a different docking port at each location. +Obsolete since is now natively supported by all dockable shuttles. diff --git a/code/modules/shuttles/web_datums.dm b/code/modules/shuttles/web_datums.dm index c56dc7419c..3e2abf9443 100644 --- a/code/modules/shuttles/web_datums.dm +++ b/code/modules/shuttles/web_datums.dm @@ -8,7 +8,7 @@ /datum/shuttle_route var/datum/shuttle_destination/start = null // One of the two sides of this route. Start just means it was the creator of this route. var/datum/shuttle_destination/end = null // The second side. - var/area/interim = null // Where the shuttle sits during the movement. Make sure no other shuttle shares this or Very Bad Things will happen. + var/var/obj/effect/shuttle_landmark/interim // Where the shuttle sits during the movement. Make sure no other shuttle shares this or Very Bad Things will happen. var/travel_time = 0 // How long it takes to move from start to end, or end to start. Set to 0 for instant travel. var/one_way = FALSE // If true, you can't travel from end to start. @@ -16,7 +16,7 @@ start = _start end = _end if(_interim) - interim = locate(_interim) + interim = SSshuttles.get_landmark(_interim) travel_time = _time one_way = _oneway @@ -50,14 +50,12 @@ // This is the second datum, and contains information on all the potential destinations for a specific shuttle. /datum/shuttle_destination var/name = "a place" // Name of the destination, used for the flight computer. - var/area/my_area = null // Where the shuttle will move to when it actually arrives. + var/obj/effect/shuttle_landmark/my_landmark = null // Where the shuttle will move to when it actually arrives. var/datum/shuttle_web_master/master = null // The datum that does the coordination with the actual shuttle datum. var/list/routes = list() // Routes that are connected to this destination. - var/preferred_interim_area = null // When building a new route, use this interim area. + var/preferred_interim_tag = null // When building a new route, use interim landmark with this tag. var/skip_me = FALSE // We will not autocreate this one. Some map must be doing it. - var/dock_target = null // The tag_id that the shuttle will use to try to dock to the destination, if able. - var/radio_announce = 0 // Whether it will make a station announcement (0) or a radio announcement (1). var/announcer = null // The name of the 'announcer' that will say the arrival/departure messages. Defaults to the map's boss name if blank. // var/arrival_message = null // Message said if the ship enters this destination. Not announced if the ship is cloaked. @@ -72,7 +70,9 @@ var/list/routes_to_make = list() /datum/shuttle_destination/New(var/new_master) - my_area = locate(my_area) + my_landmark = SSshuttles.get_landmark(my_landmark) + if(!my_landmark) + log_debug("Web shuttle destination '[name]' could not find its landmark '[my_landmark]'.") master = new_master /datum/shuttle_destination/Destroy() @@ -86,23 +86,23 @@ // This builds destination instances connected to this instance, recursively. /datum/shuttle_destination/proc/build_destinations(var/list/already_made = list()) already_made += src.type - world << "SHUTTLES: [name] is going to build destinations. already_made list is \[[english_list(already_made)]\]" + to_world("SHUTTLES: [name] is going to build destinations. already_made list is \[[english_list(already_made)]\]") for(var/type_to_make in destinations_to_create) if(type_to_make in already_made) // Avoid circular initializations. - world << "SHUTTLES: [name] can't build [type_to_make] due to being a duplicate." + to_world("SHUTTLES: [name] can't build [type_to_make] due to being a duplicate.") continue // Instance the new destination, and call this proc on their 'downstream' destinations. var/datum/shuttle_destination/new_dest = new type_to_make() - world << "SHUTTLES: [name] has created [new_dest.name] and will make it build their own destinations." + to_world("SHUTTLES: [name] has created [new_dest.name] and will make it build their own destinations.") already_made += new_dest.build_destinations(already_made) // Now link our new destination to us. var/travel_delay = destinations_to_create[type_to_make] - link_destinations(new_dest, preferred_interim_area, travel_delay) - world << "SHUTTLES: [name] has linked themselves to [new_dest.name]" + link_destinations(new_dest, preferred_interim_tag, travel_delay) + to_world("SHUTTLES: [name] has linked themselves to [new_dest.name]") - world << "SHUTTLES: [name] has finished building destinations. already_made list is \[[english_list(already_made)]\]." + to_world("SHUTTLES: [name] has finished building destinations. already_made list is \[[english_list(already_made)]\].") return already_made /datum/shuttle_destination/proc/enter(var/datum/shuttle_destination/old_destination) @@ -135,14 +135,14 @@ else global_announcer.autosay(get_arrival_message(),(announcer ? announcer : "[using_map.boss_name]")) -/datum/shuttle_destination/proc/link_destinations(var/datum/shuttle_destination/other_place, var/area/interim_area, var/travel_time = 0) +/datum/shuttle_destination/proc/link_destinations(var/datum/shuttle_destination/other_place, var/interim_tag, var/travel_time = 0) // First, check to make sure this doesn't cause a duplicate route. for(var/datum/shuttle_route/R in routes) if(R.start == other_place || R.end == other_place) return // Now we can connect them. - var/datum/shuttle_route/new_route = new(src, other_place, interim_area, travel_time) + var/datum/shuttle_route/new_route = new(src, other_place, interim_tag, travel_time) routes += new_route other_place.routes += new_route @@ -166,7 +166,7 @@ // This is the third and final datum, which coordinates with the shuttle datum to tell it where it is, where it can go, and how long it will take. // It is also responsible for instancing all the destinations it has control over, and linking them together. /datum/shuttle_web_master - var/datum/shuttle/web_shuttle/my_shuttle = null // Ref to the shuttle this datum is coordinating with. + var/datum/shuttle/autodock/web_shuttle/my_shuttle = null // Ref to the shuttle this datum is coordinating with. var/datum/shuttle_destination/current_destination = null // Where the shuttle currently is. Bit of a misnomer. var/datum/shuttle_destination/future_destination = null // Where it will be in the near future. var/datum/shuttle_destination/starting_destination = null // Where the shuttle will start at, generally at the home base. @@ -204,7 +204,7 @@ for(var/datum/shuttle_destination/D in destinations) for(var/type_to_link in D.routes_to_make) var/travel_delay = D.routes_to_make[type_to_link] - D.link_destinations(get_destination_by_type(type_to_link), D.preferred_interim_area, travel_delay) + D.link_destinations(get_destination_by_type(type_to_link), D.preferred_interim_tag, travel_delay) /datum/shuttle_web_master/proc/on_shuttle_departure() current_destination.exit() @@ -214,11 +214,6 @@ future_destination.enter() current_destination = future_destination future_destination = null - my_shuttle.current_area = current_destination.my_area - -/datum/shuttle_web_master/proc/current_dock_target() - if(current_destination) - return current_destination.dock_target /datum/shuttle_web_master/proc/get_available_routes() if(current_destination) @@ -254,10 +249,11 @@ future_destination = R.get_other_side(current_destination) var/travel_time = R.travel_time * my_shuttle.flight_time_modifier * 2 // Autopilot is less efficent than having someone flying manually. + // TODO - Leshana - Change this to use proccess stuff of autodock! if(R.interim && R.travel_time > 0) - my_shuttle.long_jump(my_shuttle.current_area, future_destination.my_area, R.interim, travel_time / 10) + my_shuttle.long_jump(future_destination.my_landmark, R.interim, travel_time / 10) else - my_shuttle.short_jump(my_shuttle.current_area, future_destination.my_area) + my_shuttle.short_jump(future_destination.my_landmark) return TRUE // Note this will return before the shuttle actually arrives. /datum/shuttle_web_master/proc/process_autopath() diff --git a/code/modules/spells/aoe_turf/charge.dm b/code/modules/spells/aoe_turf/charge.dm index 6e7f5b0050..f27a13e688 100644 --- a/code/modules/spells/aoe_turf/charge.dm +++ b/code/modules/spells/aoe_turf/charge.dm @@ -27,9 +27,9 @@ for(var/spell/S in M.spell_list) if(!istype(S, /spell/aoe_turf/charge)) S.charge_counter = S.charge_max - M <<"You feel raw magic flowing through you, it feels good!" + to_chat(M, "You feel raw magic flowing through you, it feels good!") else - M <<"You feel very strange for a moment, but then it passes." + to_chat(M, "You feel very strange for a moment, but then it passes.") return M /spell/aoe_turf/charge/proc/cast_charge(var/atom/target) diff --git a/code/modules/spells/aoe_turf/conjure/conjure.dm b/code/modules/spells/aoe_turf/conjure/conjure.dm index e96985e9bb..9e02cf9513 100644 --- a/code/modules/spells/aoe_turf/conjure/conjure.dm +++ b/code/modules/spells/aoe_turf/conjure/conjure.dm @@ -45,7 +45,7 @@ How they spawn stuff is decided by behaviour vars, which are explained below var/atom/summoned_object if(ispath(summoned_object_type,/turf)) if(istype(get_turf(user),/turf/simulated/shuttle) || istype(spawn_place, /turf/simulated/shuttle)) - user << "You can't build things on shuttles!" + to_chat(user, "You can't build things on shuttles!") continue spawn_place.ChangeTurf(summoned_object_type) summoned_object = spawn_place diff --git a/code/modules/spells/artifacts.dm b/code/modules/spells/artifacts.dm index f643012ce3..64489dea4c 100644 --- a/code/modules/spells/artifacts.dm +++ b/code/modules/spells/artifacts.dm @@ -14,10 +14,10 @@ /obj/item/weapon/scrying/attack_self(mob/user as mob) if((user.mind && !wizards.is_antagonist(user.mind))) - user << "You stare into the orb and see nothing but your own reflection." + to_chat(user, "You stare into the orb and see nothing but your own reflection.") return - user << "You can see... everything!" + to_chat(user, "You can see... everything!") visible_message("[user] stares into [src], their eyes glazing over.") user.teleop = user.ghostize(1) diff --git a/code/modules/spells/general/area_teleport.dm b/code/modules/spells/general/area_teleport.dm index 77e1ee47c4..93d86c2d20 100644 --- a/code/modules/spells/general/area_teleport.dm +++ b/code/modules/spells/general/area_teleport.dm @@ -50,7 +50,7 @@ L+=T if(!L.len) - user <<"The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry." + to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") return if(user && user.buckled) diff --git a/code/modules/spells/general/rune_write.dm b/code/modules/spells/general/rune_write.dm index c110a76ac1..2871033395 100644 --- a/code/modules/spells/general/rune_write.dm +++ b/code/modules/spells/general/rune_write.dm @@ -171,5 +171,5 @@ R.word3=cultwords["technology"] R.check_icon() else - user << " You do not have enough space to write a proper rune." + to_chat(user, " You do not have enough space to write a proper rune.") return diff --git a/code/modules/spells/spell_code.dm b/code/modules/spells/spell_code.dm index 3415ad0ead..6c57fa6037 100644 --- a/code/modules/spells/spell_code.dm +++ b/code/modules/spells/spell_code.dm @@ -156,7 +156,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now for(var/atom/target in targets) var/location = get_turf(target) if(istype(target,/mob/living) && message) - target << text("[message]") + to_chat(target, "[message]") if(sparks_spread) var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread() sparks.set_up(sparks_amt, 0, location) //no idea what the 0 is @@ -180,7 +180,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now if(!(src in user.spell_list) && holder == user) error("[user] utilized the spell '[src]' without having it.") - user << "You shouldn't have this spell! Something's wrong." + to_chat(user, "You shouldn't have this spell! Something's wrong.") return 0 if(silenced > 0) @@ -188,7 +188,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now var/turf/user_turf = get_turf(user) if(!user_turf) - user << "You cannot cast spells in null space!" + to_chat(user, "You cannot cast spells in null space!") if(spell_flags & Z2NOCAST && (user_turf.z in using_map.admin_levels)) //Certain spells are not allowed on the CentCom zlevel return 0 @@ -201,7 +201,7 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now if(istype(user, /mob/living/simple_mob) && holder == user) var/mob/living/simple_mob/SM = user if(SM.purge) - SM << "The nullrod's power interferes with your own!" + to_chat(SM, "The nullrod's power interferes with your own!") return 0 if(!src.check_charge(skipcharge, user)) //sees if we can cast based on charges alone @@ -209,12 +209,12 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now if(!(spell_flags & GHOSTCAST) && holder == user) if(user.stat && !(spell_flags & STATALLOWED)) - usr << "Not when you're incapacitated." + to_chat(usr, "Not when you're incapacitated.") return 0 if(ishuman(user) && !(invocation_type in list(SpI_EMOTE, SpI_NONE))) if(user.is_muzzled()) - user << "Mmmf mrrfff!" + to_chat(user, "Mmmf mrrfff!") return 0 var/spell/noclothes/spell = locate() in user.spell_list @@ -229,11 +229,11 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now switch(charge_type) if(Sp_RECHARGE) if(charge_counter < charge_max) - user << still_recharging_msg + to_chat(user,still_recharging_msg) return 0 if(Sp_CHARGES) if(!charge_counter) - user << "[name] has no charges left." + to_chat(user, "[name] has no charges left.") return 0 return 1 diff --git a/code/modules/spells/spellbook.dm b/code/modules/spells/spellbook.dm index 1f41ce9e3d..3ae280a65b 100644 --- a/code/modules/spells/spellbook.dm +++ b/code/modules/spells/spellbook.dm @@ -15,7 +15,7 @@ if(!user) return if((user.mind && !wizards.is_antagonist(user.mind))) - usr << "You stare at the book but cannot make sense of the markings!" + to_chat(usr, "You stare at the book but cannot make sense of the markings!") return user.set_machine(src) @@ -229,7 +229,7 @@ H.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS) H.see_in_dark = 8 H.see_invisible = SEE_INVISIBLE_LEVEL_TWO - H << "The walls suddenly disappear." + to_chat(H, "The walls suddenly disappear.") temp = "You have purchased a scrying orb, and gained x-ray vision." max_uses-- else @@ -261,15 +261,15 @@ if(user.mind) // TODO: Update to new antagonist system. if(user.mind.special_role == "apprentice" || user.mind.special_role == "Wizard") - user <<"You're already far more versed in this spell than this flimsy how-to book can provide." + to_chat(user, "You're already far more versed in this spell than this flimsy how-to book can provide.") else - user <<"You've already read this one." + to_chat(user, "You've already read this one.") return if(used) recoil(user) else user.add_spell(S) - user <<"you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!" + to_chat(user, "you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") user.attack_log += text("\[[time_stamp()]\] [user.real_name] ([user.ckey]) learned the spell [spellname] ([S]).") onlearned(user) @@ -302,7 +302,7 @@ /obj/item/weapon/spellbook/oneuse/smoke/recoil(mob/user as mob) ..() - user <<"Your stomach rumbles..." + to_chat(user, "Your stomach rumbles...") if(user.nutrition) user.nutrition -= 200 if(user.nutrition <= 0) @@ -316,7 +316,7 @@ /obj/item/weapon/spellbook/oneuse/blind/recoil(mob/user as mob) ..() - user <<"You go blind!" + to_chat(user, "You go blind!") user.Blind(10) /obj/item/weapon/spellbook/oneuse/mindswap @@ -338,10 +338,10 @@ stored_swap = null if(!stored_swap) stored_swap = user - user <<"For a moment you feel like you don't even know who you are anymore." + to_chat(user, "For a moment you feel like you don't even know who you are anymore.") return if(stored_swap == user) - user <<"You stare at the book some more, but there doesn't seem to be anything else to learn..." + to_chat(user, "You stare at the book some more, but there doesn't seem to be anything else to learn...") return if(user.mind.special_verbs.len) @@ -370,8 +370,8 @@ for(var/V in user.mind.special_verbs) user.verbs += V - stored_swap <<"You're suddenly somewhere else... and someone else?!" - user <<"Suddenly you're staring at [src] again... where are you, who are you?!" + to_chat(stored_swap, "You're suddenly somewhere else... and someone else?!") + to_chat(user, "Suddenly you're staring at [src] again... where are you, who are you?!") stored_swap = null /obj/item/weapon/spellbook/oneuse/forcewall @@ -382,7 +382,7 @@ /obj/item/weapon/spellbook/oneuse/forcewall/recoil(mob/user as mob) ..() - user <<"You suddenly feel very solid!" + to_chat(user, "You suddenly feel very solid!") var/obj/structure/closet/statue/S = new /obj/structure/closet/statue(user.loc, user) S.timer = 30 user.drop_item() @@ -396,7 +396,7 @@ /obj/item/weapon/spellbook/oneuse/knock/recoil(mob/user as mob) ..() - user <<"You're knocked down!" + to_chat(user, "You're knocked down!") user.Weaken(20) /obj/item/weapon/spellbook/oneuse/horsemask @@ -407,7 +407,7 @@ /obj/item/weapon/spellbook/oneuse/horsemask/recoil(mob/living/carbon/user as mob) if(istype(user, /mob/living/carbon/human)) - user <<"HOR-SIE HAS RISEN" + to_chat(user, "HOR-SIE HAS RISEN") var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead magichead.canremove = 0 //curses! magichead.flags_inv = null //so you can still see their face @@ -416,7 +416,7 @@ user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) qdel(src) else - user <<"I say thee neigh" + to_chat(user, "I say thee neigh") /obj/item/weapon/spellbook/oneuse/charge spell = /spell/aoe_turf/charge @@ -426,5 +426,5 @@ /obj/item/weapon/spellbook/oneuse/charge/recoil(mob/user as mob) ..() - user <<"[src] suddenly feels very warm!" + to_chat(user, "[src] suddenly feels very warm!") empulse(src, 1, 1, 1, 1) diff --git a/code/modules/spells/targeted/ethereal_jaunt.dm b/code/modules/spells/targeted/ethereal_jaunt.dm index 68cf35e9a4..e31cec5b4c 100644 --- a/code/modules/spells/targeted/ethereal_jaunt.dm +++ b/code/modules/spells/targeted/ethereal_jaunt.dm @@ -97,7 +97,7 @@ if(!T.contains_dense_objects()) last_valid_turf = T else - user << "Some strange aura is blocking the way!" + to_chat(user, "Some strange aura is blocking the way!") src.canmove = 0 spawn(2) src.canmove = 1 diff --git a/code/modules/spells/targeted/harvest.dm b/code/modules/spells/targeted/harvest.dm index 7a8d986a02..3bebe72e25 100644 --- a/code/modules/spells/targeted/harvest.dm +++ b/code/modules/spells/targeted/harvest.dm @@ -38,7 +38,7 @@ M.forceMove(destination) if(M != user) prey = 1 - user << "You warp back to Nar-Sie[prey ? " along with your prey":""]." + to_chat(user, "You warp back to Nar-Sie[prey ? " along with your prey":""].") else - user << "...something's wrong!"//There shouldn't be an instance of Harvesters when Nar-Sie isn't in the world. + to_chat(user, "...something's wrong!")//There shouldn't be an instance of Harvesters when Nar-Sie isn't in the world. */ \ No newline at end of file diff --git a/code/modules/spells/targeted/mind_transfer.dm b/code/modules/spells/targeted/mind_transfer.dm index 375328016f..e3eda58b65 100644 --- a/code/modules/spells/targeted/mind_transfer.dm +++ b/code/modules/spells/targeted/mind_transfer.dm @@ -24,15 +24,15 @@ for(var/mob/living/target in targets) if(target.stat == DEAD) - user << "You didn't study necromancy back at the Space Wizard Federation academy." + to_chat(user, "You didn't study necromancy back at the Space Wizard Federation academy.") continue if(!target.key || !target.mind) - user << "They appear to be catatonic. Not even magic can affect their vacant mind." + to_chat(user, "They appear to be catatonic. Not even magic can affect their vacant mind.") continue if(target.mind.special_role in protected_roles) - user << "Their mind is resisting your spell." + to_chat(user, "Their mind is resisting your spell.") continue var/mob/living/victim = target//The target of the spell whos body will be transferred to. @@ -78,4 +78,4 @@ //After a certain amount of time the victim gets a message about being in a different body. spawn(msg_wait) - caster << "You feel woozy and lightheaded. Your body doesn't seem like your own." + to_chat(caster, "You feel woozy and lightheaded. Your body doesn't seem like your own.") diff --git a/code/modules/surgery/bones.dm b/code/modules/surgery/bones.dm index 2f832fdb9f..6a48eb198d 100644 --- a/code/modules/surgery/bones.dm +++ b/code/modules/surgery/bones.dm @@ -24,26 +24,28 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 0 /datum/surgery_step/glue_bone/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) if (affected.stage == 0) - user.visible_message("[user] starts applying medication to the damaged bones in [target]'s [affected.name] with \the [tool]." , \ - "You start applying medication to the damaged bones in [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] starts applying medication to the damaged bones in [target]'s [affected.name] with \the [tool]." , \ + "You start applying medication to the damaged bones in [target]'s [affected.name] with \the [tool].") target.custom_pain("Something in your [affected.name] is causing you a lot of pain!", 50) ..() /datum/surgery_step/glue_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] applies some [tool] to [target]'s bone in [affected.name]", \ - "You apply some [tool] to [target]'s bone in [affected.name] with \the [tool].") + user.visible_message("[user] applies some [tool] to [target]'s bone in [affected.name]", \ + "You apply some [tool] to [target]'s bone in [affected.name] with \the [tool].") affected.stage = 1 /datum/surgery_step/glue_bone/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \ - "Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") + user.visible_message("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \ + "Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") /////////////////////////////////////////////////////////////// // Bone Setting Surgery @@ -63,30 +65,32 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && affected.organ_tag != BP_HEAD && !(affected.robotic >= ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 1 /datum/surgery_step/set_bone/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] is beginning to set the bone in [target]'s [affected.name] in place with \the [tool]." , \ - "You are beginning to set the bone in [target]'s [affected.name] in place with \the [tool].") + user.visible_message("[user] is beginning to set the bone in [target]'s [affected.name] in place with \the [tool]." , \ + "You are beginning to set the bone in [target]'s [affected.name] in place with \the [tool].") target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) ..() /datum/surgery_step/set_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) if (affected.status & ORGAN_BROKEN) - user.visible_message("[user] sets the bone in [target]'s [affected.name] in place with \the [tool].", \ - "You set the bone in [target]'s [affected.name] in place with \the [tool].") + user.visible_message("[user] sets the bone in [target]'s [affected.name] in place with \the [tool].", \ + "You set the bone in [target]'s [affected.name] in place with \the [tool].") affected.stage = 2 else - user.visible_message("[user] sets the bone in [target]'s [affected.name] in the WRONG place with \the [tool].", \ - "You set the bone in [target]'s [affected.name] in the WRONG place with \the [tool].") + user.visible_message("[user] sets the bone in [target]'s [affected.name] in the WRONG place with \the [tool].", \ + "You set the bone in [target]'s [affected.name] in the WRONG place with \the [tool].") affected.fracture() /datum/surgery_step/set_bone/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!" , \ - "Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!") + user.visible_message("[user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!" , \ + "Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!") affected.createwound(BRUISE, 5) /////////////////////////////////////////////////////////////// @@ -107,23 +111,25 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && affected.organ_tag == BP_HEAD && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 1 /datum/surgery_step/mend_skull/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] is beginning to piece together [target]'s skull with \the [tool]." , \ - "You are beginning to piece together [target]'s skull with \the [tool].") + user.visible_message("[user] is beginning to piece together [target]'s skull with \the [tool]." , \ + "You are beginning to piece together [target]'s skull with \the [tool].") ..() /datum/surgery_step/mend_skull/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] sets [target]'s skull with \the [tool]." , \ - "You set [target]'s skull with \the [tool].") + user.visible_message("[user] sets [target]'s skull with \the [tool]." , \ + "You set [target]'s skull with \the [tool].") affected.stage = 2 /datum/surgery_step/mend_skull/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, damaging [target]'s face with \the [tool]!" , \ - "Your hand slips, damaging [target]'s face with \the [tool]!") + user.visible_message("[user]'s hand slips, damaging [target]'s face with \the [tool]!" , \ + "Your hand slips, damaging [target]'s face with \the [tool]!") var/obj/item/organ/external/head/h = affected h.createwound(BRUISE, 10) h.disfigured = 1 @@ -149,25 +155,27 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && affected.open >= 2 && !(affected.robotic >= ORGAN_ROBOT) && affected.stage == 2 /datum/surgery_step/finish_bone/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] starts to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].", \ - "You start to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] starts to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].", \ + "You start to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].") ..() /datum/surgery_step/finish_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has mended the damaged bones in [target]'s [affected.name] with \the [tool]." , \ - "You have mended the damaged bones in [target]'s [affected.name] with \the [tool]." ) + user.visible_message("[user] has mended the damaged bones in [target]'s [affected.name] with \the [tool]." , \ + "You have mended the damaged bones in [target]'s [affected.name] with \the [tool]." ) affected.status &= ~ORGAN_BROKEN affected.stage = 0 /datum/surgery_step/finish_bone/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \ - "Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") + user.visible_message("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \ + "Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") /////////////////////////////////////////////////////////////// // Bone Clamp Surgery @@ -188,24 +196,26 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 0 /datum/surgery_step/clamp_bone/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) if (affected.stage == 0) - user.visible_message("[user] starts repairing the damaged bones in [target]'s [affected.name] with \the [tool]." , \ - "You starts repairing the damaged bones in [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] starts repairing the damaged bones in [target]'s [affected.name] with \the [tool]." , \ + "You starts repairing the damaged bones in [target]'s [affected.name] with \the [tool].") target.custom_pain("Something in your [affected.name] is causing you a lot of pain!", 50) ..() /datum/surgery_step/clamp_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] sets the bone in [target]'s [affected.name] with \the [tool].", \ - "You sets [target]'s bone in [affected.name] with \the [tool].") + user.visible_message("[user] sets the bone in [target]'s [affected.name] with \the [tool].", \ + "You sets [target]'s bone in [affected.name] with \the [tool].") affected.status &= ~ORGAN_BROKEN /datum/surgery_step/clamp_bone/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!" , \ - "Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!") + user.visible_message("[user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!" , \ + "Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!") affected.createwound(BRUISE, 5) \ No newline at end of file diff --git a/code/modules/surgery/encased.dm b/code/modules/surgery/encased.dm index 4e03024a01..d69c456f7c 100644 --- a/code/modules/surgery/encased.dm +++ b/code/modules/surgery/encased.dm @@ -12,6 +12,8 @@ return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && !(affected.robotic >= ORGAN_ROBOT) && affected.encased && affected.open >= 2 /////////////////////////////////////////////////////////////// @@ -48,8 +50,8 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has cut [target]'s [affected.encased] open with \the [tool].", \ - "You have cut [target]'s [affected.encased] open with \the [tool].") + user.visible_message("[user] has cut [target]'s [affected.encased] open with \the [tool].", \ + "You have cut [target]'s [affected.encased] open with \the [tool].") affected.open = 2.5 /datum/surgery_step/open_encased/saw/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) @@ -57,8 +59,8 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, cracking [target]'s [affected.encased] with \the [tool]!" , \ - "Your hand slips, cracking [target]'s [affected.encased] with \the [tool]!" ) + user.visible_message("[user]'s hand slips, cracking [target]'s [affected.encased] with \the [tool]!" , \ + "Your hand slips, cracking [target]'s [affected.encased] with \the [tool]!" ) affected.createwound(CUT, 20) affected.fracture() @@ -98,8 +100,8 @@ if (!hasorgans(target)) return var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user] forces open [target]'s [affected.encased] with \the [tool]." - var/self_msg = "You force open [target]'s [affected.encased] with \the [tool]." + var/msg = "[user] forces open [target]'s [affected.encased] with \the [tool]." + var/self_msg = "You force open [target]'s [affected.encased] with \the [tool]." user.visible_message(msg, self_msg) affected.open = 3 @@ -109,8 +111,8 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user]'s hand slips, cracking [target]'s [affected.encased]!" - var/self_msg = "Your hand slips, cracking [target]'s [affected.encased]!" + var/msg = "[user]'s hand slips, cracking [target]'s [affected.encased]!" + var/self_msg = "Your hand slips, cracking [target]'s [affected.encased]!" user.visible_message(msg, self_msg) affected.createwound(BRUISE, 20) @@ -152,8 +154,8 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user] bends [target]'s [affected.encased] back into place with \the [tool]." - var/self_msg = "You bend [target]'s [affected.encased] back into place with \the [tool]." + var/msg = "[user] bends [target]'s [affected.encased] back into place with \the [tool]." + var/self_msg = "You bend [target]'s [affected.encased] back into place with \the [tool]." user.visible_message(msg, self_msg) affected.open = 2.5 @@ -163,15 +165,15 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user]'s hand slips, bending [target]'s [affected.encased] the wrong way!" - var/self_msg = "Your hand slips, bending [target]'s [affected.encased] the wrong way!" + var/msg = "[user]'s hand slips, bending [target]'s [affected.encased] the wrong way!" + var/self_msg = "Your hand slips, bending [target]'s [affected.encased] the wrong way!" user.visible_message(msg, self_msg) affected.createwound(BRUISE, 20) affected.fracture() /*if (prob(40)) //TODO: ORGAN REMOVAL UPDATE. - user.visible_message(" A rib pierces the lung!") + user.visible_message(" A rib pierces the lung!") target.rupture_lung()*/ /////////////////////////////////////////////////////////////// @@ -210,8 +212,8 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user] applied \the [tool] to [target]'s [affected.encased]." - var/self_msg = "You applied \the [tool] to [target]'s [affected.encased]." + var/msg = "[user] applied \the [tool] to [target]'s [affected.encased]." + var/self_msg = "You applied \the [tool] to [target]'s [affected.encased]." user.visible_message(msg, self_msg) affected.open = 2 @@ -250,8 +252,8 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has cut [target]'s [affected.encased] wide open with \the [tool].", \ - "You have cut [target]'s [affected.encased] wide open with \the [tool].") + user.visible_message("[user] has cut [target]'s [affected.encased] wide open with \the [tool].", \ + "You have cut [target]'s [affected.encased] wide open with \the [tool].") affected.open = 3 /datum/surgery_step/open_encased/advancedsaw_open/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) @@ -259,8 +261,8 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, searing [target]'s [affected.encased] with \the [tool]!" , \ - "Your hand slips, searing [target]'s [affected.encased] with \the [tool]!" ) + user.visible_message("[user]'s hand slips, searing [target]'s [affected.encased] with \the [tool]!" , \ + "Your hand slips, searing [target]'s [affected.encased] with \the [tool]!" ) affected.createwound(CUT, 20) affected.createwound(BURN, 15) @@ -300,8 +302,8 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user] sealed \the [target]'s [affected.encased] with \the [tool]." - var/self_msg = "You sealed \the [target]'s [affected.encased] with \the [tool]." + var/msg = "[user] sealed \the [target]'s [affected.encased] with \the [tool]." + var/self_msg = "You sealed \the [target]'s [affected.encased] with \the [tool]." user.visible_message(msg, self_msg) affected.open = 2 diff --git a/code/modules/surgery/external_repair.dm b/code/modules/surgery/external_repair.dm index 4ed2d96c1a..4f8707e82d 100644 --- a/code/modules/surgery/external_repair.dm +++ b/code/modules/surgery/external_repair.dm @@ -26,6 +26,8 @@ return 0 if (affected.robotic >= ORGAN_ROBOT) return 0 + if(coverage_check(user, target, affected, tool)) + return 0 return 1 diff --git a/code/modules/surgery/face.dm b/code/modules/surgery/face.dm index 30f50b14ec..b670497fc9 100644 --- a/code/modules/surgery/face.dm +++ b/code/modules/surgery/face.dm @@ -14,6 +14,8 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) if (!affected || (affected.robotic >= ORGAN_ROBOT)) return 0 + if(coverage_check(user, target, affected, tool)) + return 0 return target_zone == O_MOUTH /////////////////////////////////////////////////////////////// @@ -39,14 +41,14 @@ ..() /datum/surgery_step/generic/cut_face/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] has cut open [target]'s face and neck with \the [tool]." , \ - " You have cut open[target]'s face and neck with \the [tool].",) + user.visible_message("[user] has cut open [target]'s face and neck with \the [tool]." , \ + " You have cut open[target]'s face and neck with \the [tool].",) target.op_stage.face = 1 /datum/surgery_step/generic/cut_face/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, slicing [target]'s throat wth \the [tool]!" , \ - "Your hand slips, slicing [target]'s throat wth \the [tool]!" ) + user.visible_message("[user]'s hand slips, slicing [target]'s throat wth \the [tool]!" , \ + "Your hand slips, slicing [target]'s throat wth \the [tool]!" ) affected.createwound(CUT, 60) target.AdjustLosebreath(10) @@ -73,13 +75,13 @@ ..() /datum/surgery_step/face/mend_vocal/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] mends [target]'s vocal cords with \the [tool].", \ - "You mend [target]'s vocal cords with \the [tool].") + user.visible_message("[user] mends [target]'s vocal cords with \the [tool].", \ + "You mend [target]'s vocal cords with \the [tool].") target.op_stage.face = 2 /datum/surgery_step/face/mend_vocal/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user]'s hand slips, clamping [target]'s trachea shut for a moment with \the [tool]!", \ - "Your hand slips, clamping [user]'s trachea shut for a moment with \the [tool]!") + user.visible_message("[user]'s hand slips, clamping [target]'s trachea shut for a moment with \the [tool]!", \ + "Your hand slips, clamping [user]'s trachea shut for a moment with \the [tool]!") target.AdjustLosebreath(10) /////////////////////////////////////////////////////////////// @@ -106,14 +108,14 @@ ..() /datum/surgery_step/face/fix_face/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] pulls the skin on [target]'s face back in place with \the [tool].", \ - "You pull the skin on [target]'s face back in place with \the [tool].") + user.visible_message("[user] pulls the skin on [target]'s face back in place with \the [tool].", \ + "You pull the skin on [target]'s face back in place with \the [tool].") target.op_stage.face = 3 /datum/surgery_step/face/fix_face/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, tearing skin on [target]'s face with \the [tool]!", \ - "Your hand slips, tearing skin on [target]'s face with \the [tool]!") + user.visible_message("[user]'s hand slips, tearing skin on [target]'s face with \the [tool]!", \ + "Your hand slips, tearing skin on [target]'s face with \the [tool]!") target.apply_damage(10, BRUTE, affected, sharp=1, sharp=1) /////////////////////////////////////////////////////////////// @@ -135,14 +137,14 @@ return ..() && target.op_stage.face > 0 /datum/surgery_step/face/cauterize/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] is beginning to cauterize the incision on [target]'s face and neck with \the [tool]." , \ - "You are beginning to cauterize the incision on [target]'s face and neck with \the [tool].") + user.visible_message("[user] is beginning to cauterize the incision on [target]'s face and neck with \the [tool]." , \ + "You are beginning to cauterize the incision on [target]'s face and neck with \the [tool].") ..() /datum/surgery_step/face/cauterize/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] cauterizes the incision on [target]'s face and neck with \the [tool].", \ - "You cauterize the incision on [target]'s face and neck with \the [tool].") + user.visible_message("[user] cauterizes the incision on [target]'s face and neck with \the [tool].", \ + "You cauterize the incision on [target]'s face and neck with \the [tool].") affected.open = 0 affected.status &= ~ORGAN_BLEEDING if (target.op_stage.face == 3) @@ -152,6 +154,6 @@ /datum/surgery_step/face/cauterize/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, leaving a small burn on [target]'s face with \the [tool]!", \ - "Your hand slips, leaving a small burn on [target]'s face with \the [tool]!") + user.visible_message("[user]'s hand slips, leaving a small burn on [target]'s face with \the [tool]!", \ + "Your hand slips, leaving a small burn on [target]'s face with \the [tool]!") target.apply_damage(4, BURN, affected) \ No newline at end of file diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index 02003dd950..a5ccd15050 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -20,6 +20,8 @@ return 0 if (affected.robotic >= ORGAN_ROBOT) return 0 + if(coverage_check(user, target, affected, tool)) + return 0 return 1 /////////////////////////////////////////////////////////////// @@ -51,8 +53,8 @@ /datum/surgery_step/generic/cut_open/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has made an incision on [target]'s [affected.name] with \the [tool].", \ - "You have made an incision on [target]'s [affected.name] with \the [tool].",) + user.visible_message("[user] has made an incision on [target]'s [affected.name] with \the [tool].", \ + "You have made an incision on [target]'s [affected.name] with \the [tool].",) affected.open = 1 if(istype(target) && target.should_have_organ(O_HEART)) @@ -62,8 +64,8 @@ /datum/surgery_step/generic/cut_open/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, slicing open [target]'s [affected.name] in the wrong place with \the [tool]!", \ - "Your hand slips, slicing open [target]'s [affected.name] in the wrong place with \the [tool]!") + user.visible_message("[user]'s hand slips, slicing open [target]'s [affected.name] in the wrong place with \the [tool]!", \ + "Your hand slips, slicing open [target]'s [affected.name] in the wrong place with \the [tool]!") affected.createwound(CUT, 10) /////////////////////////////////////////////////////////////// @@ -96,8 +98,8 @@ /datum/surgery_step/generic/cut_with_laser/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has made a bloodless incision on [target]'s [affected.name] with \the [tool].", \ - "You have made a bloodless incision on [target]'s [affected.name] with \the [tool].",) + user.visible_message("[user] has made a bloodless incision on [target]'s [affected.name] with \the [tool].", \ + "You have made a bloodless incision on [target]'s [affected.name] with \the [tool].",) //Could be cleaner ... affected.open = 1 @@ -107,8 +109,8 @@ /datum/surgery_step/generic/cut_with_laser/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips as the blade sputters, searing a long gash in [target]'s [affected.name] with \the [tool]!", \ - "Your hand slips as the blade sputters, searing a long gash in [target]'s [affected.name] with \the [tool]!") + user.visible_message("[user]'s hand slips as the blade sputters, searing a long gash in [target]'s [affected.name] with \the [tool]!", \ + "Your hand slips as the blade sputters, searing a long gash in [target]'s [affected.name] with \the [tool]!") affected.createwound(CUT, 7.5) affected.createwound(BURN, 12.5) @@ -140,8 +142,8 @@ /datum/surgery_step/generic/incision_manager/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has constructed a prepared incision on and within [target]'s [affected.name] with \the [tool].", \ - "You have constructed a prepared incision on and within [target]'s [affected.name] with \the [tool].",) + user.visible_message("[user] has constructed a prepared incision on and within [target]'s [affected.name] with \the [tool].", \ + "You have constructed a prepared incision on and within [target]'s [affected.name] with \the [tool].",) affected.open = 1 if(istype(target) && target.should_have_organ(O_HEART)) @@ -153,8 +155,8 @@ /datum/surgery_step/generic/incision_manager/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand jolts as the system sparks, ripping a gruesome hole in [target]'s [affected.name] with \the [tool]!", \ - "Your hand jolts as the system sparks, ripping a gruesome hole in [target]'s [affected.name] with \the [tool]!") + user.visible_message("[user]'s hand jolts as the system sparks, ripping a gruesome hole in [target]'s [affected.name] with \the [tool]!", \ + "Your hand jolts as the system sparks, ripping a gruesome hole in [target]'s [affected.name] with \the [tool]!") affected.createwound(CUT, 20) affected.createwound(BURN, 15) @@ -186,15 +188,15 @@ /datum/surgery_step/generic/clamp_bleeders/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] clamps bleeders in [target]'s [affected.name] with \the [tool].", \ - "You clamp bleeders in [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] clamps bleeders in [target]'s [affected.name] with \the [tool].", \ + "You clamp bleeders in [target]'s [affected.name] with \the [tool].") affected.organ_clamp() spread_germs_to_organ(affected, user) /datum/surgery_step/generic/clamp_bleeders/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, tearing blood vessals and causing massive bleeding in [target]'s [affected.name] with \the [tool]!", \ - "Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!",) + user.visible_message("[user]'s hand slips, tearing blood vessals and causing massive bleeding in [target]'s [affected.name] with \the [tool]!", \ + "Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!",) affected.createwound(CUT, 10) /////////////////////////////////////////////////////////////// @@ -233,27 +235,27 @@ /datum/surgery_step/generic/retract_skin/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user] keeps the incision open on [target]'s [affected.name] with \the [tool]." - var/self_msg = "You keep the incision open on [target]'s [affected.name] with \the [tool]." + var/msg = "[user] keeps the incision open on [target]'s [affected.name] with \the [tool]." + var/self_msg = "You keep the incision open on [target]'s [affected.name] with \the [tool]." if (target_zone == BP_TORSO) - msg = "[user] keeps the ribcage open on [target]'s torso with \the [tool]." - self_msg = "You keep the ribcage open on [target]'s torso with \the [tool]." + msg = "[user] keeps the ribcage open on [target]'s torso with \the [tool]." + self_msg = "You keep the ribcage open on [target]'s torso with \the [tool]." if (target_zone == BP_GROIN) - msg = "[user] keeps the incision open on [target]'s lower abdomen with \the [tool]." - self_msg = "You keep the incision open on [target]'s lower abdomen with \the [tool]." + msg = "[user] keeps the incision open on [target]'s lower abdomen with \the [tool]." + self_msg = "You keep the incision open on [target]'s lower abdomen with \the [tool]." user.visible_message(msg, self_msg) affected.open = 2 /datum/surgery_step/generic/retract_skin/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - var/msg = "[user]'s hand slips, tearing the edges of the incision on [target]'s [affected.name] with \the [tool]!" - var/self_msg = "Your hand slips, tearing the edges of the incision on [target]'s [affected.name] with \the [tool]!" + var/msg = "[user]'s hand slips, tearing the edges of the incision on [target]'s [affected.name] with \the [tool]!" + var/self_msg = "Your hand slips, tearing the edges of the incision on [target]'s [affected.name] with \the [tool]!" if (target_zone == BP_TORSO) - msg = "[user]'s hand slips, damaging several organs in [target]'s torso with \the [tool]!" - self_msg = "Your hand slips, damaging several organs in [target]'s torso with \the [tool]!" + msg = "[user]'s hand slips, damaging several organs in [target]'s torso with \the [tool]!" + self_msg = "Your hand slips, damaging several organs in [target]'s torso with \the [tool]!" if (target_zone == BP_GROIN) - msg = "[user]'s hand slips, damaging several organs in [target]'s lower abdomen with \the [tool]!" - self_msg = "Your hand slips, damaging several organs in [target]'s lower abdomen with \the [tool]!" + msg = "[user]'s hand slips, damaging several organs in [target]'s lower abdomen with \the [tool]!" + self_msg = "Your hand slips, damaging several organs in [target]'s lower abdomen with \the [tool]!" user.visible_message(msg, self_msg) target.apply_damage(12, BRUTE, affected, sharp=1) @@ -286,16 +288,16 @@ /datum/surgery_step/generic/cauterize/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] cauterizes the incision on [target]'s [affected.name] with \the [tool].", \ - "You cauterize the incision on [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] cauterizes the incision on [target]'s [affected.name] with \the [tool].", \ + "You cauterize the incision on [target]'s [affected.name] with \the [tool].") affected.open = 0 affected.germ_level = 0 affected.status &= ~ORGAN_BLEEDING /datum/surgery_step/generic/cauterize/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!", \ - "Your hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!") + user.visible_message("[user]'s hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!", \ + "Your hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!") target.apply_damage(3, BURN, affected) /////////////////////////////////////////////////////////////// @@ -331,13 +333,13 @@ /datum/surgery_step/generic/amputate/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] amputates [target]'s [affected.name] at the [affected.amputation_point] with \the [tool].", \ - "You amputate [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] amputates [target]'s [affected.name] at the [affected.amputation_point] with \the [tool].", \ + "You amputate [target]'s [affected.name] with \the [tool].") affected.droplimb(1,DROPLIMB_EDGE) /datum/surgery_step/generic/amputate/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!", \ - "Your hand slips, sawwing through the bone in [target]'s [affected.name] with \the [tool]!") + user.visible_message("[user]'s hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!", \ + "Your hand slips, sawwing through the bone in [target]'s [affected.name] with \the [tool]!") affected.createwound(CUT, 30) affected.fracture() diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index 58a322febd..e7e6eb6b4b 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -6,37 +6,40 @@ /datum/surgery_step/cavity priority = 1 - can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - if(!hasorgans(target)) - return 0 - var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && affected.open == (affected.encased ? 3 : 2) && !(affected.status & ORGAN_BLEEDING) - proc/get_max_wclass(var/obj/item/organ/external/affected) - switch (affected.organ_tag) - if (BP_HEAD) - return ITEMSIZE_TINY - if (BP_TORSO) - return ITEMSIZE_NORMAL - if (BP_GROIN) - return ITEMSIZE_SMALL +/datum/surgery_step/cavity/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if(!hasorgans(target)) return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + return affected && affected.open == (affected.encased ? 3 : 2) && !(affected.status & ORGAN_BLEEDING) - proc/get_cavity(var/obj/item/organ/external/affected) - switch (affected.organ_tag) - if (BP_HEAD) - return "cranial" - if (BP_TORSO) - return "thoracic" - if (BP_GROIN) - return "abdominal" - return "" +/datum/surgery_step/cavity/proc/get_max_wclass(var/obj/item/organ/external/affected) + switch (affected.organ_tag) + if (BP_HEAD) + return ITEMSIZE_TINY + if (BP_TORSO) + return ITEMSIZE_NORMAL + if (BP_GROIN) + return ITEMSIZE_SMALL + return 0 - fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!", \ - "Your hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!") - affected.createwound(CUT, 20) +/datum/surgery_step/cavity/proc/get_cavity(var/obj/item/organ/external/affected) + switch (affected.organ_tag) + if (BP_HEAD) + return "cranial" + if (BP_TORSO) + return "thoracic" + if (BP_GROIN) + return "abdominal" + return "" + +/datum/surgery_step/cavity/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) + user.visible_message("[user]'s hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!", \ + "Your hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!") + affected.createwound(CUT, 20) /////////////////////////////////////////////////////////////// // Space Making Surgery @@ -52,23 +55,23 @@ min_duration = 60 max_duration = 80 - can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - if(..()) - var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && !affected.cavity - - begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) +/datum/surgery_step/cavity/make_space/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if(..()) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] starts making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", \ - "You start making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." ) - target.custom_pain("The pain in your chest is living hell!",1) - affected.cavity = 1 - ..() + return affected && !affected.cavity - end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - user.visible_message("[user] makes some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", \ - "You make some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." ) +/datum/surgery_step/cavity/make_space/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message("[user] starts making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", \ + "You start making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." ) + target.custom_pain("The pain in your chest is living hell!",1) + affected.cavity = 1 + ..() + +/datum/surgery_step/cavity/make_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) + user.visible_message("[user] makes some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", \ + "You make some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." ) /////////////////////////////////////////////////////////////// // Cavity Closing Surgery @@ -101,8 +104,8 @@ /datum/surgery_step/cavity/close_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - user.visible_message("[user] mends [target]'s [get_cavity(affected)] cavity walls with \the [tool].", \ - " You mend[target]'s [get_cavity(affected)] cavity walls with \the [tool]." ) + user.visible_message("[user] mends [target]'s [get_cavity(affected)] cavity walls with \the [tool].", \ + " You mend[target]'s [get_cavity(affected)] cavity walls with \the [tool]." ) /////////////////////////////////////////////////////////////// // Item Implantation Surgery @@ -130,18 +133,18 @@ /datum/surgery_step/cavity/place_item/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] starts putting \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \ - "You start putting \the [tool] inside [target]'s [get_cavity(affected)] cavity." ) //Nobody will probably ever see this, but I made these two blue. ~CK + user.visible_message("[user] starts putting \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \ + "You start putting \the [tool] inside [target]'s [get_cavity(affected)] cavity." ) //Nobody will probably ever see this, but I made these two blue. ~CK target.custom_pain("The pain in your chest is living hell!",1) ..() /datum/surgery_step/cavity/place_item/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - user.visible_message("[user] puts \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \ - "You put \the [tool] inside [target]'s [get_cavity(affected)] cavity." ) + user.visible_message("[user] puts \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \ + "You put \the [tool] inside [target]'s [get_cavity(affected)] cavity." ) if (tool.w_class > get_max_wclass(affected)/2 && prob(50) && (affected.robotic < ORGAN_ROBOT)) - user << " You tear some blood vessels trying to fit such a big object in this cavity." + to_chat(user, " You tear some blood vessels trying to fit such a big object in this cavity.") var/datum/wound/internal_bleeding/I = new (10) affected.wounds += I affected.owner.custom_pain("You feel something rip in your [affected.name]!", 1) @@ -176,8 +179,8 @@ /datum/surgery_step/cavity/implant_removal/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] starts poking around inside [target]'s [affected.name] with \the [tool].", \ - "You start poking around inside [target]'s [affected.name] with \the [tool]." ) + user.visible_message("[user] starts poking around inside [target]'s [affected.name] with \the [tool].", \ + "You start poking around inside [target]'s [affected.name] with \the [tool]." ) target.custom_pain("The pain in your [affected.name] is living hell!",1) ..() @@ -200,8 +203,8 @@ find_prob +=50 if (prob(find_prob)) - user.visible_message("[user] takes something out of incision on [target]'s [affected.name] with \the [tool]!", \ - "You take [obj] out of incision on [target]'s [affected.name]s with \the [tool]!" ) + user.visible_message("[user] takes something out of incision on [target]'s [affected.name] with \the [tool]!", \ + "You take [obj] out of incision on [target]'s [affected.name]s with \the [tool]!" ) affected.implants -= obj BITSET(target.hud_updateflag, IMPLOYAL_HUD) @@ -223,11 +226,11 @@ imp.implanted = 0 else if(istype(tool,/obj/item/device/nif)){var/obj/item/device/nif/N = tool;N.unimplant(target)} //VOREStation Add - NIF support else - user.visible_message("[user] removes \the [tool] from [target]'s [affected.name].", \ - "There's something inside [target]'s [affected.name], but you just missed it this time." ) + user.visible_message("[user] removes \the [tool] from [target]'s [affected.name].", \ + "There's something inside [target]'s [affected.name], but you just missed it this time." ) else - user.visible_message("[user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.", \ - "You could not find anything inside [target]'s [affected.name]." ) + user.visible_message("[user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.", \ + "You could not find anything inside [target]'s [affected.name]." ) /datum/surgery_step/cavity/implant_removal/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) ..() @@ -237,7 +240,7 @@ fail_prob += 100 - tool_quality(tool) if (prob(fail_prob)) var/obj/item/weapon/implant/imp = affected.implants[1] - user.visible_message(" Something beeps inside [target]'s [affected.name]!") + user.visible_message(" Something beeps inside [target]'s [affected.name]!") playsound(imp.loc, 'sound/items/countdown.ogg', 75, 1, -3) spawn(25) imp.activate() diff --git a/code/modules/surgery/neck.dm b/code/modules/surgery/neck.dm index ec9803309f..6b970ad4c2 100644 --- a/code/modules/surgery/neck.dm +++ b/code/modules/surgery/neck.dm @@ -14,6 +14,8 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) if (!affected || (affected.robotic >= ORGAN_ROBOT) || !(affected.open >= 3)) return 0 + if(coverage_check(user, target, affected, tool)) + return 0 return target_zone == BP_HEAD ///////////////////////////// @@ -39,14 +41,14 @@ ..() /datum/surgery_step/brainstem/mend_vessels/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] has mended the blood vessels on [target]'s brainstem with \the [tool]." , \ - " You have mended the blood vessels on [target]'s brainstem with \the [tool].",) + user.visible_message("[user] has mended the blood vessels on [target]'s brainstem with \the [tool]." , \ + " You have mended the blood vessels on [target]'s brainstem with \the [tool].",) target.op_stage.brainstem = 1 /datum/surgery_step/brainstem/mend_vessels/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, tearing at [target]'s brainstem with \the [tool]!" , \ - "Your hand slips, tearing at [target]'s brainstem with \the [tool]!" ) + user.visible_message("[user]'s hand slips, tearing at [target]'s brainstem with \the [tool]!" , \ + "Your hand slips, tearing at [target]'s brainstem with \the [tool]!" ) affected.createwound(PIERCE, 10) target.AdjustParalysis(10) @@ -77,16 +79,16 @@ /datum/surgery_step/brainstem/drill_vertebrae/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has drilled around [target]'s brainstem with \the [tool]." , \ - " You have drilled around [target]'s brainstem with \the [tool].",) + user.visible_message("[user] has drilled around [target]'s brainstem with \the [tool]." , \ + " You have drilled around [target]'s brainstem with \the [tool].",) target.AdjustParalysis(10) //We're getting Invasive here. This only ticks down when the person is alive, so it's a good side-effect for this step. Rattling the braincase with a drill is not optimal. target.op_stage.brainstem = 2 affected.fracture() //Does not apply damage, simply breaks it if it wasn't already. Doesn't stop a defib on its own. /datum/surgery_step/brainstem/drill_vertebrae/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, shredding [target]'s brainstem with \the [tool]!" , \ - "Your hand slips, shredding [target]'s brainstem with \the [tool]!" ) + user.visible_message("[user]'s hand slips, shredding [target]'s brainstem with \the [tool]!" , \ + "Your hand slips, shredding [target]'s brainstem with \the [tool]!" ) affected.createwound(PIERCE, 10) target.AdjustParalysis(15) spawn() @@ -117,15 +119,15 @@ ..() /datum/surgery_step/brainstem/clean_chips/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] has cleaned around [target]'s brainstem with \the [tool]." , \ - " You have cleaned around [target]'s brainstem with \the [tool].",) + user.visible_message("[user] has cleaned around [target]'s brainstem with \the [tool]." , \ + " You have cleaned around [target]'s brainstem with \the [tool].",) target.AdjustParalysis(10) //Still invasive. target.op_stage.brainstem = 3 /datum/surgery_step/brainstem/clean_chips/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, gouging [target]'s brainstem with \the [tool]!" , \ - "Your hand slips, gouging [target]'s brainstem with \the [tool]!" ) + user.visible_message("[user]'s hand slips, gouging [target]'s brainstem with \the [tool]!" , \ + "Your hand slips, gouging [target]'s brainstem with \the [tool]!" ) affected.createwound(CUT, 5) target.AdjustParalysis(10) spawn() @@ -156,16 +158,16 @@ ..() /datum/surgery_step/brainstem/mend_cord/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] has fused [target]'s spinal cord with \the [tool]." , \ - " You have fused [target]'s spinal cord with \the [tool].",) + user.visible_message("[user] has fused [target]'s spinal cord with \the [tool]." , \ + " You have fused [target]'s spinal cord with \the [tool].",) target.op_stage.brainstem = 4 target.AdjustParalysis(5) target.add_modifier(/datum/modifier/franken_sickness, 20 MINUTES) /datum/surgery_step/brainstem/mend_cord/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, tearing at [target]'s spinal cord with \the [tool]!" , \ - "Your hand slips, tearing at [target]'s spinal cord with \the [tool]!" ) + user.visible_message("[user]'s hand slips, tearing at [target]'s spinal cord with \the [tool]!" , \ + "Your hand slips, tearing at [target]'s spinal cord with \the [tool]!" ) affected.createwound(PIERCE, 5) target.AdjustParalysis(20) spawn() @@ -195,15 +197,15 @@ ..() /datum/surgery_step/brainstem/mend_vertebrae/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] has mended [target]'s vertebrae with \the [tool]." , \ - " You have mended [target]'s vertebrae with \the [tool].",) + user.visible_message("[user] has mended [target]'s vertebrae with \the [tool]." , \ + " You have mended [target]'s vertebrae with \the [tool].",) target.can_defib = 1 target.op_stage.brainstem = 5 /datum/surgery_step/brainstem/mend_vertebrae/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, tearing at [target]'s spinal cord with \the [tool]!" , \ - "Your hand slips, tearing at [target]'s spinal cord with \the [tool]!" ) + user.visible_message("[user]'s hand slips, tearing at [target]'s spinal cord with \the [tool]!" , \ + "Your hand slips, tearing at [target]'s spinal cord with \the [tool]!" ) affected.createwound(PIERCE, 5) target.AdjustParalysis(15) spawn() @@ -234,15 +236,15 @@ ..() /datum/surgery_step/brainstem/realign_tissue/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] has realigned the tissues in [target]'s skull back into place with \the [tool]." , \ - " You have realigned the tissues in [target]'s skull back into place with \the [tool].",) + user.visible_message("[user] has realigned the tissues in [target]'s skull back into place with \the [tool]." , \ + " You have realigned the tissues in [target]'s skull back into place with \the [tool].",) target.AdjustParalysis(5) //I n v a s i v e target.op_stage.brainstem = 0 //The cycle begins anew. /datum/surgery_step/brainstem/realign_tissue/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, gouging [target]'s brainstem with \the [tool]!" , \ - "Your hand slips, gouging [target]'s brainstem with \the [tool]!" ) + user.visible_message("[user]'s hand slips, gouging [target]'s brainstem with \the [tool]!" , \ + "Your hand slips, gouging [target]'s brainstem with \the [tool]!" ) affected.createwound(CUT, 5) target.AdjustParalysis(30) spawn() diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 8d261f7535..c09d10bebe 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -10,6 +10,8 @@ return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && affected.open == (affected.encased ? 3 : 2) @@ -289,11 +291,11 @@ return 0 if((affected.robotic >= ORGAN_ROBOT) && !(O.robotic >= ORGAN_ROBOT)) - user << "You cannot install a naked organ into a robotic body." + to_chat(user, "You cannot install a naked organ into a robotic body.") return SURGERY_FAILURE if(!target.species) - user << "You have no idea what species this person is. Report this on the bug tracker." + to_chat(user, "You have no idea what species this person is. Report this on the bug tracker.") return SURGERY_FAILURE var/o_is = (O.gender == PLURAL) ? "are" : "is" @@ -301,20 +303,20 @@ var/o_do = (O.gender == PLURAL) ? "don't" : "doesn't" if(O.damage > (O.max_damage * 0.75)) - user << "\The [O.organ_tag] [o_is] in no state to be transplanted." + to_chat(user, "\The [O.organ_tag] [o_is] in no state to be transplanted.") return SURGERY_FAILURE if(!target.internal_organs_by_name[O.organ_tag]) organ_missing = 1 else - user << "\The [target] already has [o_a][O.organ_tag]." + to_chat(user, "\The [target] already has [o_a][O.organ_tag].") return SURGERY_FAILURE if(O && affected.organ_tag == O.parent_organ) organ_compatible = 1 else - user << "\The [O.organ_tag] [o_do] normally go in \the [affected.name]." + to_chat(user, "\The [O.organ_tag] [o_do] normally go in \the [affected.name].") return SURGERY_FAILURE return ..() && organ_missing && organ_compatible diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm index 782074b2ad..fe7d004dc4 100644 --- a/code/modules/surgery/other.dm +++ b/code/modules/surgery/other.dm @@ -25,6 +25,8 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) if(!affected) return + if(coverage_check(user, target, affected, tool)) + return 0 var/internal_bleeding = 0 for(var/datum/wound/W in affected.wounds) if(W.internal) internal_bleeding = 1 @@ -41,8 +43,8 @@ /datum/surgery_step/fix_vein/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has patched the damaged vein in [target]'s [affected.name] with \the [tool].", \ - "You have patched the damaged vein in [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] has patched the damaged vein in [target]'s [affected.name] with \the [tool].", \ + "You have patched the damaged vein in [target]'s [affected.name] with \the [tool].") for(var/datum/wound/W in affected.wounds) if(W.internal) affected.wounds -= W @@ -51,8 +53,8 @@ /datum/surgery_step/fix_vein/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \ - "Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") + user.visible_message("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \ + "Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") affected.take_damage(5, 0) /////////////////////////////////////////////////////////////// @@ -80,6 +82,8 @@ return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && affected.open >= 2 && (affected.status & ORGAN_DEAD) @@ -92,14 +96,14 @@ /datum/surgery_step/fix_dead_tissue/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user] has cut away necrotic tissue in [target]'s [affected.name] with \the [tool].", \ - "You have cut away necrotic tissue in [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] has cut away necrotic tissue in [target]'s [affected.name] with \the [tool].", \ + "You have cut away necrotic tissue in [target]'s [affected.name] with \the [tool].") affected.open = 3 /datum/surgery_step/fix_dead_tissue/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - user.visible_message("[user]'s hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!", \ - "Your hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!") + user.visible_message("[user]'s hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!", \ + "Your hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!") affected.createwound(CUT, 20, 1) /////////////////////////////////////////////////////////////// @@ -136,6 +140,8 @@ return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 return affected && affected.open == 3 && (affected.status & ORGAN_DEAD) /datum/surgery_step/treat_necrosis/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) @@ -158,8 +164,8 @@ affected.status &= ~ORGAN_DEAD affected.owner.update_icons_body() - user.visible_message("[user] applies [trans] units of the solution to affected tissue in [target]'s [affected.name].", \ - "You apply [trans] units of the solution to affected tissue in [target]'s [affected.name] with \the [tool].") + user.visible_message("[user] applies [trans] units of the solution to affected tissue in [target]'s [affected.name].", \ + "You apply [trans] units of the solution to affected tissue in [target]'s [affected.name] with \the [tool].") /datum/surgery_step/treat_necrosis/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) @@ -171,8 +177,8 @@ var/trans = container.reagents.trans_to_mob(target, container.amount_per_transfer_from_this, CHEM_BLOOD) - user.visible_message("[user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!" , \ - "Your hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!") + user.visible_message("[user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!" , \ + "Your hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!") //no damage or anything, just wastes medicine @@ -219,7 +225,7 @@ rig = target.belt if(!istype(rig)) return - rig.reset() + rig.cut_suit() user.visible_message("[user] has cut through the support systems of \the [rig] on [target] with \the [tool].", \ "You have cut through the support systems of \the [rig] on [target] with \the [tool].") diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index a1de76461c..0f59be6a2b 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -20,6 +20,8 @@ return 0 if (!(affected.robotic == ORGAN_ROBOT || affected.robotic == ORGAN_LIFELIKE)) //VOREStation Edit - No good on ORGAN_NANOFORM return 0 + if(coverage_check(user, target, affected, tool)) + return 0 return 1 /////////////////////////////////////////////////////////////// @@ -423,20 +425,20 @@ /* VOREStation Edit - Don't worry about it. We can put these in regardless, because resleeving might make it useful after. if(!M.brainmob || !M.brainmob.client || !M.brainmob.ckey || M.brainmob.stat >= DEAD) - user << "That brain is not usable." + to_chat(user, "That brain is not usable.") return SURGERY_FAILURE */ if(!(affected.robotic >= ORGAN_ROBOT)) - user << "You cannot install a computer brain into a meat skull." + to_chat(user, "You cannot install a computer brain into a meat skull.") return SURGERY_FAILURE if(!target.should_have_organ("brain")) - user << "You're pretty sure [target.species.name_plural] don't normally have a brain." + to_chat(user, "You're pretty sure [target.species.name_plural] don't normally have a brain.") return SURGERY_FAILURE if(!isnull(target.internal_organs["brain"])) - user << "Your subject already has a brain." + to_chat(user, "Your subject already has a brain.") return SURGERY_FAILURE return 1 @@ -481,3 +483,96 @@ /datum/surgery_step/robotics/install_mmi/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) user.visible_message("[user]'s hand slips.", \ "Your hand slips.") + +/* + * Install a Diona Nymph into a Nymph Mech + */ + +/datum/surgery_step/robotics/install_nymph + allowed_tools = list( + /obj/item/weapon/holder/diona = 100 + ) + + min_duration = 60 + max_duration = 80 + +/datum/surgery_step/robotics/install_nymph/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if(target_zone != BP_TORSO) + return + + var/obj/item/weapon/holder/diona/N = tool + var/obj/item/organ/external/affected = target.get_organ(target_zone) + + if(!(affected && affected.open == 3)) + return 0 + + if(!istype(N)) + return 0 + + if(!N.held_mob.client || N.held_mob.stat >= DEAD) + to_chat(user, "That nymph is not viable.") + return SURGERY_FAILURE + + if(!(affected.robotic >= ORGAN_ROBOT)) + to_chat(user, "You cannot install a nymph into a meat puppet.") + return SURGERY_FAILURE + + if(!(affected.model != "Skrellian Exoskeleton")) + to_chat(user, "You're fairly certain a nymph can't pilot a normal robot.") + return SURGERY_FAILURE + + if(!target.should_have_organ("brain")) + to_chat(user, "You're pretty sure [target.species.name_plural] don't normally have a brain.") + return SURGERY_FAILURE + + if(!isnull(target.internal_organs["brain"])) + to_chat(user, "Your subject already has a cephalon.") + return SURGERY_FAILURE + + return 1 + +/datum/surgery_step/robotics/install_nymph/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message("[user] starts setting \the [tool] into [target]'s [affected.name].", \ + "You start setting \the [tool] into [target]'s [affected.name].") + ..() + +/datum/surgery_step/robotics/install_nymph/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message("[user] has installed \the [tool] into [target]'s [affected.name].", \ + "You have installed \the [tool] into [target]'s [affected.name].") + + var/obj/item/weapon/holder/diona/N = tool + var/obj/item/organ/internal/brain/cephalon/cephalon = new(target, 1) + target.internal_organs_by_name["brain"] = cephalon + var/mob/living/carbon/alien/diona/D = N.held_mob + user.drop_from_inventory(tool) + + if(D && D.mind) + D.mind.transfer_to(target) + target.languages |= D.languages + + qdel(D) + + target.species = GLOB.all_species[SPECIES_DIONA] + + target.verbs |= /mob/living/carbon/human/proc/diona_split_nymph + target.verbs |= /mob/living/carbon/human/proc/regenerate + + spawn(0) //Name yourself on your own damn time + var/new_name = "" + while(!new_name) + if(!target) return + var/try_name = input(target,"Pick a name for your new form!", "New Name", target.name) + var/clean_name = sanitizeName(try_name, allow_numbers = TRUE) + if(clean_name) + var/okay = alert(target,"New name will be '[clean_name]', ok?", "Confirmation","Cancel","Ok") + if(okay == "Ok") + new_name = clean_name + + target.name = new_name + target.real_name = target.name + +/datum/surgery_step/robotics/install_nymph/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + user.visible_message("[user]'s hand slips.", \ + "Your hand slips.") diff --git a/code/modules/surgery/slimes.dm b/code/modules/surgery/slimes.dm index e049349f83..e73633397a 100644 --- a/code/modules/surgery/slimes.dm +++ b/code/modules/surgery/slimes.dm @@ -29,13 +29,13 @@ "You start cutting through [target]'s flesh with \the [tool].") /datum/surgery_step/slime/cut_flesh/end_step(mob/living/user, mob/living/simple_mob/slime/target, target_zone, obj/item/tool) - user.visible_message("[user] cuts through [target]'s flesh with \the [tool].", \ - "You cut through [target]'s flesh with \the [tool], revealing its silky innards.") + user.visible_message("[user] cuts through [target]'s flesh with \the [tool].", \ + "You cut through [target]'s flesh with \the [tool], revealing its silky innards.") target.core_removal_stage = 1 /datum/surgery_step/slime/cut_flesh/fail_step(mob/living/user, mob/living/simple_mob/slime/target, target_zone, obj/item/tool) - user.visible_message("[user]'s hand slips, tearing [target]'s flesh with \the [tool]!", \ - "Your hand slips, tearing [target]'s flesh with \the [tool]!") + user.visible_message("[user]'s hand slips, tearing [target]'s flesh with \the [tool]!", \ + "Your hand slips, tearing [target]'s flesh with \the [tool]!") @@ -57,13 +57,13 @@ "You start cutting [target]'s silky innards apart with \the [tool].") /datum/surgery_step/slime/cut_innards/end_step(mob/living/user, mob/living/simple_mob/slime/target, target_zone, obj/item/tool) - user.visible_message("[user] cuts [target]'s innards apart with \the [tool], exposing the cores.", \ - "You cut [target]'s innards apart with \the [tool], exposing the cores.") + user.visible_message("[user] cuts [target]'s innards apart with \the [tool], exposing the cores.", \ + "You cut [target]'s innards apart with \the [tool], exposing the cores.") target.core_removal_stage = 2 /datum/surgery_step/slime/cut_innards/fail_step(mob/living/user, mob/living/simple_mob/slime/target, target_zone, obj/item/tool) - user.visible_message("[user]'s hand slips, tearing [target]'s innards with \the [tool]!", \ - "Your hand slips, tearing [target]'s innards with \the [tool]!") + user.visible_message("[user]'s hand slips, tearing [target]'s innards with \the [tool]!", \ + "Your hand slips, tearing [target]'s innards with \the [tool]!") @@ -85,8 +85,8 @@ /datum/surgery_step/slime/saw_core/end_step(mob/living/user, mob/living/simple_mob/slime/target, target_zone, obj/item/tool) target.cores-- - user.visible_message("[user] cuts out one of [target]'s cores with \the [tool].",, \ - "You cut out one of [target]'s cores with \the [tool]. [target.cores] cores left.") + user.visible_message("[user] cuts out one of [target]'s cores with \the [tool].",, \ + "You cut out one of [target]'s cores with \the [tool]. [target.cores] cores left.") if(target.cores >= 0) new target.coretype(target.loc) @@ -96,5 +96,5 @@ /datum/surgery_step/slime/saw_core/fail_step(mob/living/user, mob/living/simple_mob/slime/target, target_zone, obj/item/tool) var/datum/gender/T = gender_datums[user.get_visible_gender()] - user.visible_message("[user]'s hand slips, causing [T.him] to miss the core!", \ - "Your hand slips, causing you to miss the core!") \ No newline at end of file + user.visible_message("[user]'s hand slips, causing [T.him] to miss the core!", \ + "Your hand slips, causing you to miss the core!") \ No newline at end of file diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 837e10a5a0..03fb7ad40f 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -67,6 +67,21 @@ return 1 +// Let's check if stuff blocks us from doing surgery on them +// TODO: make it based on area coverage rather than just forbid spacesuits? +// Returns true if target organ is covered +/datum/surgery_step/proc/coverage_check(mob/living/user, mob/living/carbon/human/target, obj/item/organ/external/affected, obj/item/tool) + if(!affected) + return FALSE + + if(affected.organ_tag == BP_HEAD) + if(target.head && istype(target.head,/obj/item/clothing/head/helmet/space)) + return TRUE + else + if(target.wear_suit && istype(target.wear_suit,/obj/item/clothing/suit/space)) + return TRUE + + return FALSE // checks whether this step can be applied with the given user and target /datum/surgery_step/proc/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) @@ -110,22 +125,19 @@ return 0 if(!ishuman(M)) return 1 - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting) - if(affected) - for(var/datum/surgery_step/S in surgery_steps) - if(!affected.open && S.req_open) - return 0 - return 0 + + return 1 /obj/item/proc/do_surgery(mob/living/carbon/M, mob/living/user) + if(!can_do_surgery(M, user)) + return 0 if(!istype(M)) return 0 if (user.a_intent == I_HURT) //check for Hippocratic Oath return 0 var/zone = user.zone_sel.selecting if(zone in M.op_stage.in_progress) //Can't operate on someone repeatedly. - user << "You can't operate on this area while surgery is already in progress." + to_chat(user, "You can't operate on this area while surgery is already in progress.") return 1 for(var/datum/surgery_step/S in surgery_steps) //check if tool is right or close enough and if this step is possible diff --git a/code/modules/tables/flipping.dm b/code/modules/tables/flipping.dm index 5de58c1354..18bcc4fe58 100644 --- a/code/modules/tables/flipping.dm +++ b/code/modules/tables/flipping.dm @@ -22,7 +22,7 @@ return if(flipped < 0 || !flip(get_cardinal_dir(usr,src))) - usr << "It won't budge." + to_chat(usr, "It won't budge.") return usr.visible_message("[usr] flips \the [src]!") @@ -39,7 +39,7 @@ var/obj/occupied = turf_is_crowded() if(occupied) - usr << "There's \a [occupied] in the way." + to_chat(usr, "There's \a [occupied] in the way.") return 0 var/list/L = list() @@ -65,7 +65,7 @@ return if (!unflipping_check()) - usr << "It won't budge." + to_chat(usr, "It won't budge.") return unflip() diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm index e28f54f9e4..1455fc0192 100644 --- a/code/modules/tables/interactions.dm +++ b/code/modules/tables/interactions.dm @@ -65,13 +65,11 @@ return ..() if(isrobot(user)) return - user.drop_item() - if (O.loc != src.loc) - step(O, get_dir(O, src)) + user.unEquip(O, 0, src.loc) return -/obj/structure/table/attackby(obj/item/W as obj, mob/user as mob) +/obj/structure/table/attackby(obj/item/W as obj, mob/user as mob, var/hit_modifier, var/click_parameters) if (!W) return // Handle harm intent grabbing/tabling. @@ -81,7 +79,7 @@ var/mob/living/M = G.affecting var/obj/occupied = turf_is_crowded() if(occupied) - user << "There's \a [occupied] in the way." + to_chat(user, "There's \a [occupied] in the way.") return if(!user.Adjacent(M)) return @@ -104,7 +102,7 @@ if(prob(2)) M.embed(S, def_zone = BP_HEAD) else - user << "You need a better grip to do that!" + to_chat(user, "You need a better grip to do that!") return else if(G.state > GRAB_AGGRESSIVE || world.time >= (G.last_action + UPGRADE_COOLDOWN)) M.forceMove(get_turf(src)) @@ -136,12 +134,52 @@ return if(can_plate && !material) - user << "There's nothing to put \the [W] on! Try adding plating to \the [src] first." + to_chat(user, "There's nothing to put \the [W] on! Try adding plating to \the [src] first.") return - if(item_place) - user.drop_item(src.loc) - return +// Placing stuff on tables + if(user.unEquip(W, 0, src.loc) && user.is_preference_enabled(/datum/client_preference/precision_placement)) + auto_align(W, click_parameters) + return 1 + +#define CELLS 8 //Amount of cells per row/column in grid +#define CELLSIZE (world.icon_size/CELLS) //Size of a cell in pixels +/* +Automatic alignment of items to an invisible grid, defined by CELLS and CELLSIZE. +Since the grid will be shifted to own a cell that is perfectly centered on the turf, we end up with two 'cell halves' +on edges of each row/column. +Each item defines a center_of_mass, which is the pixel of a sprite where its projected center of mass toward a turf +surface can be assumed. For a piece of paper, this will be in its center. For a bottle, it will be (near) the bottom +of the sprite. +auto_align() will then place the sprite so the defined center_of_mass is at the bottom left corner of the grid cell +closest to where the cursor has clicked on. +Note: This proc can be overwritten to allow for different types of auto-alignment. +*/ + +/obj/item/var/list/center_of_mass = list("x" = 16,"y" = 16) + +/obj/structure/table/proc/auto_align(obj/item/W, click_parameters) + if(!W.center_of_mass) + W.randpixel_xy() + return + + if(!click_parameters) + return + + var/list/mouse_control = params2list(click_parameters) + + var/mouse_x = text2num(mouse_control["icon-x"]) + var/mouse_y = text2num(mouse_control["icon-y"]) + + if(isnum(mouse_x) && isnum(mouse_y)) + var/cell_x = max(0, min(CELLS-1, round(mouse_x/CELLSIZE))) + var/cell_y = max(0, min(CELLS-1, round(mouse_y/CELLSIZE))) + + W.pixel_x = (CELLSIZE * (0.5 + cell_x)) - W.center_of_mass["x"] + W.pixel_y = (CELLSIZE * (0.5 + cell_y)) - W.center_of_mass["y"] + +#undef CELLS +#undef CELLSIZE /obj/structure/table/attack_tk() // no telehulk sorry return diff --git a/code/modules/tables/presets_vr.dm b/code/modules/tables/presets_vr.dm new file mode 100644 index 0000000000..085c4c6d8f --- /dev/null +++ b/code/modules/tables/presets_vr.dm @@ -0,0 +1,21 @@ +/obj/structure/table/darkglass + name = "darkglass table" + desc = "Shiny!" + icon = 'icons/obj/tables_vr.dmi' + icon_state = "darkglass_table_preview" + flipped = -1 + can_reinforce = FALSE + can_plate = FALSE + +/obj/structure/table/darkglass/New() + material = get_material_by_name("darkglass") + verbs -= /obj/structure/table/verb/do_flip + verbs -= /obj/structure/table/proc/do_put + + ..() + +/obj/structure/table/darkglass/dismantle(obj/item/weapon/wrench/W, mob/user) + to_chat(user, "You cannot dismantle \the [src].") + return +/obj/structure/table/alien/blue + icon = 'icons/turf/shuttle_alien_blue.dmi' diff --git a/code/modules/tables/rack.dm b/code/modules/tables/rack.dm index ad44431655..b07aa188b7 100644 --- a/code/modules/tables/rack.dm +++ b/code/modules/tables/rack.dm @@ -24,5 +24,5 @@ return /obj/structure/table/rack/holorack/dismantle(obj/item/weapon/wrench/W, mob/user) - user << "You cannot dismantle \the [src]." + to_chat(user, "You cannot dismantle \the [src].") return diff --git a/code/modules/telesci/quantum_pad.dm b/code/modules/telesci/quantum_pad.dm index f36c05ade4..91b5c19dc2 100644 --- a/code/modules/telesci/quantum_pad.dm +++ b/code/modules/telesci/quantum_pad.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/telescience.dmi' icon_state = "qpad-idle" anchored = TRUE - use_power = 1 + use_power = USE_POWER_IDLE idle_power_usage = 200 active_power_usage = 5000 circuit = /obj/item/weapon/circuitboard/quantumpad @@ -36,15 +36,15 @@ for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) E += M.rating power_efficiency = E - + E = 0 for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) E += C.rating - + teleport_speed = initial(teleport_speed) - teleport_speed -= (E*10) + teleport_speed = max(15, (teleport_speed - (E * 10))) teleport_cooldown = initial(teleport_cooldown) - teleport_cooldown -= (E * 100) + teleport_cooldown = max(50, (teleport_cooldown - (E * 100))) /obj/machinery/power/quantumpad/attackby(obj/item/I, mob/user, params) if(default_deconstruction_screwdriver(user, I)) @@ -86,7 +86,7 @@ if(panel_open) to_chat(user, "The panel must be closed before operating this machine!") return - + if(istype(get_area(src), /area/shuttle)) to_chat(user, "This is too unstable a platform for \the [src] to operate on!") return diff --git a/code/modules/telesci/telepad.dm b/code/modules/telesci/telepad.dm index 1440482241..a5ee8cbd93 100644 --- a/code/modules/telesci/telepad.dm +++ b/code/modules/telesci/telepad.dm @@ -5,7 +5,7 @@ icon = 'icons/obj/telescience.dmi' icon_state = "pad-idle" anchored = 1 - use_power = 1 + use_power = USE_POWER_IDLE circuit = /obj/item/weapon/circuitboard/telesci_pad idle_power_usage = 200 active_power_usage = 5000 @@ -41,7 +41,7 @@ if(istype(W, /obj/item/device/multitool)) var/obj/item/device/multitool/M = W M.connectable = src - user << "You save the data in the [M.name]'s buffer." + to_chat(user, "You save the data in the [M.name]'s buffer.") return 1 return ..() diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm index b2cc345b8c..db5459404d 100644 --- a/code/modules/telesci/telesci_computer.dm +++ b/code/modules/telesci/telesci_computer.dm @@ -28,6 +28,7 @@ var/powerCoefficient = 12.5 var/list/crystals = list() var/obj/item/device/gps/inserted_gps + var/overmap_range = 3 /obj/machinery/computer/telescience/Destroy() eject() @@ -38,7 +39,7 @@ /obj/machinery/computer/telescience/examine(mob/user) ..() - user << "There are [crystals.len ? crystals.len : "no"] bluespace crystal\s in the crystal slots." + to_chat(user, "There are [crystals.len ? crystals.len : "no"] bluespace crystal\s in the crystal slots.") /obj/machinery/computer/telescience/Initialize() . = ..() @@ -49,7 +50,7 @@ /obj/machinery/computer/telescience/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/weapon/ore/bluespace_crystal)) if(crystals.len >= max_crystals) - user << "There are not enough crystal slots." + to_chat(user, "There are not enough crystal slots.") return if(!user.unEquip(W)) return @@ -68,7 +69,7 @@ if(M.connectable && istype(M.connectable, /obj/machinery/telepad)) telepad = M.connectable M.connectable = null - user << "You upload the data from the [W.name]'s buffer." + to_chat(user, "You upload the data from the [W.name]'s buffer.") else return ..() @@ -105,9 +106,8 @@ if(telepad.panel_open) data["tempMsg"] = "Telepad undergoing physical maintenance operations." - data["sectorOptions"] = list() - for(var/z in using_map.player_levels) - data["sectorOptions"] += z + //We'll base our options on connected z's or overmap + data["sectorOptions"] = using_map.get_map_levels(z, TRUE, overmap_range) if(last_tele_data) data["lastTeleData"] = list() diff --git a/code/modules/turbolift/turbolift.dm b/code/modules/turbolift/turbolift.dm index ea9c9e13ab..db1ec9805e 100644 --- a/code/modules/turbolift/turbolift.dm +++ b/code/modules/turbolift/turbolift.dm @@ -14,7 +14,8 @@ var/fire_mode = FALSE // Flag to indicate firefighter mode is active. var/tmp/moving_upwards - var/tmp/busy + var/tmp/busy_state // Used for controller processing. + var/tmp/next_process // world.time process() should next do something /datum/turbolift/proc/emergency_stop() cancel_pending_floors() @@ -97,7 +98,42 @@ door.close() return +#define LIFT_MOVING 1 // Lift will try moving. +#define LIFT_WAITING_A 2 // Waiting 15ds after arrival to announce, then goto LIFT_WAITING_B +#define LIFT_WAITING_B 3 // Waiting floor_wait_delay after announcement before potentially moving again. + +/datum/turbolift/process() + if(world.time < next_process) + return + switch(busy_state) + if(LIFT_MOVING) + if(!do_move()) + if(target_floor) + // TODO - This logic copied from old processor. Would be better to have error states. + target_floor.ext_panel.reset() + target_floor = null + return PROCESS_KILL + else if(!next_process) + log_debug("Turbolift [src] do_move() returned 1 but next_process = null; busy_state=[busy_state]") + return PROCESS_KILL + if(LIFT_WAITING_A) + var/area/turbolift/origin = locate(current_floor.area_ref) + control_panel_interior.visible_message("The elevator announces, \"[origin.lift_announce_str]\"") + next_process = world.time + floor_wait_delay + busy_state = LIFT_WAITING_B + if(LIFT_WAITING_B) + if(queued_floors.len) + busy_state = LIFT_MOVING + else + busy_state = null + return PROCESS_KILL + else + log_debug("Turbolift [src] process() called with unknown busy_state='[busy_state]'") + return PROCESS_KILL + +// Called by process when in LIFT_MOVING /datum/turbolift/proc/do_move() + next_process = null var/current_floor_index = floors.Find(current_floor) @@ -116,6 +152,7 @@ if(!doors_closing) close_doors() doors_closing = 1 + next_process = world.time + 1 SECOND // Wait for doors to close return 1 else // We failed to close the doors - probably, someone is blocking them; stop trying to move doors_closing = 0 @@ -135,10 +172,8 @@ target_floor.arrived(src) target_floor = null - sleep(15) - control_panel_interior.visible_message("The elevator announces, \"[origin.lift_announce_str]\"") - sleep(floor_wait_delay) - + next_process = world.time + 15 + busy_state = LIFT_WAITING_A return 1 // Work out where we're headed. @@ -171,15 +206,21 @@ current_floor = next_floor control_panel_interior.visible_message("The elevator [moving_upwards ? "rises" : "descends"] smoothly.") - return (next_floor.delay_time || move_delay || 30) + next_process = world.time + (next_floor.delay_time || move_delay) + return 1 /datum/turbolift/proc/queue_move_to(var/datum/turbolift_floor/floor) if(!floor || !(floor in floors) || (floor in queued_floors)) return // STOP PRESSING THE BUTTON. floor.pending_move(src) queued_floors |= floor - turbolift_controller.lift_is_moving(src) + busy_state = LIFT_MOVING + START_PROCESSING(SSprocessing, src) // TODO: dummy machine ('lift mechanism') in powered area for functionality/blackout checks. /datum/turbolift/proc/is_functional() return 1 + +#undef LIFT_MOVING +#undef LIFT_WAITING_A +#undef LIFT_WAITING_B diff --git a/code/modules/turbolift/turbolift_door_vr.dm b/code/modules/turbolift/turbolift_door_vr.dm index 299140c4a2..ab7ca44993 100644 --- a/code/modules/turbolift/turbolift_door_vr.dm +++ b/code/modules/turbolift/turbolift_door_vr.dm @@ -1,5 +1,5 @@ // Vore specific code for /obj/machinery/door/airlock/lift /obj/machinery/door/airlock/lift/emag_act(var/uses_left, var/mob/user) - user << "This door is internally controlled." + to_chat(user, "This door is internally controlled.") return 0 // Prevents the cryptographic sequencer from using a charge fruitlessly diff --git a/code/modules/turbolift/turbolift_process.dm b/code/modules/turbolift/turbolift_process.dm deleted file mode 100644 index 124049d42e..0000000000 --- a/code/modules/turbolift/turbolift_process.dm +++ /dev/null @@ -1,36 +0,0 @@ -var/datum/controller/process/turbolift/turbolift_controller - -/datum/controller/process/turbolift - var/list/moving_lifts = list() - -/datum/controller/process/turbolift/New() - ..() - turbolift_controller = src - -/datum/controller/process/turbolift/setup() - name = "turbolift controller" - schedule_interval = 10 - -/datum/controller/process/turbolift/doWork() - for(var/liftref in moving_lifts) - if(world.time < moving_lifts[liftref]) - continue - var/datum/turbolift/lift = locate(liftref) - if(lift.busy) - continue - spawn(0) - lift.busy = 1 - var/floor_delay - if(!(floor_delay = lift.do_move())) - moving_lifts[liftref] = null - moving_lifts -= liftref - if(lift.target_floor) - lift.target_floor.ext_panel.reset() - lift.target_floor = null - else - lift_is_moving(lift,floor_delay) - lift.busy = 0 - SCHECK - -/datum/controller/process/turbolift/proc/lift_is_moving(var/datum/turbolift/lift,var/floor_delay) - moving_lifts["\ref[lift]"] = world.time + floor_delay diff --git a/code/modules/turbolift/turbolift_turfs.dm b/code/modules/turbolift/turbolift_turfs.dm index 632160efaa..8949f71448 100644 --- a/code/modules/turbolift/turbolift_turfs.dm +++ b/code/modules/turbolift/turbolift_turfs.dm @@ -1,2 +1,2 @@ -/turf/simulated/wall/elevator/New(var/newloc) - ..(newloc,"elevatorium") +/turf/simulated/wall/elevator/Initialize(mapload) + ..(mapload, "elevatorium") diff --git a/code/modules/vchat/css/css-testing.html b/code/modules/vchat/css/css-testing.html new file mode 100644 index 0000000000..94e2844b91 --- /dev/null +++ b/code/modules/vchat/css/css-testing.html @@ -0,0 +1,110 @@ + + + + + + + + + + + +
    +
    + Test Person says, "Testing say message."
    + Testing notice message.
    + Testing danger message.
    + [Security] Secu Person says, "Testing radio message."
    + Testing OOC message.
    + Testing LOOC message.
    + Testing asay message.
    + Test Person says, "Testing say message 2."
    + Testing notice message 2.
    + Testing danger message 2.
    + [Security] Secu Person says, "Testing radio message 2".
    + Testing OOC message 2.
    + Testing LOOC message 2.
    + Testing asay message 2.
    + Test Person says, "Testing say message 3."
    + Testing notice message 3.
    + Testing danger message 3.
    + [Security] Secu Person says, "Testing radio message 3".
    + Testing OOC message 3.
    + Testing LOOC message 3.
    + Testing asay message 3.
    + Test Person says, "Testing say message 4."
    + Testing notice message 4.
    + Testing danger message 4.
    + [Security] Secu Person says, "Testing radio message 4".
    + Testing OOC message 4.
    + Testing LOOC message 4.
    + Testing asay message 4.
    + Test Person says, "Testing say message 5."
    + Testing notice message 5.
    + Testing danger message 5.
    + [Security] Secu Person says, "Testing radio message 5".
    + Testing OOC message 5.
    + Testing LOOC message 5.
    + Testing asay message 5.
    +
    +
    + Test Person says, "Testing say message."
    + Testing notice message.
    + Testing danger message.
    + [Security] Secu Person says, "Testing radio message."
    + Testing OOC message.
    + Testing LOOC message.
    + Testing asay message.
    + Test Person says, "Testing say message 2."
    + Testing notice message 2.
    + Testing danger message 2.
    + [Security] Secu Person says, "Testing radio message 2".
    + Testing OOC message 2.
    + Testing LOOC message 2.
    + Testing asay message 2.
    + Test Person says, "Testing say message 3."
    + Testing notice message 3.
    + Testing danger message 3.
    + [Security] Secu Person says, "Testing radio message 3".
    + Testing OOC message 3.
    + Testing LOOC message 3.
    + Testing asay message 3.
    + Test Person says, "Testing say message 4."
    + Testing notice message 4.
    + Testing danger message 4.
    + [Security] Secu Person says, "Testing radio message 4".
    + Testing OOC message 4.
    + Testing LOOC message 4.
    + Testing asay message 4.
    + Test Person says, "Testing say message 5."
    + Testing notice message 5.
    + Testing danger message 5.
    + [Security] Secu Person says, "Testing radio message 5".
    + Testing OOC message 5.
    + Testing LOOC message 5.
    + Testing asay message 5.
    +
    +
    + + + \ No newline at end of file diff --git a/code/modules/vchat/css/semantic.min.css b/code/modules/vchat/css/semantic.min.css new file mode 100644 index 0000000000..776f2e0a6b --- /dev/null +++ b/code/modules/vchat/css/semantic.min.css @@ -0,0 +1,404 @@ + /* + * # Fomantic UI - 2.8.3 + * https://github.com/fomantic/Fomantic-UI + * http://fomantic-ui.com/ + * + * Copyright 2014 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic&subset=latin&display=swap);/*! + * # Fomantic-UI - Reset + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */*,:after,:before{-webkit-box-sizing:inherit;box-sizing:inherit}html{-webkit-box-sizing:border-box;box-sizing:border-box}input[type=email],input[type=password],input[type=search],input[type=text]{-webkit-appearance:none;-moz-appearance:none}/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}/*! + * # Fomantic-UI - Site + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */body,html{height:100%}html{font-size:14px}body{margin:0;padding:0;overflow-x:hidden;min-width:320px;background:#fff;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:14px;line-height:1.4285em;color:rgba(0,0,0,.87)}h1,h2,h3,h4,h5{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;line-height:1.28571429em;margin:calc(2rem - .1428571428571429em) 0 1rem;font-weight:700;padding:0}h1{min-height:1rem;font-size:2rem}h2{font-size:1.71428571rem}h3{font-size:1.28571429rem}h4{font-size:1.07142857rem}h5{font-size:1rem}h1:first-child,h2:first-child,h3:first-child,h4:first-child,h5:first-child{margin-top:0}h1:last-child,h2:last-child,h3:last-child,h4:last-child,h5:last-child{margin-bottom:0}p{margin:0 0 1em;line-height:1.4285em}p:first-child{margin-top:0}p:last-child{margin-bottom:0}a{color:#4183c4;text-decoration:none}a:hover{color:#1e70bf;text-decoration:none}::-webkit-selection{background-color:#cce2ff;color:rgba(0,0,0,.87)}::-moz-selection{background-color:#cce2ff;color:rgba(0,0,0,.87)}::selection{background-color:#cce2ff;color:rgba(0,0,0,.87)}input::-webkit-selection,textarea::-webkit-selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.87)}input::-moz-selection,textarea::-moz-selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.87)}input::selection,textarea::selection{background-color:rgba(100,100,100,.4);color:rgba(0,0,0,.87)}body ::-webkit-scrollbar{-webkit-appearance:none;width:10px;height:10px}body ::-webkit-scrollbar-track{background:rgba(0,0,0,.1);border-radius:0}body ::-webkit-scrollbar-thumb{cursor:pointer;border-radius:5px;background:rgba(0,0,0,.25);-webkit-transition:color .2s ease;transition:color .2s ease}body ::-webkit-scrollbar-thumb:window-inactive{background:rgba(0,0,0,.15)}body ::-webkit-scrollbar-thumb:hover{background:rgba(128,135,139,.8)}body .ui.inverted:not(.dimmer)::-webkit-scrollbar-track{background:rgba(255,255,255,.1)}body .ui.inverted:not(.dimmer)::-webkit-scrollbar-thumb{background:rgba(255,255,255,.25)}body .ui.inverted:not(.dimmer)::-webkit-scrollbar-thumb:window-inactive{background:rgba(255,255,255,.15)}body .ui.inverted:not(.dimmer)::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.35)}/*! + * # Fomantic-UI - Button + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.button{cursor:pointer;display:inline-block;min-height:1em;outline:0;border:none;vertical-align:baseline;background:#e0e1e2 none;color:rgba(0,0,0,.6);font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;margin:0 .25em 0 0;padding:.78571429em 1.5em .78571429em;text-transform:none;text-shadow:none;font-weight:700;line-height:1em;font-style:normal;text-align:center;text-decoration:none;border-radius:.28571429rem;-webkit-box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease,-webkit-box-shadow .1s ease;will-change:auto;-webkit-tap-highlight-color:transparent}.ui.button:hover{background-color:#cacbcd;background-image:none;-webkit-box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;color:rgba(0,0,0,.8)}.ui.button:hover .icon{opacity:.85}.ui.button:focus{background-color:#cacbcd;color:rgba(0,0,0,.8);background-image:none;-webkit-box-shadow:'';box-shadow:''}.ui.button:focus .icon{opacity:.85}.ui.active.button:active,.ui.button:active{background-color:#babbbc;background-image:'';color:rgba(0,0,0,.9);-webkit-box-shadow:0 0 0 1px transparent inset,none;box-shadow:0 0 0 1px transparent inset,none}.ui.active.button{background-color:#c0c1c2;background-image:none;-webkit-box-shadow:0 0 0 1px transparent inset;box-shadow:0 0 0 1px transparent inset;color:rgba(0,0,0,.95)}.ui.active.button:hover{background-color:#c0c1c2;background-image:none;color:rgba(0,0,0,.95)}.ui.active.button:active{background-color:#c0c1c2;background-image:none}.ui.loading.loading.loading.loading.loading.loading.button{position:relative;cursor:default;text-shadow:none!important;color:transparent;opacity:1;pointer-events:auto;-webkit-transition:all 0s linear,opacity .1s ease;transition:all 0s linear,opacity .1s ease}.ui.loading.button:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;border:.2em solid rgba(0,0,0,.15)}.ui.loading.button:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid currentColor;color:#fff;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent}.ui.labeled.icon.loading.button .icon{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.ui.basic.loading.button:not(.inverted):before{border-color:rgba(0,0,0,.1)}.ui.basic.loading.button:not(.inverted):after{border-color:#767676}.ui.button:disabled,.ui.buttons .disabled.button:not(.basic),.ui.disabled.active.button,.ui.disabled.button,.ui.disabled.button:hover{cursor:default;opacity:.45!important;background-image:none;-webkit-box-shadow:none;box-shadow:none;pointer-events:none!important}.ui.basic.buttons .ui.disabled.button{border-color:rgba(34,36,38,.5)}.ui.animated.button{position:relative;overflow:hidden;padding-right:0!important;vertical-align:middle;z-index:1}.ui.animated.button .content{will-change:transform,opacity}.ui.animated.button .visible.content{position:relative;margin-right:1.5em}.ui.animated.button .hidden.content{position:absolute;width:100%}.ui.animated.button .hidden.content,.ui.animated.button .visible.content{-webkit-transition:right .3s ease 0s;transition:right .3s ease 0s}.ui.animated.button .visible.content{left:auto;right:0}.ui.animated.button .hidden.content{top:50%;left:auto;right:-100%;margin-top:-.5em}.ui.animated.button:focus .visible.content,.ui.animated.button:hover .visible.content{left:auto;right:200%}.ui.animated.button:focus .hidden.content,.ui.animated.button:hover .hidden.content{left:auto;right:0}.ui.vertical.animated.button .hidden.content,.ui.vertical.animated.button .visible.content{-webkit-transition:top .3s ease,-webkit-transform .3s ease;transition:top .3s ease,-webkit-transform .3s ease;transition:top .3s ease,transform .3s ease;transition:top .3s ease,transform .3s ease,-webkit-transform .3s ease}.ui.vertical.animated.button .visible.content{-webkit-transform:translateY(0);transform:translateY(0);right:auto}.ui.vertical.animated.button .hidden.content{top:-50%;left:0;right:auto}.ui.vertical.animated.button:focus .visible.content,.ui.vertical.animated.button:hover .visible.content{-webkit-transform:translateY(200%);transform:translateY(200%);right:auto}.ui.vertical.animated.button:focus .hidden.content,.ui.vertical.animated.button:hover .hidden.content{top:50%;right:auto}.ui.fade.animated.button .hidden.content,.ui.fade.animated.button .visible.content{-webkit-transition:opacity .3s ease,-webkit-transform .3s ease;transition:opacity .3s ease,-webkit-transform .3s ease;transition:opacity .3s ease,transform .3s ease;transition:opacity .3s ease,transform .3s ease,-webkit-transform .3s ease}.ui.fade.animated.button .visible.content{left:auto;right:auto;opacity:1;-webkit-transform:scale(1);transform:scale(1)}.ui.fade.animated.button .hidden.content{opacity:0;left:0;right:auto;-webkit-transform:scale(1.5);transform:scale(1.5)}.ui.fade.animated.button:focus .visible.content,.ui.fade.animated.button:hover .visible.content{left:auto;right:auto;opacity:0;-webkit-transform:scale(.75);transform:scale(.75)}.ui.fade.animated.button:focus .hidden.content,.ui.fade.animated.button:hover .hidden.content{left:0;right:auto;opacity:1;-webkit-transform:scale(1);transform:scale(1)}.ui.inverted.button{-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset;background:transparent none;color:#fff;text-shadow:none!important}.ui.inverted.buttons .button{margin:0 0 0 -2px}.ui.inverted.buttons .button:first-child{margin-left:0}.ui.inverted.vertical.buttons .button{margin:0 0 -2px 0}.ui.inverted.vertical.buttons .button:first-child{margin-top:0}.ui.inverted.button:hover{background:#fff;-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset;color:rgba(0,0,0,.8)}.ui.inverted.button.active,.ui.inverted.button:focus{background:#fff;-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset;color:rgba(0,0,0,.8)}.ui.inverted.button.active:focus{background:#dcddde;-webkit-box-shadow:0 0 0 2px #dcddde inset;box-shadow:0 0 0 2px #dcddde inset;color:rgba(0,0,0,.8)}.ui.labeled.button:not(.icon){display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;background:0 0;padding:0!important;border:none;-webkit-box-shadow:none;box-shadow:none}.ui.labeled.button>.button{margin:0}.ui.labeled.button>.label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 0 0 -1px!important;font-size:1em;padding:'';border-color:rgba(34,36,38,.15)}.ui.labeled.button>.tag.label:before{width:1.85em;height:1.85em}.ui.labeled.button:not([class*="left labeled"])>.button{border-top-right-radius:0;border-bottom-right-radius:0}.ui.labeled.button:not([class*="left labeled"])>.label{border-top-left-radius:0;border-bottom-left-radius:0}.ui[class*="left labeled"].button>.button{border-top-left-radius:0;border-bottom-left-radius:0}.ui[class*="left labeled"].button>.label{border-top-right-radius:0;border-bottom-right-radius:0}.ui.facebook.button{background-color:#3b5998;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.facebook.button:hover{background-color:#304d8a;color:#fff;text-shadow:none}.ui.facebook.button:active{background-color:#2d4373;color:#fff;text-shadow:none}.ui.twitter.button{background-color:#1da1f2;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.twitter.button:hover{background-color:#0298f3;color:#fff;text-shadow:none}.ui.twitter.button:active{background-color:#0c85d0;color:#fff;text-shadow:none}.ui.google.plus.button{background-color:#dd4b39;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.google.plus.button:hover{background-color:#e0321c;color:#fff;text-shadow:none}.ui.google.plus.button:active{background-color:#c23321;color:#fff;text-shadow:none}.ui.linkedin.button{background-color:#0077b5;color:#fff;text-shadow:none}.ui.linkedin.button:hover{background-color:#00669c;color:#fff;text-shadow:none}.ui.linkedin.button:active{background-color:#005582;color:#fff;text-shadow:none}.ui.youtube.button{background-color:red;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.youtube.button:hover{background-color:#e60000;color:#fff;text-shadow:none}.ui.youtube.button:active{background-color:#c00;color:#fff;text-shadow:none}.ui.instagram.button{background-color:#49769c;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.instagram.button:hover{background-color:#3d698e;color:#fff;text-shadow:none}.ui.instagram.button:active{background-color:#395c79;color:#fff;text-shadow:none}.ui.pinterest.button{background-color:#bd081c;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.pinterest.button:hover{background-color:#ac0013;color:#fff;text-shadow:none}.ui.pinterest.button:active{background-color:#8c0615;color:#fff;text-shadow:none}.ui.vk.button{background-color:#45668e;color:#fff;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.vk.button:hover{background-color:#395980;color:#fff}.ui.vk.button:active{background-color:#344d6c;color:#fff}.ui.whatsapp.button{background-color:#25d366;color:#fff;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.whatsapp.button:hover{background-color:#19c55a;color:#fff}.ui.whatsapp.button:active{background-color:#1da851;color:#fff}.ui.telegram.button{background-color:#08c;color:#fff;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.telegram.button:hover{background-color:#0077b3;color:#fff}.ui.telegram.button:active{background-color:#069;color:#fff}.ui.button>.icon:not(.button){height:auto;opacity:.8;margin:0 .42857143em 0 -.21428571em;-webkit-transition:opacity .1s ease;transition:opacity .1s ease;vertical-align:'';color:''}.ui.button:not(.icon)>.icon:not(.button):not(.dropdown),.ui.button:not(.icon)>.icons:not(.button):not(.dropdown){margin:0 .42857143em 0 -.21428571em}.ui.button:not(.icon)>.right.icon:not(.button):not(.dropdown){margin:0 -.21428571em 0 .42857143em}.ui[class*="left floated"].button,.ui[class*="left floated"].buttons{float:left;margin-left:0;margin-right:.25em}.ui[class*="right floated"].button,.ui[class*="right floated"].buttons{float:right;margin-right:0;margin-left:.25em}.ui.compact.button,.ui.compact.buttons .button{padding:.58928571em 1.125em .58928571em}.ui.compact.icon.button,.ui.compact.icon.buttons .button{padding:.58928571em .58928571em .58928571em}.ui.compact.labeled.icon.button,.ui.compact.labeled.icon.buttons .button{padding:.58928571em 3.69642857em .58928571em}.ui.compact.labeled.icon.button>.icon,.ui.compact.labeled.icon.buttons .button>.icon{padding:.58928571em 0 .58928571em 0}.ui.button,.ui.buttons .button,.ui.buttons .or{font-size:1rem}.ui.mini.buttons .button,.ui.mini.buttons .dropdown,.ui.mini.buttons .dropdown .menu>.item,.ui.mini.buttons .or,.ui.ui.ui.ui.mini.button{font-size:.78571429rem}.ui.tiny.buttons .button,.ui.tiny.buttons .dropdown,.ui.tiny.buttons .dropdown .menu>.item,.ui.tiny.buttons .or,.ui.ui.ui.ui.tiny.button{font-size:.85714286rem}.ui.small.buttons .button,.ui.small.buttons .dropdown,.ui.small.buttons .dropdown .menu>.item,.ui.small.buttons .or,.ui.ui.ui.ui.small.button{font-size:.92857143rem}.ui.large.buttons .button,.ui.large.buttons .dropdown,.ui.large.buttons .dropdown .menu>.item,.ui.large.buttons .or,.ui.ui.ui.ui.large.button{font-size:1.14285714rem}.ui.big.buttons .button,.ui.big.buttons .dropdown,.ui.big.buttons .dropdown .menu>.item,.ui.big.buttons .or,.ui.ui.ui.ui.big.button{font-size:1.28571429rem}.ui.huge.buttons .button,.ui.huge.buttons .dropdown,.ui.huge.buttons .dropdown .menu>.item,.ui.huge.buttons .or,.ui.ui.ui.ui.huge.button{font-size:1.42857143rem}.ui.massive.buttons .button,.ui.massive.buttons .dropdown,.ui.massive.buttons .dropdown .menu>.item,.ui.massive.buttons .or,.ui.ui.ui.ui.massive.button{font-size:1.71428571rem}.ui.icon.button,.ui.icon.buttons .button{padding:.78571429em .78571429em .78571429em}.ui.icon.button>.icon,.ui.icon.buttons .button>.icon{opacity:.9;margin:0!important;vertical-align:top}.ui.basic.button,.ui.basic.buttons .button{background:transparent none;color:rgba(0,0,0,.6);font-weight:400;border-radius:.28571429rem;text-transform:none;text-shadow:none!important;-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset}.ui.basic.buttons{-webkit-box-shadow:none;box-shadow:none;border:1px solid rgba(34,36,38,.15);border-radius:.28571429rem}.ui.basic.buttons .button{border-radius:0}.ui.basic.button:hover,.ui.basic.buttons .button:hover{background:#fff;color:rgba(0,0,0,.8);-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset}.ui.basic.button:focus,.ui.basic.buttons .button:focus{background:#fff;color:rgba(0,0,0,.8);-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset}.ui.basic.button:active,.ui.basic.buttons .button:active{background:#f8f8f8;color:rgba(0,0,0,.9);-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 1px 4px 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 1px 4px 0 rgba(34,36,38,.15) inset}.ui.basic.active.button,.ui.basic.buttons .active.button{background:rgba(0,0,0,.05);-webkit-box-shadow:'';box-shadow:'';color:rgba(0,0,0,.95)}.ui.basic.active.button:hover,.ui.basic.buttons .active.button:hover{background-color:rgba(0,0,0,.05)}.ui.basic.buttons .button:hover{-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset inset;box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset inset}.ui.basic.buttons .button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 1px 4px 0 rgba(34,36,38,.15) inset inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 1px 4px 0 rgba(34,36,38,.15) inset inset}.ui.basic.buttons .active.button{-webkit-box-shadow:'';box-shadow:''}.ui.basic.inverted.button,.ui.basic.inverted.buttons .button{background-color:transparent;color:#f9fafb;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset}.ui.basic.inverted.button:hover,.ui.basic.inverted.buttons .button:hover{color:#fff;-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset}.ui.basic.inverted.button:focus,.ui.basic.inverted.buttons .button:focus{color:#fff;-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset}.ui.basic.inverted.button:active,.ui.basic.inverted.buttons .button:active{background-color:rgba(255,255,255,.08);color:#fff;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.9) inset;box-shadow:0 0 0 2px rgba(255,255,255,.9) inset}.ui.basic.inverted.active.button,.ui.basic.inverted.buttons .active.button{background-color:rgba(255,255,255,.08);color:#fff;text-shadow:none;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.7) inset;box-shadow:0 0 0 2px rgba(255,255,255,.7) inset}.ui.basic.inverted.active.button:hover,.ui.basic.inverted.buttons .active.button:hover{background-color:rgba(255,255,255,.15);-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset}.ui.basic.buttons .button{border-left:1px solid rgba(34,36,38,.15);-webkit-box-shadow:none;box-shadow:none}.ui.basic.vertical.buttons .button{border-left:none;border-left-width:0;border-top:1px solid rgba(34,36,38,.15)}.ui.basic.vertical.buttons .button:first-child{border-top-width:0}.ui.tertiary.button{-webkit-transition:color .1s ease!important;transition:color .1s ease!important;border-radius:0;margin:.28571429em .25em .28571429em 0!important;padding:.5em!important;-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6);background:0 0}.ui.tertiary.button:hover{-webkit-box-shadow:inset 0 -.2em 0 #666;box-shadow:inset 0 -.2em 0 #666;color:#333;background:0 0}.ui.tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #666;box-shadow:inset 0 -.2em 0 #666;color:#333;background:0 0}.ui.tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #999;box-shadow:inset 0 -.2em 0 #999;border-radius:.28571429rem .28571429rem 0 0;color:#666;background:0 0}.ui.labeled.icon.button,.ui.labeled.icon.buttons .button{position:relative;padding-left:4.07142857em!important;padding-right:1.5em!important}.ui.labeled.icon.button>.icon,.ui.labeled.icon.buttons>.button>.icon{position:absolute;top:0;left:0;height:100%;line-height:1;border-radius:0;border-top-left-radius:inherit;border-bottom-left-radius:inherit;text-align:center;-webkit-animation:none;animation:none;padding:.78571429em 0 .78571429em 0;margin:0;width:2.57142857em;background-color:rgba(0,0,0,.05);color:'';-webkit-box-shadow:-1px 0 0 0 transparent inset;box-shadow:-1px 0 0 0 transparent inset}.ui[class*="right labeled"].icon.button{padding-right:4.07142857em!important;padding-left:1.5em!important}.ui[class*="right labeled"].icon.button>.icon{left:auto;right:0;border-radius:0;border-top-right-radius:inherit;border-bottom-right-radius:inherit;-webkit-box-shadow:1px 0 0 0 transparent inset;box-shadow:1px 0 0 0 transparent inset}.ui.labeled.icon.button>.icon:after,.ui.labeled.icon.button>.icon:before,.ui.labeled.icon.buttons>.button>.icon:after,.ui.labeled.icon.buttons>.button>.icon:before{display:block;position:relative;width:100%;top:0;text-align:center}.ui.labeled.icon.buttons .button>.icon{border-radius:0}.ui.labeled.icon.buttons .button:first-child>.icon{border-top-left-radius:.28571429rem;border-bottom-left-radius:.28571429rem}.ui.labeled.icon.buttons .button:last-child>.icon{border-top-right-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.vertical.labeled.icon.buttons .button:first-child>.icon{border-radius:0;border-top-left-radius:.28571429rem}.ui.vertical.labeled.icon.buttons .button:last-child>.icon{border-radius:0;border-bottom-left-radius:.28571429rem}.ui.labeled.icon.button>.loading.icon:before{-webkit-animation:loader 2s linear infinite;animation:loader 2s linear infinite}.ui.button.toggle.active,.ui.buttons .button.toggle.active,.ui.toggle.buttons .active.button{background-color:#21ba45;-webkit-box-shadow:none;box-shadow:none;text-shadow:none;color:#fff}.ui.button.toggle.active:hover{background-color:#16ab39;text-shadow:none;color:#fff}.ui.circular.button{border-radius:10em}.ui.circular.button>.icon{width:1em;vertical-align:baseline}.ui.buttons .or{position:relative;width:.3em;height:2.57142857em;z-index:3}.ui.buttons .or:before{position:absolute;text-align:center;border-radius:500rem;content:'or';top:50%;left:50%;background-color:#fff;text-shadow:none;margin-top:-.89285714em;margin-left:-.89285714em;width:1.78571429em;height:1.78571429em;line-height:1.78571429em;color:rgba(0,0,0,.4);font-style:normal;font-weight:700;-webkit-box-shadow:0 0 0 1px transparent inset;box-shadow:0 0 0 1px transparent inset}.ui.buttons .or[data-text]:before{content:attr(data-text)}.ui.fluid.buttons .or{width:0!important}.ui.fluid.buttons .or:after{display:none}.ui.attached.button{position:relative;display:block;margin:0;border-radius:0;-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.15);box-shadow:0 0 0 1px rgba(34,36,38,.15)}.ui.attached.top.button{border-radius:.28571429rem .28571429rem 0 0}.ui.attached.bottom.button{border-radius:0 0 .28571429rem .28571429rem}.ui.left.attached.button{display:inline-block;border-left:none;text-align:right;padding-right:.75em;border-radius:.28571429rem 0 0 .28571429rem}.ui.right.attached.button{display:inline-block;text-align:left;padding-left:.75em;border-radius:0 .28571429rem .28571429rem 0}.ui.attached.buttons{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;border-radius:0;width:auto!important;z-index:auto;margin-left:-1px;margin-right:-1px}.ui.attached.buttons .button{margin:0}.ui.attached.buttons .button:first-child{border-radius:0}.ui.attached.buttons .button:last-child{border-radius:0}.ui[class*="top attached"].buttons{margin-bottom:-1px;border-radius:.28571429rem .28571429rem 0 0}.ui[class*="top attached"].buttons .button:first-child{border-radius:.28571429rem 0 0 0}.ui[class*="top attached"].buttons .button:last-child{border-radius:0 .28571429rem 0 0}.ui[class*="bottom attached"].buttons{margin-top:-1px;border-radius:0 0 .28571429rem .28571429rem}.ui[class*="bottom attached"].buttons .button:first-child{border-radius:0 0 0 .28571429rem}.ui[class*="bottom attached"].buttons .button:last-child{border-radius:0 0 .28571429rem 0}.ui[class*="left attached"].buttons{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:0;margin-left:-1px;border-radius:0 .28571429rem .28571429rem 0}.ui[class*="left attached"].buttons .button:first-child{margin-left:-1px;border-radius:0 .28571429rem 0 0}.ui[class*="left attached"].buttons .button:last-child{margin-left:-1px;border-radius:0 0 .28571429rem 0}.ui[class*="right attached"].buttons{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-left:0;margin-right:-1px;border-radius:.28571429rem 0 0 .28571429rem}.ui[class*="right attached"].buttons .button:first-child{margin-left:-1px;border-radius:.28571429rem 0 0 0}.ui[class*="right attached"].buttons .button:last-child{margin-left:-1px;border-radius:0 0 0 .28571429rem}.ui.fluid.button,.ui.fluid.buttons{width:100%}.ui.fluid.button{display:block}.ui.two.buttons{width:100%}.ui.two.buttons>.button{width:50%}.ui.three.buttons{width:100%}.ui.three.buttons>.button{width:33.333%}.ui.four.buttons{width:100%}.ui.four.buttons>.button{width:25%}.ui.five.buttons{width:100%}.ui.five.buttons>.button{width:20%}.ui.six.buttons{width:100%}.ui.six.buttons>.button{width:16.666%}.ui.seven.buttons{width:100%}.ui.seven.buttons>.button{width:14.285%}.ui.eight.buttons{width:100%}.ui.eight.buttons>.button{width:12.5%}.ui.nine.buttons{width:100%}.ui.nine.buttons>.button{width:11.11%}.ui.ten.buttons{width:100%}.ui.ten.buttons>.button{width:10%}.ui.eleven.buttons{width:100%}.ui.eleven.buttons>.button{width:9.09%}.ui.twelve.buttons{width:100%}.ui.twelve.buttons>.button{width:8.3333%}.ui.fluid.vertical.buttons,.ui.fluid.vertical.buttons>.button{display:-webkit-box;display:-ms-flexbox;display:flex;width:auto;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.two.vertical.buttons>.button{height:50%}.ui.three.vertical.buttons>.button{height:33.333%}.ui.four.vertical.buttons>.button{height:25%}.ui.five.vertical.buttons>.button{height:20%}.ui.six.vertical.buttons>.button{height:16.666%}.ui.seven.vertical.buttons>.button{height:14.285%}.ui.eight.vertical.buttons>.button{height:12.5%}.ui.nine.vertical.buttons>.button{height:11.11%}.ui.ten.vertical.buttons>.button{height:10%}.ui.eleven.vertical.buttons>.button{height:9.09%}.ui.twelve.vertical.buttons>.button{height:8.3333%}.ui.primary.button,.ui.primary.buttons .button{background-color:#2185d0;color:#fff;text-shadow:none;background-image:none}.ui.primary.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.primary.button:hover,.ui.primary.buttons .button:hover{background-color:#1678c2;color:#fff;text-shadow:none}.ui.primary.button:focus,.ui.primary.buttons .button:focus{background-color:#0d71bb;color:#fff;text-shadow:none}.ui.primary.button:active,.ui.primary.buttons .button:active{background-color:#1a69a4;color:#fff;text-shadow:none}.ui.primary.active.button,.ui.primary.button .active.button:active,.ui.primary.buttons .active.button,.ui.primary.buttons .active.button:active{background-color:#1279c6;color:#fff;text-shadow:none}.ui.basic.primary.button,.ui.basic.primary.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #2185d0 inset;box-shadow:0 0 0 1px #2185d0 inset;color:#2185d0}.ui.basic.primary.button:hover,.ui.basic.primary.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #1678c2 inset;box-shadow:0 0 0 1px #1678c2 inset;color:#1678c2}.ui.basic.primary.button:focus,.ui.basic.primary.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #0d71bb inset;box-shadow:0 0 0 1px #0d71bb inset;color:#1678c2}.ui.basic.primary.active.button,.ui.basic.primary.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #1279c6 inset;box-shadow:0 0 0 1px #1279c6 inset;color:#1a69a4}.ui.basic.primary.button:active,.ui.basic.primary.buttons .button:active{-webkit-box-shadow:0 0 0 1px #1a69a4 inset;box-shadow:0 0 0 1px #1a69a4 inset;color:#1a69a4}.ui.buttons:not(.vertical)>.basic.primary.button:not(:first-child){margin-left:-1px}.ui.inverted.primary.button,.ui.inverted.primary.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #54c8ff inset;box-shadow:0 0 0 2px #54c8ff inset;color:#54c8ff}.ui.inverted.primary.button.active,.ui.inverted.primary.button:active,.ui.inverted.primary.button:focus,.ui.inverted.primary.button:hover,.ui.inverted.primary.buttons .button.active,.ui.inverted.primary.buttons .button:active,.ui.inverted.primary.buttons .button:focus,.ui.inverted.primary.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.primary.button:hover,.ui.inverted.primary.buttons .button:hover{background-color:#21b8ff}.ui.inverted.primary.button:focus,.ui.inverted.primary.buttons .button:focus{background-color:#2bbbff}.ui.inverted.primary.active.button,.ui.inverted.primary.buttons .active.button{background-color:#3ac0ff}.ui.inverted.primary.button:active,.ui.inverted.primary.buttons .button:active{background-color:#21b8ff}.ui.inverted.primary.basic.button,.ui.inverted.primary.basic.buttons .button,.ui.inverted.primary.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.primary.basic.button:hover,.ui.inverted.primary.basic.buttons .button:hover,.ui.inverted.primary.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #21b8ff inset;box-shadow:0 0 0 2px #21b8ff inset;color:#54c8ff}.ui.inverted.primary.basic.button:focus,.ui.inverted.primary.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #2bbbff inset;box-shadow:0 0 0 2px #2bbbff inset;color:#54c8ff}.ui.inverted.primary.basic.active.button,.ui.inverted.primary.basic.buttons .active.button,.ui.inverted.primary.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #3ac0ff inset;box-shadow:0 0 0 2px #3ac0ff inset;color:#54c8ff}.ui.inverted.primary.basic.button:active,.ui.inverted.primary.basic.buttons .button:active,.ui.inverted.primary.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #21b8ff inset;box-shadow:0 0 0 2px #21b8ff inset;color:#54c8ff}.ui.tertiary.primary.button,.ui.tertiary.primary.buttons .button,.ui.tertiary.primary.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#2185d0}.ui.tertiary.primary.button:hover,.ui.tertiary.primary.buttons .button:hover,.ui.tertiary.primary.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #2b75ac;box-shadow:inset 0 -.2em 0 #2b75ac;color:#2b75ac}.ui.tertiary.primary.button:focus,.ui.tertiary.primary.buttons .button:focus,.ui.tertiary.primary.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #216ea7;box-shadow:inset 0 -.2em 0 #216ea7;color:#216ea7}.ui.tertiary.primary.active.button,.ui.tertiary.primary.button:active,.ui.tertiary.primary.buttons .active.button,.ui.tertiary.primary.buttons .button:active,.ui.tertiary.primary.buttons .tertiary.active.button,.ui.tertiary.primary.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #007bd8;box-shadow:inset 0 -.2em 0 #007bd8;color:#1279c6}.ui.secondary.button,.ui.secondary.buttons .button{background-color:#1b1c1d;color:#fff;text-shadow:none;background-image:none}.ui.secondary.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.secondary.button:hover,.ui.secondary.buttons .button:hover{background-color:#27292a;color:#fff;text-shadow:none}.ui.secondary.button:focus,.ui.secondary.buttons .button:focus{background-color:#2e3032;color:#fff;text-shadow:none}.ui.secondary.button:active,.ui.secondary.buttons .button:active{background-color:#343637;color:#fff;text-shadow:none}.ui.secondary.active.button,.ui.secondary.button .active.button:active,.ui.secondary.buttons .active.button,.ui.secondary.buttons .active.button:active{background-color:#27292a;color:#fff;text-shadow:none}.ui.basic.secondary.button,.ui.basic.secondary.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #1b1c1d inset;box-shadow:0 0 0 1px #1b1c1d inset;color:#1b1c1d}.ui.basic.secondary.button:hover,.ui.basic.secondary.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #27292a inset;box-shadow:0 0 0 1px #27292a inset;color:#27292a}.ui.basic.secondary.button:focus,.ui.basic.secondary.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #2e3032 inset;box-shadow:0 0 0 1px #2e3032 inset;color:#27292a}.ui.basic.secondary.active.button,.ui.basic.secondary.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #27292a inset;box-shadow:0 0 0 1px #27292a inset;color:#343637}.ui.basic.secondary.button:active,.ui.basic.secondary.buttons .button:active{-webkit-box-shadow:0 0 0 1px #343637 inset;box-shadow:0 0 0 1px #343637 inset;color:#343637}.ui.buttons:not(.vertical)>.basic.secondary.button:not(:first-child){margin-left:-1px}.ui.inverted.secondary.button,.ui.inverted.secondary.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #545454 inset;box-shadow:0 0 0 2px #545454 inset;color:#545454}.ui.inverted.secondary.button.active,.ui.inverted.secondary.button:active,.ui.inverted.secondary.button:focus,.ui.inverted.secondary.button:hover,.ui.inverted.secondary.buttons .button.active,.ui.inverted.secondary.buttons .button:active,.ui.inverted.secondary.buttons .button:focus,.ui.inverted.secondary.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.secondary.button:hover,.ui.inverted.secondary.buttons .button:hover{background-color:#6e6e6e}.ui.inverted.secondary.button:focus,.ui.inverted.secondary.buttons .button:focus{background-color:#686868}.ui.inverted.secondary.active.button,.ui.inverted.secondary.buttons .active.button{background-color:#616161}.ui.inverted.secondary.button:active,.ui.inverted.secondary.buttons .button:active{background-color:#6e6e6e}.ui.inverted.secondary.basic.button,.ui.inverted.secondary.basic.buttons .button,.ui.inverted.secondary.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.secondary.basic.button:hover,.ui.inverted.secondary.basic.buttons .button:hover,.ui.inverted.secondary.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #6e6e6e inset;box-shadow:0 0 0 2px #6e6e6e inset;color:#545454}.ui.inverted.secondary.basic.button:focus,.ui.inverted.secondary.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #686868 inset;box-shadow:0 0 0 2px #686868 inset;color:#545454}.ui.inverted.secondary.basic.active.button,.ui.inverted.secondary.basic.buttons .active.button,.ui.inverted.secondary.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #616161 inset;box-shadow:0 0 0 2px #616161 inset;color:#545454}.ui.inverted.secondary.basic.button:active,.ui.inverted.secondary.basic.buttons .button:active,.ui.inverted.secondary.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #6e6e6e inset;box-shadow:0 0 0 2px #6e6e6e inset;color:#545454}.ui.tertiary.secondary.button,.ui.tertiary.secondary.buttons .button,.ui.tertiary.secondary.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#1b1c1d}.ui.tertiary.secondary.button:hover,.ui.tertiary.secondary.buttons .button:hover,.ui.tertiary.secondary.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #292929;box-shadow:inset 0 -.2em 0 #292929;color:#292929}.ui.tertiary.secondary.button:focus,.ui.tertiary.secondary.buttons .button:focus,.ui.tertiary.secondary.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #303030;box-shadow:inset 0 -.2em 0 #303030;color:#303030}.ui.tertiary.secondary.active.button,.ui.tertiary.secondary.button:active,.ui.tertiary.secondary.buttons .active.button,.ui.tertiary.secondary.buttons .button:active,.ui.tertiary.secondary.buttons .tertiary.active.button,.ui.tertiary.secondary.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #1f2933;box-shadow:inset 0 -.2em 0 #1f2933;color:#27292a}.ui.red.button,.ui.red.buttons .button{background-color:#db2828;color:#fff;text-shadow:none;background-image:none}.ui.red.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.red.button:hover,.ui.red.buttons .button:hover{background-color:#d01919;color:#fff;text-shadow:none}.ui.red.button:focus,.ui.red.buttons .button:focus{background-color:#ca1010;color:#fff;text-shadow:none}.ui.red.button:active,.ui.red.buttons .button:active{background-color:#b21e1e;color:#fff;text-shadow:none}.ui.red.active.button,.ui.red.button .active.button:active,.ui.red.buttons .active.button,.ui.red.buttons .active.button:active{background-color:#d41515;color:#fff;text-shadow:none}.ui.basic.red.button,.ui.basic.red.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #db2828 inset;box-shadow:0 0 0 1px #db2828 inset;color:#db2828}.ui.basic.red.button:hover,.ui.basic.red.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #d01919 inset;box-shadow:0 0 0 1px #d01919 inset;color:#d01919}.ui.basic.red.button:focus,.ui.basic.red.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #ca1010 inset;box-shadow:0 0 0 1px #ca1010 inset;color:#d01919}.ui.basic.red.active.button,.ui.basic.red.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #d41515 inset;box-shadow:0 0 0 1px #d41515 inset;color:#b21e1e}.ui.basic.red.button:active,.ui.basic.red.buttons .button:active{-webkit-box-shadow:0 0 0 1px #b21e1e inset;box-shadow:0 0 0 1px #b21e1e inset;color:#b21e1e}.ui.buttons:not(.vertical)>.basic.red.button:not(:first-child){margin-left:-1px}.ui.inverted.red.button,.ui.inverted.red.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #ff695e inset;box-shadow:0 0 0 2px #ff695e inset;color:#ff695e}.ui.inverted.red.button.active,.ui.inverted.red.button:active,.ui.inverted.red.button:focus,.ui.inverted.red.button:hover,.ui.inverted.red.buttons .button.active,.ui.inverted.red.buttons .button:active,.ui.inverted.red.buttons .button:focus,.ui.inverted.red.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.red.button:hover,.ui.inverted.red.buttons .button:hover{background-color:#ff392b}.ui.inverted.red.button:focus,.ui.inverted.red.buttons .button:focus{background-color:#ff4335}.ui.inverted.red.active.button,.ui.inverted.red.buttons .active.button{background-color:#ff5144}.ui.inverted.red.button:active,.ui.inverted.red.buttons .button:active{background-color:#ff392b}.ui.inverted.red.basic.button,.ui.inverted.red.basic.buttons .button,.ui.inverted.red.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.red.basic.button:hover,.ui.inverted.red.basic.buttons .button:hover,.ui.inverted.red.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #ff392b inset;box-shadow:0 0 0 2px #ff392b inset;color:#ff695e}.ui.inverted.red.basic.button:focus,.ui.inverted.red.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #ff4335 inset;box-shadow:0 0 0 2px #ff4335 inset;color:#ff695e}.ui.inverted.red.basic.active.button,.ui.inverted.red.basic.buttons .active.button,.ui.inverted.red.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #ff5144 inset;box-shadow:0 0 0 2px #ff5144 inset;color:#ff695e}.ui.inverted.red.basic.button:active,.ui.inverted.red.basic.buttons .button:active,.ui.inverted.red.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #ff392b inset;box-shadow:0 0 0 2px #ff392b inset;color:#ff695e}.ui.tertiary.red.button,.ui.tertiary.red.buttons .button,.ui.tertiary.red.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#db2828}.ui.tertiary.red.button:hover,.ui.tertiary.red.buttons .button:hover,.ui.tertiary.red.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #b93131;box-shadow:inset 0 -.2em 0 #b93131;color:#b93131}.ui.tertiary.red.button:focus,.ui.tertiary.red.buttons .button:focus,.ui.tertiary.red.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #b52626;box-shadow:inset 0 -.2em 0 #b52626;color:#b52626}.ui.tertiary.red.active.button,.ui.tertiary.red.button:active,.ui.tertiary.red.buttons .active.button,.ui.tertiary.red.buttons .button:active,.ui.tertiary.red.buttons .tertiary.active.button,.ui.tertiary.red.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #ea0000;box-shadow:inset 0 -.2em 0 #ea0000;color:#d41515}.ui.orange.button,.ui.orange.buttons .button{background-color:#f2711c;color:#fff;text-shadow:none;background-image:none}.ui.orange.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.orange.button:hover,.ui.orange.buttons .button:hover{background-color:#f26202;color:#fff;text-shadow:none}.ui.orange.button:focus,.ui.orange.buttons .button:focus{background-color:#e55b00;color:#fff;text-shadow:none}.ui.orange.button:active,.ui.orange.buttons .button:active{background-color:#cf590c;color:#fff;text-shadow:none}.ui.orange.active.button,.ui.orange.button .active.button:active,.ui.orange.buttons .active.button,.ui.orange.buttons .active.button:active{background-color:#f56100;color:#fff;text-shadow:none}.ui.basic.orange.button,.ui.basic.orange.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #f2711c inset;box-shadow:0 0 0 1px #f2711c inset;color:#f2711c}.ui.basic.orange.button:hover,.ui.basic.orange.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #f26202 inset;box-shadow:0 0 0 1px #f26202 inset;color:#f26202}.ui.basic.orange.button:focus,.ui.basic.orange.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #e55b00 inset;box-shadow:0 0 0 1px #e55b00 inset;color:#f26202}.ui.basic.orange.active.button,.ui.basic.orange.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #f56100 inset;box-shadow:0 0 0 1px #f56100 inset;color:#cf590c}.ui.basic.orange.button:active,.ui.basic.orange.buttons .button:active{-webkit-box-shadow:0 0 0 1px #cf590c inset;box-shadow:0 0 0 1px #cf590c inset;color:#cf590c}.ui.buttons:not(.vertical)>.basic.orange.button:not(:first-child){margin-left:-1px}.ui.inverted.orange.button,.ui.inverted.orange.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #ff851b inset;box-shadow:0 0 0 2px #ff851b inset;color:#ff851b}.ui.inverted.orange.button.active,.ui.inverted.orange.button:active,.ui.inverted.orange.button:focus,.ui.inverted.orange.button:hover,.ui.inverted.orange.buttons .button.active,.ui.inverted.orange.buttons .button:active,.ui.inverted.orange.buttons .button:focus,.ui.inverted.orange.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.orange.button:hover,.ui.inverted.orange.buttons .button:hover{background-color:#e76b00}.ui.inverted.orange.button:focus,.ui.inverted.orange.buttons .button:focus{background-color:#f17000}.ui.inverted.orange.active.button,.ui.inverted.orange.buttons .active.button{background-color:#ff7701}.ui.inverted.orange.button:active,.ui.inverted.orange.buttons .button:active{background-color:#e76b00}.ui.inverted.orange.basic.button,.ui.inverted.orange.basic.buttons .button,.ui.inverted.orange.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.orange.basic.button:hover,.ui.inverted.orange.basic.buttons .button:hover,.ui.inverted.orange.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #e76b00 inset;box-shadow:0 0 0 2px #e76b00 inset;color:#ff851b}.ui.inverted.orange.basic.button:focus,.ui.inverted.orange.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #f17000 inset;box-shadow:0 0 0 2px #f17000 inset;color:#ff851b}.ui.inverted.orange.basic.active.button,.ui.inverted.orange.basic.buttons .active.button,.ui.inverted.orange.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #ff7701 inset;box-shadow:0 0 0 2px #ff7701 inset;color:#ff851b}.ui.inverted.orange.basic.button:active,.ui.inverted.orange.basic.buttons .button:active,.ui.inverted.orange.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #e76b00 inset;box-shadow:0 0 0 2px #e76b00 inset;color:#ff851b}.ui.tertiary.orange.button,.ui.tertiary.orange.buttons .button,.ui.tertiary.orange.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#f2711c}.ui.tertiary.orange.button:hover,.ui.tertiary.orange.buttons .button:hover,.ui.tertiary.orange.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #da671b;box-shadow:inset 0 -.2em 0 #da671b;color:#da671b}.ui.tertiary.orange.button:focus,.ui.tertiary.orange.buttons .button:focus,.ui.tertiary.orange.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #ce6017;box-shadow:inset 0 -.2em 0 #ce6017;color:#ce6017}.ui.tertiary.orange.active.button,.ui.tertiary.orange.button:active,.ui.tertiary.orange.buttons .active.button,.ui.tertiary.orange.buttons .button:active,.ui.tertiary.orange.buttons .tertiary.active.button,.ui.tertiary.orange.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #f56100;box-shadow:inset 0 -.2em 0 #f56100;color:#f56100}.ui.yellow.button,.ui.yellow.buttons .button{background-color:#fbbd08;color:#fff;text-shadow:none;background-image:none}.ui.yellow.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.yellow.button:hover,.ui.yellow.buttons .button:hover{background-color:#eaae00;color:#fff;text-shadow:none}.ui.yellow.button:focus,.ui.yellow.buttons .button:focus{background-color:#daa300;color:#fff;text-shadow:none}.ui.yellow.button:active,.ui.yellow.buttons .button:active{background-color:#cd9903;color:#fff;text-shadow:none}.ui.yellow.active.button,.ui.yellow.button .active.button:active,.ui.yellow.buttons .active.button,.ui.yellow.buttons .active.button:active{background-color:#eaae00;color:#fff;text-shadow:none}.ui.basic.yellow.button,.ui.basic.yellow.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #fbbd08 inset;box-shadow:0 0 0 1px #fbbd08 inset;color:#fbbd08}.ui.basic.yellow.button:hover,.ui.basic.yellow.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #eaae00 inset;box-shadow:0 0 0 1px #eaae00 inset;color:#eaae00}.ui.basic.yellow.button:focus,.ui.basic.yellow.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #daa300 inset;box-shadow:0 0 0 1px #daa300 inset;color:#eaae00}.ui.basic.yellow.active.button,.ui.basic.yellow.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #eaae00 inset;box-shadow:0 0 0 1px #eaae00 inset;color:#cd9903}.ui.basic.yellow.button:active,.ui.basic.yellow.buttons .button:active{-webkit-box-shadow:0 0 0 1px #cd9903 inset;box-shadow:0 0 0 1px #cd9903 inset;color:#cd9903}.ui.buttons:not(.vertical)>.basic.yellow.button:not(:first-child){margin-left:-1px}.ui.inverted.yellow.button,.ui.inverted.yellow.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #ffe21f inset;box-shadow:0 0 0 2px #ffe21f inset;color:#ffe21f}.ui.inverted.yellow.button.active,.ui.inverted.yellow.button:active,.ui.inverted.yellow.button:focus,.ui.inverted.yellow.button:hover,.ui.inverted.yellow.buttons .button.active,.ui.inverted.yellow.buttons .button:active,.ui.inverted.yellow.buttons .button:focus,.ui.inverted.yellow.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6)}.ui.inverted.yellow.button:hover,.ui.inverted.yellow.buttons .button:hover{background-color:#ebcd00}.ui.inverted.yellow.button:focus,.ui.inverted.yellow.buttons .button:focus{background-color:#f5d500}.ui.inverted.yellow.active.button,.ui.inverted.yellow.buttons .active.button{background-color:#ffdf05}.ui.inverted.yellow.button:active,.ui.inverted.yellow.buttons .button:active{background-color:#ebcd00}.ui.inverted.yellow.basic.button,.ui.inverted.yellow.basic.buttons .button,.ui.inverted.yellow.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.yellow.basic.button:hover,.ui.inverted.yellow.basic.buttons .button:hover,.ui.inverted.yellow.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #ebcd00 inset;box-shadow:0 0 0 2px #ebcd00 inset;color:#ffe21f}.ui.inverted.yellow.basic.button:focus,.ui.inverted.yellow.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #f5d500 inset;box-shadow:0 0 0 2px #f5d500 inset;color:#ffe21f}.ui.inverted.yellow.basic.active.button,.ui.inverted.yellow.basic.buttons .active.button,.ui.inverted.yellow.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #ffdf05 inset;box-shadow:0 0 0 2px #ffdf05 inset;color:#ffe21f}.ui.inverted.yellow.basic.button:active,.ui.inverted.yellow.basic.buttons .button:active,.ui.inverted.yellow.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #ebcd00 inset;box-shadow:0 0 0 2px #ebcd00 inset;color:#ffe21f}.ui.tertiary.yellow.button,.ui.tertiary.yellow.buttons .button,.ui.tertiary.yellow.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#fbbd08}.ui.tertiary.yellow.button:hover,.ui.tertiary.yellow.buttons .button:hover,.ui.tertiary.yellow.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #d2a217;box-shadow:inset 0 -.2em 0 #d2a217;color:#d2a217}.ui.tertiary.yellow.button:focus,.ui.tertiary.yellow.buttons .button:focus,.ui.tertiary.yellow.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #c49816;box-shadow:inset 0 -.2em 0 #c49816;color:#c49816}.ui.tertiary.yellow.active.button,.ui.tertiary.yellow.button:active,.ui.tertiary.yellow.buttons .active.button,.ui.tertiary.yellow.buttons .button:active,.ui.tertiary.yellow.buttons .tertiary.active.button,.ui.tertiary.yellow.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #eaae00;box-shadow:inset 0 -.2em 0 #eaae00;color:#eaae00}.ui.olive.button,.ui.olive.buttons .button{background-color:#b5cc18;color:#fff;text-shadow:none;background-image:none}.ui.olive.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.olive.button:hover,.ui.olive.buttons .button:hover{background-color:#a7bd0d;color:#fff;text-shadow:none}.ui.olive.button:focus,.ui.olive.buttons .button:focus{background-color:#a0b605;color:#fff;text-shadow:none}.ui.olive.button:active,.ui.olive.buttons .button:active{background-color:#8d9e13;color:#fff;text-shadow:none}.ui.olive.active.button,.ui.olive.button .active.button:active,.ui.olive.buttons .active.button,.ui.olive.buttons .active.button:active{background-color:#aac109;color:#fff;text-shadow:none}.ui.basic.olive.button,.ui.basic.olive.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #b5cc18 inset;box-shadow:0 0 0 1px #b5cc18 inset;color:#b5cc18}.ui.basic.olive.button:hover,.ui.basic.olive.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #a7bd0d inset;box-shadow:0 0 0 1px #a7bd0d inset;color:#a7bd0d}.ui.basic.olive.button:focus,.ui.basic.olive.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #a0b605 inset;box-shadow:0 0 0 1px #a0b605 inset;color:#a7bd0d}.ui.basic.olive.active.button,.ui.basic.olive.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #aac109 inset;box-shadow:0 0 0 1px #aac109 inset;color:#8d9e13}.ui.basic.olive.button:active,.ui.basic.olive.buttons .button:active{-webkit-box-shadow:0 0 0 1px #8d9e13 inset;box-shadow:0 0 0 1px #8d9e13 inset;color:#8d9e13}.ui.buttons:not(.vertical)>.basic.olive.button:not(:first-child){margin-left:-1px}.ui.inverted.olive.button,.ui.inverted.olive.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #d9e778 inset;box-shadow:0 0 0 2px #d9e778 inset;color:#d9e778}.ui.inverted.olive.button.active,.ui.inverted.olive.button:active,.ui.inverted.olive.button:focus,.ui.inverted.olive.button:hover,.ui.inverted.olive.buttons .button.active,.ui.inverted.olive.buttons .button:active,.ui.inverted.olive.buttons .button:focus,.ui.inverted.olive.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6)}.ui.inverted.olive.button:hover,.ui.inverted.olive.buttons .button:hover{background-color:#d2e745}.ui.inverted.olive.button:focus,.ui.inverted.olive.buttons .button:focus{background-color:#daef47}.ui.inverted.olive.active.button,.ui.inverted.olive.buttons .active.button{background-color:#daed59}.ui.inverted.olive.button:active,.ui.inverted.olive.buttons .button:active{background-color:#cddf4d}.ui.inverted.olive.basic.button,.ui.inverted.olive.basic.buttons .button,.ui.inverted.olive.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.olive.basic.button:hover,.ui.inverted.olive.basic.buttons .button:hover,.ui.inverted.olive.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #d2e745 inset;box-shadow:0 0 0 2px #d2e745 inset;color:#d9e778}.ui.inverted.olive.basic.button:focus,.ui.inverted.olive.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #daef47 inset;box-shadow:0 0 0 2px #daef47 inset;color:#d9e778}.ui.inverted.olive.basic.active.button,.ui.inverted.olive.basic.buttons .active.button,.ui.inverted.olive.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #daed59 inset;box-shadow:0 0 0 2px #daed59 inset;color:#d9e778}.ui.inverted.olive.basic.button:active,.ui.inverted.olive.basic.buttons .button:active,.ui.inverted.olive.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #cddf4d inset;box-shadow:0 0 0 2px #cddf4d inset;color:#d9e778}.ui.tertiary.olive.button,.ui.tertiary.olive.buttons .button,.ui.tertiary.olive.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#b5cc18}.ui.tertiary.olive.button:hover,.ui.tertiary.olive.buttons .button:hover,.ui.tertiary.olive.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #98a922;box-shadow:inset 0 -.2em 0 #98a922;color:#98a922}.ui.tertiary.olive.button:focus,.ui.tertiary.olive.buttons .button:focus,.ui.tertiary.olive.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #92a418;box-shadow:inset 0 -.2em 0 #92a418;color:#92a418}.ui.tertiary.olive.active.button,.ui.tertiary.olive.button:active,.ui.tertiary.olive.buttons .active.button,.ui.tertiary.olive.buttons .button:active,.ui.tertiary.olive.buttons .tertiary.active.button,.ui.tertiary.olive.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #b1cb00;box-shadow:inset 0 -.2em 0 #b1cb00;color:#aac109}.ui.green.button,.ui.green.buttons .button{background-color:#21ba45;color:#fff;text-shadow:none;background-image:none}.ui.green.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.green.button:hover,.ui.green.buttons .button:hover{background-color:#16ab39;color:#fff;text-shadow:none}.ui.green.button:focus,.ui.green.buttons .button:focus{background-color:#0ea432;color:#fff;text-shadow:none}.ui.green.button:active,.ui.green.buttons .button:active{background-color:#198f35;color:#fff;text-shadow:none}.ui.green.active.button,.ui.green.button .active.button:active,.ui.green.buttons .active.button,.ui.green.buttons .active.button:active{background-color:#13ae38;color:#fff;text-shadow:none}.ui.basic.green.button,.ui.basic.green.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #21ba45 inset;box-shadow:0 0 0 1px #21ba45 inset;color:#21ba45}.ui.basic.green.button:hover,.ui.basic.green.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #16ab39 inset;box-shadow:0 0 0 1px #16ab39 inset;color:#16ab39}.ui.basic.green.button:focus,.ui.basic.green.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #0ea432 inset;box-shadow:0 0 0 1px #0ea432 inset;color:#16ab39}.ui.basic.green.active.button,.ui.basic.green.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #13ae38 inset;box-shadow:0 0 0 1px #13ae38 inset;color:#198f35}.ui.basic.green.button:active,.ui.basic.green.buttons .button:active{-webkit-box-shadow:0 0 0 1px #198f35 inset;box-shadow:0 0 0 1px #198f35 inset;color:#198f35}.ui.buttons:not(.vertical)>.basic.green.button:not(:first-child){margin-left:-1px}.ui.inverted.green.button,.ui.inverted.green.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #2ecc40 inset;box-shadow:0 0 0 2px #2ecc40 inset;color:#2ecc40}.ui.inverted.green.button.active,.ui.inverted.green.button:active,.ui.inverted.green.button:focus,.ui.inverted.green.button:hover,.ui.inverted.green.buttons .button.active,.ui.inverted.green.buttons .button:active,.ui.inverted.green.buttons .button:focus,.ui.inverted.green.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.green.button:hover,.ui.inverted.green.buttons .button:hover{background-color:#1ea92e}.ui.inverted.green.button:focus,.ui.inverted.green.buttons .button:focus{background-color:#19b82b}.ui.inverted.green.active.button,.ui.inverted.green.buttons .active.button{background-color:#1fc231}.ui.inverted.green.button:active,.ui.inverted.green.buttons .button:active{background-color:#25a233}.ui.inverted.green.basic.button,.ui.inverted.green.basic.buttons .button,.ui.inverted.green.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.green.basic.button:hover,.ui.inverted.green.basic.buttons .button:hover,.ui.inverted.green.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #1ea92e inset;box-shadow:0 0 0 2px #1ea92e inset;color:#2ecc40}.ui.inverted.green.basic.button:focus,.ui.inverted.green.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #19b82b inset;box-shadow:0 0 0 2px #19b82b inset;color:#2ecc40}.ui.inverted.green.basic.active.button,.ui.inverted.green.basic.buttons .active.button,.ui.inverted.green.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #1fc231 inset;box-shadow:0 0 0 2px #1fc231 inset;color:#2ecc40}.ui.inverted.green.basic.button:active,.ui.inverted.green.basic.buttons .button:active,.ui.inverted.green.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #25a233 inset;box-shadow:0 0 0 2px #25a233 inset;color:#2ecc40}.ui.tertiary.green.button,.ui.tertiary.green.buttons .button,.ui.tertiary.green.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#21ba45}.ui.tertiary.green.button:hover,.ui.tertiary.green.buttons .button:hover,.ui.tertiary.green.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #2a9844;box-shadow:inset 0 -.2em 0 #2a9844;color:#2a9844}.ui.tertiary.green.button:focus,.ui.tertiary.green.buttons .button:focus,.ui.tertiary.green.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #20923b;box-shadow:inset 0 -.2em 0 #20923b;color:#20923b}.ui.tertiary.green.active.button,.ui.tertiary.green.button:active,.ui.tertiary.green.buttons .active.button,.ui.tertiary.green.buttons .button:active,.ui.tertiary.green.buttons .tertiary.active.button,.ui.tertiary.green.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #00c22e;box-shadow:inset 0 -.2em 0 #00c22e;color:#13ae38}.ui.teal.button,.ui.teal.buttons .button{background-color:#00b5ad;color:#fff;text-shadow:none;background-image:none}.ui.teal.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.teal.button:hover,.ui.teal.buttons .button:hover{background-color:#009c95;color:#fff;text-shadow:none}.ui.teal.button:focus,.ui.teal.buttons .button:focus{background-color:#008c86;color:#fff;text-shadow:none}.ui.teal.button:active,.ui.teal.buttons .button:active{background-color:#00827c;color:#fff;text-shadow:none}.ui.teal.active.button,.ui.teal.button .active.button:active,.ui.teal.buttons .active.button,.ui.teal.buttons .active.button:active{background-color:#009c95;color:#fff;text-shadow:none}.ui.basic.teal.button,.ui.basic.teal.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #00b5ad inset;box-shadow:0 0 0 1px #00b5ad inset;color:#00b5ad}.ui.basic.teal.button:hover,.ui.basic.teal.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #009c95 inset;box-shadow:0 0 0 1px #009c95 inset;color:#009c95}.ui.basic.teal.button:focus,.ui.basic.teal.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #008c86 inset;box-shadow:0 0 0 1px #008c86 inset;color:#009c95}.ui.basic.teal.active.button,.ui.basic.teal.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #009c95 inset;box-shadow:0 0 0 1px #009c95 inset;color:#00827c}.ui.basic.teal.button:active,.ui.basic.teal.buttons .button:active{-webkit-box-shadow:0 0 0 1px #00827c inset;box-shadow:0 0 0 1px #00827c inset;color:#00827c}.ui.buttons:not(.vertical)>.basic.teal.button:not(:first-child){margin-left:-1px}.ui.inverted.teal.button,.ui.inverted.teal.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #6dffff inset;box-shadow:0 0 0 2px #6dffff inset;color:#6dffff}.ui.inverted.teal.button.active,.ui.inverted.teal.button:active,.ui.inverted.teal.button:focus,.ui.inverted.teal.button:hover,.ui.inverted.teal.buttons .button.active,.ui.inverted.teal.buttons .button:active,.ui.inverted.teal.buttons .button:focus,.ui.inverted.teal.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6)}.ui.inverted.teal.button:hover,.ui.inverted.teal.buttons .button:hover{background-color:#3affff}.ui.inverted.teal.button:focus,.ui.inverted.teal.buttons .button:focus{background-color:#4ff}.ui.inverted.teal.active.button,.ui.inverted.teal.buttons .active.button{background-color:#54ffff}.ui.inverted.teal.button:active,.ui.inverted.teal.buttons .button:active{background-color:#3affff}.ui.inverted.teal.basic.button,.ui.inverted.teal.basic.buttons .button,.ui.inverted.teal.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.teal.basic.button:hover,.ui.inverted.teal.basic.buttons .button:hover,.ui.inverted.teal.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #3affff inset;box-shadow:0 0 0 2px #3affff inset;color:#6dffff}.ui.inverted.teal.basic.button:focus,.ui.inverted.teal.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #4ff inset;box-shadow:0 0 0 2px #4ff inset;color:#6dffff}.ui.inverted.teal.basic.active.button,.ui.inverted.teal.basic.buttons .active.button,.ui.inverted.teal.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #54ffff inset;box-shadow:0 0 0 2px #54ffff inset;color:#6dffff}.ui.inverted.teal.basic.button:active,.ui.inverted.teal.basic.buttons .button:active,.ui.inverted.teal.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #3affff inset;box-shadow:0 0 0 2px #3affff inset;color:#6dffff}.ui.tertiary.teal.button,.ui.tertiary.teal.buttons .button,.ui.tertiary.teal.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#00b5ad}.ui.tertiary.teal.button:hover,.ui.tertiary.teal.buttons .button:hover,.ui.tertiary.teal.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #108c86;box-shadow:inset 0 -.2em 0 #108c86;color:#108c86}.ui.tertiary.teal.button:focus,.ui.tertiary.teal.buttons .button:focus,.ui.tertiary.teal.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #0e7e79;box-shadow:inset 0 -.2em 0 #0e7e79;color:#0e7e79}.ui.tertiary.teal.active.button,.ui.tertiary.teal.button:active,.ui.tertiary.teal.buttons .active.button,.ui.tertiary.teal.buttons .button:active,.ui.tertiary.teal.buttons .tertiary.active.button,.ui.tertiary.teal.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #009c95;box-shadow:inset 0 -.2em 0 #009c95;color:#009c95}.ui.blue.button,.ui.blue.buttons .button{background-color:#2185d0;color:#fff;text-shadow:none;background-image:none}.ui.blue.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.blue.button:hover,.ui.blue.buttons .button:hover{background-color:#1678c2;color:#fff;text-shadow:none}.ui.blue.button:focus,.ui.blue.buttons .button:focus{background-color:#0d71bb;color:#fff;text-shadow:none}.ui.blue.button:active,.ui.blue.buttons .button:active{background-color:#1a69a4;color:#fff;text-shadow:none}.ui.blue.active.button,.ui.blue.button .active.button:active,.ui.blue.buttons .active.button,.ui.blue.buttons .active.button:active{background-color:#1279c6;color:#fff;text-shadow:none}.ui.basic.blue.button,.ui.basic.blue.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #2185d0 inset;box-shadow:0 0 0 1px #2185d0 inset;color:#2185d0}.ui.basic.blue.button:hover,.ui.basic.blue.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #1678c2 inset;box-shadow:0 0 0 1px #1678c2 inset;color:#1678c2}.ui.basic.blue.button:focus,.ui.basic.blue.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #0d71bb inset;box-shadow:0 0 0 1px #0d71bb inset;color:#1678c2}.ui.basic.blue.active.button,.ui.basic.blue.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #1279c6 inset;box-shadow:0 0 0 1px #1279c6 inset;color:#1a69a4}.ui.basic.blue.button:active,.ui.basic.blue.buttons .button:active{-webkit-box-shadow:0 0 0 1px #1a69a4 inset;box-shadow:0 0 0 1px #1a69a4 inset;color:#1a69a4}.ui.buttons:not(.vertical)>.basic.blue.button:not(:first-child){margin-left:-1px}.ui.inverted.blue.button,.ui.inverted.blue.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #54c8ff inset;box-shadow:0 0 0 2px #54c8ff inset;color:#54c8ff}.ui.inverted.blue.button.active,.ui.inverted.blue.button:active,.ui.inverted.blue.button:focus,.ui.inverted.blue.button:hover,.ui.inverted.blue.buttons .button.active,.ui.inverted.blue.buttons .button:active,.ui.inverted.blue.buttons .button:focus,.ui.inverted.blue.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.blue.button:hover,.ui.inverted.blue.buttons .button:hover{background-color:#21b8ff}.ui.inverted.blue.button:focus,.ui.inverted.blue.buttons .button:focus{background-color:#2bbbff}.ui.inverted.blue.active.button,.ui.inverted.blue.buttons .active.button{background-color:#3ac0ff}.ui.inverted.blue.button:active,.ui.inverted.blue.buttons .button:active{background-color:#21b8ff}.ui.inverted.blue.basic.button,.ui.inverted.blue.basic.buttons .button,.ui.inverted.blue.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.blue.basic.button:hover,.ui.inverted.blue.basic.buttons .button:hover,.ui.inverted.blue.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #21b8ff inset;box-shadow:0 0 0 2px #21b8ff inset;color:#54c8ff}.ui.inverted.blue.basic.button:focus,.ui.inverted.blue.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #2bbbff inset;box-shadow:0 0 0 2px #2bbbff inset;color:#54c8ff}.ui.inverted.blue.basic.active.button,.ui.inverted.blue.basic.buttons .active.button,.ui.inverted.blue.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #3ac0ff inset;box-shadow:0 0 0 2px #3ac0ff inset;color:#54c8ff}.ui.inverted.blue.basic.button:active,.ui.inverted.blue.basic.buttons .button:active,.ui.inverted.blue.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #21b8ff inset;box-shadow:0 0 0 2px #21b8ff inset;color:#54c8ff}.ui.tertiary.blue.button,.ui.tertiary.blue.buttons .button,.ui.tertiary.blue.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#2185d0}.ui.tertiary.blue.button:hover,.ui.tertiary.blue.buttons .button:hover,.ui.tertiary.blue.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #2b75ac;box-shadow:inset 0 -.2em 0 #2b75ac;color:#2b75ac}.ui.tertiary.blue.button:focus,.ui.tertiary.blue.buttons .button:focus,.ui.tertiary.blue.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #216ea7;box-shadow:inset 0 -.2em 0 #216ea7;color:#216ea7}.ui.tertiary.blue.active.button,.ui.tertiary.blue.button:active,.ui.tertiary.blue.buttons .active.button,.ui.tertiary.blue.buttons .button:active,.ui.tertiary.blue.buttons .tertiary.active.button,.ui.tertiary.blue.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #007bd8;box-shadow:inset 0 -.2em 0 #007bd8;color:#1279c6}.ui.violet.button,.ui.violet.buttons .button{background-color:#6435c9;color:#fff;text-shadow:none;background-image:none}.ui.violet.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.violet.button:hover,.ui.violet.buttons .button:hover{background-color:#5829bb;color:#fff;text-shadow:none}.ui.violet.button:focus,.ui.violet.buttons .button:focus{background-color:#4f20b5;color:#fff;text-shadow:none}.ui.violet.button:active,.ui.violet.buttons .button:active{background-color:#502aa1;color:#fff;text-shadow:none}.ui.violet.active.button,.ui.violet.button .active.button:active,.ui.violet.buttons .active.button,.ui.violet.buttons .active.button:active{background-color:#5626bf;color:#fff;text-shadow:none}.ui.basic.violet.button,.ui.basic.violet.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #6435c9 inset;box-shadow:0 0 0 1px #6435c9 inset;color:#6435c9}.ui.basic.violet.button:hover,.ui.basic.violet.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #5829bb inset;box-shadow:0 0 0 1px #5829bb inset;color:#5829bb}.ui.basic.violet.button:focus,.ui.basic.violet.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #4f20b5 inset;box-shadow:0 0 0 1px #4f20b5 inset;color:#5829bb}.ui.basic.violet.active.button,.ui.basic.violet.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #5626bf inset;box-shadow:0 0 0 1px #5626bf inset;color:#502aa1}.ui.basic.violet.button:active,.ui.basic.violet.buttons .button:active{-webkit-box-shadow:0 0 0 1px #502aa1 inset;box-shadow:0 0 0 1px #502aa1 inset;color:#502aa1}.ui.buttons:not(.vertical)>.basic.violet.button:not(:first-child){margin-left:-1px}.ui.inverted.violet.button,.ui.inverted.violet.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #a291fb inset;box-shadow:0 0 0 2px #a291fb inset;color:#a291fb}.ui.inverted.violet.button.active,.ui.inverted.violet.button:active,.ui.inverted.violet.button:focus,.ui.inverted.violet.button:hover,.ui.inverted.violet.buttons .button.active,.ui.inverted.violet.buttons .button:active,.ui.inverted.violet.buttons .button:focus,.ui.inverted.violet.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.violet.button:hover,.ui.inverted.violet.buttons .button:hover{background-color:#745aff}.ui.inverted.violet.button:focus,.ui.inverted.violet.buttons .button:focus{background-color:#7d64ff}.ui.inverted.violet.active.button,.ui.inverted.violet.buttons .active.button{background-color:#8a73ff}.ui.inverted.violet.button:active,.ui.inverted.violet.buttons .button:active{background-color:#7860f9}.ui.inverted.violet.basic.button,.ui.inverted.violet.basic.buttons .button,.ui.inverted.violet.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.violet.basic.button:hover,.ui.inverted.violet.basic.buttons .button:hover,.ui.inverted.violet.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #745aff inset;box-shadow:0 0 0 2px #745aff inset;color:#a291fb}.ui.inverted.violet.basic.button:focus,.ui.inverted.violet.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #7d64ff inset;box-shadow:0 0 0 2px #7d64ff inset;color:#a291fb}.ui.inverted.violet.basic.active.button,.ui.inverted.violet.basic.buttons .active.button,.ui.inverted.violet.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #8a73ff inset;box-shadow:0 0 0 2px #8a73ff inset;color:#a291fb}.ui.inverted.violet.basic.button:active,.ui.inverted.violet.basic.buttons .button:active,.ui.inverted.violet.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #7860f9 inset;box-shadow:0 0 0 2px #7860f9 inset;color:#a291fb}.ui.tertiary.violet.button,.ui.tertiary.violet.buttons .button,.ui.tertiary.violet.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#6435c9}.ui.tertiary.violet.button:hover,.ui.tertiary.violet.buttons .button:hover,.ui.tertiary.violet.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #6040a5;box-shadow:inset 0 -.2em 0 #6040a5;color:#6040a5}.ui.tertiary.violet.button:focus,.ui.tertiary.violet.buttons .button:focus,.ui.tertiary.violet.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #5735a0;box-shadow:inset 0 -.2em 0 #5735a0;color:#5735a0}.ui.tertiary.violet.active.button,.ui.tertiary.violet.button:active,.ui.tertiary.violet.buttons .active.button,.ui.tertiary.violet.buttons .button:active,.ui.tertiary.violet.buttons .tertiary.active.button,.ui.tertiary.violet.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #4e0fd6;box-shadow:inset 0 -.2em 0 #4e0fd6;color:#5626bf}.ui.purple.button,.ui.purple.buttons .button{background-color:#a333c8;color:#fff;text-shadow:none;background-image:none}.ui.purple.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.purple.button:hover,.ui.purple.buttons .button:hover{background-color:#9627ba;color:#fff;text-shadow:none}.ui.purple.button:focus,.ui.purple.buttons .button:focus{background-color:#8f1eb4;color:#fff;text-shadow:none}.ui.purple.button:active,.ui.purple.buttons .button:active{background-color:#82299f;color:#fff;text-shadow:none}.ui.purple.active.button,.ui.purple.button .active.button:active,.ui.purple.buttons .active.button,.ui.purple.buttons .active.button:active{background-color:#9724be;color:#fff;text-shadow:none}.ui.basic.purple.button,.ui.basic.purple.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #a333c8 inset;box-shadow:0 0 0 1px #a333c8 inset;color:#a333c8}.ui.basic.purple.button:hover,.ui.basic.purple.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #9627ba inset;box-shadow:0 0 0 1px #9627ba inset;color:#9627ba}.ui.basic.purple.button:focus,.ui.basic.purple.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #8f1eb4 inset;box-shadow:0 0 0 1px #8f1eb4 inset;color:#9627ba}.ui.basic.purple.active.button,.ui.basic.purple.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #9724be inset;box-shadow:0 0 0 1px #9724be inset;color:#82299f}.ui.basic.purple.button:active,.ui.basic.purple.buttons .button:active{-webkit-box-shadow:0 0 0 1px #82299f inset;box-shadow:0 0 0 1px #82299f inset;color:#82299f}.ui.buttons:not(.vertical)>.basic.purple.button:not(:first-child){margin-left:-1px}.ui.inverted.purple.button,.ui.inverted.purple.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #dc73ff inset;box-shadow:0 0 0 2px #dc73ff inset;color:#dc73ff}.ui.inverted.purple.button.active,.ui.inverted.purple.button:active,.ui.inverted.purple.button:focus,.ui.inverted.purple.button:hover,.ui.inverted.purple.buttons .button.active,.ui.inverted.purple.buttons .button:active,.ui.inverted.purple.buttons .button:focus,.ui.inverted.purple.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.purple.button:hover,.ui.inverted.purple.buttons .button:hover{background-color:#cf40ff}.ui.inverted.purple.button:focus,.ui.inverted.purple.buttons .button:focus{background-color:#d24aff}.ui.inverted.purple.active.button,.ui.inverted.purple.buttons .active.button{background-color:#d65aff}.ui.inverted.purple.button:active,.ui.inverted.purple.buttons .button:active{background-color:#cf40ff}.ui.inverted.purple.basic.button,.ui.inverted.purple.basic.buttons .button,.ui.inverted.purple.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.purple.basic.button:hover,.ui.inverted.purple.basic.buttons .button:hover,.ui.inverted.purple.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #cf40ff inset;box-shadow:0 0 0 2px #cf40ff inset;color:#dc73ff}.ui.inverted.purple.basic.button:focus,.ui.inverted.purple.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #d24aff inset;box-shadow:0 0 0 2px #d24aff inset;color:#dc73ff}.ui.inverted.purple.basic.active.button,.ui.inverted.purple.basic.buttons .active.button,.ui.inverted.purple.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #d65aff inset;box-shadow:0 0 0 2px #d65aff inset;color:#dc73ff}.ui.inverted.purple.basic.button:active,.ui.inverted.purple.basic.buttons .button:active,.ui.inverted.purple.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #cf40ff inset;box-shadow:0 0 0 2px #cf40ff inset;color:#dc73ff}.ui.tertiary.purple.button,.ui.tertiary.purple.buttons .button,.ui.tertiary.purple.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#a333c8}.ui.tertiary.purple.button:hover,.ui.tertiary.purple.buttons .button:hover,.ui.tertiary.purple.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #8a3ea4;box-shadow:inset 0 -.2em 0 #8a3ea4;color:#8a3ea4}.ui.tertiary.purple.button:focus,.ui.tertiary.purple.buttons .button:focus,.ui.tertiary.purple.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #84339f;box-shadow:inset 0 -.2em 0 #84339f;color:#84339f}.ui.tertiary.purple.active.button,.ui.tertiary.purple.button:active,.ui.tertiary.purple.buttons .active.button,.ui.tertiary.purple.buttons .button:active,.ui.tertiary.purple.buttons .tertiary.active.button,.ui.tertiary.purple.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #a30dd4;box-shadow:inset 0 -.2em 0 #a30dd4;color:#9724be}.ui.pink.button,.ui.pink.buttons .button{background-color:#e03997;color:#fff;text-shadow:none;background-image:none}.ui.pink.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.pink.button:hover,.ui.pink.buttons .button:hover{background-color:#e61a8d;color:#fff;text-shadow:none}.ui.pink.button:focus,.ui.pink.buttons .button:focus{background-color:#e10f85;color:#fff;text-shadow:none}.ui.pink.button:active,.ui.pink.buttons .button:active{background-color:#c71f7e;color:#fff;text-shadow:none}.ui.pink.active.button,.ui.pink.button .active.button:active,.ui.pink.buttons .active.button,.ui.pink.buttons .active.button:active{background-color:#ea158d;color:#fff;text-shadow:none}.ui.basic.pink.button,.ui.basic.pink.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #e03997 inset;box-shadow:0 0 0 1px #e03997 inset;color:#e03997}.ui.basic.pink.button:hover,.ui.basic.pink.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #e61a8d inset;box-shadow:0 0 0 1px #e61a8d inset;color:#e61a8d}.ui.basic.pink.button:focus,.ui.basic.pink.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #e10f85 inset;box-shadow:0 0 0 1px #e10f85 inset;color:#e61a8d}.ui.basic.pink.active.button,.ui.basic.pink.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #ea158d inset;box-shadow:0 0 0 1px #ea158d inset;color:#c71f7e}.ui.basic.pink.button:active,.ui.basic.pink.buttons .button:active{-webkit-box-shadow:0 0 0 1px #c71f7e inset;box-shadow:0 0 0 1px #c71f7e inset;color:#c71f7e}.ui.buttons:not(.vertical)>.basic.pink.button:not(:first-child){margin-left:-1px}.ui.inverted.pink.button,.ui.inverted.pink.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #ff8edf inset;box-shadow:0 0 0 2px #ff8edf inset;color:#ff8edf}.ui.inverted.pink.button.active,.ui.inverted.pink.button:active,.ui.inverted.pink.button:focus,.ui.inverted.pink.button:hover,.ui.inverted.pink.buttons .button.active,.ui.inverted.pink.buttons .button:active,.ui.inverted.pink.buttons .button:focus,.ui.inverted.pink.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.pink.button:hover,.ui.inverted.pink.buttons .button:hover{background-color:#ff5bd1}.ui.inverted.pink.button:focus,.ui.inverted.pink.buttons .button:focus{background-color:#ff65d3}.ui.inverted.pink.active.button,.ui.inverted.pink.buttons .active.button{background-color:#ff74d8}.ui.inverted.pink.button:active,.ui.inverted.pink.buttons .button:active{background-color:#ff5bd1}.ui.inverted.pink.basic.button,.ui.inverted.pink.basic.buttons .button,.ui.inverted.pink.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.pink.basic.button:hover,.ui.inverted.pink.basic.buttons .button:hover,.ui.inverted.pink.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #ff5bd1 inset;box-shadow:0 0 0 2px #ff5bd1 inset;color:#ff8edf}.ui.inverted.pink.basic.button:focus,.ui.inverted.pink.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #ff65d3 inset;box-shadow:0 0 0 2px #ff65d3 inset;color:#ff8edf}.ui.inverted.pink.basic.active.button,.ui.inverted.pink.basic.buttons .active.button,.ui.inverted.pink.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #ff74d8 inset;box-shadow:0 0 0 2px #ff74d8 inset;color:#ff8edf}.ui.inverted.pink.basic.button:active,.ui.inverted.pink.basic.buttons .button:active,.ui.inverted.pink.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #ff5bd1 inset;box-shadow:0 0 0 2px #ff5bd1 inset;color:#ff8edf}.ui.tertiary.pink.button,.ui.tertiary.pink.buttons .button,.ui.tertiary.pink.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#e03997}.ui.tertiary.pink.button:hover,.ui.tertiary.pink.buttons .button:hover,.ui.tertiary.pink.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #cc3389;box-shadow:inset 0 -.2em 0 #cc3389;color:#cc3389}.ui.tertiary.pink.button:focus,.ui.tertiary.pink.buttons .button:focus,.ui.tertiary.pink.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #c92782;box-shadow:inset 0 -.2em 0 #c92782;color:#c92782}.ui.tertiary.pink.active.button,.ui.tertiary.pink.button:active,.ui.tertiary.pink.buttons .active.button,.ui.tertiary.pink.buttons .button:active,.ui.tertiary.pink.buttons .tertiary.active.button,.ui.tertiary.pink.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #ff0090;box-shadow:inset 0 -.2em 0 #ff0090;color:#ea158d}.ui.brown.button,.ui.brown.buttons .button{background-color:#a5673f;color:#fff;text-shadow:none;background-image:none}.ui.brown.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.brown.button:hover,.ui.brown.buttons .button:hover{background-color:#975b33;color:#fff;text-shadow:none}.ui.brown.button:focus,.ui.brown.buttons .button:focus{background-color:#90532b;color:#fff;text-shadow:none}.ui.brown.button:active,.ui.brown.buttons .button:active{background-color:#805031;color:#fff;text-shadow:none}.ui.brown.active.button,.ui.brown.button .active.button:active,.ui.brown.buttons .active.button,.ui.brown.buttons .active.button:active{background-color:#995a31;color:#fff;text-shadow:none}.ui.basic.brown.button,.ui.basic.brown.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #a5673f inset;box-shadow:0 0 0 1px #a5673f inset;color:#a5673f}.ui.basic.brown.button:hover,.ui.basic.brown.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #975b33 inset;box-shadow:0 0 0 1px #975b33 inset;color:#975b33}.ui.basic.brown.button:focus,.ui.basic.brown.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #90532b inset;box-shadow:0 0 0 1px #90532b inset;color:#975b33}.ui.basic.brown.active.button,.ui.basic.brown.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #995a31 inset;box-shadow:0 0 0 1px #995a31 inset;color:#805031}.ui.basic.brown.button:active,.ui.basic.brown.buttons .button:active{-webkit-box-shadow:0 0 0 1px #805031 inset;box-shadow:0 0 0 1px #805031 inset;color:#805031}.ui.buttons:not(.vertical)>.basic.brown.button:not(:first-child){margin-left:-1px}.ui.inverted.brown.button,.ui.inverted.brown.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #d67c1c inset;box-shadow:0 0 0 2px #d67c1c inset;color:#d67c1c}.ui.inverted.brown.button.active,.ui.inverted.brown.button:active,.ui.inverted.brown.button:focus,.ui.inverted.brown.button:hover,.ui.inverted.brown.buttons .button.active,.ui.inverted.brown.buttons .button:active,.ui.inverted.brown.buttons .button:focus,.ui.inverted.brown.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.brown.button:hover,.ui.inverted.brown.buttons .button:hover{background-color:#b0620f}.ui.inverted.brown.button:focus,.ui.inverted.brown.buttons .button:focus{background-color:#c16808}.ui.inverted.brown.active.button,.ui.inverted.brown.buttons .active.button{background-color:#cc6f0d}.ui.inverted.brown.button:active,.ui.inverted.brown.buttons .button:active{background-color:#a96216}.ui.inverted.brown.basic.button,.ui.inverted.brown.basic.buttons .button,.ui.inverted.brown.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.brown.basic.button:hover,.ui.inverted.brown.basic.buttons .button:hover,.ui.inverted.brown.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #b0620f inset;box-shadow:0 0 0 2px #b0620f inset;color:#d67c1c}.ui.inverted.brown.basic.button:focus,.ui.inverted.brown.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #c16808 inset;box-shadow:0 0 0 2px #c16808 inset;color:#d67c1c}.ui.inverted.brown.basic.active.button,.ui.inverted.brown.basic.buttons .active.button,.ui.inverted.brown.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #cc6f0d inset;box-shadow:0 0 0 2px #cc6f0d inset;color:#d67c1c}.ui.inverted.brown.basic.button:active,.ui.inverted.brown.basic.buttons .button:active,.ui.inverted.brown.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #a96216 inset;box-shadow:0 0 0 2px #a96216 inset;color:#d67c1c}.ui.tertiary.brown.button,.ui.tertiary.brown.buttons .button,.ui.tertiary.brown.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#a5673f}.ui.tertiary.brown.button:hover,.ui.tertiary.brown.buttons .button:hover,.ui.tertiary.brown.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #835f48;box-shadow:inset 0 -.2em 0 #835f48;color:#835f48}.ui.tertiary.brown.button:focus,.ui.tertiary.brown.buttons .button:focus,.ui.tertiary.brown.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #7d573e;box-shadow:inset 0 -.2em 0 #7d573e;color:#7d573e}.ui.tertiary.brown.active.button,.ui.tertiary.brown.button:active,.ui.tertiary.brown.buttons .active.button,.ui.tertiary.brown.buttons .button:active,.ui.tertiary.brown.buttons .tertiary.active.button,.ui.tertiary.brown.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #ae561d;box-shadow:inset 0 -.2em 0 #ae561d;color:#995a31}.ui.grey.button,.ui.grey.buttons .button{background-color:#767676;color:#fff;text-shadow:none;background-image:none}.ui.grey.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.grey.button:hover,.ui.grey.buttons .button:hover{background-color:#838383;color:#fff;text-shadow:none}.ui.grey.button:focus,.ui.grey.buttons .button:focus{background-color:#8a8a8a;color:#fff;text-shadow:none}.ui.grey.button:active,.ui.grey.buttons .button:active{background-color:#909090;color:#fff;text-shadow:none}.ui.grey.active.button,.ui.grey.button .active.button:active,.ui.grey.buttons .active.button,.ui.grey.buttons .active.button:active{background-color:#696969;color:#fff;text-shadow:none}.ui.basic.grey.button,.ui.basic.grey.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #767676 inset;box-shadow:0 0 0 1px #767676 inset;color:#767676}.ui.basic.grey.button:hover,.ui.basic.grey.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #838383 inset;box-shadow:0 0 0 1px #838383 inset;color:#838383}.ui.basic.grey.button:focus,.ui.basic.grey.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #8a8a8a inset;box-shadow:0 0 0 1px #8a8a8a inset;color:#838383}.ui.basic.grey.active.button,.ui.basic.grey.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #696969 inset;box-shadow:0 0 0 1px #696969 inset;color:#909090}.ui.basic.grey.button:active,.ui.basic.grey.buttons .button:active{-webkit-box-shadow:0 0 0 1px #909090 inset;box-shadow:0 0 0 1px #909090 inset;color:#909090}.ui.buttons:not(.vertical)>.basic.grey.button:not(:first-child){margin-left:-1px}.ui.inverted.grey.button,.ui.inverted.grey.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #d4d4d5 inset;box-shadow:0 0 0 2px #d4d4d5 inset;color:#fff}.ui.inverted.grey.button.active,.ui.inverted.grey.button:active,.ui.inverted.grey.button:focus,.ui.inverted.grey.button:hover,.ui.inverted.grey.buttons .button.active,.ui.inverted.grey.buttons .button:active,.ui.inverted.grey.buttons .button:focus,.ui.inverted.grey.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6)}.ui.inverted.grey.button:hover,.ui.inverted.grey.buttons .button:hover{background-color:#c2c4c5}.ui.inverted.grey.button:focus,.ui.inverted.grey.buttons .button:focus{background-color:#c7c9cb}.ui.inverted.grey.active.button,.ui.inverted.grey.buttons .active.button{background-color:#cfd0d2}.ui.inverted.grey.button:active,.ui.inverted.grey.buttons .button:active{background-color:#c2c4c5}.ui.inverted.grey.basic.button,.ui.inverted.grey.basic.buttons .button,.ui.inverted.grey.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.grey.basic.button:hover,.ui.inverted.grey.basic.buttons .button:hover,.ui.inverted.grey.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #c2c4c5 inset;box-shadow:0 0 0 2px #c2c4c5 inset;color:#fff}.ui.inverted.grey.basic.button:focus,.ui.inverted.grey.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #c7c9cb inset;box-shadow:0 0 0 2px #c7c9cb inset;color:#dcddde}.ui.inverted.grey.basic.active.button,.ui.inverted.grey.basic.buttons .active.button,.ui.inverted.grey.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #cfd0d2 inset;box-shadow:0 0 0 2px #cfd0d2 inset;color:#fff}.ui.inverted.grey.basic.button:active,.ui.inverted.grey.basic.buttons .button:active,.ui.inverted.grey.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #c2c4c5 inset;box-shadow:0 0 0 2px #c2c4c5 inset;color:#fff}.ui.tertiary.grey.button,.ui.tertiary.grey.buttons .button,.ui.tertiary.grey.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#767676}.ui.tertiary.grey.button:hover,.ui.tertiary.grey.buttons .button:hover,.ui.tertiary.grey.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #838383;box-shadow:inset 0 -.2em 0 #838383;color:#838383}.ui.tertiary.grey.button:focus,.ui.tertiary.grey.buttons .button:focus,.ui.tertiary.grey.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #8a8a8a;box-shadow:inset 0 -.2em 0 #8a8a8a;color:#8a8a8a}.ui.tertiary.grey.active.button,.ui.tertiary.grey.button:active,.ui.tertiary.grey.buttons .active.button,.ui.tertiary.grey.buttons .button:active,.ui.tertiary.grey.buttons .tertiary.active.button,.ui.tertiary.grey.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #7e5454;box-shadow:inset 0 -.2em 0 #7e5454;color:#696969}.ui.black.button,.ui.black.buttons .button{background-color:#1b1c1d;color:#fff;text-shadow:none;background-image:none}.ui.black.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.black.button:hover,.ui.black.buttons .button:hover{background-color:#27292a;color:#fff;text-shadow:none}.ui.black.button:focus,.ui.black.buttons .button:focus{background-color:#2f3032;color:#fff;text-shadow:none}.ui.black.button:active,.ui.black.buttons .button:active{background-color:#343637;color:#fff;text-shadow:none}.ui.black.active.button,.ui.black.button .active.button:active,.ui.black.buttons .active.button,.ui.black.buttons .active.button:active{background-color:#0f0f10;color:#fff;text-shadow:none}.ui.basic.black.button,.ui.basic.black.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #1b1c1d inset;box-shadow:0 0 0 1px #1b1c1d inset;color:#1b1c1d}.ui.basic.black.button:hover,.ui.basic.black.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #27292a inset;box-shadow:0 0 0 1px #27292a inset;color:#27292a}.ui.basic.black.button:focus,.ui.basic.black.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #2f3032 inset;box-shadow:0 0 0 1px #2f3032 inset;color:#27292a}.ui.basic.black.active.button,.ui.basic.black.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #0f0f10 inset;box-shadow:0 0 0 1px #0f0f10 inset;color:#343637}.ui.basic.black.button:active,.ui.basic.black.buttons .button:active{-webkit-box-shadow:0 0 0 1px #343637 inset;box-shadow:0 0 0 1px #343637 inset;color:#343637}.ui.buttons:not(.vertical)>.basic.black.button:not(:first-child){margin-left:-1px}.ui.inverted.black.button,.ui.inverted.black.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #d4d4d5 inset;box-shadow:0 0 0 2px #d4d4d5 inset;color:#fff}.ui.inverted.black.button.active,.ui.inverted.black.button:active,.ui.inverted.black.button:focus,.ui.inverted.black.button:hover,.ui.inverted.black.buttons .button.active,.ui.inverted.black.buttons .button:active,.ui.inverted.black.buttons .button:focus,.ui.inverted.black.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.black.button:hover,.ui.inverted.black.buttons .button:hover{background-color:#000}.ui.inverted.black.button:focus,.ui.inverted.black.buttons .button:focus{background-color:#000}.ui.inverted.black.active.button,.ui.inverted.black.buttons .active.button{background-color:#000}.ui.inverted.black.button:active,.ui.inverted.black.buttons .button:active{background-color:#000}.ui.inverted.black.basic.button,.ui.inverted.black.basic.buttons .button,.ui.inverted.black.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.black.basic.button:hover,.ui.inverted.black.basic.buttons .button:hover,.ui.inverted.black.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #000 inset;box-shadow:0 0 0 2px #000 inset;color:#fff}.ui.inverted.black.basic.button:focus,.ui.inverted.black.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #000 inset;box-shadow:0 0 0 2px #000 inset;color:#545454}.ui.inverted.black.basic.active.button,.ui.inverted.black.basic.buttons .active.button,.ui.inverted.black.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #000 inset;box-shadow:0 0 0 2px #000 inset;color:#fff}.ui.inverted.black.basic.button:active,.ui.inverted.black.basic.buttons .button:active,.ui.inverted.black.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #000 inset;box-shadow:0 0 0 2px #000 inset;color:#fff}.ui.tertiary.black.button,.ui.tertiary.black.buttons .button,.ui.tertiary.black.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#1b1c1d}.ui.tertiary.black.button:hover,.ui.tertiary.black.buttons .button:hover,.ui.tertiary.black.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #8b8f93;box-shadow:inset 0 -.2em 0 #8b8f93;color:#8b8f93}.ui.tertiary.black.button:focus,.ui.tertiary.black.buttons .button:focus,.ui.tertiary.black.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #93969a;box-shadow:inset 0 -.2em 0 #93969a;color:#93969a}.ui.tertiary.black.active.button,.ui.tertiary.black.button:active,.ui.tertiary.black.buttons .active.button,.ui.tertiary.black.buttons .button:active,.ui.tertiary.black.buttons .tertiary.active.button,.ui.tertiary.black.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #404245;box-shadow:inset 0 -.2em 0 #404245;color:#0f0f10}.ui.positive.button,.ui.positive.buttons .button{background-color:#21ba45;color:#fff;text-shadow:none;background-image:none}.ui.positive.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.positive.button:hover,.ui.positive.buttons .button:hover{background-color:#16ab39;color:#fff;text-shadow:none}.ui.positive.button:focus,.ui.positive.buttons .button:focus{background-color:#0ea432;color:#fff;text-shadow:none}.ui.positive.button:active,.ui.positive.buttons .button:active{background-color:#198f35;color:#fff;text-shadow:none}.ui.positive.active.button,.ui.positive.button .active.button:active,.ui.positive.buttons .active.button,.ui.positive.buttons .active.button:active{background-color:#13ae38;color:#fff;text-shadow:none}.ui.basic.positive.button,.ui.basic.positive.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #21ba45 inset;box-shadow:0 0 0 1px #21ba45 inset;color:#21ba45}.ui.basic.positive.button:hover,.ui.basic.positive.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #16ab39 inset;box-shadow:0 0 0 1px #16ab39 inset;color:#16ab39}.ui.basic.positive.button:focus,.ui.basic.positive.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #0ea432 inset;box-shadow:0 0 0 1px #0ea432 inset;color:#16ab39}.ui.basic.positive.active.button,.ui.basic.positive.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #13ae38 inset;box-shadow:0 0 0 1px #13ae38 inset;color:#198f35}.ui.basic.positive.button:active,.ui.basic.positive.buttons .button:active{-webkit-box-shadow:0 0 0 1px #198f35 inset;box-shadow:0 0 0 1px #198f35 inset;color:#198f35}.ui.buttons:not(.vertical)>.basic.positive.button:not(:first-child){margin-left:-1px}.ui.negative.button,.ui.negative.buttons .button{background-color:#db2828;color:#fff;text-shadow:none;background-image:none}.ui.negative.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.negative.button:hover,.ui.negative.buttons .button:hover{background-color:#d01919;color:#fff;text-shadow:none}.ui.negative.button:focus,.ui.negative.buttons .button:focus{background-color:#ca1010;color:#fff;text-shadow:none}.ui.negative.button:active,.ui.negative.buttons .button:active{background-color:#b21e1e;color:#fff;text-shadow:none}.ui.negative.active.button,.ui.negative.button .active.button:active,.ui.negative.buttons .active.button,.ui.negative.buttons .active.button:active{background-color:#d41515;color:#fff;text-shadow:none}.ui.basic.negative.button,.ui.basic.negative.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #db2828 inset;box-shadow:0 0 0 1px #db2828 inset;color:#db2828}.ui.basic.negative.button:hover,.ui.basic.negative.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #d01919 inset;box-shadow:0 0 0 1px #d01919 inset;color:#d01919}.ui.basic.negative.button:focus,.ui.basic.negative.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #ca1010 inset;box-shadow:0 0 0 1px #ca1010 inset;color:#d01919}.ui.basic.negative.active.button,.ui.basic.negative.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #d41515 inset;box-shadow:0 0 0 1px #d41515 inset;color:#b21e1e}.ui.basic.negative.button:active,.ui.basic.negative.buttons .button:active{-webkit-box-shadow:0 0 0 1px #b21e1e inset;box-shadow:0 0 0 1px #b21e1e inset;color:#b21e1e}.ui.buttons:not(.vertical)>.basic.negative.button:not(:first-child){margin-left:-1px}.ui.buttons{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;font-size:0;vertical-align:baseline;margin:0 .25em 0 0}.ui.buttons:not(.basic):not(.inverted){-webkit-box-shadow:none;box-shadow:none}.ui.buttons:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui.buttons .button{-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;border-radius:0;margin:0}.ui.buttons:not(.basic):not(.inverted)>.button:not(.basic):not(.inverted){-webkit-box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset}.ui.buttons .button:first-child{border-left:none;margin-left:0;border-top-left-radius:.28571429rem;border-bottom-left-radius:.28571429rem}.ui.buttons .button:last-child{border-top-right-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.vertical.buttons{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.vertical.buttons .button{display:block;float:none;width:100%;margin:0;-webkit-box-shadow:none;box-shadow:none;border-radius:0}.ui.vertical.buttons .button:first-child{border-top-left-radius:.28571429rem;border-top-right-radius:.28571429rem}.ui.vertical.buttons .button:last-child{margin-bottom:0;border-bottom-left-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.vertical.buttons .button:only-child{border-radius:.28571429rem}/*! + * # Fomantic-UI - Container + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.container{display:block;max-width:100%}@media only screen and (max-width:767.98px){.ui.ui.ui.container:not(.fluid){width:auto;margin-left:1em;margin-right:1em}.ui.ui.ui.grid.container{width:auto}.ui.ui.ui.relaxed.grid.container{width:auto}.ui.ui.ui.very.relaxed.grid.container{width:auto}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.ui.ui.container:not(.fluid){width:723px;margin-left:auto;margin-right:auto}.ui.ui.ui.grid.container{width:calc(723px + 2rem)}.ui.ui.ui.relaxed.grid.container{width:calc(723px + 3rem)}.ui.ui.ui.very.relaxed.grid.container{width:calc(723px + 5rem)}}@media only screen and (min-width:992px) and (max-width:1199.98px){.ui.ui.ui.container:not(.fluid){width:933px;margin-left:auto;margin-right:auto}.ui.ui.ui.grid.container{width:calc(933px + 2rem)}.ui.ui.ui.relaxed.grid.container{width:calc(933px + 3rem)}.ui.ui.ui.very.relaxed.grid.container{width:calc(933px + 5rem)}}@media only screen and (min-width:1200px){.ui.ui.ui.container:not(.fluid){width:1127px;margin-left:auto;margin-right:auto}.ui.ui.ui.grid.container{width:calc(1127px + 2rem)}.ui.ui.ui.relaxed.grid.container{width:calc(1127px + 3rem)}.ui.ui.ui.very.relaxed.grid.container{width:calc(1127px + 5rem)}}.ui.text.container{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;max-width:700px;line-height:1.5;font-size:1.14285714rem}.ui.fluid.container{width:100%}.ui[class*="left aligned"].container{text-align:left}.ui[class*="center aligned"].container{text-align:center}.ui[class*="right aligned"].container{text-align:right}.ui.justified.container{text-align:justify;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}/*! + * # Fomantic-UI - Divider + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.divider{margin:1rem 0;line-height:1;height:0;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:rgba(0,0,0,.85);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ui.divider:not(.vertical):not(.horizontal){border-top:1px solid rgba(34,36,38,.15);border-bottom:1px solid rgba(255,255,255,.1)}.ui.grid>.column+.divider,.ui.grid>.row>.column+.divider{left:auto}.ui.horizontal.divider{display:table;white-space:nowrap;height:auto;margin:'';line-height:1;text-align:center}.ui.horizontal.divider:after,.ui.horizontal.divider:before{content:'';display:table-cell;position:relative;top:50%;width:50%;background-repeat:no-repeat}.ui.horizontal.divider:before{background-position:right 1em top 50%}.ui.horizontal.divider:after{background-position:left 1em top 50%}.ui.vertical.divider{position:absolute;z-index:2;top:50%;left:50%;margin:0;padding:0;width:auto;height:50%;line-height:0;text-align:center;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.ui.vertical.divider:after,.ui.vertical.divider:before{position:absolute;left:50%;content:'';z-index:3;border-left:1px solid rgba(34,36,38,.15);border-right:1px solid rgba(255,255,255,.1);width:0;height:calc(100% - 1rem)}.ui.vertical.divider:before{top:-100%}.ui.vertical.divider:after{top:auto;bottom:0}@media only screen and (max-width:767.98px){.ui.grid .stackable.row .ui.vertical.divider,.ui.stackable.grid .ui.vertical.divider{display:table;white-space:nowrap;height:auto;margin:'';overflow:hidden;line-height:1;text-align:center;position:static;top:0;left:0;-webkit-transform:none;transform:none}.ui.grid .stackable.row .ui.vertical.divider:after,.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:before{left:0;border-left:none;border-right:none;content:'';display:table-cell;position:relative;top:50%;width:50%;background-repeat:no-repeat}.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:before{background-position:right 1em top 50%}.ui.grid .stackable.row .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:after{background-position:left 1em top 50%}}.ui.divider>.icon{margin:0;font-size:1rem;height:1em;vertical-align:middle}.ui.horizontal.divider[class*="left aligned"]:before{display:none}.ui.horizontal.divider[class*="left aligned"]:after{width:100%}.ui.horizontal.divider[class*="right aligned"]:before{width:100%}.ui.horizontal.divider[class*="right aligned"]:after{display:none}.ui.hidden.divider{border-color:transparent!important}.ui.hidden.divider:after,.ui.hidden.divider:before{display:none}.ui.divider.inverted,.ui.horizontal.inverted.divider,.ui.vertical.inverted.divider{color:#fff}.ui.divider.inverted,.ui.divider.inverted:after,.ui.divider.inverted:before{border-top-color:rgba(34,36,38,.15)!important;border-left-color:rgba(34,36,38,.15)!important;border-bottom-color:rgba(255,255,255,.15)!important;border-right-color:rgba(255,255,255,.15)!important}.ui.fitted.divider{margin:0}.ui.clearing.divider{clear:both}.ui.section.divider{margin-top:2rem;margin-bottom:2rem}.ui.divider{font-size:1rem}.ui.mini.divider{font-size:.78571429rem}.ui.tiny.divider{font-size:.85714286rem}.ui.small.divider{font-size:.92857143rem}.ui.large.divider{font-size:1.14285714rem}.ui.big.divider{font-size:1.28571429rem}.ui.huge.divider{font-size:1.42857143rem}.ui.massive.divider{font-size:1.71428571rem}.ui.horizontal.divider:after,.ui.horizontal.divider:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC)}@media only screen and (max-width:767px){.ui.grid .stackable.row .ui.vertical.divider:after,.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC)}}/*! + * # Fomantic UI - Emoji + * https://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * https://github.com/fomantic/Fomantic-UI/blob/master/LICENSE.md + * + */em[data-emoji]{opacity:1;speak:none;-webkit-backface-visibility:hidden;backface-visibility:hidden}em[data-emoji]:before{content:'\00A0\00A0\00A0\00A0\00A0\00A0\00A0';display:inline-block;line-height:1.28571429em;background-repeat:no-repeat;background-position:center center}em[data-emoji].disabled{opacity:.45}em[data-emoji].loading:before{-webkit-animation:loader 2s linear infinite;animation:loader 2s linear infinite}em[data-emoji].link{cursor:pointer}em[data-emoji].small{font-size:1.5em;vertical-align:middle}em[data-emoji].medium{font-size:3em;vertical-align:middle}em[data-emoji].large{font-size:6em;vertical-align:middle}em[data-emoji].big{font-size:7.5em;vertical-align:middle}em[data-emoji=":interrobang:"]:before,em[data-emoji=interrobang]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2049.svg)}em[data-emoji=":tm:"]:before,em[data-emoji=tm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2122.svg)}em[data-emoji=":information_source:"]:before,em[data-emoji=information_source]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2139.svg)}em[data-emoji=":left_right_arrow:"]:before,em[data-emoji=left_right_arrow]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2194.svg)}em[data-emoji=":arrow_up_down:"]:before,em[data-emoji=arrow_up_down]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2195.svg)}em[data-emoji=":arrow_upper_left:"]:before,em[data-emoji=arrow_upper_left]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2196.svg)}em[data-emoji=":arrow_upper_right:"]:before,em[data-emoji=arrow_upper_right]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2197.svg)}em[data-emoji=":arrow_lower_right:"]:before,em[data-emoji=arrow_lower_right]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2198.svg)}em[data-emoji=":arrow_lower_left:"]:before,em[data-emoji=arrow_lower_left]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2199.svg)}em[data-emoji=":keyboard:"]:before,em[data-emoji=keyboard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2328.svg)}em[data-emoji=":sunny:"]:before,em[data-emoji=sunny]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2600.svg)}em[data-emoji=":cloud:"]:before,em[data-emoji=cloud]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2601.svg)}em[data-emoji=":umbrella2:"]:before,em[data-emoji=umbrella2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2602.svg)}em[data-emoji=":snowman2:"]:before,em[data-emoji=snowman2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2603.svg)}em[data-emoji=":comet:"]:before,em[data-emoji=comet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2604.svg)}em[data-emoji=":ballot_box_with_check:"]:before,em[data-emoji=ballot_box_with_check]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2611.svg)}em[data-emoji=":umbrella:"]:before,em[data-emoji=umbrella]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2614.svg)}em[data-emoji=":coffee:"]:before,em[data-emoji=coffee]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2615.svg)}em[data-emoji=":shamrock:"]:before,em[data-emoji=shamrock]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2618.svg)}em[data-emoji=":skull_crossbones:"]:before,em[data-emoji=skull_crossbones]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2620.svg)}em[data-emoji=":radioactive:"]:before,em[data-emoji=radioactive]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2622.svg)}em[data-emoji=":biohazard:"]:before,em[data-emoji=biohazard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2623.svg)}em[data-emoji=":orthodox_cross:"]:before,em[data-emoji=orthodox_cross]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2626.svg)}em[data-emoji=":wheel_of_dharma:"]:before,em[data-emoji=wheel_of_dharma]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2638.svg)}em[data-emoji=":frowning2:"]:before,em[data-emoji=frowning2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2639.svg)}em[data-emoji=":female_sign:"]:before,em[data-emoji=female_sign]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2640.svg)}em[data-emoji=":male_sign:"]:before,em[data-emoji=male_sign]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2642.svg)}em[data-emoji=":aries:"]:before,em[data-emoji=aries]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2648.svg)}em[data-emoji=":taurus:"]:before,em[data-emoji=taurus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2649.svg)}em[data-emoji=":sagittarius:"]:before,em[data-emoji=sagittarius]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2650.svg)}em[data-emoji=":capricorn:"]:before,em[data-emoji=capricorn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2651.svg)}em[data-emoji=":aquarius:"]:before,em[data-emoji=aquarius]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2652.svg)}em[data-emoji=":pisces:"]:before,em[data-emoji=pisces]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2653.svg)}em[data-emoji=":spades:"]:before,em[data-emoji=spades]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2660.svg)}em[data-emoji=":clubs:"]:before,em[data-emoji=clubs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2663.svg)}em[data-emoji=":hearts:"]:before,em[data-emoji=hearts]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2665.svg)}em[data-emoji=":diamonds:"]:before,em[data-emoji=diamonds]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2666.svg)}em[data-emoji=":hotsprings:"]:before,em[data-emoji=hotsprings]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2668.svg)}em[data-emoji=":hammer_pick:"]:before,em[data-emoji=hammer_pick]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2692.svg)}em[data-emoji=":anchor:"]:before,em[data-emoji=anchor]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2693.svg)}em[data-emoji=":crossed_swords:"]:before,em[data-emoji=crossed_swords]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2694.svg)}em[data-emoji=":medical_symbol:"]:before,em[data-emoji=medical_symbol]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2695.svg)}em[data-emoji=":scales:"]:before,em[data-emoji=scales]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2696.svg)}em[data-emoji=":alembic:"]:before,em[data-emoji=alembic]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2697.svg)}em[data-emoji=":gear:"]:before,em[data-emoji=gear]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2699.svg)}em[data-emoji=":scissors:"]:before,em[data-emoji=scissors]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2702.svg)}em[data-emoji=":white_check_mark:"]:before,em[data-emoji=white_check_mark]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2705.svg)}em[data-emoji=":airplane:"]:before,em[data-emoji=airplane]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2708.svg)}em[data-emoji=":envelope:"]:before,em[data-emoji=envelope]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2709.svg)}em[data-emoji=":black_nib:"]:before,em[data-emoji=black_nib]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2712.svg)}em[data-emoji=":heavy_check_mark:"]:before,em[data-emoji=heavy_check_mark]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2714.svg)}em[data-emoji=":heavy_multiplication_x:"]:before,em[data-emoji=heavy_multiplication_x]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2716.svg)}em[data-emoji=":star_of_david:"]:before,em[data-emoji=star_of_david]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2721.svg)}em[data-emoji=":sparkles:"]:before,em[data-emoji=sparkles]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2728.svg)}em[data-emoji=":eight_spoked_asterisk:"]:before,em[data-emoji=eight_spoked_asterisk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2733.svg)}em[data-emoji=":eight_pointed_black_star:"]:before,em[data-emoji=eight_pointed_black_star]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2734.svg)}em[data-emoji=":snowflake:"]:before,em[data-emoji=snowflake]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2744.svg)}em[data-emoji=":sparkle:"]:before,em[data-emoji=sparkle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2747.svg)}em[data-emoji=":question:"]:before,em[data-emoji=question]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2753.svg)}em[data-emoji=":grey_question:"]:before,em[data-emoji=grey_question]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2754.svg)}em[data-emoji=":grey_exclamation:"]:before,em[data-emoji=grey_exclamation]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2755.svg)}em[data-emoji=":exclamation:"]:before,em[data-emoji=exclamation]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2757.svg)}em[data-emoji=":heart_exclamation:"]:before,em[data-emoji=heart_exclamation]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2763.svg)}em[data-emoji=":heart:"]:before,em[data-emoji=heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2764.svg)}em[data-emoji=":heavy_plus_sign:"]:before,em[data-emoji=heavy_plus_sign]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2795.svg)}em[data-emoji=":heavy_minus_sign:"]:before,em[data-emoji=heavy_minus_sign]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2796.svg)}em[data-emoji=":heavy_division_sign:"]:before,em[data-emoji=heavy_division_sign]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2797.svg)}em[data-emoji=":arrow_heading_up:"]:before,em[data-emoji=arrow_heading_up]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2934.svg)}em[data-emoji=":arrow_heading_down:"]:before,em[data-emoji=arrow_heading_down]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2935.svg)}em[data-emoji=":wavy_dash:"]:before,em[data-emoji=wavy_dash]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/3030.svg)}em[data-emoji=":congratulations:"]:before,em[data-emoji=congratulations]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/3297.svg)}em[data-emoji=":secret:"]:before,em[data-emoji=secret]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/3299.svg)}em[data-emoji=":orange_heart:"]:before,em[data-emoji=orange_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e1.svg)}em[data-emoji=":yellow_heart:"]:before,em[data-emoji=yellow_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f49b.svg)}em[data-emoji=":green_heart:"]:before,em[data-emoji=green_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f49a.svg)}em[data-emoji=":blue_heart:"]:before,em[data-emoji=blue_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f499.svg)}em[data-emoji=":purple_heart:"]:before,em[data-emoji=purple_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f49c.svg)}em[data-emoji=":black_heart:"]:before,em[data-emoji=black_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5a4.svg)}em[data-emoji=":brown_heart:"]:before,em[data-emoji=brown_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f90e.svg)}em[data-emoji=":white_heart:"]:before,em[data-emoji=white_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f90d.svg)}em[data-emoji=":broken_heart:"]:before,em[data-emoji=broken_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f494.svg)}em[data-emoji=":two_hearts:"]:before,em[data-emoji=two_hearts]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f495.svg)}em[data-emoji=":revolving_hearts:"]:before,em[data-emoji=revolving_hearts]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f49e.svg)}em[data-emoji=":heartbeat:"]:before,em[data-emoji=heartbeat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f493.svg)}em[data-emoji=":heartpulse:"]:before,em[data-emoji=heartpulse]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f497.svg)}em[data-emoji=":sparkling_heart:"]:before,em[data-emoji=sparkling_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f496.svg)}em[data-emoji=":cupid:"]:before,em[data-emoji=cupid]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f498.svg)}em[data-emoji=":gift_heart:"]:before,em[data-emoji=gift_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f49d.svg)}em[data-emoji=":heart_decoration:"]:before,em[data-emoji=heart_decoration]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f49f.svg)}em[data-emoji=":peace:"]:before,em[data-emoji=peace]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/262e.svg)}em[data-emoji=":cross:"]:before,em[data-emoji=cross]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/271d.svg)}em[data-emoji=":star_and_crescent:"]:before,em[data-emoji=star_and_crescent]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/262a.svg)}em[data-emoji=":om_symbol:"]:before,em[data-emoji=om_symbol]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f549.svg)}em[data-emoji=":six_pointed_star:"]:before,em[data-emoji=six_pointed_star]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f52f.svg)}em[data-emoji=":menorah:"]:before,em[data-emoji=menorah]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f54e.svg)}em[data-emoji=":yin_yang:"]:before,em[data-emoji=yin_yang]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/262f.svg)}em[data-emoji=":place_of_worship:"]:before,em[data-emoji=place_of_worship]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6d0.svg)}em[data-emoji=":ophiuchus:"]:before,em[data-emoji=ophiuchus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26ce.svg)}em[data-emoji=":gemini:"]:before,em[data-emoji=gemini]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/264a.svg)}em[data-emoji=":cancer:"]:before,em[data-emoji=cancer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/264b.svg)}em[data-emoji=":leo:"]:before,em[data-emoji=leo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/264c.svg)}em[data-emoji=":virgo:"]:before,em[data-emoji=virgo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/264d.svg)}em[data-emoji=":libra:"]:before,em[data-emoji=libra]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/264e.svg)}em[data-emoji=":scorpius:"]:before,em[data-emoji=scorpius]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/264f.svg)}em[data-emoji=":id:"]:before,em[data-emoji=id]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f194.svg)}em[data-emoji=":atom:"]:before,em[data-emoji=atom]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/269b.svg)}em[data-emoji=":accept:"]:before,em[data-emoji=accept]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f251.svg)}em[data-emoji=":mobile_phone_off:"]:before,em[data-emoji=mobile_phone_off]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f4.svg)}em[data-emoji=":vibration_mode:"]:before,em[data-emoji=vibration_mode]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f3.svg)}em[data-emoji=":u6709:"]:before,em[data-emoji=u6709]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f236.svg)}em[data-emoji=":u7121:"]:before,em[data-emoji=u7121]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f21a.svg)}em[data-emoji=":u7533:"]:before,em[data-emoji=u7533]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f238.svg)}em[data-emoji=":u55b6:"]:before,em[data-emoji=u55b6]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f23a.svg)}em[data-emoji=":u6708:"]:before,em[data-emoji=u6708]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f237.svg)}em[data-emoji=":vs:"]:before,em[data-emoji=vs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f19a.svg)}em[data-emoji=":white_flower:"]:before,em[data-emoji=white_flower]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ae.svg)}em[data-emoji=":ideograph_advantage:"]:before,em[data-emoji=ideograph_advantage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f250.svg)}em[data-emoji=":u5408:"]:before,em[data-emoji=u5408]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f234.svg)}em[data-emoji=":u6e80:"]:before,em[data-emoji=u6e80]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f235.svg)}em[data-emoji=":u5272:"]:before,em[data-emoji=u5272]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f239.svg)}em[data-emoji=":u7981:"]:before,em[data-emoji=u7981]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f232.svg)}em[data-emoji=":a:"]:before,em[data-emoji="a"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f170.svg)}em[data-emoji=":b:"]:before,em[data-emoji="b"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f171.svg)}em[data-emoji=":ab:"]:before,em[data-emoji=ab]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f18e.svg)}em[data-emoji=":cl:"]:before,em[data-emoji=cl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f191.svg)}em[data-emoji=":o2:"]:before,em[data-emoji=o2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f17e.svg)}em[data-emoji=":sos:"]:before,em[data-emoji=sos]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f198.svg)}em[data-emoji=":x:"]:before,em[data-emoji="x"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/274c.svg)}em[data-emoji=":o:"]:before,em[data-emoji="o"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2b55.svg)}em[data-emoji=":octagonal_sign:"]:before,em[data-emoji=octagonal_sign]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6d1.svg)}em[data-emoji=":no_entry:"]:before,em[data-emoji=no_entry]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26d4.svg)}em[data-emoji=":name_badge:"]:before,em[data-emoji=name_badge]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4db.svg)}em[data-emoji=":no_entry_sign:"]:before,em[data-emoji=no_entry_sign]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6ab.svg)}em[data-emoji="100"]:before,em[data-emoji=":100:"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4af.svg)}em[data-emoji=":anger:"]:before,em[data-emoji=anger]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a2.svg)}em[data-emoji=":no_pedestrians:"]:before,em[data-emoji=no_pedestrians]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b7.svg)}em[data-emoji=":do_not_litter:"]:before,em[data-emoji=do_not_litter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6af.svg)}em[data-emoji=":no_bicycles:"]:before,em[data-emoji=no_bicycles]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b3.svg)}em[data-emoji=":non-potable_water:"]:before,em[data-emoji=non-potable_water]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b1.svg)}em[data-emoji=":underage:"]:before,em[data-emoji=underage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f51e.svg)}em[data-emoji=":no_mobile_phones:"]:before,em[data-emoji=no_mobile_phones]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f5.svg)}em[data-emoji=":no_smoking:"]:before,em[data-emoji=no_smoking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6ad.svg)}em[data-emoji=":bangbang:"]:before,em[data-emoji=bangbang]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/203c.svg)}em[data-emoji=":low_brightness:"]:before,em[data-emoji=low_brightness]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f505.svg)}em[data-emoji=":high_brightness:"]:before,em[data-emoji=high_brightness]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f506.svg)}em[data-emoji=":part_alternation_mark:"]:before,em[data-emoji=part_alternation_mark]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/303d.svg)}em[data-emoji=":warning:"]:before,em[data-emoji=warning]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26a0.svg)}em[data-emoji=":children_crossing:"]:before,em[data-emoji=children_crossing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b8.svg)}em[data-emoji=":trident:"]:before,em[data-emoji=trident]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f531.svg)}em[data-emoji=":fleur-de-lis:"]:before,em[data-emoji=fleur-de-lis]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/269c.svg)}em[data-emoji=":beginner:"]:before,em[data-emoji=beginner]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f530.svg)}em[data-emoji=":recycle:"]:before,em[data-emoji=recycle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/267b.svg)}em[data-emoji=":u6307:"]:before,em[data-emoji=u6307]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f22f.svg)}em[data-emoji=":chart:"]:before,em[data-emoji=chart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b9.svg)}em[data-emoji=":negative_squared_cross_mark:"]:before,em[data-emoji=negative_squared_cross_mark]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/274e.svg)}em[data-emoji=":globe_with_meridians:"]:before,em[data-emoji=globe_with_meridians]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f310.svg)}em[data-emoji=":diamond_shape_with_a_dot_inside:"]:before,em[data-emoji=diamond_shape_with_a_dot_inside]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a0.svg)}em[data-emoji=":m:"]:before,em[data-emoji="m"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/24c2.svg)}em[data-emoji=":cyclone:"]:before,em[data-emoji=cyclone]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f300.svg)}em[data-emoji=":zzz:"]:before,em[data-emoji=zzz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a4.svg)}em[data-emoji=":atm:"]:before,em[data-emoji=atm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e7.svg)}em[data-emoji=":wc:"]:before,em[data-emoji=wc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6be.svg)}em[data-emoji=":wheelchair:"]:before,em[data-emoji=wheelchair]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/267f.svg)}em[data-emoji=":parking:"]:before,em[data-emoji=parking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f17f.svg)}em[data-emoji=":u7a7a:"]:before,em[data-emoji=u7a7a]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f233.svg)}em[data-emoji=":sa:"]:before,em[data-emoji=sa]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f202.svg)}em[data-emoji=":passport_control:"]:before,em[data-emoji=passport_control]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c2.svg)}em[data-emoji=":customs:"]:before,em[data-emoji=customs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c3.svg)}em[data-emoji=":baggage_claim:"]:before,em[data-emoji=baggage_claim]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c4.svg)}em[data-emoji=":left_luggage:"]:before,em[data-emoji=left_luggage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c5.svg)}em[data-emoji=":mens:"]:before,em[data-emoji=mens]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b9.svg)}em[data-emoji=":womens:"]:before,em[data-emoji=womens]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6ba.svg)}em[data-emoji=":baby_symbol:"]:before,em[data-emoji=baby_symbol]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6bc.svg)}em[data-emoji=":restroom:"]:before,em[data-emoji=restroom]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6bb.svg)}em[data-emoji=":put_litter_in_its_place:"]:before,em[data-emoji=put_litter_in_its_place]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6ae.svg)}em[data-emoji=":cinema:"]:before,em[data-emoji=cinema]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a6.svg)}em[data-emoji=":signal_strength:"]:before,em[data-emoji=signal_strength]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f6.svg)}em[data-emoji=":koko:"]:before,em[data-emoji=koko]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f201.svg)}em[data-emoji=":symbols:"]:before,em[data-emoji=symbols]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f523.svg)}em[data-emoji=":abc:"]:before,em[data-emoji=abc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f524.svg)}em[data-emoji=":abcd:"]:before,em[data-emoji=abcd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f521.svg)}em[data-emoji=":capital_abcd:"]:before,em[data-emoji=capital_abcd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f520.svg)}em[data-emoji=":ng:"]:before,em[data-emoji=ng]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f196.svg)}em[data-emoji=":ok:"]:before,em[data-emoji=ok]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f197.svg)}em[data-emoji=":up:"]:before,em[data-emoji=up]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f199.svg)}em[data-emoji=":cool:"]:before,em[data-emoji=cool]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f192.svg)}em[data-emoji=":new:"]:before,em[data-emoji=new]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f195.svg)}em[data-emoji=":free:"]:before,em[data-emoji=free]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f193.svg)}em[data-emoji=":zero:"]:before,em[data-emoji=zero]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/30-20e3.svg)}em[data-emoji=":one:"]:before,em[data-emoji=one]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/31-20e3.svg)}em[data-emoji=":two:"]:before,em[data-emoji=two]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/32-20e3.svg)}em[data-emoji=":three:"]:before,em[data-emoji=three]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/33-20e3.svg)}em[data-emoji=":four:"]:before,em[data-emoji=four]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/34-20e3.svg)}em[data-emoji=":five:"]:before,em[data-emoji=five]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/35-20e3.svg)}em[data-emoji=":six:"]:before,em[data-emoji=six]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/36-20e3.svg)}em[data-emoji=":seven:"]:before,em[data-emoji=seven]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/37-20e3.svg)}em[data-emoji=":eight:"]:before,em[data-emoji=eight]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/38-20e3.svg)}em[data-emoji=":nine:"]:before,em[data-emoji=nine]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/39-20e3.svg)}em[data-emoji=":keycap_ten:"]:before,em[data-emoji=keycap_ten]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f51f.svg)}em[data-emoji="1234"]:before,em[data-emoji=":1234:"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f522.svg)}em[data-emoji=":hash:"]:before,em[data-emoji=hash]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23-20e3.svg)}em[data-emoji=":asterisk:"]:before,em[data-emoji=asterisk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2a-20e3.svg)}em[data-emoji=":eject:"]:before,em[data-emoji=eject]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23cf.svg)}em[data-emoji=":arrow_forward:"]:before,em[data-emoji=arrow_forward]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/25b6.svg)}em[data-emoji=":pause_button:"]:before,em[data-emoji=pause_button]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23f8.svg)}em[data-emoji=":play_pause:"]:before,em[data-emoji=play_pause]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23ef.svg)}em[data-emoji=":stop_button:"]:before,em[data-emoji=stop_button]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23f9.svg)}em[data-emoji=":record_button:"]:before,em[data-emoji=record_button]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23fa.svg)}em[data-emoji=":track_next:"]:before,em[data-emoji=track_next]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23ed.svg)}em[data-emoji=":track_previous:"]:before,em[data-emoji=track_previous]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23ee.svg)}em[data-emoji=":fast_forward:"]:before,em[data-emoji=fast_forward]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23e9.svg)}em[data-emoji=":rewind:"]:before,em[data-emoji=rewind]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23ea.svg)}em[data-emoji=":arrow_double_up:"]:before,em[data-emoji=arrow_double_up]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23eb.svg)}em[data-emoji=":arrow_double_down:"]:before,em[data-emoji=arrow_double_down]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23ec.svg)}em[data-emoji=":arrow_backward:"]:before,em[data-emoji=arrow_backward]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/25c0.svg)}em[data-emoji=":arrow_up_small:"]:before,em[data-emoji=arrow_up_small]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f53c.svg)}em[data-emoji=":arrow_down_small:"]:before,em[data-emoji=arrow_down_small]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f53d.svg)}em[data-emoji=":arrow_right:"]:before,em[data-emoji=arrow_right]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/27a1.svg)}em[data-emoji=":arrow_left:"]:before,em[data-emoji=arrow_left]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2b05.svg)}em[data-emoji=":arrow_up:"]:before,em[data-emoji=arrow_up]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2b06.svg)}em[data-emoji=":arrow_down:"]:before,em[data-emoji=arrow_down]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2b07.svg)}em[data-emoji=":arrow_right_hook:"]:before,em[data-emoji=arrow_right_hook]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/21aa.svg)}em[data-emoji=":leftwards_arrow_with_hook:"]:before,em[data-emoji=leftwards_arrow_with_hook]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/21a9.svg)}em[data-emoji=":twisted_rightwards_arrows:"]:before,em[data-emoji=twisted_rightwards_arrows]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f500.svg)}em[data-emoji=":repeat:"]:before,em[data-emoji=repeat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f501.svg)}em[data-emoji=":repeat_one:"]:before,em[data-emoji=repeat_one]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f502.svg)}em[data-emoji=":arrows_counterclockwise:"]:before,em[data-emoji=arrows_counterclockwise]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f504.svg)}em[data-emoji=":arrows_clockwise:"]:before,em[data-emoji=arrows_clockwise]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f503.svg)}em[data-emoji=":musical_note:"]:before,em[data-emoji=musical_note]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b5.svg)}em[data-emoji=":notes:"]:before,em[data-emoji=notes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b6.svg)}em[data-emoji=":infinity:"]:before,em[data-emoji=infinity]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/267e.svg)}em[data-emoji=":heavy_dollar_sign:"]:before,em[data-emoji=heavy_dollar_sign]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b2.svg)}em[data-emoji=":currency_exchange:"]:before,em[data-emoji=currency_exchange]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b1.svg)}em[data-emoji=":copyright:"]:before,em[data-emoji=copyright]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/a9.svg)}em[data-emoji=":registered:"]:before,em[data-emoji=registered]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/ae.svg)}em[data-emoji=":curly_loop:"]:before,em[data-emoji=curly_loop]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/27b0.svg)}em[data-emoji=":loop:"]:before,em[data-emoji=loop]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/27bf.svg)}em[data-emoji=":end:"]:before,em[data-emoji=end]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f51a.svg)}em[data-emoji=":back:"]:before,em[data-emoji=back]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f519.svg)}em[data-emoji=":on:"]:before,em[data-emoji=on]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f51b.svg)}em[data-emoji=":top:"]:before,em[data-emoji=top]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f51d.svg)}em[data-emoji=":soon:"]:before,em[data-emoji=soon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f51c.svg)}em[data-emoji=":radio_button:"]:before,em[data-emoji=radio_button]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f518.svg)}em[data-emoji=":white_circle:"]:before,em[data-emoji=white_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26aa.svg)}em[data-emoji=":black_circle:"]:before,em[data-emoji=black_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26ab.svg)}em[data-emoji=":red_circle:"]:before,em[data-emoji=red_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f534.svg)}em[data-emoji=":blue_circle:"]:before,em[data-emoji=blue_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f535.svg)}em[data-emoji=":brown_circle:"]:before,em[data-emoji=brown_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e4.svg)}em[data-emoji=":purple_circle:"]:before,em[data-emoji=purple_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e3.svg)}em[data-emoji=":green_circle:"]:before,em[data-emoji=green_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e2.svg)}em[data-emoji=":yellow_circle:"]:before,em[data-emoji=yellow_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e1.svg)}em[data-emoji=":orange_circle:"]:before,em[data-emoji=orange_circle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e0.svg)}em[data-emoji=":small_red_triangle:"]:before,em[data-emoji=small_red_triangle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f53a.svg)}em[data-emoji=":small_red_triangle_down:"]:before,em[data-emoji=small_red_triangle_down]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f53b.svg)}em[data-emoji=":small_orange_diamond:"]:before,em[data-emoji=small_orange_diamond]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f538.svg)}em[data-emoji=":small_blue_diamond:"]:before,em[data-emoji=small_blue_diamond]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f539.svg)}em[data-emoji=":large_orange_diamond:"]:before,em[data-emoji=large_orange_diamond]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f536.svg)}em[data-emoji=":large_blue_diamond:"]:before,em[data-emoji=large_blue_diamond]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f537.svg)}em[data-emoji=":white_square_button:"]:before,em[data-emoji=white_square_button]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f533.svg)}em[data-emoji=":black_square_button:"]:before,em[data-emoji=black_square_button]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f532.svg)}em[data-emoji=":black_small_square:"]:before,em[data-emoji=black_small_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/25aa.svg)}em[data-emoji=":white_small_square:"]:before,em[data-emoji=white_small_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/25ab.svg)}em[data-emoji=":black_medium_small_square:"]:before,em[data-emoji=black_medium_small_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/25fe.svg)}em[data-emoji=":white_medium_small_square:"]:before,em[data-emoji=white_medium_small_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/25fd.svg)}em[data-emoji=":black_medium_square:"]:before,em[data-emoji=black_medium_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/25fc.svg)}em[data-emoji=":white_medium_square:"]:before,em[data-emoji=white_medium_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/25fb.svg)}em[data-emoji=":black_large_square:"]:before,em[data-emoji=black_large_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2b1b.svg)}em[data-emoji=":white_large_square:"]:before,em[data-emoji=white_large_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2b1c.svg)}em[data-emoji=":orange_square:"]:before,em[data-emoji=orange_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e7.svg)}em[data-emoji=":blue_square:"]:before,em[data-emoji=blue_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e6.svg)}em[data-emoji=":red_square:"]:before,em[data-emoji=red_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e5.svg)}em[data-emoji=":brown_square:"]:before,em[data-emoji=brown_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7eb.svg)}em[data-emoji=":purple_square:"]:before,em[data-emoji=purple_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7ea.svg)}em[data-emoji=":green_square:"]:before,em[data-emoji=green_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e9.svg)}em[data-emoji=":yellow_square:"]:before,em[data-emoji=yellow_square]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f7e8.svg)}em[data-emoji=":speaker:"]:before,em[data-emoji=speaker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f508.svg)}em[data-emoji=":mute:"]:before,em[data-emoji=mute]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f507.svg)}em[data-emoji=":sound:"]:before,em[data-emoji=sound]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f509.svg)}em[data-emoji=":loud_sound:"]:before,em[data-emoji=loud_sound]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f50a.svg)}em[data-emoji=":bell:"]:before,em[data-emoji=bell]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f514.svg)}em[data-emoji=":no_bell:"]:before,em[data-emoji=no_bell]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f515.svg)}em[data-emoji=":mega:"]:before,em[data-emoji=mega]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e3.svg)}em[data-emoji=":loudspeaker:"]:before,em[data-emoji=loudspeaker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e2.svg)}em[data-emoji=":speech_left:"]:before,em[data-emoji=speech_left]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5e8.svg)}em[data-emoji=":eye_in_speech_bubble:"]:before,em[data-emoji=eye_in_speech_bubble]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f441-200d-1f5e8.svg)}em[data-emoji=":speech_balloon:"]:before,em[data-emoji=speech_balloon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ac.svg)}em[data-emoji=":thought_balloon:"]:before,em[data-emoji=thought_balloon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ad.svg)}em[data-emoji=":anger_right:"]:before,em[data-emoji=anger_right]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5ef.svg)}em[data-emoji=":black_joker:"]:before,em[data-emoji=black_joker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f0cf.svg)}em[data-emoji=":flower_playing_cards:"]:before,em[data-emoji=flower_playing_cards]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b4.svg)}em[data-emoji=":mahjong:"]:before,em[data-emoji=mahjong]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f004.svg)}em[data-emoji=":clock1:"]:before,em[data-emoji=clock1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f550.svg)}em[data-emoji=":clock2:"]:before,em[data-emoji=clock2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f551.svg)}em[data-emoji=":clock3:"]:before,em[data-emoji=clock3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f552.svg)}em[data-emoji=":clock4:"]:before,em[data-emoji=clock4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f553.svg)}em[data-emoji=":clock5:"]:before,em[data-emoji=clock5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f554.svg)}em[data-emoji=":clock6:"]:before,em[data-emoji=clock6]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f555.svg)}em[data-emoji=":clock7:"]:before,em[data-emoji=clock7]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f556.svg)}em[data-emoji=":clock8:"]:before,em[data-emoji=clock8]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f557.svg)}em[data-emoji=":clock9:"]:before,em[data-emoji=clock9]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f558.svg)}em[data-emoji=":clock10:"]:before,em[data-emoji=clock10]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f559.svg)}em[data-emoji=":clock11:"]:before,em[data-emoji=clock11]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f55a.svg)}em[data-emoji=":clock12:"]:before,em[data-emoji=clock12]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f55b.svg)}em[data-emoji=":clock130:"]:before,em[data-emoji=clock130]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f55c.svg)}em[data-emoji=":clock230:"]:before,em[data-emoji=clock230]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f55d.svg)}em[data-emoji=":clock330:"]:before,em[data-emoji=clock330]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f55e.svg)}em[data-emoji=":clock430:"]:before,em[data-emoji=clock430]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f55f.svg)}em[data-emoji=":clock530:"]:before,em[data-emoji=clock530]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f560.svg)}em[data-emoji=":clock630:"]:before,em[data-emoji=clock630]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f561.svg)}em[data-emoji=":clock730:"]:before,em[data-emoji=clock730]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f562.svg)}em[data-emoji=":clock830:"]:before,em[data-emoji=clock830]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f563.svg)}em[data-emoji=":clock930:"]:before,em[data-emoji=clock930]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f564.svg)}em[data-emoji=":clock1030:"]:before,em[data-emoji=clock1030]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f565.svg)}em[data-emoji=":clock1130:"]:before,em[data-emoji=clock1130]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f566.svg)}em[data-emoji=":clock1230:"]:before,em[data-emoji=clock1230]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f567.svg)}em[data-emoji=":digit_zero:"]:before,em[data-emoji=digit_zero]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/30-20e3.svg)}em[data-emoji=":digit_one:"]:before,em[data-emoji=digit_one]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/31-20e3.svg)}em[data-emoji=":digit_two:"]:before,em[data-emoji=digit_two]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/32-20e3.svg)}em[data-emoji=":digit_three:"]:before,em[data-emoji=digit_three]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/33-20e3.svg)}em[data-emoji=":digit_four:"]:before,em[data-emoji=digit_four]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/34-20e3.svg)}em[data-emoji=":digit_five:"]:before,em[data-emoji=digit_five]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/35-20e3.svg)}em[data-emoji=":digit_six:"]:before,em[data-emoji=digit_six]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/36-20e3.svg)}em[data-emoji=":digit_seven:"]:before,em[data-emoji=digit_seven]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/37-20e3.svg)}em[data-emoji=":digit_eight:"]:before,em[data-emoji=digit_eight]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/38-20e3.svg)}em[data-emoji=":digit_nine:"]:before,em[data-emoji=digit_nine]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/39-20e3.svg)}em[data-emoji=":pound_symbol:"]:before,em[data-emoji=pound_symbol]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23-20e3.svg)}em[data-emoji=":asterisk_symbol:"]:before,em[data-emoji=asterisk_symbol]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2a-20e3.svg)}em[data-emoji=":soccer:"]:before,em[data-emoji=soccer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26bd.svg)}em[data-emoji=":basketball:"]:before,em[data-emoji=basketball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c0.svg)}em[data-emoji=":football:"]:before,em[data-emoji=football]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c8.svg)}em[data-emoji=":baseball:"]:before,em[data-emoji=baseball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26be.svg)}em[data-emoji=":softball:"]:before,em[data-emoji=softball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f94e.svg)}em[data-emoji=":tennis:"]:before,em[data-emoji=tennis]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3be.svg)}em[data-emoji=":volleyball:"]:before,em[data-emoji=volleyball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d0.svg)}em[data-emoji=":rugby_football:"]:before,em[data-emoji=rugby_football]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c9.svg)}em[data-emoji=":flying_disc:"]:before,em[data-emoji=flying_disc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f94f.svg)}em[data-emoji="8ball"]:before,em[data-emoji=":8ball:"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b1.svg)}em[data-emoji=":ping_pong:"]:before,em[data-emoji=ping_pong]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d3.svg)}em[data-emoji=":badminton:"]:before,em[data-emoji=badminton]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f8.svg)}em[data-emoji=":hockey:"]:before,em[data-emoji=hockey]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d2.svg)}em[data-emoji=":field_hockey:"]:before,em[data-emoji=field_hockey]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d1.svg)}em[data-emoji=":lacrosse:"]:before,em[data-emoji=lacrosse]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f94d.svg)}em[data-emoji=":cricket_game:"]:before,em[data-emoji=cricket_game]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cf.svg)}em[data-emoji=":goal:"]:before,em[data-emoji=goal]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f945.svg)}em[data-emoji=":golf:"]:before,em[data-emoji=golf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f3.svg)}em[data-emoji=":bow_and_arrow:"]:before,em[data-emoji=bow_and_arrow]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f9.svg)}em[data-emoji=":fishing_pole_and_fish:"]:before,em[data-emoji=fishing_pole_and_fish]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a3.svg)}em[data-emoji=":boxing_glove:"]:before,em[data-emoji=boxing_glove]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f94a.svg)}em[data-emoji=":martial_arts_uniform:"]:before,em[data-emoji=martial_arts_uniform]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f94b.svg)}em[data-emoji=":running_shirt_with_sash:"]:before,em[data-emoji=running_shirt_with_sash]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3bd.svg)}em[data-emoji=":skateboard:"]:before,em[data-emoji=skateboard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6f9.svg)}em[data-emoji=":sled:"]:before,em[data-emoji=sled]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6f7.svg)}em[data-emoji=":parachute:"]:before,em[data-emoji=parachute]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa82.svg)}em[data-emoji=":ice_skate:"]:before,em[data-emoji=ice_skate]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f8.svg)}em[data-emoji=":curling_stone:"]:before,em[data-emoji=curling_stone]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f94c.svg)}em[data-emoji=":ski:"]:before,em[data-emoji=ski]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3bf.svg)}em[data-emoji=":skier:"]:before,em[data-emoji=skier]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f7.svg)}em[data-emoji=":snowboarder:"]:before,em[data-emoji=snowboarder]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c2.svg)}em[data-emoji=":snowboarder_tone1:"]:before,em[data-emoji=snowboarder_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c2-1f3fb.svg)}em[data-emoji=":snowboarder_tone2:"]:before,em[data-emoji=snowboarder_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c2-1f3fc.svg)}em[data-emoji=":snowboarder_tone3:"]:before,em[data-emoji=snowboarder_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c2-1f3fd.svg)}em[data-emoji=":snowboarder_tone4:"]:before,em[data-emoji=snowboarder_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c2-1f3fe.svg)}em[data-emoji=":snowboarder_tone5:"]:before,em[data-emoji=snowboarder_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c2-1f3ff.svg)}em[data-emoji=":person_lifting_weights:"]:before,em[data-emoji=person_lifting_weights]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb.svg)}em[data-emoji=":person_lifting_weights_tone1:"]:before,em[data-emoji=person_lifting_weights_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fb.svg)}em[data-emoji=":person_lifting_weights_tone2:"]:before,em[data-emoji=person_lifting_weights_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fc.svg)}em[data-emoji=":person_lifting_weights_tone3:"]:before,em[data-emoji=person_lifting_weights_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fd.svg)}em[data-emoji=":person_lifting_weights_tone4:"]:before,em[data-emoji=person_lifting_weights_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fe.svg)}em[data-emoji=":person_lifting_weights_tone5:"]:before,em[data-emoji=person_lifting_weights_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3ff.svg)}em[data-emoji=":woman_lifting_weights:"]:before,em[data-emoji=woman_lifting_weights]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-fe0f-200d-2640-fe0f.svg)}em[data-emoji=":woman_lifting_weights_tone1:"]:before,em[data-emoji=woman_lifting_weights_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_lifting_weights_tone2:"]:before,em[data-emoji=woman_lifting_weights_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_lifting_weights_tone3:"]:before,em[data-emoji=woman_lifting_weights_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_lifting_weights_tone4:"]:before,em[data-emoji=woman_lifting_weights_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_lifting_weights_tone5:"]:before,em[data-emoji=woman_lifting_weights_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_lifting_weights:"]:before,em[data-emoji=man_lifting_weights]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-fe0f-200d-2642-fe0f.svg)}em[data-emoji=":man_lifting_weights_tone1:"]:before,em[data-emoji=man_lifting_weights_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_lifting_weights_tone2:"]:before,em[data-emoji=man_lifting_weights_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_lifting_weights_tone3:"]:before,em[data-emoji=man_lifting_weights_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_lifting_weights_tone4:"]:before,em[data-emoji=man_lifting_weights_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_lifting_weights_tone5:"]:before,em[data-emoji=man_lifting_weights_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cb-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":people_wrestling:"]:before,em[data-emoji=people_wrestling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93c.svg)}em[data-emoji=":women_wrestling:"]:before,em[data-emoji=women_wrestling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93c-200d-2640-fe0f.svg)}em[data-emoji=":men_wrestling:"]:before,em[data-emoji=men_wrestling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93c-200d-2642-fe0f.svg)}em[data-emoji=":person_doing_cartwheel:"]:before,em[data-emoji=person_doing_cartwheel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938.svg)}em[data-emoji=":person_doing_cartwheel_tone1:"]:before,em[data-emoji=person_doing_cartwheel_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fb.svg)}em[data-emoji=":person_doing_cartwheel_tone2:"]:before,em[data-emoji=person_doing_cartwheel_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fc.svg)}em[data-emoji=":person_doing_cartwheel_tone3:"]:before,em[data-emoji=person_doing_cartwheel_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fd.svg)}em[data-emoji=":person_doing_cartwheel_tone4:"]:before,em[data-emoji=person_doing_cartwheel_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fe.svg)}em[data-emoji=":person_doing_cartwheel_tone5:"]:before,em[data-emoji=person_doing_cartwheel_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3ff.svg)}em[data-emoji=":woman_cartwheeling:"]:before,em[data-emoji=woman_cartwheeling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-200d-2640-fe0f.svg)}em[data-emoji=":woman_cartwheeling_tone1:"]:before,em[data-emoji=woman_cartwheeling_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_cartwheeling_tone2:"]:before,em[data-emoji=woman_cartwheeling_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_cartwheeling_tone3:"]:before,em[data-emoji=woman_cartwheeling_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_cartwheeling_tone4:"]:before,em[data-emoji=woman_cartwheeling_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_cartwheeling_tone5:"]:before,em[data-emoji=woman_cartwheeling_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_cartwheeling:"]:before,em[data-emoji=man_cartwheeling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-200d-2642-fe0f.svg)}em[data-emoji=":man_cartwheeling_tone1:"]:before,em[data-emoji=man_cartwheeling_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_cartwheeling_tone2:"]:before,em[data-emoji=man_cartwheeling_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_cartwheeling_tone3:"]:before,em[data-emoji=man_cartwheeling_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_cartwheeling_tone4:"]:before,em[data-emoji=man_cartwheeling_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_cartwheeling_tone5:"]:before,em[data-emoji=man_cartwheeling_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f938-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_bouncing_ball:"]:before,em[data-emoji=person_bouncing_ball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9.svg)}em[data-emoji=":person_bouncing_ball_tone1:"]:before,em[data-emoji=person_bouncing_ball_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fb.svg)}em[data-emoji=":person_bouncing_ball_tone2:"]:before,em[data-emoji=person_bouncing_ball_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fc.svg)}em[data-emoji=":person_bouncing_ball_tone3:"]:before,em[data-emoji=person_bouncing_ball_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fd.svg)}em[data-emoji=":person_bouncing_ball_tone4:"]:before,em[data-emoji=person_bouncing_ball_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fe.svg)}em[data-emoji=":person_bouncing_ball_tone5:"]:before,em[data-emoji=person_bouncing_ball_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3ff.svg)}em[data-emoji=":woman_bouncing_ball:"]:before,em[data-emoji=woman_bouncing_ball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-fe0f-200d-2640-fe0f.svg)}em[data-emoji=":woman_bouncing_ball_tone1:"]:before,em[data-emoji=woman_bouncing_ball_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_bouncing_ball_tone2:"]:before,em[data-emoji=woman_bouncing_ball_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_bouncing_ball_tone3:"]:before,em[data-emoji=woman_bouncing_ball_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_bouncing_ball_tone4:"]:before,em[data-emoji=woman_bouncing_ball_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_bouncing_ball_tone5:"]:before,em[data-emoji=woman_bouncing_ball_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_bouncing_ball:"]:before,em[data-emoji=man_bouncing_ball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-fe0f-200d-2642-fe0f.svg)}em[data-emoji=":man_bouncing_ball_tone1:"]:before,em[data-emoji=man_bouncing_ball_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_bouncing_ball_tone2:"]:before,em[data-emoji=man_bouncing_ball_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_bouncing_ball_tone3:"]:before,em[data-emoji=man_bouncing_ball_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_bouncing_ball_tone4:"]:before,em[data-emoji=man_bouncing_ball_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_bouncing_ball_tone5:"]:before,em[data-emoji=man_bouncing_ball_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f9-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_fencing:"]:before,em[data-emoji=person_fencing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93a.svg)}em[data-emoji=":person_playing_handball:"]:before,em[data-emoji=person_playing_handball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e.svg)}em[data-emoji=":person_playing_handball_tone1:"]:before,em[data-emoji=person_playing_handball_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fb.svg)}em[data-emoji=":person_playing_handball_tone2:"]:before,em[data-emoji=person_playing_handball_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fc.svg)}em[data-emoji=":person_playing_handball_tone3:"]:before,em[data-emoji=person_playing_handball_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fd.svg)}em[data-emoji=":person_playing_handball_tone4:"]:before,em[data-emoji=person_playing_handball_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fe.svg)}em[data-emoji=":person_playing_handball_tone5:"]:before,em[data-emoji=person_playing_handball_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3ff.svg)}em[data-emoji=":woman_playing_handball:"]:before,em[data-emoji=woman_playing_handball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_handball_tone1:"]:before,em[data-emoji=woman_playing_handball_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_handball_tone2:"]:before,em[data-emoji=woman_playing_handball_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_handball_tone3:"]:before,em[data-emoji=woman_playing_handball_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_handball_tone4:"]:before,em[data-emoji=woman_playing_handball_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_handball_tone5:"]:before,em[data-emoji=woman_playing_handball_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_playing_handball:"]:before,em[data-emoji=man_playing_handball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_handball_tone1:"]:before,em[data-emoji=man_playing_handball_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_handball_tone2:"]:before,em[data-emoji=man_playing_handball_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_handball_tone3:"]:before,em[data-emoji=man_playing_handball_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_handball_tone4:"]:before,em[data-emoji=man_playing_handball_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_handball_tone5:"]:before,em[data-emoji=man_playing_handball_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93e-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_golfing:"]:before,em[data-emoji=person_golfing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc.svg)}em[data-emoji=":person_golfing_tone1:"]:before,em[data-emoji=person_golfing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fb.svg)}em[data-emoji=":person_golfing_tone2:"]:before,em[data-emoji=person_golfing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fc.svg)}em[data-emoji=":person_golfing_tone3:"]:before,em[data-emoji=person_golfing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fd.svg)}em[data-emoji=":person_golfing_tone4:"]:before,em[data-emoji=person_golfing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fe.svg)}em[data-emoji=":person_golfing_tone5:"]:before,em[data-emoji=person_golfing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3ff.svg)}em[data-emoji=":woman_golfing:"]:before,em[data-emoji=woman_golfing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-fe0f-200d-2640-fe0f.svg)}em[data-emoji=":woman_golfing_tone1:"]:before,em[data-emoji=woman_golfing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_golfing_tone2:"]:before,em[data-emoji=woman_golfing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_golfing_tone3:"]:before,em[data-emoji=woman_golfing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_golfing_tone4:"]:before,em[data-emoji=woman_golfing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_golfing_tone5:"]:before,em[data-emoji=woman_golfing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_golfing:"]:before,em[data-emoji=man_golfing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-fe0f-200d-2642-fe0f.svg)}em[data-emoji=":man_golfing_tone1:"]:before,em[data-emoji=man_golfing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_golfing_tone2:"]:before,em[data-emoji=man_golfing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_golfing_tone3:"]:before,em[data-emoji=man_golfing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_golfing_tone4:"]:before,em[data-emoji=man_golfing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_golfing_tone5:"]:before,em[data-emoji=man_golfing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cc-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":horse_racing:"]:before,em[data-emoji=horse_racing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c7.svg)}em[data-emoji=":horse_racing_tone1:"]:before,em[data-emoji=horse_racing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c7-1f3fb.svg)}em[data-emoji=":horse_racing_tone2:"]:before,em[data-emoji=horse_racing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c7-1f3fc.svg)}em[data-emoji=":horse_racing_tone3:"]:before,em[data-emoji=horse_racing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c7-1f3fd.svg)}em[data-emoji=":horse_racing_tone4:"]:before,em[data-emoji=horse_racing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c7-1f3fe.svg)}em[data-emoji=":horse_racing_tone5:"]:before,em[data-emoji=horse_racing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c7-1f3ff.svg)}em[data-emoji=":person_in_lotus_position:"]:before,em[data-emoji=person_in_lotus_position]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8.svg)}em[data-emoji=":person_in_lotus_position_tone1:"]:before,em[data-emoji=person_in_lotus_position_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fb.svg)}em[data-emoji=":person_in_lotus_position_tone2:"]:before,em[data-emoji=person_in_lotus_position_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fc.svg)}em[data-emoji=":person_in_lotus_position_tone3:"]:before,em[data-emoji=person_in_lotus_position_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fd.svg)}em[data-emoji=":person_in_lotus_position_tone4:"]:before,em[data-emoji=person_in_lotus_position_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fe.svg)}em[data-emoji=":person_in_lotus_position_tone5:"]:before,em[data-emoji=person_in_lotus_position_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3ff.svg)}em[data-emoji=":woman_in_lotus_position:"]:before,em[data-emoji=woman_in_lotus_position]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_lotus_position_tone1:"]:before,em[data-emoji=woman_in_lotus_position_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_lotus_position_tone2:"]:before,em[data-emoji=woman_in_lotus_position_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_lotus_position_tone3:"]:before,em[data-emoji=woman_in_lotus_position_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_lotus_position_tone4:"]:before,em[data-emoji=woman_in_lotus_position_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_lotus_position_tone5:"]:before,em[data-emoji=woman_in_lotus_position_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_in_lotus_position:"]:before,em[data-emoji=man_in_lotus_position]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-200d-2642-fe0f.svg)}em[data-emoji=":man_in_lotus_position_tone1:"]:before,em[data-emoji=man_in_lotus_position_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_in_lotus_position_tone2:"]:before,em[data-emoji=man_in_lotus_position_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_in_lotus_position_tone3:"]:before,em[data-emoji=man_in_lotus_position_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_in_lotus_position_tone4:"]:before,em[data-emoji=man_in_lotus_position_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_in_lotus_position_tone5:"]:before,em[data-emoji=man_in_lotus_position_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d8-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_surfing:"]:before,em[data-emoji=person_surfing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4.svg)}em[data-emoji=":person_surfing_tone1:"]:before,em[data-emoji=person_surfing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fb.svg)}em[data-emoji=":person_surfing_tone2:"]:before,em[data-emoji=person_surfing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fc.svg)}em[data-emoji=":person_surfing_tone3:"]:before,em[data-emoji=person_surfing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fd.svg)}em[data-emoji=":person_surfing_tone4:"]:before,em[data-emoji=person_surfing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fe.svg)}em[data-emoji=":person_surfing_tone5:"]:before,em[data-emoji=person_surfing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3ff.svg)}em[data-emoji=":woman_surfing:"]:before,em[data-emoji=woman_surfing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-200d-2640-fe0f.svg)}em[data-emoji=":woman_surfing_tone1:"]:before,em[data-emoji=woman_surfing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_surfing_tone2:"]:before,em[data-emoji=woman_surfing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_surfing_tone3:"]:before,em[data-emoji=woman_surfing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_surfing_tone4:"]:before,em[data-emoji=woman_surfing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_surfing_tone5:"]:before,em[data-emoji=woman_surfing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_surfing:"]:before,em[data-emoji=man_surfing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-200d-2642-fe0f.svg)}em[data-emoji=":man_surfing_tone1:"]:before,em[data-emoji=man_surfing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_surfing_tone2:"]:before,em[data-emoji=man_surfing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_surfing_tone3:"]:before,em[data-emoji=man_surfing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_surfing_tone4:"]:before,em[data-emoji=man_surfing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_surfing_tone5:"]:before,em[data-emoji=man_surfing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c4-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_swimming:"]:before,em[data-emoji=person_swimming]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca.svg)}em[data-emoji=":person_swimming_tone1:"]:before,em[data-emoji=person_swimming_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fb.svg)}em[data-emoji=":person_swimming_tone2:"]:before,em[data-emoji=person_swimming_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fc.svg)}em[data-emoji=":person_swimming_tone3:"]:before,em[data-emoji=person_swimming_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fd.svg)}em[data-emoji=":person_swimming_tone4:"]:before,em[data-emoji=person_swimming_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fe.svg)}em[data-emoji=":person_swimming_tone5:"]:before,em[data-emoji=person_swimming_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3ff.svg)}em[data-emoji=":woman_swimming:"]:before,em[data-emoji=woman_swimming]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-200d-2640-fe0f.svg)}em[data-emoji=":woman_swimming_tone1:"]:before,em[data-emoji=woman_swimming_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_swimming_tone2:"]:before,em[data-emoji=woman_swimming_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_swimming_tone3:"]:before,em[data-emoji=woman_swimming_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_swimming_tone4:"]:before,em[data-emoji=woman_swimming_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_swimming_tone5:"]:before,em[data-emoji=woman_swimming_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_swimming:"]:before,em[data-emoji=man_swimming]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-200d-2642-fe0f.svg)}em[data-emoji=":man_swimming_tone1:"]:before,em[data-emoji=man_swimming_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_swimming_tone2:"]:before,em[data-emoji=man_swimming_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_swimming_tone3:"]:before,em[data-emoji=man_swimming_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_swimming_tone4:"]:before,em[data-emoji=man_swimming_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_swimming_tone5:"]:before,em[data-emoji=man_swimming_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ca-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_playing_water_polo:"]:before,em[data-emoji=person_playing_water_polo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d.svg)}em[data-emoji=":person_playing_water_polo_tone1:"]:before,em[data-emoji=person_playing_water_polo_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fb.svg)}em[data-emoji=":person_playing_water_polo_tone2:"]:before,em[data-emoji=person_playing_water_polo_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fc.svg)}em[data-emoji=":person_playing_water_polo_tone3:"]:before,em[data-emoji=person_playing_water_polo_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fd.svg)}em[data-emoji=":person_playing_water_polo_tone4:"]:before,em[data-emoji=person_playing_water_polo_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fe.svg)}em[data-emoji=":person_playing_water_polo_tone5:"]:before,em[data-emoji=person_playing_water_polo_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3ff.svg)}em[data-emoji=":woman_playing_water_polo:"]:before,em[data-emoji=woman_playing_water_polo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_water_polo_tone1:"]:before,em[data-emoji=woman_playing_water_polo_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_water_polo_tone2:"]:before,em[data-emoji=woman_playing_water_polo_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_water_polo_tone3:"]:before,em[data-emoji=woman_playing_water_polo_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_water_polo_tone4:"]:before,em[data-emoji=woman_playing_water_polo_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_playing_water_polo_tone5:"]:before,em[data-emoji=woman_playing_water_polo_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_playing_water_polo:"]:before,em[data-emoji=man_playing_water_polo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_water_polo_tone1:"]:before,em[data-emoji=man_playing_water_polo_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_water_polo_tone2:"]:before,em[data-emoji=man_playing_water_polo_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_water_polo_tone3:"]:before,em[data-emoji=man_playing_water_polo_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_water_polo_tone4:"]:before,em[data-emoji=man_playing_water_polo_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_playing_water_polo_tone5:"]:before,em[data-emoji=man_playing_water_polo_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93d-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_rowing_boat:"]:before,em[data-emoji=person_rowing_boat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3.svg)}em[data-emoji=":person_rowing_boat_tone1:"]:before,em[data-emoji=person_rowing_boat_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fb.svg)}em[data-emoji=":person_rowing_boat_tone2:"]:before,em[data-emoji=person_rowing_boat_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fc.svg)}em[data-emoji=":person_rowing_boat_tone3:"]:before,em[data-emoji=person_rowing_boat_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fd.svg)}em[data-emoji=":person_rowing_boat_tone4:"]:before,em[data-emoji=person_rowing_boat_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fe.svg)}em[data-emoji=":person_rowing_boat_tone5:"]:before,em[data-emoji=person_rowing_boat_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3ff.svg)}em[data-emoji=":woman_rowing_boat:"]:before,em[data-emoji=woman_rowing_boat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-200d-2640-fe0f.svg)}em[data-emoji=":woman_rowing_boat_tone1:"]:before,em[data-emoji=woman_rowing_boat_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_rowing_boat_tone2:"]:before,em[data-emoji=woman_rowing_boat_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_rowing_boat_tone3:"]:before,em[data-emoji=woman_rowing_boat_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_rowing_boat_tone4:"]:before,em[data-emoji=woman_rowing_boat_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_rowing_boat_tone5:"]:before,em[data-emoji=woman_rowing_boat_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_rowing_boat:"]:before,em[data-emoji=man_rowing_boat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-200d-2642-fe0f.svg)}em[data-emoji=":man_rowing_boat_tone1:"]:before,em[data-emoji=man_rowing_boat_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_rowing_boat_tone2:"]:before,em[data-emoji=man_rowing_boat_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_rowing_boat_tone3:"]:before,em[data-emoji=man_rowing_boat_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_rowing_boat_tone4:"]:before,em[data-emoji=man_rowing_boat_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_rowing_boat_tone5:"]:before,em[data-emoji=man_rowing_boat_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a3-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_climbing:"]:before,em[data-emoji=person_climbing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7.svg)}em[data-emoji=":person_climbing_tone1:"]:before,em[data-emoji=person_climbing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fb.svg)}em[data-emoji=":person_climbing_tone2:"]:before,em[data-emoji=person_climbing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fc.svg)}em[data-emoji=":person_climbing_tone3:"]:before,em[data-emoji=person_climbing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fd.svg)}em[data-emoji=":person_climbing_tone4:"]:before,em[data-emoji=person_climbing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fe.svg)}em[data-emoji=":person_climbing_tone5:"]:before,em[data-emoji=person_climbing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3ff.svg)}em[data-emoji=":woman_climbing:"]:before,em[data-emoji=woman_climbing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-200d-2640-fe0f.svg)}em[data-emoji=":woman_climbing_tone1:"]:before,em[data-emoji=woman_climbing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_climbing_tone2:"]:before,em[data-emoji=woman_climbing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_climbing_tone3:"]:before,em[data-emoji=woman_climbing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_climbing_tone4:"]:before,em[data-emoji=woman_climbing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_climbing_tone5:"]:before,em[data-emoji=woman_climbing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_climbing:"]:before,em[data-emoji=man_climbing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-200d-2642-fe0f.svg)}em[data-emoji=":man_climbing_tone1:"]:before,em[data-emoji=man_climbing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_climbing_tone2:"]:before,em[data-emoji=man_climbing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_climbing_tone3:"]:before,em[data-emoji=man_climbing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_climbing_tone4:"]:before,em[data-emoji=man_climbing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_climbing_tone5:"]:before,em[data-emoji=man_climbing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d7-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_mountain_biking:"]:before,em[data-emoji=person_mountain_biking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5.svg)}em[data-emoji=":person_mountain_biking_tone1:"]:before,em[data-emoji=person_mountain_biking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fb.svg)}em[data-emoji=":person_mountain_biking_tone2:"]:before,em[data-emoji=person_mountain_biking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fc.svg)}em[data-emoji=":person_mountain_biking_tone3:"]:before,em[data-emoji=person_mountain_biking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fd.svg)}em[data-emoji=":person_mountain_biking_tone4:"]:before,em[data-emoji=person_mountain_biking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fe.svg)}em[data-emoji=":person_mountain_biking_tone5:"]:before,em[data-emoji=person_mountain_biking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3ff.svg)}em[data-emoji=":woman_mountain_biking:"]:before,em[data-emoji=woman_mountain_biking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-200d-2640-fe0f.svg)}em[data-emoji=":woman_mountain_biking_tone1:"]:before,em[data-emoji=woman_mountain_biking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_mountain_biking_tone2:"]:before,em[data-emoji=woman_mountain_biking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_mountain_biking_tone3:"]:before,em[data-emoji=woman_mountain_biking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_mountain_biking_tone4:"]:before,em[data-emoji=woman_mountain_biking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_mountain_biking_tone5:"]:before,em[data-emoji=woman_mountain_biking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_mountain_biking:"]:before,em[data-emoji=man_mountain_biking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-200d-2642-fe0f.svg)}em[data-emoji=":man_mountain_biking_tone1:"]:before,em[data-emoji=man_mountain_biking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_mountain_biking_tone2:"]:before,em[data-emoji=man_mountain_biking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_mountain_biking_tone3:"]:before,em[data-emoji=man_mountain_biking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_mountain_biking_tone4:"]:before,em[data-emoji=man_mountain_biking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_mountain_biking_tone5:"]:before,em[data-emoji=man_mountain_biking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b5-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_biking:"]:before,em[data-emoji=person_biking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4.svg)}em[data-emoji=":person_biking_tone1:"]:before,em[data-emoji=person_biking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fb.svg)}em[data-emoji=":person_biking_tone2:"]:before,em[data-emoji=person_biking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fc.svg)}em[data-emoji=":person_biking_tone3:"]:before,em[data-emoji=person_biking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fd.svg)}em[data-emoji=":person_biking_tone4:"]:before,em[data-emoji=person_biking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fe.svg)}em[data-emoji=":person_biking_tone5:"]:before,em[data-emoji=person_biking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3ff.svg)}em[data-emoji=":woman_biking:"]:before,em[data-emoji=woman_biking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-200d-2640-fe0f.svg)}em[data-emoji=":woman_biking_tone1:"]:before,em[data-emoji=woman_biking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_biking_tone2:"]:before,em[data-emoji=woman_biking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_biking_tone3:"]:before,em[data-emoji=woman_biking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_biking_tone4:"]:before,em[data-emoji=woman_biking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_biking_tone5:"]:before,em[data-emoji=woman_biking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_biking:"]:before,em[data-emoji=man_biking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-200d-2642-fe0f.svg)}em[data-emoji=":man_biking_tone1:"]:before,em[data-emoji=man_biking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_biking_tone2:"]:before,em[data-emoji=man_biking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_biking_tone3:"]:before,em[data-emoji=man_biking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_biking_tone4:"]:before,em[data-emoji=man_biking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_biking_tone5:"]:before,em[data-emoji=man_biking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b4-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":trophy:"]:before,em[data-emoji=trophy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c6.svg)}em[data-emoji=":first_place:"]:before,em[data-emoji=first_place]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f947.svg)}em[data-emoji=":second_place:"]:before,em[data-emoji=second_place]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f948.svg)}em[data-emoji=":third_place:"]:before,em[data-emoji=third_place]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f949.svg)}em[data-emoji=":medal:"]:before,em[data-emoji=medal]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c5.svg)}em[data-emoji=":military_medal:"]:before,em[data-emoji=military_medal]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f396.svg)}em[data-emoji=":rosette:"]:before,em[data-emoji=rosette]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f5.svg)}em[data-emoji=":reminder_ribbon:"]:before,em[data-emoji=reminder_ribbon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f397.svg)}em[data-emoji=":ticket:"]:before,em[data-emoji=ticket]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ab.svg)}em[data-emoji=":tickets:"]:before,em[data-emoji=tickets]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f39f.svg)}em[data-emoji=":circus_tent:"]:before,em[data-emoji=circus_tent]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3aa.svg)}em[data-emoji=":person_juggling:"]:before,em[data-emoji=person_juggling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939.svg)}em[data-emoji=":person_juggling_tone1:"]:before,em[data-emoji=person_juggling_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fb.svg)}em[data-emoji=":person_juggling_tone2:"]:before,em[data-emoji=person_juggling_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fc.svg)}em[data-emoji=":person_juggling_tone3:"]:before,em[data-emoji=person_juggling_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fd.svg)}em[data-emoji=":person_juggling_tone4:"]:before,em[data-emoji=person_juggling_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fe.svg)}em[data-emoji=":person_juggling_tone5:"]:before,em[data-emoji=person_juggling_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3ff.svg)}em[data-emoji=":woman_juggling:"]:before,em[data-emoji=woman_juggling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-200d-2640-fe0f.svg)}em[data-emoji=":woman_juggling_tone1:"]:before,em[data-emoji=woman_juggling_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_juggling_tone2:"]:before,em[data-emoji=woman_juggling_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_juggling_tone3:"]:before,em[data-emoji=woman_juggling_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_juggling_tone4:"]:before,em[data-emoji=woman_juggling_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_juggling_tone5:"]:before,em[data-emoji=woman_juggling_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_juggling:"]:before,em[data-emoji=man_juggling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-200d-2642-fe0f.svg)}em[data-emoji=":man_juggling_tone1:"]:before,em[data-emoji=man_juggling_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_juggling_tone2:"]:before,em[data-emoji=man_juggling_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_juggling_tone3:"]:before,em[data-emoji=man_juggling_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_juggling_tone4:"]:before,em[data-emoji=man_juggling_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_juggling_tone5:"]:before,em[data-emoji=man_juggling_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f939-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":performing_arts:"]:before,em[data-emoji=performing_arts]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ad.svg)}em[data-emoji=":art:"]:before,em[data-emoji=art]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a8.svg)}em[data-emoji=":clapper:"]:before,em[data-emoji=clapper]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ac.svg)}em[data-emoji=":microphone:"]:before,em[data-emoji=microphone]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a4.svg)}em[data-emoji=":headphones:"]:before,em[data-emoji=headphones]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a7.svg)}em[data-emoji=":musical_score:"]:before,em[data-emoji=musical_score]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3bc.svg)}em[data-emoji=":musical_keyboard:"]:before,em[data-emoji=musical_keyboard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b9.svg)}em[data-emoji=":drum:"]:before,em[data-emoji=drum]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f941.svg)}em[data-emoji=":saxophone:"]:before,em[data-emoji=saxophone]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b7.svg)}em[data-emoji=":trumpet:"]:before,em[data-emoji=trumpet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ba.svg)}em[data-emoji=":banjo:"]:before,em[data-emoji=banjo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa95.svg)}em[data-emoji=":guitar:"]:before,em[data-emoji=guitar]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b8.svg)}em[data-emoji=":violin:"]:before,em[data-emoji=violin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3bb.svg)}em[data-emoji=":game_die:"]:before,em[data-emoji=game_die]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b2.svg)}em[data-emoji=":chess_pawn:"]:before,em[data-emoji=chess_pawn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/265f.svg)}em[data-emoji=":dart:"]:before,em[data-emoji=dart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3af.svg)}em[data-emoji=":kite:"]:before,em[data-emoji=kite]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa81.svg)}em[data-emoji=":yo_yo:"]:before,em[data-emoji=yo_yo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa80.svg)}em[data-emoji=":bowling:"]:before,em[data-emoji=bowling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b3.svg)}em[data-emoji=":video_game:"]:before,em[data-emoji=video_game]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ae.svg)}em[data-emoji=":slot_machine:"]:before,em[data-emoji=slot_machine]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3b0.svg)}em[data-emoji=":jigsaw:"]:before,em[data-emoji=jigsaw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e9.svg)}em[data-emoji=":watch:"]:before,em[data-emoji=watch]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/231a.svg)}em[data-emoji=":iphone:"]:before,em[data-emoji=iphone]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f1.svg)}em[data-emoji=":calling:"]:before,em[data-emoji=calling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f2.svg)}em[data-emoji=":computer:"]:before,em[data-emoji=computer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4bb.svg)}em[data-emoji=":desktop:"]:before,em[data-emoji=desktop]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5a5.svg)}em[data-emoji=":printer:"]:before,em[data-emoji=printer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5a8.svg)}em[data-emoji=":mouse_three_button:"]:before,em[data-emoji=mouse_three_button]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5b1.svg)}em[data-emoji=":trackball:"]:before,em[data-emoji=trackball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5b2.svg)}em[data-emoji=":joystick:"]:before,em[data-emoji=joystick]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f579.svg)}em[data-emoji=":compression:"]:before,em[data-emoji=compression]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5dc.svg)}em[data-emoji=":minidisc:"]:before,em[data-emoji=minidisc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4bd.svg)}em[data-emoji=":floppy_disk:"]:before,em[data-emoji=floppy_disk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4be.svg)}em[data-emoji=":cd:"]:before,em[data-emoji=cd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4bf.svg)}em[data-emoji=":dvd:"]:before,em[data-emoji=dvd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c0.svg)}em[data-emoji=":vhs:"]:before,em[data-emoji=vhs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4fc.svg)}em[data-emoji=":camera:"]:before,em[data-emoji=camera]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f7.svg)}em[data-emoji=":camera_with_flash:"]:before,em[data-emoji=camera_with_flash]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f8.svg)}em[data-emoji=":video_camera:"]:before,em[data-emoji=video_camera]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f9.svg)}em[data-emoji=":movie_camera:"]:before,em[data-emoji=movie_camera]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a5.svg)}em[data-emoji=":projector:"]:before,em[data-emoji=projector]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4fd.svg)}em[data-emoji=":film_frames:"]:before,em[data-emoji=film_frames]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f39e.svg)}em[data-emoji=":telephone_receiver:"]:before,em[data-emoji=telephone_receiver]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4de.svg)}em[data-emoji=":telephone:"]:before,em[data-emoji=telephone]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/260e.svg)}em[data-emoji=":pager:"]:before,em[data-emoji=pager]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4df.svg)}em[data-emoji=":fax:"]:before,em[data-emoji=fax]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e0.svg)}em[data-emoji=":tv:"]:before,em[data-emoji=tv]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4fa.svg)}em[data-emoji=":radio:"]:before,em[data-emoji=radio]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4fb.svg)}em[data-emoji=":microphone2:"]:before,em[data-emoji=microphone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f399.svg)}em[data-emoji=":level_slider:"]:before,em[data-emoji=level_slider]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f39a.svg)}em[data-emoji=":control_knobs:"]:before,em[data-emoji=control_knobs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f39b.svg)}em[data-emoji=":compass:"]:before,em[data-emoji=compass]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ed.svg)}em[data-emoji=":stopwatch:"]:before,em[data-emoji=stopwatch]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23f1.svg)}em[data-emoji=":timer:"]:before,em[data-emoji=timer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23f2.svg)}em[data-emoji=":alarm_clock:"]:before,em[data-emoji=alarm_clock]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23f0.svg)}em[data-emoji=":clock:"]:before,em[data-emoji=clock]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f570.svg)}em[data-emoji=":hourglass:"]:before,em[data-emoji=hourglass]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/231b.svg)}em[data-emoji=":hourglass_flowing_sand:"]:before,em[data-emoji=hourglass_flowing_sand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/23f3.svg)}em[data-emoji=":satellite:"]:before,em[data-emoji=satellite]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e1.svg)}em[data-emoji=":battery:"]:before,em[data-emoji=battery]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f50b.svg)}em[data-emoji=":electric_plug:"]:before,em[data-emoji=electric_plug]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f50c.svg)}em[data-emoji=":bulb:"]:before,em[data-emoji=bulb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a1.svg)}em[data-emoji=":flashlight:"]:before,em[data-emoji=flashlight]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f526.svg)}em[data-emoji=":candle:"]:before,em[data-emoji=candle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f56f.svg)}em[data-emoji=":fire_extinguisher:"]:before,em[data-emoji=fire_extinguisher]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ef.svg)}em[data-emoji=":oil:"]:before,em[data-emoji=oil]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6e2.svg)}em[data-emoji=":money_with_wings:"]:before,em[data-emoji=money_with_wings]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b8.svg)}em[data-emoji=":dollar:"]:before,em[data-emoji=dollar]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b5.svg)}em[data-emoji=":yen:"]:before,em[data-emoji=yen]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b4.svg)}em[data-emoji=":euro:"]:before,em[data-emoji=euro]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b6.svg)}em[data-emoji=":pound:"]:before,em[data-emoji=pound]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b7.svg)}em[data-emoji=":moneybag:"]:before,em[data-emoji=moneybag]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b0.svg)}em[data-emoji=":credit_card:"]:before,em[data-emoji=credit_card]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4b3.svg)}em[data-emoji=":gem:"]:before,em[data-emoji=gem]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f48e.svg)}em[data-emoji=":toolbox:"]:before,em[data-emoji=toolbox]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f0.svg)}em[data-emoji=":wrench:"]:before,em[data-emoji=wrench]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f527.svg)}em[data-emoji=":hammer:"]:before,em[data-emoji=hammer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f528.svg)}em[data-emoji=":tools:"]:before,em[data-emoji=tools]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6e0.svg)}em[data-emoji=":pick:"]:before,em[data-emoji=pick]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26cf.svg)}em[data-emoji=":nut_and_bolt:"]:before,em[data-emoji=nut_and_bolt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f529.svg)}em[data-emoji=":bricks:"]:before,em[data-emoji=bricks]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f1.svg)}em[data-emoji=":chains:"]:before,em[data-emoji=chains]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26d3.svg)}em[data-emoji=":magnet:"]:before,em[data-emoji=magnet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f2.svg)}em[data-emoji=":gun:"]:before,em[data-emoji=gun]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f52b.svg)}em[data-emoji=":bomb:"]:before,em[data-emoji=bomb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a3.svg)}em[data-emoji=":firecracker:"]:before,em[data-emoji=firecracker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e8.svg)}em[data-emoji=":axe:"]:before,em[data-emoji=axe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa93.svg)}em[data-emoji=":razor:"]:before,em[data-emoji=razor]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa92.svg)}em[data-emoji=":knife:"]:before,em[data-emoji=knife]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f52a.svg)}em[data-emoji=":dagger:"]:before,em[data-emoji=dagger]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5e1.svg)}em[data-emoji=":shield:"]:before,em[data-emoji=shield]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6e1.svg)}em[data-emoji=":smoking:"]:before,em[data-emoji=smoking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6ac.svg)}em[data-emoji=":coffin:"]:before,em[data-emoji=coffin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26b0.svg)}em[data-emoji=":urn:"]:before,em[data-emoji=urn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26b1.svg)}em[data-emoji=":amphora:"]:before,em[data-emoji=amphora]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3fa.svg)}em[data-emoji=":diya_lamp:"]:before,em[data-emoji=diya_lamp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa94.svg)}em[data-emoji=":crystal_ball:"]:before,em[data-emoji=crystal_ball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f52e.svg)}em[data-emoji=":prayer_beads:"]:before,em[data-emoji=prayer_beads]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ff.svg)}em[data-emoji=":nazar_amulet:"]:before,em[data-emoji=nazar_amulet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ff.svg)}em[data-emoji=":barber:"]:before,em[data-emoji=barber]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f488.svg)}em[data-emoji=":telescope:"]:before,em[data-emoji=telescope]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f52d.svg)}em[data-emoji=":microscope:"]:before,em[data-emoji=microscope]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f52c.svg)}em[data-emoji=":hole:"]:before,em[data-emoji=hole]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f573.svg)}em[data-emoji=":probing_cane:"]:before,em[data-emoji=probing_cane]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9af.svg)}em[data-emoji=":stethoscope:"]:before,em[data-emoji=stethoscope]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa7a.svg)}em[data-emoji=":adhesive_bandage:"]:before,em[data-emoji=adhesive_bandage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa79.svg)}em[data-emoji=":pill:"]:before,em[data-emoji=pill]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f48a.svg)}em[data-emoji=":syringe:"]:before,em[data-emoji=syringe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f489.svg)}em[data-emoji=":drop_of_blood:"]:before,em[data-emoji=drop_of_blood]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa78.svg)}em[data-emoji=":dna:"]:before,em[data-emoji=dna]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ec.svg)}em[data-emoji=":microbe:"]:before,em[data-emoji=microbe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9a0.svg)}em[data-emoji=":petri_dish:"]:before,em[data-emoji=petri_dish]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9eb.svg)}em[data-emoji=":test_tube:"]:before,em[data-emoji=test_tube]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ea.svg)}em[data-emoji=":thermometer:"]:before,em[data-emoji=thermometer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f321.svg)}em[data-emoji=":chair:"]:before,em[data-emoji=chair]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa91.svg)}em[data-emoji=":broom:"]:before,em[data-emoji=broom]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f9.svg)}em[data-emoji=":basket:"]:before,em[data-emoji=basket]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9fa.svg)}em[data-emoji=":roll_of_paper:"]:before,em[data-emoji=roll_of_paper]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9fb.svg)}em[data-emoji=":toilet:"]:before,em[data-emoji=toilet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6bd.svg)}em[data-emoji=":potable_water:"]:before,em[data-emoji=potable_water]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b0.svg)}em[data-emoji=":shower:"]:before,em[data-emoji=shower]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6bf.svg)}em[data-emoji=":bathtub:"]:before,em[data-emoji=bathtub]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c1.svg)}em[data-emoji=":bath:"]:before,em[data-emoji=bath]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c0.svg)}em[data-emoji=":bath_tone1:"]:before,em[data-emoji=bath_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c0-1f3fb.svg)}em[data-emoji=":bath_tone2:"]:before,em[data-emoji=bath_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c0-1f3fc.svg)}em[data-emoji=":bath_tone3:"]:before,em[data-emoji=bath_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c0-1f3fd.svg)}em[data-emoji=":bath_tone4:"]:before,em[data-emoji=bath_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c0-1f3fe.svg)}em[data-emoji=":bath_tone5:"]:before,em[data-emoji=bath_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6c0-1f3ff.svg)}em[data-emoji=":soap:"]:before,em[data-emoji=soap]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9fc.svg)}em[data-emoji=":sponge:"]:before,em[data-emoji=sponge]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9fd.svg)}em[data-emoji=":squeeze_bottle:"]:before,em[data-emoji=squeeze_bottle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f4.svg)}em[data-emoji=":bellhop:"]:before,em[data-emoji=bellhop]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6ce.svg)}em[data-emoji=":key:"]:before,em[data-emoji=key]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f511.svg)}em[data-emoji=":key2:"]:before,em[data-emoji=key2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5dd.svg)}em[data-emoji=":door:"]:before,em[data-emoji=door]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6aa.svg)}em[data-emoji=":couch:"]:before,em[data-emoji=couch]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cb.svg)}em[data-emoji=":bed:"]:before,em[data-emoji=bed]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cf.svg)}em[data-emoji=":sleeping_accommodation:"]:before,em[data-emoji=sleeping_accommodation]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cc.svg)}em[data-emoji=":person_in_bed_tone1:"]:before,em[data-emoji=person_in_bed_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cc-1f3fb.svg)}em[data-emoji=":person_in_bed_tone2:"]:before,em[data-emoji=person_in_bed_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cc-1f3fc.svg)}em[data-emoji=":person_in_bed_tone3:"]:before,em[data-emoji=person_in_bed_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cc-1f3fd.svg)}em[data-emoji=":person_in_bed_tone4:"]:before,em[data-emoji=person_in_bed_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cc-1f3fe.svg)}em[data-emoji=":person_in_bed_tone5:"]:before,em[data-emoji=person_in_bed_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cc-1f3ff.svg)}em[data-emoji=":teddy_bear:"]:before,em[data-emoji=teddy_bear]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f8.svg)}em[data-emoji=":frame_photo:"]:before,em[data-emoji=frame_photo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5bc.svg)}em[data-emoji=":shopping_bags:"]:before,em[data-emoji=shopping_bags]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6cd.svg)}em[data-emoji=":shopping_cart:"]:before,em[data-emoji=shopping_cart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6d2.svg)}em[data-emoji=":gift:"]:before,em[data-emoji=gift]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f381.svg)}em[data-emoji=":balloon:"]:before,em[data-emoji=balloon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f388.svg)}em[data-emoji=":flags:"]:before,em[data-emoji=flags]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f38f.svg)}em[data-emoji=":ribbon:"]:before,em[data-emoji=ribbon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f380.svg)}em[data-emoji=":confetti_ball:"]:before,em[data-emoji=confetti_ball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f38a.svg)}em[data-emoji=":tada:"]:before,em[data-emoji=tada]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f389.svg)}em[data-emoji=":dolls:"]:before,em[data-emoji=dolls]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f38e.svg)}em[data-emoji=":izakaya_lantern:"]:before,em[data-emoji=izakaya_lantern]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ee.svg)}em[data-emoji=":wind_chime:"]:before,em[data-emoji=wind_chime]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f390.svg)}em[data-emoji=":red_envelope:"]:before,em[data-emoji=red_envelope]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e7.svg)}em[data-emoji=":envelope_with_arrow:"]:before,em[data-emoji=envelope_with_arrow]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e9.svg)}em[data-emoji=":incoming_envelope:"]:before,em[data-emoji=incoming_envelope]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e8.svg)}em[data-emoji=":e-mail:"]:before,em[data-emoji=e-mail]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e7.svg)}em[data-emoji=":love_letter:"]:before,em[data-emoji=love_letter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f48c.svg)}em[data-emoji=":inbox_tray:"]:before,em[data-emoji=inbox_tray]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e5.svg)}em[data-emoji=":outbox_tray:"]:before,em[data-emoji=outbox_tray]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e4.svg)}em[data-emoji=":package:"]:before,em[data-emoji=package]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4e6.svg)}em[data-emoji=":label:"]:before,em[data-emoji=label]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f7.svg)}em[data-emoji=":mailbox_closed:"]:before,em[data-emoji=mailbox_closed]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ea.svg)}em[data-emoji=":mailbox:"]:before,em[data-emoji=mailbox]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4eb.svg)}em[data-emoji=":mailbox_with_mail:"]:before,em[data-emoji=mailbox_with_mail]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ec.svg)}em[data-emoji=":mailbox_with_no_mail:"]:before,em[data-emoji=mailbox_with_no_mail]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ed.svg)}em[data-emoji=":postbox:"]:before,em[data-emoji=postbox]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ee.svg)}em[data-emoji=":postal_horn:"]:before,em[data-emoji=postal_horn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ef.svg)}em[data-emoji=":scroll:"]:before,em[data-emoji=scroll]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4dc.svg)}em[data-emoji=":page_with_curl:"]:before,em[data-emoji=page_with_curl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c3.svg)}em[data-emoji=":page_facing_up:"]:before,em[data-emoji=page_facing_up]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c4.svg)}em[data-emoji=":bookmark_tabs:"]:before,em[data-emoji=bookmark_tabs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d1.svg)}em[data-emoji=":receipt:"]:before,em[data-emoji=receipt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9fe.svg)}em[data-emoji=":bar_chart:"]:before,em[data-emoji=bar_chart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ca.svg)}em[data-emoji=":chart_with_upwards_trend:"]:before,em[data-emoji=chart_with_upwards_trend]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c8.svg)}em[data-emoji=":chart_with_downwards_trend:"]:before,em[data-emoji=chart_with_downwards_trend]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c9.svg)}em[data-emoji=":notepad_spiral:"]:before,em[data-emoji=notepad_spiral]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5d2.svg)}em[data-emoji=":calendar_spiral:"]:before,em[data-emoji=calendar_spiral]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5d3.svg)}em[data-emoji=":calendar:"]:before,em[data-emoji=calendar]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c6.svg)}em[data-emoji=":date:"]:before,em[data-emoji=date]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c5.svg)}em[data-emoji=":wastebasket:"]:before,em[data-emoji=wastebasket]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5d1.svg)}em[data-emoji=":card_index:"]:before,em[data-emoji=card_index]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c7.svg)}em[data-emoji=":card_box:"]:before,em[data-emoji=card_box]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5c3.svg)}em[data-emoji=":ballot_box:"]:before,em[data-emoji=ballot_box]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5f3.svg)}em[data-emoji=":file_cabinet:"]:before,em[data-emoji=file_cabinet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5c4.svg)}em[data-emoji=":clipboard:"]:before,em[data-emoji=clipboard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4cb.svg)}em[data-emoji=":file_folder:"]:before,em[data-emoji=file_folder]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c1.svg)}em[data-emoji=":open_file_folder:"]:before,em[data-emoji=open_file_folder]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4c2.svg)}em[data-emoji=":dividers:"]:before,em[data-emoji=dividers]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5c2.svg)}em[data-emoji=":newspaper2:"]:before,em[data-emoji=newspaper2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5de.svg)}em[data-emoji=":newspaper:"]:before,em[data-emoji=newspaper]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4f0.svg)}em[data-emoji=":notebook:"]:before,em[data-emoji=notebook]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d3.svg)}em[data-emoji=":notebook_with_decorative_cover:"]:before,em[data-emoji=notebook_with_decorative_cover]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d4.svg)}em[data-emoji=":ledger:"]:before,em[data-emoji=ledger]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d2.svg)}em[data-emoji=":closed_book:"]:before,em[data-emoji=closed_book]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d5.svg)}em[data-emoji=":green_book:"]:before,em[data-emoji=green_book]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d7.svg)}em[data-emoji=":blue_book:"]:before,em[data-emoji=blue_book]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d8.svg)}em[data-emoji=":orange_book:"]:before,em[data-emoji=orange_book]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d9.svg)}em[data-emoji=":books:"]:before,em[data-emoji=books]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4da.svg)}em[data-emoji=":book:"]:before,em[data-emoji=book]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d6.svg)}em[data-emoji=":bookmark:"]:before,em[data-emoji=bookmark]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f516.svg)}em[data-emoji=":safety_pin:"]:before,em[data-emoji=safety_pin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f7.svg)}em[data-emoji=":link:"]:before,em[data-emoji=link]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f517.svg)}em[data-emoji=":paperclip:"]:before,em[data-emoji=paperclip]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ce.svg)}em[data-emoji=":paperclips:"]:before,em[data-emoji=paperclips]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f587.svg)}em[data-emoji=":triangular_ruler:"]:before,em[data-emoji=triangular_ruler]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4d0.svg)}em[data-emoji=":straight_ruler:"]:before,em[data-emoji=straight_ruler]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4cf.svg)}em[data-emoji=":abacus:"]:before,em[data-emoji=abacus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ee.svg)}em[data-emoji=":pushpin:"]:before,em[data-emoji=pushpin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4cc.svg)}em[data-emoji=":round_pushpin:"]:before,em[data-emoji=round_pushpin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4cd.svg)}em[data-emoji=":pen_ballpoint:"]:before,em[data-emoji=pen_ballpoint]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f58a.svg)}em[data-emoji=":pen_fountain:"]:before,em[data-emoji=pen_fountain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f58b.svg)}em[data-emoji=":paintbrush:"]:before,em[data-emoji=paintbrush]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f58c.svg)}em[data-emoji=":crayon:"]:before,em[data-emoji=crayon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f58d.svg)}em[data-emoji=":pencil:"]:before,em[data-emoji=pencil]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4dd.svg)}em[data-emoji=":pencil2:"]:before,em[data-emoji=pencil2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270f.svg)}em[data-emoji=":mag:"]:before,em[data-emoji=mag]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f50d.svg)}em[data-emoji=":mag_right:"]:before,em[data-emoji=mag_right]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f50e.svg)}em[data-emoji=":lock_with_ink_pen:"]:before,em[data-emoji=lock_with_ink_pen]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f50f.svg)}em[data-emoji=":closed_lock_with_key:"]:before,em[data-emoji=closed_lock_with_key]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f510.svg)}em[data-emoji=":lock:"]:before,em[data-emoji=lock]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f512.svg)}em[data-emoji=":unlock:"]:before,em[data-emoji=unlock]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f513.svg)}em[data-emoji=":dog:"]:before,em[data-emoji=dog]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f436.svg)}em[data-emoji=":cat:"]:before,em[data-emoji=cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f431.svg)}em[data-emoji=":mouse:"]:before,em[data-emoji=mouse]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f42d.svg)}em[data-emoji=":hamster:"]:before,em[data-emoji=hamster]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f439.svg)}em[data-emoji=":rabbit:"]:before,em[data-emoji=rabbit]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f430.svg)}em[data-emoji=":fox:"]:before,em[data-emoji=fox]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f98a.svg)}em[data-emoji=":bear:"]:before,em[data-emoji=bear]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f43b.svg)}em[data-emoji=":panda_face:"]:before,em[data-emoji=panda_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f43c.svg)}em[data-emoji=":koala:"]:before,em[data-emoji=koala]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f428.svg)}em[data-emoji=":tiger:"]:before,em[data-emoji=tiger]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f42f.svg)}em[data-emoji=":lion_face:"]:before,em[data-emoji=lion_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f981.svg)}em[data-emoji=":cow:"]:before,em[data-emoji=cow]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f42e.svg)}em[data-emoji=":pig:"]:before,em[data-emoji=pig]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f437.svg)}em[data-emoji=":pig_nose:"]:before,em[data-emoji=pig_nose]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f43d.svg)}em[data-emoji=":frog:"]:before,em[data-emoji=frog]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f438.svg)}em[data-emoji=":monkey_face:"]:before,em[data-emoji=monkey_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f435.svg)}em[data-emoji=":see_no_evil:"]:before,em[data-emoji=see_no_evil]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f648.svg)}em[data-emoji=":hear_no_evil:"]:before,em[data-emoji=hear_no_evil]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f649.svg)}em[data-emoji=":speak_no_evil:"]:before,em[data-emoji=speak_no_evil]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64a.svg)}em[data-emoji=":monkey:"]:before,em[data-emoji=monkey]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f412.svg)}em[data-emoji=":chicken:"]:before,em[data-emoji=chicken]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f414.svg)}em[data-emoji=":penguin:"]:before,em[data-emoji=penguin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f427.svg)}em[data-emoji=":bird:"]:before,em[data-emoji=bird]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f426.svg)}em[data-emoji=":baby_chick:"]:before,em[data-emoji=baby_chick]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f424.svg)}em[data-emoji=":hatching_chick:"]:before,em[data-emoji=hatching_chick]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f423.svg)}em[data-emoji=":hatched_chick:"]:before,em[data-emoji=hatched_chick]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f425.svg)}em[data-emoji=":duck:"]:before,em[data-emoji=duck]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f986.svg)}em[data-emoji=":eagle:"]:before,em[data-emoji=eagle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f985.svg)}em[data-emoji=":owl:"]:before,em[data-emoji=owl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f989.svg)}em[data-emoji=":bat:"]:before,em[data-emoji=bat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f987.svg)}em[data-emoji=":wolf:"]:before,em[data-emoji=wolf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f43a.svg)}em[data-emoji=":boar:"]:before,em[data-emoji=boar]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f417.svg)}em[data-emoji=":horse:"]:before,em[data-emoji=horse]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f434.svg)}em[data-emoji=":unicorn:"]:before,em[data-emoji=unicorn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f984.svg)}em[data-emoji=":bee:"]:before,em[data-emoji=bee]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f41d.svg)}em[data-emoji=":bug:"]:before,em[data-emoji=bug]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f41b.svg)}em[data-emoji=":butterfly:"]:before,em[data-emoji=butterfly]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f98b.svg)}em[data-emoji=":snail:"]:before,em[data-emoji=snail]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f40c.svg)}em[data-emoji=":shell:"]:before,em[data-emoji=shell]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f41a.svg)}em[data-emoji=":beetle:"]:before,em[data-emoji=beetle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f41e.svg)}em[data-emoji=":ant:"]:before,em[data-emoji=ant]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f41c.svg)}em[data-emoji=":mosquito:"]:before,em[data-emoji=mosquito]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f99f.svg)}em[data-emoji=":cricket:"]:before,em[data-emoji=cricket]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f997.svg)}em[data-emoji=":spider:"]:before,em[data-emoji=spider]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f577.svg)}em[data-emoji=":spider_web:"]:before,em[data-emoji=spider_web]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f578.svg)}em[data-emoji=":scorpion:"]:before,em[data-emoji=scorpion]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f982.svg)}em[data-emoji=":turtle:"]:before,em[data-emoji=turtle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f422.svg)}em[data-emoji=":snake:"]:before,em[data-emoji=snake]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f40d.svg)}em[data-emoji=":lizard:"]:before,em[data-emoji=lizard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f98e.svg)}em[data-emoji=":t_rex:"]:before,em[data-emoji=t_rex]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f996.svg)}em[data-emoji=":sauropod:"]:before,em[data-emoji=sauropod]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f995.svg)}em[data-emoji=":octopus:"]:before,em[data-emoji=octopus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f419.svg)}em[data-emoji=":squid:"]:before,em[data-emoji=squid]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f991.svg)}em[data-emoji=":shrimp:"]:before,em[data-emoji=shrimp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f990.svg)}em[data-emoji=":lobster:"]:before,em[data-emoji=lobster]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f99e.svg)}em[data-emoji=":oyster:"]:before,em[data-emoji=oyster]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9aa.svg)}em[data-emoji=":crab:"]:before,em[data-emoji=crab]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f980.svg)}em[data-emoji=":blowfish:"]:before,em[data-emoji=blowfish]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f421.svg)}em[data-emoji=":tropical_fish:"]:before,em[data-emoji=tropical_fish]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f420.svg)}em[data-emoji=":fish:"]:before,em[data-emoji=fish]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f41f.svg)}em[data-emoji=":dolphin:"]:before,em[data-emoji=dolphin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f42c.svg)}em[data-emoji=":whale:"]:before,em[data-emoji=whale]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f433.svg)}em[data-emoji=":whale2:"]:before,em[data-emoji=whale2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f40b.svg)}em[data-emoji=":shark:"]:before,em[data-emoji=shark]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f988.svg)}em[data-emoji=":crocodile:"]:before,em[data-emoji=crocodile]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f40a.svg)}em[data-emoji=":tiger2:"]:before,em[data-emoji=tiger2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f405.svg)}em[data-emoji=":leopard:"]:before,em[data-emoji=leopard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f406.svg)}em[data-emoji=":zebra:"]:before,em[data-emoji=zebra]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f993.svg)}em[data-emoji=":gorilla:"]:before,em[data-emoji=gorilla]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f98d.svg)}em[data-emoji=":orangutan:"]:before,em[data-emoji=orangutan]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9a7.svg)}em[data-emoji=":elephant:"]:before,em[data-emoji=elephant]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f418.svg)}em[data-emoji=":hippopotamus:"]:before,em[data-emoji=hippopotamus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f99b.svg)}em[data-emoji=":rhino:"]:before,em[data-emoji=rhino]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f98f.svg)}em[data-emoji=":dromedary_camel:"]:before,em[data-emoji=dromedary_camel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f42a.svg)}em[data-emoji=":camel:"]:before,em[data-emoji=camel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f42b.svg)}em[data-emoji=":giraffe:"]:before,em[data-emoji=giraffe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f992.svg)}em[data-emoji=":kangaroo:"]:before,em[data-emoji=kangaroo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f998.svg)}em[data-emoji=":water_buffalo:"]:before,em[data-emoji=water_buffalo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f403.svg)}em[data-emoji=":ox:"]:before,em[data-emoji=ox]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f402.svg)}em[data-emoji=":cow2:"]:before,em[data-emoji=cow2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f404.svg)}em[data-emoji=":racehorse:"]:before,em[data-emoji=racehorse]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f40e.svg)}em[data-emoji=":pig2:"]:before,em[data-emoji=pig2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f416.svg)}em[data-emoji=":ram:"]:before,em[data-emoji=ram]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f40f.svg)}em[data-emoji=":llama:"]:before,em[data-emoji=llama]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f999.svg)}em[data-emoji=":sheep:"]:before,em[data-emoji=sheep]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f411.svg)}em[data-emoji=":goat:"]:before,em[data-emoji=goat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f410.svg)}em[data-emoji=":deer:"]:before,em[data-emoji=deer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f98c.svg)}em[data-emoji=":dog2:"]:before,em[data-emoji=dog2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f415.svg)}em[data-emoji=":guide_dog:"]:before,em[data-emoji=guide_dog]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ae.svg)}em[data-emoji=":service_dog:"]:before,em[data-emoji=service_dog]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f415-200d-1f9ba.svg)}em[data-emoji=":poodle:"]:before,em[data-emoji=poodle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f429.svg)}em[data-emoji=":cat2:"]:before,em[data-emoji=cat2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f408.svg)}em[data-emoji=":rooster:"]:before,em[data-emoji=rooster]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f413.svg)}em[data-emoji=":turkey:"]:before,em[data-emoji=turkey]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f983.svg)}em[data-emoji=":peacock:"]:before,em[data-emoji=peacock]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f99a.svg)}em[data-emoji=":parrot:"]:before,em[data-emoji=parrot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f99c.svg)}em[data-emoji=":swan:"]:before,em[data-emoji=swan]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9a2.svg)}em[data-emoji=":flamingo:"]:before,em[data-emoji=flamingo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9a9.svg)}em[data-emoji=":dove:"]:before,em[data-emoji=dove]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f54a.svg)}em[data-emoji=":rabbit2:"]:before,em[data-emoji=rabbit2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f407.svg)}em[data-emoji=":sloth:"]:before,em[data-emoji=sloth]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9a5.svg)}em[data-emoji=":otter:"]:before,em[data-emoji=otter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9a6.svg)}em[data-emoji=":skunk:"]:before,em[data-emoji=skunk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9a8.svg)}em[data-emoji=":raccoon:"]:before,em[data-emoji=raccoon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f99d.svg)}em[data-emoji=":badger:"]:before,em[data-emoji=badger]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9a1.svg)}em[data-emoji=":mouse2:"]:before,em[data-emoji=mouse2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f401.svg)}em[data-emoji=":rat:"]:before,em[data-emoji=rat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f400.svg)}em[data-emoji=":chipmunk:"]:before,em[data-emoji=chipmunk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f43f.svg)}em[data-emoji=":hedgehog:"]:before,em[data-emoji=hedgehog]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f994.svg)}em[data-emoji=":feet:"]:before,em[data-emoji=feet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f43e.svg)}em[data-emoji=":dragon:"]:before,em[data-emoji=dragon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f409.svg)}em[data-emoji=":dragon_face:"]:before,em[data-emoji=dragon_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f432.svg)}em[data-emoji=":cactus:"]:before,em[data-emoji=cactus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f335.svg)}em[data-emoji=":christmas_tree:"]:before,em[data-emoji=christmas_tree]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f384.svg)}em[data-emoji=":evergreen_tree:"]:before,em[data-emoji=evergreen_tree]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f332.svg)}em[data-emoji=":deciduous_tree:"]:before,em[data-emoji=deciduous_tree]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f333.svg)}em[data-emoji=":palm_tree:"]:before,em[data-emoji=palm_tree]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f334.svg)}em[data-emoji=":seedling:"]:before,em[data-emoji=seedling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f331.svg)}em[data-emoji=":herb:"]:before,em[data-emoji=herb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f33f.svg)}em[data-emoji=":four_leaf_clover:"]:before,em[data-emoji=four_leaf_clover]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f340.svg)}em[data-emoji=":bamboo:"]:before,em[data-emoji=bamboo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f38d.svg)}em[data-emoji=":tanabata_tree:"]:before,em[data-emoji=tanabata_tree]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f38b.svg)}em[data-emoji=":leaves:"]:before,em[data-emoji=leaves]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f343.svg)}em[data-emoji=":fallen_leaf:"]:before,em[data-emoji=fallen_leaf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f342.svg)}em[data-emoji=":maple_leaf:"]:before,em[data-emoji=maple_leaf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f341.svg)}em[data-emoji=":mushroom:"]:before,em[data-emoji=mushroom]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f344.svg)}em[data-emoji=":ear_of_rice:"]:before,em[data-emoji=ear_of_rice]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f33e.svg)}em[data-emoji=":bouquet:"]:before,em[data-emoji=bouquet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f490.svg)}em[data-emoji=":tulip:"]:before,em[data-emoji=tulip]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f337.svg)}em[data-emoji=":rose:"]:before,em[data-emoji=rose]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f339.svg)}em[data-emoji=":wilted_rose:"]:before,em[data-emoji=wilted_rose]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f940.svg)}em[data-emoji=":hibiscus:"]:before,em[data-emoji=hibiscus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f33a.svg)}em[data-emoji=":cherry_blossom:"]:before,em[data-emoji=cherry_blossom]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f338.svg)}em[data-emoji=":blossom:"]:before,em[data-emoji=blossom]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f33c.svg)}em[data-emoji=":sunflower:"]:before,em[data-emoji=sunflower]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f33b.svg)}em[data-emoji=":sun_with_face:"]:before,em[data-emoji=sun_with_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f31e.svg)}em[data-emoji=":full_moon_with_face:"]:before,em[data-emoji=full_moon_with_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f31d.svg)}em[data-emoji=":first_quarter_moon_with_face:"]:before,em[data-emoji=first_quarter_moon_with_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f31b.svg)}em[data-emoji=":last_quarter_moon_with_face:"]:before,em[data-emoji=last_quarter_moon_with_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f31c.svg)}em[data-emoji=":new_moon_with_face:"]:before,em[data-emoji=new_moon_with_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f31a.svg)}em[data-emoji=":full_moon:"]:before,em[data-emoji=full_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f315.svg)}em[data-emoji=":waning_gibbous_moon:"]:before,em[data-emoji=waning_gibbous_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f316.svg)}em[data-emoji=":last_quarter_moon:"]:before,em[data-emoji=last_quarter_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f317.svg)}em[data-emoji=":waning_crescent_moon:"]:before,em[data-emoji=waning_crescent_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f318.svg)}em[data-emoji=":new_moon:"]:before,em[data-emoji=new_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f311.svg)}em[data-emoji=":waxing_crescent_moon:"]:before,em[data-emoji=waxing_crescent_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f312.svg)}em[data-emoji=":first_quarter_moon:"]:before,em[data-emoji=first_quarter_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f313.svg)}em[data-emoji=":waxing_gibbous_moon:"]:before,em[data-emoji=waxing_gibbous_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f314.svg)}em[data-emoji=":crescent_moon:"]:before,em[data-emoji=crescent_moon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f319.svg)}em[data-emoji=":earth_americas:"]:before,em[data-emoji=earth_americas]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f30e.svg)}em[data-emoji=":earth_africa:"]:before,em[data-emoji=earth_africa]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f30d.svg)}em[data-emoji=":earth_asia:"]:before,em[data-emoji=earth_asia]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f30f.svg)}em[data-emoji=":ringed_planet:"]:before,em[data-emoji=ringed_planet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa90.svg)}em[data-emoji=":dizzy:"]:before,em[data-emoji=dizzy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ab.svg)}em[data-emoji=":star:"]:before,em[data-emoji=star]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/2b50.svg)}em[data-emoji=":star2:"]:before,em[data-emoji=star2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f31f.svg)}em[data-emoji=":zap:"]:before,em[data-emoji=zap]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26a1.svg)}em[data-emoji=":boom:"]:before,em[data-emoji=boom]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a5.svg)}em[data-emoji=":fire:"]:before,em[data-emoji=fire]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f525.svg)}em[data-emoji=":cloud_tornado:"]:before,em[data-emoji=cloud_tornado]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f32a.svg)}em[data-emoji=":rainbow:"]:before,em[data-emoji=rainbow]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f308.svg)}em[data-emoji=":white_sun_small_cloud:"]:before,em[data-emoji=white_sun_small_cloud]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f324.svg)}em[data-emoji=":partly_sunny:"]:before,em[data-emoji=partly_sunny]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26c5.svg)}em[data-emoji=":white_sun_cloud:"]:before,em[data-emoji=white_sun_cloud]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f325.svg)}em[data-emoji=":white_sun_rain_cloud:"]:before,em[data-emoji=white_sun_rain_cloud]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f326.svg)}em[data-emoji=":cloud_rain:"]:before,em[data-emoji=cloud_rain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f327.svg)}em[data-emoji=":thunder_cloud_rain:"]:before,em[data-emoji=thunder_cloud_rain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26c8.svg)}em[data-emoji=":cloud_lightning:"]:before,em[data-emoji=cloud_lightning]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f329.svg)}em[data-emoji=":cloud_snow:"]:before,em[data-emoji=cloud_snow]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f328.svg)}em[data-emoji=":snowman:"]:before,em[data-emoji=snowman]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26c4.svg)}em[data-emoji=":wind_blowing_face:"]:before,em[data-emoji=wind_blowing_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f32c.svg)}em[data-emoji=":dash:"]:before,em[data-emoji=dash]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a8.svg)}em[data-emoji=":droplet:"]:before,em[data-emoji=droplet]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a7.svg)}em[data-emoji=":sweat_drops:"]:before,em[data-emoji=sweat_drops]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a6.svg)}em[data-emoji=":ocean:"]:before,em[data-emoji=ocean]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f30a.svg)}em[data-emoji=":fog:"]:before,em[data-emoji=fog]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f32b.svg)}em[data-emoji=":green_apple:"]:before,em[data-emoji=green_apple]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f34f.svg)}em[data-emoji=":apple:"]:before,em[data-emoji=apple]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f34e.svg)}em[data-emoji=":pear:"]:before,em[data-emoji=pear]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f350.svg)}em[data-emoji=":tangerine:"]:before,em[data-emoji=tangerine]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f34a.svg)}em[data-emoji=":lemon:"]:before,em[data-emoji=lemon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f34b.svg)}em[data-emoji=":banana:"]:before,em[data-emoji=banana]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f34c.svg)}em[data-emoji=":watermelon:"]:before,em[data-emoji=watermelon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f349.svg)}em[data-emoji=":grapes:"]:before,em[data-emoji=grapes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f347.svg)}em[data-emoji=":strawberry:"]:before,em[data-emoji=strawberry]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f353.svg)}em[data-emoji=":melon:"]:before,em[data-emoji=melon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f348.svg)}em[data-emoji=":cherries:"]:before,em[data-emoji=cherries]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f352.svg)}em[data-emoji=":peach:"]:before,em[data-emoji=peach]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f351.svg)}em[data-emoji=":mango:"]:before,em[data-emoji=mango]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f96d.svg)}em[data-emoji=":pineapple:"]:before,em[data-emoji=pineapple]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f34d.svg)}em[data-emoji=":coconut:"]:before,em[data-emoji=coconut]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f965.svg)}em[data-emoji=":kiwi:"]:before,em[data-emoji=kiwi]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f95d.svg)}em[data-emoji=":tomato:"]:before,em[data-emoji=tomato]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f345.svg)}em[data-emoji=":eggplant:"]:before,em[data-emoji=eggplant]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f346.svg)}em[data-emoji=":avocado:"]:before,em[data-emoji=avocado]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f951.svg)}em[data-emoji=":broccoli:"]:before,em[data-emoji=broccoli]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f966.svg)}em[data-emoji=":leafy_green:"]:before,em[data-emoji=leafy_green]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f96c.svg)}em[data-emoji=":cucumber:"]:before,em[data-emoji=cucumber]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f952.svg)}em[data-emoji=":hot_pepper:"]:before,em[data-emoji=hot_pepper]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f336.svg)}em[data-emoji=":corn:"]:before,em[data-emoji=corn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f33d.svg)}em[data-emoji=":carrot:"]:before,em[data-emoji=carrot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f955.svg)}em[data-emoji=":onion:"]:before,em[data-emoji=onion]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c5.svg)}em[data-emoji=":garlic:"]:before,em[data-emoji=garlic]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c4.svg)}em[data-emoji=":potato:"]:before,em[data-emoji=potato]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f954.svg)}em[data-emoji=":sweet_potato:"]:before,em[data-emoji=sweet_potato]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f360.svg)}em[data-emoji=":croissant:"]:before,em[data-emoji=croissant]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f950.svg)}em[data-emoji=":bagel:"]:before,em[data-emoji=bagel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f96f.svg)}em[data-emoji=":bread:"]:before,em[data-emoji=bread]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f35e.svg)}em[data-emoji=":french_bread:"]:before,em[data-emoji=french_bread]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f956.svg)}em[data-emoji=":pretzel:"]:before,em[data-emoji=pretzel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f968.svg)}em[data-emoji=":cheese:"]:before,em[data-emoji=cheese]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c0.svg)}em[data-emoji=":egg:"]:before,em[data-emoji=egg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f95a.svg)}em[data-emoji=":cooking:"]:before,em[data-emoji=cooking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f373.svg)}em[data-emoji=":pancakes:"]:before,em[data-emoji=pancakes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f95e.svg)}em[data-emoji=":waffle:"]:before,em[data-emoji=waffle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c7.svg)}em[data-emoji=":bacon:"]:before,em[data-emoji=bacon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f953.svg)}em[data-emoji=":cut_of_meat:"]:before,em[data-emoji=cut_of_meat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f969.svg)}em[data-emoji=":poultry_leg:"]:before,em[data-emoji=poultry_leg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f357.svg)}em[data-emoji=":meat_on_bone:"]:before,em[data-emoji=meat_on_bone]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f356.svg)}em[data-emoji=":hotdog:"]:before,em[data-emoji=hotdog]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f32d.svg)}em[data-emoji=":hamburger:"]:before,em[data-emoji=hamburger]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f354.svg)}em[data-emoji=":fries:"]:before,em[data-emoji=fries]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f35f.svg)}em[data-emoji=":pizza:"]:before,em[data-emoji=pizza]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f355.svg)}em[data-emoji=":sandwich:"]:before,em[data-emoji=sandwich]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f96a.svg)}em[data-emoji=":falafel:"]:before,em[data-emoji=falafel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c6.svg)}em[data-emoji=":stuffed_flatbread:"]:before,em[data-emoji=stuffed_flatbread]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f959.svg)}em[data-emoji=":taco:"]:before,em[data-emoji=taco]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f32e.svg)}em[data-emoji=":burrito:"]:before,em[data-emoji=burrito]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f32f.svg)}em[data-emoji=":salad:"]:before,em[data-emoji=salad]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f957.svg)}em[data-emoji=":shallow_pan_of_food:"]:before,em[data-emoji=shallow_pan_of_food]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f958.svg)}em[data-emoji=":canned_food:"]:before,em[data-emoji=canned_food]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f96b.svg)}em[data-emoji=":spaghetti:"]:before,em[data-emoji=spaghetti]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f35d.svg)}em[data-emoji=":ramen:"]:before,em[data-emoji=ramen]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f35c.svg)}em[data-emoji=":stew:"]:before,em[data-emoji=stew]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f372.svg)}em[data-emoji=":curry:"]:before,em[data-emoji=curry]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f35b.svg)}em[data-emoji=":sushi:"]:before,em[data-emoji=sushi]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f363.svg)}em[data-emoji=":bento:"]:before,em[data-emoji=bento]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f371.svg)}em[data-emoji=":dumpling:"]:before,em[data-emoji=dumpling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f95f.svg)}em[data-emoji=":fried_shrimp:"]:before,em[data-emoji=fried_shrimp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f364.svg)}em[data-emoji=":rice_ball:"]:before,em[data-emoji=rice_ball]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f359.svg)}em[data-emoji=":rice:"]:before,em[data-emoji=rice]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f35a.svg)}em[data-emoji=":rice_cracker:"]:before,em[data-emoji=rice_cracker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f358.svg)}em[data-emoji=":fish_cake:"]:before,em[data-emoji=fish_cake]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f365.svg)}em[data-emoji=":fortune_cookie:"]:before,em[data-emoji=fortune_cookie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f960.svg)}em[data-emoji=":moon_cake:"]:before,em[data-emoji=moon_cake]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f96e.svg)}em[data-emoji=":oden:"]:before,em[data-emoji=oden]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f362.svg)}em[data-emoji=":dango:"]:before,em[data-emoji=dango]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f361.svg)}em[data-emoji=":shaved_ice:"]:before,em[data-emoji=shaved_ice]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f367.svg)}em[data-emoji=":ice_cream:"]:before,em[data-emoji=ice_cream]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f368.svg)}em[data-emoji=":icecream:"]:before,em[data-emoji=icecream]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f366.svg)}em[data-emoji=":pie:"]:before,em[data-emoji=pie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f967.svg)}em[data-emoji=":cupcake:"]:before,em[data-emoji=cupcake]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c1.svg)}em[data-emoji=":cake:"]:before,em[data-emoji=cake]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f370.svg)}em[data-emoji=":birthday:"]:before,em[data-emoji=birthday]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f382.svg)}em[data-emoji=":custard:"]:before,em[data-emoji=custard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f36e.svg)}em[data-emoji=":lollipop:"]:before,em[data-emoji=lollipop]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f36d.svg)}em[data-emoji=":candy:"]:before,em[data-emoji=candy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f36c.svg)}em[data-emoji=":chocolate_bar:"]:before,em[data-emoji=chocolate_bar]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f36b.svg)}em[data-emoji=":popcorn:"]:before,em[data-emoji=popcorn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f37f.svg)}em[data-emoji=":doughnut:"]:before,em[data-emoji=doughnut]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f369.svg)}em[data-emoji=":cookie:"]:before,em[data-emoji=cookie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f36a.svg)}em[data-emoji=":chestnut:"]:before,em[data-emoji=chestnut]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f330.svg)}em[data-emoji=":peanuts:"]:before,em[data-emoji=peanuts]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f95c.svg)}em[data-emoji=":honey_pot:"]:before,em[data-emoji=honey_pot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f36f.svg)}em[data-emoji=":butter:"]:before,em[data-emoji=butter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c8.svg)}em[data-emoji=":milk:"]:before,em[data-emoji=milk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f95b.svg)}em[data-emoji=":baby_bottle:"]:before,em[data-emoji=baby_bottle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f37c.svg)}em[data-emoji=":tea:"]:before,em[data-emoji=tea]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f375.svg)}em[data-emoji=":mate:"]:before,em[data-emoji=mate]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c9.svg)}em[data-emoji=":cup_with_straw:"]:before,em[data-emoji=cup_with_straw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f964.svg)}em[data-emoji=":beverage_box:"]:before,em[data-emoji=beverage_box]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c3.svg)}em[data-emoji=":ice_cube:"]:before,em[data-emoji=ice_cube]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ca.svg)}em[data-emoji=":sake:"]:before,em[data-emoji=sake]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f376.svg)}em[data-emoji=":beer:"]:before,em[data-emoji=beer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f37a.svg)}em[data-emoji=":beers:"]:before,em[data-emoji=beers]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f37b.svg)}em[data-emoji=":champagne_glass:"]:before,em[data-emoji=champagne_glass]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f942.svg)}em[data-emoji=":wine_glass:"]:before,em[data-emoji=wine_glass]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f377.svg)}em[data-emoji=":tumbler_glass:"]:before,em[data-emoji=tumbler_glass]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f943.svg)}em[data-emoji=":cocktail:"]:before,em[data-emoji=cocktail]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f378.svg)}em[data-emoji=":tropical_drink:"]:before,em[data-emoji=tropical_drink]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f379.svg)}em[data-emoji=":champagne:"]:before,em[data-emoji=champagne]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f37e.svg)}em[data-emoji=":spoon:"]:before,em[data-emoji=spoon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f944.svg)}em[data-emoji=":fork_and_knife:"]:before,em[data-emoji=fork_and_knife]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f374.svg)}em[data-emoji=":fork_knife_plate:"]:before,em[data-emoji=fork_knife_plate]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f37d.svg)}em[data-emoji=":bowl_with_spoon:"]:before,em[data-emoji=bowl_with_spoon]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f963.svg)}em[data-emoji=":takeout_box:"]:before,em[data-emoji=takeout_box]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f961.svg)}em[data-emoji=":chopsticks:"]:before,em[data-emoji=chopsticks]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f962.svg)}em[data-emoji=":salt:"]:before,em[data-emoji=salt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9c2.svg)}em[data-emoji=":blush:"]:before,em[data-emoji=blush]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f60a.svg)}em[data-emoji=":innocent:"]:before,em[data-emoji=innocent]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f607.svg)}em[data-emoji=":slight_smile:"]:before,em[data-emoji=slight_smile]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f642.svg)}em[data-emoji=":upside_down:"]:before,em[data-emoji=upside_down]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f643.svg)}em[data-emoji=":wink:"]:before,em[data-emoji=wink]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f609.svg)}em[data-emoji=":grinning:"]:before,em[data-emoji=grinning]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f600.svg)}em[data-emoji=":smiley:"]:before,em[data-emoji=smiley]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f603.svg)}em[data-emoji=":smile:"]:before,em[data-emoji=smile]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f604.svg)}em[data-emoji=":grin:"]:before,em[data-emoji=grin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f601.svg)}em[data-emoji=":laughing:"]:before,em[data-emoji=laughing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f606.svg)}em[data-emoji=":sweat_smile:"]:before,em[data-emoji=sweat_smile]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f605.svg)}em[data-emoji=":joy:"]:before,em[data-emoji=joy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f602.svg)}em[data-emoji=":rofl:"]:before,em[data-emoji=rofl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f923.svg)}em[data-emoji=":relaxed:"]:before,em[data-emoji=relaxed]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/263a.svg)}em[data-emoji=":relieved:"]:before,em[data-emoji=relieved]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f60c.svg)}em[data-emoji=":heart_eyes:"]:before,em[data-emoji=heart_eyes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f60d.svg)}em[data-emoji=":smiling_face_with_3_hearts:"]:before,em[data-emoji=smiling_face_with_3_hearts]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f970.svg)}em[data-emoji=":kissing_heart:"]:before,em[data-emoji=kissing_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f618.svg)}em[data-emoji=":kissing:"]:before,em[data-emoji=kissing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f617.svg)}em[data-emoji=":kissing_smiling_eyes:"]:before,em[data-emoji=kissing_smiling_eyes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f619.svg)}em[data-emoji=":kissing_closed_eyes:"]:before,em[data-emoji=kissing_closed_eyes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f61a.svg)}em[data-emoji=":yum:"]:before,em[data-emoji=yum]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f60b.svg)}em[data-emoji=":stuck_out_tongue:"]:before,em[data-emoji=stuck_out_tongue]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f61b.svg)}em[data-emoji=":stuck_out_tongue_closed_eyes:"]:before,em[data-emoji=stuck_out_tongue_closed_eyes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f61d.svg)}em[data-emoji=":stuck_out_tongue_winking_eye:"]:before,em[data-emoji=stuck_out_tongue_winking_eye]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f61c.svg)}em[data-emoji=":zany_face:"]:before,em[data-emoji=zany_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f92a.svg)}em[data-emoji=":face_with_raised_eyebrow:"]:before,em[data-emoji=face_with_raised_eyebrow]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f928.svg)}em[data-emoji=":face_with_monocle:"]:before,em[data-emoji=face_with_monocle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d0.svg)}em[data-emoji=":nerd:"]:before,em[data-emoji=nerd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f913.svg)}em[data-emoji=":sunglasses:"]:before,em[data-emoji=sunglasses]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f60e.svg)}em[data-emoji=":star_struck:"]:before,em[data-emoji=star_struck]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f929.svg)}em[data-emoji=":partying_face:"]:before,em[data-emoji=partying_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f973.svg)}em[data-emoji=":smirk:"]:before,em[data-emoji=smirk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f60f.svg)}em[data-emoji=":unamused:"]:before,em[data-emoji=unamused]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f612.svg)}em[data-emoji=":disappointed:"]:before,em[data-emoji=disappointed]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f61e.svg)}em[data-emoji=":pensive:"]:before,em[data-emoji=pensive]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f614.svg)}em[data-emoji=":worried:"]:before,em[data-emoji=worried]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f61f.svg)}em[data-emoji=":confused:"]:before,em[data-emoji=confused]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f615.svg)}em[data-emoji=":slight_frown:"]:before,em[data-emoji=slight_frown]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f641.svg)}em[data-emoji=":persevere:"]:before,em[data-emoji=persevere]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f623.svg)}em[data-emoji=":confounded:"]:before,em[data-emoji=confounded]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f616.svg)}em[data-emoji=":tired_face:"]:before,em[data-emoji=tired_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f62b.svg)}em[data-emoji=":weary:"]:before,em[data-emoji=weary]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f629.svg)}em[data-emoji=":yawning_face:"]:before,em[data-emoji=yawning_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f971.svg)}em[data-emoji=":pleading_face:"]:before,em[data-emoji=pleading_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f97a.svg)}em[data-emoji=":cry:"]:before,em[data-emoji=cry]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f622.svg)}em[data-emoji=":sob:"]:before,em[data-emoji=sob]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f62d.svg)}em[data-emoji=":triumph:"]:before,em[data-emoji=triumph]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f624.svg)}em[data-emoji=":angry:"]:before,em[data-emoji=angry]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f620.svg)}em[data-emoji=":rage:"]:before,em[data-emoji=rage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f621.svg)}em[data-emoji=":face_with_symbols_over_mouth:"]:before,em[data-emoji=face_with_symbols_over_mouth]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f92c.svg)}em[data-emoji=":exploding_head:"]:before,em[data-emoji=exploding_head]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f92f.svg)}em[data-emoji=":flushed:"]:before,em[data-emoji=flushed]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f633.svg)}em[data-emoji=":hot_face:"]:before,em[data-emoji=hot_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f975.svg)}em[data-emoji=":cold_face:"]:before,em[data-emoji=cold_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f976.svg)}em[data-emoji=":scream:"]:before,em[data-emoji=scream]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f631.svg)}em[data-emoji=":fearful:"]:before,em[data-emoji=fearful]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f628.svg)}em[data-emoji=":cold_sweat:"]:before,em[data-emoji=cold_sweat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f630.svg)}em[data-emoji=":disappointed_relieved:"]:before,em[data-emoji=disappointed_relieved]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f625.svg)}em[data-emoji=":sweat:"]:before,em[data-emoji=sweat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f613.svg)}em[data-emoji=":hugging:"]:before,em[data-emoji=hugging]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f917.svg)}em[data-emoji=":thinking:"]:before,em[data-emoji=thinking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f914.svg)}em[data-emoji=":face_with_hand_over_mouth:"]:before,em[data-emoji=face_with_hand_over_mouth]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f92d.svg)}em[data-emoji=":shushing_face:"]:before,em[data-emoji=shushing_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f92b.svg)}em[data-emoji=":lying_face:"]:before,em[data-emoji=lying_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f925.svg)}em[data-emoji=":no_mouth:"]:before,em[data-emoji=no_mouth]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f636.svg)}em[data-emoji=":neutral_face:"]:before,em[data-emoji=neutral_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f610.svg)}em[data-emoji=":expressionless:"]:before,em[data-emoji=expressionless]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f611.svg)}em[data-emoji=":grimacing:"]:before,em[data-emoji=grimacing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f62c.svg)}em[data-emoji=":rolling_eyes:"]:before,em[data-emoji=rolling_eyes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f644.svg)}em[data-emoji=":hushed:"]:before,em[data-emoji=hushed]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f62f.svg)}em[data-emoji=":frowning:"]:before,em[data-emoji=frowning]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f626.svg)}em[data-emoji=":anguished:"]:before,em[data-emoji=anguished]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f627.svg)}em[data-emoji=":open_mouth:"]:before,em[data-emoji=open_mouth]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f62e.svg)}em[data-emoji=":astonished:"]:before,em[data-emoji=astonished]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f632.svg)}em[data-emoji=":sleeping:"]:before,em[data-emoji=sleeping]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f634.svg)}em[data-emoji=":drooling_face:"]:before,em[data-emoji=drooling_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f924.svg)}em[data-emoji=":sleepy:"]:before,em[data-emoji=sleepy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f62a.svg)}em[data-emoji=":dizzy_face:"]:before,em[data-emoji=dizzy_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f635.svg)}em[data-emoji=":zipper_mouth:"]:before,em[data-emoji=zipper_mouth]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f910.svg)}em[data-emoji=":woozy_face:"]:before,em[data-emoji=woozy_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f974.svg)}em[data-emoji=":nauseated_face:"]:before,em[data-emoji=nauseated_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f922.svg)}em[data-emoji=":face_vomiting:"]:before,em[data-emoji=face_vomiting]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f92e.svg)}em[data-emoji=":sneezing_face:"]:before,em[data-emoji=sneezing_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f927.svg)}em[data-emoji=":mask:"]:before,em[data-emoji=mask]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f637.svg)}em[data-emoji=":thermometer_face:"]:before,em[data-emoji=thermometer_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f912.svg)}em[data-emoji=":head_bandage:"]:before,em[data-emoji=head_bandage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f915.svg)}em[data-emoji=":money_mouth:"]:before,em[data-emoji=money_mouth]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f911.svg)}em[data-emoji=":cowboy:"]:before,em[data-emoji=cowboy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f920.svg)}em[data-emoji=":smiling_imp:"]:before,em[data-emoji=smiling_imp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f608.svg)}em[data-emoji=":imp:"]:before,em[data-emoji=imp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47f.svg)}em[data-emoji=":japanese_ogre:"]:before,em[data-emoji=japanese_ogre]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f479.svg)}em[data-emoji=":japanese_goblin:"]:before,em[data-emoji=japanese_goblin]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47a.svg)}em[data-emoji=":clown:"]:before,em[data-emoji=clown]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f921.svg)}em[data-emoji=":poop:"]:before,em[data-emoji=poop]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4a9.svg)}em[data-emoji=":ghost:"]:before,em[data-emoji=ghost]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47b.svg)}em[data-emoji=":skull:"]:before,em[data-emoji=skull]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f480.svg)}em[data-emoji=":alien:"]:before,em[data-emoji=alien]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47d.svg)}em[data-emoji=":space_invader:"]:before,em[data-emoji=space_invader]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47e.svg)}em[data-emoji=":robot:"]:before,em[data-emoji=robot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f916.svg)}em[data-emoji=":jack_o_lantern:"]:before,em[data-emoji=jack_o_lantern]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f383.svg)}em[data-emoji=":smiley_cat:"]:before,em[data-emoji=smiley_cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f63a.svg)}em[data-emoji=":smile_cat:"]:before,em[data-emoji=smile_cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f638.svg)}em[data-emoji=":joy_cat:"]:before,em[data-emoji=joy_cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f639.svg)}em[data-emoji=":heart_eyes_cat:"]:before,em[data-emoji=heart_eyes_cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f63b.svg)}em[data-emoji=":smirk_cat:"]:before,em[data-emoji=smirk_cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f63c.svg)}em[data-emoji=":kissing_cat:"]:before,em[data-emoji=kissing_cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f63d.svg)}em[data-emoji=":scream_cat:"]:before,em[data-emoji=scream_cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f640.svg)}em[data-emoji=":crying_cat_face:"]:before,em[data-emoji=crying_cat_face]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f63f.svg)}em[data-emoji=":pouting_cat:"]:before,em[data-emoji=pouting_cat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f63e.svg)}em[data-emoji=":handshake:"]:before,em[data-emoji=handshake]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91d.svg)}em[data-emoji=":palms_up_together:"]:before,em[data-emoji=palms_up_together]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f932.svg)}em[data-emoji=":palms_up_together_tone1:"]:before,em[data-emoji=palms_up_together_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f932-1f3fb.svg)}em[data-emoji=":palms_up_together_tone2:"]:before,em[data-emoji=palms_up_together_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f932-1f3fc.svg)}em[data-emoji=":palms_up_together_tone3:"]:before,em[data-emoji=palms_up_together_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f932-1f3fd.svg)}em[data-emoji=":palms_up_together_tone4:"]:before,em[data-emoji=palms_up_together_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f932-1f3fe.svg)}em[data-emoji=":palms_up_together_tone5:"]:before,em[data-emoji=palms_up_together_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f932-1f3ff.svg)}em[data-emoji=":open_hands:"]:before,em[data-emoji=open_hands]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f450.svg)}em[data-emoji=":open_hands_tone1:"]:before,em[data-emoji=open_hands_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f450-1f3fb.svg)}em[data-emoji=":open_hands_tone2:"]:before,em[data-emoji=open_hands_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f450-1f3fc.svg)}em[data-emoji=":open_hands_tone3:"]:before,em[data-emoji=open_hands_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f450-1f3fd.svg)}em[data-emoji=":open_hands_tone4:"]:before,em[data-emoji=open_hands_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f450-1f3fe.svg)}em[data-emoji=":open_hands_tone5:"]:before,em[data-emoji=open_hands_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f450-1f3ff.svg)}em[data-emoji=":raised_hands:"]:before,em[data-emoji=raised_hands]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64c.svg)}em[data-emoji=":raised_hands_tone1:"]:before,em[data-emoji=raised_hands_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64c-1f3fb.svg)}em[data-emoji=":raised_hands_tone2:"]:before,em[data-emoji=raised_hands_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64c-1f3fc.svg)}em[data-emoji=":raised_hands_tone3:"]:before,em[data-emoji=raised_hands_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64c-1f3fd.svg)}em[data-emoji=":raised_hands_tone4:"]:before,em[data-emoji=raised_hands_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64c-1f3fe.svg)}em[data-emoji=":raised_hands_tone5:"]:before,em[data-emoji=raised_hands_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64c-1f3ff.svg)}em[data-emoji=":clap:"]:before,em[data-emoji=clap]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44f.svg)}em[data-emoji=":clap_tone1:"]:before,em[data-emoji=clap_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44f-1f3fb.svg)}em[data-emoji=":clap_tone2:"]:before,em[data-emoji=clap_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44f-1f3fc.svg)}em[data-emoji=":clap_tone3:"]:before,em[data-emoji=clap_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44f-1f3fd.svg)}em[data-emoji=":clap_tone4:"]:before,em[data-emoji=clap_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44f-1f3fe.svg)}em[data-emoji=":clap_tone5:"]:before,em[data-emoji=clap_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44f-1f3ff.svg)}em[data-emoji=":thumbsup:"]:before,em[data-emoji=thumbsup]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44d.svg)}em[data-emoji=":thumbsup_tone1:"]:before,em[data-emoji=thumbsup_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44d-1f3fb.svg)}em[data-emoji=":thumbsup_tone2:"]:before,em[data-emoji=thumbsup_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44d-1f3fc.svg)}em[data-emoji=":thumbsup_tone3:"]:before,em[data-emoji=thumbsup_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44d-1f3fd.svg)}em[data-emoji=":thumbsup_tone4:"]:before,em[data-emoji=thumbsup_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44d-1f3fe.svg)}em[data-emoji=":thumbsup_tone5:"]:before,em[data-emoji=thumbsup_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44d-1f3ff.svg)}em[data-emoji=":thumbsdown:"]:before,em[data-emoji=thumbsdown]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44e.svg)}em[data-emoji=":thumbsdown_tone1:"]:before,em[data-emoji=thumbsdown_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44e-1f3fb.svg)}em[data-emoji=":thumbsdown_tone2:"]:before,em[data-emoji=thumbsdown_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44e-1f3fc.svg)}em[data-emoji=":thumbsdown_tone3:"]:before,em[data-emoji=thumbsdown_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44e-1f3fd.svg)}em[data-emoji=":thumbsdown_tone4:"]:before,em[data-emoji=thumbsdown_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44e-1f3fe.svg)}em[data-emoji=":thumbsdown_tone5:"]:before,em[data-emoji=thumbsdown_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44e-1f3ff.svg)}em[data-emoji=":punch:"]:before,em[data-emoji=punch]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44a.svg)}em[data-emoji=":punch_tone1:"]:before,em[data-emoji=punch_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44a-1f3fb.svg)}em[data-emoji=":punch_tone2:"]:before,em[data-emoji=punch_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44a-1f3fc.svg)}em[data-emoji=":punch_tone3:"]:before,em[data-emoji=punch_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44a-1f3fd.svg)}em[data-emoji=":punch_tone4:"]:before,em[data-emoji=punch_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44a-1f3fe.svg)}em[data-emoji=":punch_tone5:"]:before,em[data-emoji=punch_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44a-1f3ff.svg)}em[data-emoji=":fist:"]:before,em[data-emoji=fist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270a.svg)}em[data-emoji=":fist_tone1:"]:before,em[data-emoji=fist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270a-1f3fb.svg)}em[data-emoji=":fist_tone2:"]:before,em[data-emoji=fist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270a-1f3fc.svg)}em[data-emoji=":fist_tone3:"]:before,em[data-emoji=fist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270a-1f3fd.svg)}em[data-emoji=":fist_tone4:"]:before,em[data-emoji=fist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270a-1f3fe.svg)}em[data-emoji=":fist_tone5:"]:before,em[data-emoji=fist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270a-1f3ff.svg)}em[data-emoji=":left_facing_fist:"]:before,em[data-emoji=left_facing_fist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91b.svg)}em[data-emoji=":left_facing_fist_tone1:"]:before,em[data-emoji=left_facing_fist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91b-1f3fb.svg)}em[data-emoji=":left_facing_fist_tone2:"]:before,em[data-emoji=left_facing_fist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91b-1f3fc.svg)}em[data-emoji=":left_facing_fist_tone3:"]:before,em[data-emoji=left_facing_fist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91b-1f3fd.svg)}em[data-emoji=":left_facing_fist_tone4:"]:before,em[data-emoji=left_facing_fist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91b-1f3fe.svg)}em[data-emoji=":left_facing_fist_tone5:"]:before,em[data-emoji=left_facing_fist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91b-1f3ff.svg)}em[data-emoji=":right_facing_fist:"]:before,em[data-emoji=right_facing_fist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91c.svg)}em[data-emoji=":right_facing_fist_tone1:"]:before,em[data-emoji=right_facing_fist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91c-1f3fb.svg)}em[data-emoji=":right_facing_fist_tone2:"]:before,em[data-emoji=right_facing_fist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91c-1f3fc.svg)}em[data-emoji=":right_facing_fist_tone3:"]:before,em[data-emoji=right_facing_fist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91c-1f3fd.svg)}em[data-emoji=":right_facing_fist_tone4:"]:before,em[data-emoji=right_facing_fist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91c-1f3fe.svg)}em[data-emoji=":right_facing_fist_tone5:"]:before,em[data-emoji=right_facing_fist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91c-1f3ff.svg)}em[data-emoji=":fingers_crossed:"]:before,em[data-emoji=fingers_crossed]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91e.svg)}em[data-emoji=":fingers_crossed_tone1:"]:before,em[data-emoji=fingers_crossed_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91e-1f3fb.svg)}em[data-emoji=":fingers_crossed_tone2:"]:before,em[data-emoji=fingers_crossed_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91e-1f3fc.svg)}em[data-emoji=":fingers_crossed_tone3:"]:before,em[data-emoji=fingers_crossed_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91e-1f3fd.svg)}em[data-emoji=":fingers_crossed_tone4:"]:before,em[data-emoji=fingers_crossed_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91e-1f3fe.svg)}em[data-emoji=":fingers_crossed_tone5:"]:before,em[data-emoji=fingers_crossed_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91e-1f3ff.svg)}em[data-emoji=":v:"]:before,em[data-emoji="v"]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270c.svg)}em[data-emoji=":v_tone1:"]:before,em[data-emoji=v_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270c-1f3fb.svg)}em[data-emoji=":v_tone2:"]:before,em[data-emoji=v_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270c-1f3fc.svg)}em[data-emoji=":v_tone3:"]:before,em[data-emoji=v_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270c-1f3fd.svg)}em[data-emoji=":v_tone4:"]:before,em[data-emoji=v_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270c-1f3fe.svg)}em[data-emoji=":v_tone5:"]:before,em[data-emoji=v_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270c-1f3ff.svg)}em[data-emoji=":love_you_gesture:"]:before,em[data-emoji=love_you_gesture]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91f.svg)}em[data-emoji=":love_you_gesture_tone1:"]:before,em[data-emoji=love_you_gesture_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91f-1f3fb.svg)}em[data-emoji=":love_you_gesture_tone2:"]:before,em[data-emoji=love_you_gesture_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91f-1f3fc.svg)}em[data-emoji=":love_you_gesture_tone3:"]:before,em[data-emoji=love_you_gesture_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91f-1f3fd.svg)}em[data-emoji=":love_you_gesture_tone4:"]:before,em[data-emoji=love_you_gesture_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91f-1f3fe.svg)}em[data-emoji=":love_you_gesture_tone5:"]:before,em[data-emoji=love_you_gesture_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91f-1f3ff.svg)}em[data-emoji=":metal:"]:before,em[data-emoji=metal]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f918.svg)}em[data-emoji=":metal_tone1:"]:before,em[data-emoji=metal_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f918-1f3fb.svg)}em[data-emoji=":metal_tone2:"]:before,em[data-emoji=metal_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f918-1f3fc.svg)}em[data-emoji=":metal_tone3:"]:before,em[data-emoji=metal_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f918-1f3fd.svg)}em[data-emoji=":metal_tone4:"]:before,em[data-emoji=metal_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f918-1f3fe.svg)}em[data-emoji=":metal_tone5:"]:before,em[data-emoji=metal_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f918-1f3ff.svg)}em[data-emoji=":ok_hand:"]:before,em[data-emoji=ok_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44c.svg)}em[data-emoji=":ok_hand_tone1:"]:before,em[data-emoji=ok_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44c-1f3fb.svg)}em[data-emoji=":ok_hand_tone2:"]:before,em[data-emoji=ok_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44c-1f3fc.svg)}em[data-emoji=":ok_hand_tone3:"]:before,em[data-emoji=ok_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44c-1f3fd.svg)}em[data-emoji=":ok_hand_tone4:"]:before,em[data-emoji=ok_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44c-1f3fe.svg)}em[data-emoji=":ok_hand_tone5:"]:before,em[data-emoji=ok_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44c-1f3ff.svg)}em[data-emoji=":pinching_hand:"]:before,em[data-emoji=pinching_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f90f.svg)}em[data-emoji=":pinching_hand_tone1:"]:before,em[data-emoji=pinching_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f90f-1f3fb.svg)}em[data-emoji=":pinching_hand_tone2:"]:before,em[data-emoji=pinching_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f90f-1f3fc.svg)}em[data-emoji=":pinching_hand_tone3:"]:before,em[data-emoji=pinching_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f90f-1f3fd.svg)}em[data-emoji=":pinching_hand_tone4:"]:before,em[data-emoji=pinching_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f90f-1f3fe.svg)}em[data-emoji=":pinching_hand_tone5:"]:before,em[data-emoji=pinching_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f90f-1f3ff.svg)}em[data-emoji=":point_left:"]:before,em[data-emoji=point_left]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f448.svg)}em[data-emoji=":point_left_tone1:"]:before,em[data-emoji=point_left_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f448-1f3fb.svg)}em[data-emoji=":point_left_tone2:"]:before,em[data-emoji=point_left_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f448-1f3fc.svg)}em[data-emoji=":point_left_tone3:"]:before,em[data-emoji=point_left_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f448-1f3fd.svg)}em[data-emoji=":point_left_tone4:"]:before,em[data-emoji=point_left_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f448-1f3fe.svg)}em[data-emoji=":point_left_tone5:"]:before,em[data-emoji=point_left_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f448-1f3ff.svg)}em[data-emoji=":point_right:"]:before,em[data-emoji=point_right]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f449.svg)}em[data-emoji=":point_right_tone1:"]:before,em[data-emoji=point_right_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f449-1f3fb.svg)}em[data-emoji=":point_right_tone2:"]:before,em[data-emoji=point_right_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f449-1f3fc.svg)}em[data-emoji=":point_right_tone3:"]:before,em[data-emoji=point_right_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f449-1f3fd.svg)}em[data-emoji=":point_right_tone4:"]:before,em[data-emoji=point_right_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f449-1f3fe.svg)}em[data-emoji=":point_right_tone5:"]:before,em[data-emoji=point_right_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f449-1f3ff.svg)}em[data-emoji=":point_up_2:"]:before,em[data-emoji=point_up_2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f446.svg)}em[data-emoji=":point_up_2_tone1:"]:before,em[data-emoji=point_up_2_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f446-1f3fb.svg)}em[data-emoji=":point_up_2_tone2:"]:before,em[data-emoji=point_up_2_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f446-1f3fc.svg)}em[data-emoji=":point_up_2_tone3:"]:before,em[data-emoji=point_up_2_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f446-1f3fd.svg)}em[data-emoji=":point_up_2_tone4:"]:before,em[data-emoji=point_up_2_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f446-1f3fe.svg)}em[data-emoji=":point_up_2_tone5:"]:before,em[data-emoji=point_up_2_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f446-1f3ff.svg)}em[data-emoji=":point_down:"]:before,em[data-emoji=point_down]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f447.svg)}em[data-emoji=":point_down_tone1:"]:before,em[data-emoji=point_down_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f447-1f3fb.svg)}em[data-emoji=":point_down_tone2:"]:before,em[data-emoji=point_down_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f447-1f3fc.svg)}em[data-emoji=":point_down_tone3:"]:before,em[data-emoji=point_down_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f447-1f3fd.svg)}em[data-emoji=":point_down_tone4:"]:before,em[data-emoji=point_down_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f447-1f3fe.svg)}em[data-emoji=":point_down_tone5:"]:before,em[data-emoji=point_down_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f447-1f3ff.svg)}em[data-emoji=":point_up:"]:before,em[data-emoji=point_up]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/261d.svg)}em[data-emoji=":point_up_tone1:"]:before,em[data-emoji=point_up_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/261d-1f3fb.svg)}em[data-emoji=":point_up_tone2:"]:before,em[data-emoji=point_up_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/261d-1f3fc.svg)}em[data-emoji=":point_up_tone3:"]:before,em[data-emoji=point_up_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/261d-1f3fd.svg)}em[data-emoji=":point_up_tone4:"]:before,em[data-emoji=point_up_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/261d-1f3fe.svg)}em[data-emoji=":point_up_tone5:"]:before,em[data-emoji=point_up_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/261d-1f3ff.svg)}em[data-emoji=":raised_hand:"]:before,em[data-emoji=raised_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270b.svg)}em[data-emoji=":raised_hand_tone1:"]:before,em[data-emoji=raised_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270b-1f3fb.svg)}em[data-emoji=":raised_hand_tone2:"]:before,em[data-emoji=raised_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270b-1f3fc.svg)}em[data-emoji=":raised_hand_tone3:"]:before,em[data-emoji=raised_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270b-1f3fd.svg)}em[data-emoji=":raised_hand_tone4:"]:before,em[data-emoji=raised_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270b-1f3fe.svg)}em[data-emoji=":raised_hand_tone5:"]:before,em[data-emoji=raised_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270b-1f3ff.svg)}em[data-emoji=":raised_back_of_hand:"]:before,em[data-emoji=raised_back_of_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91a.svg)}em[data-emoji=":raised_back_of_hand_tone1:"]:before,em[data-emoji=raised_back_of_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91a-1f3fb.svg)}em[data-emoji=":raised_back_of_hand_tone2:"]:before,em[data-emoji=raised_back_of_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91a-1f3fc.svg)}em[data-emoji=":raised_back_of_hand_tone3:"]:before,em[data-emoji=raised_back_of_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91a-1f3fd.svg)}em[data-emoji=":raised_back_of_hand_tone4:"]:before,em[data-emoji=raised_back_of_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91a-1f3fe.svg)}em[data-emoji=":raised_back_of_hand_tone5:"]:before,em[data-emoji=raised_back_of_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f91a-1f3ff.svg)}em[data-emoji=":hand_splayed:"]:before,em[data-emoji=hand_splayed]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f590.svg)}em[data-emoji=":hand_splayed_tone1:"]:before,em[data-emoji=hand_splayed_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f590-1f3fb.svg)}em[data-emoji=":hand_splayed_tone2:"]:before,em[data-emoji=hand_splayed_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f590-1f3fc.svg)}em[data-emoji=":hand_splayed_tone3:"]:before,em[data-emoji=hand_splayed_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f590-1f3fd.svg)}em[data-emoji=":hand_splayed_tone4:"]:before,em[data-emoji=hand_splayed_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f590-1f3fe.svg)}em[data-emoji=":hand_splayed_tone5:"]:before,em[data-emoji=hand_splayed_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f590-1f3ff.svg)}em[data-emoji=":vulcan:"]:before,em[data-emoji=vulcan]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f596.svg)}em[data-emoji=":vulcan_tone1:"]:before,em[data-emoji=vulcan_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f596-1f3fb.svg)}em[data-emoji=":vulcan_tone2:"]:before,em[data-emoji=vulcan_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f596-1f3fc.svg)}em[data-emoji=":vulcan_tone3:"]:before,em[data-emoji=vulcan_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f596-1f3fd.svg)}em[data-emoji=":vulcan_tone4:"]:before,em[data-emoji=vulcan_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f596-1f3fe.svg)}em[data-emoji=":vulcan_tone5:"]:before,em[data-emoji=vulcan_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f596-1f3ff.svg)}em[data-emoji=":wave:"]:before,em[data-emoji=wave]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg)}em[data-emoji=":wave_tone1:"]:before,em[data-emoji=wave_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3fb.svg)}em[data-emoji=":wave_tone2:"]:before,em[data-emoji=wave_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3fc.svg)}em[data-emoji=":wave_tone3:"]:before,em[data-emoji=wave_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3fd.svg)}em[data-emoji=":wave_tone4:"]:before,em[data-emoji=wave_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3fe.svg)}em[data-emoji=":wave_tone5:"]:before,em[data-emoji=wave_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3ff.svg)}em[data-emoji=":call_me:"]:before,em[data-emoji=call_me]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f919.svg)}em[data-emoji=":call_me_tone1:"]:before,em[data-emoji=call_me_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f919-1f3fb.svg)}em[data-emoji=":call_me_tone2:"]:before,em[data-emoji=call_me_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f919-1f3fc.svg)}em[data-emoji=":call_me_tone3:"]:before,em[data-emoji=call_me_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f919-1f3fd.svg)}em[data-emoji=":call_me_tone4:"]:before,em[data-emoji=call_me_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f919-1f3fe.svg)}em[data-emoji=":call_me_tone5:"]:before,em[data-emoji=call_me_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f919-1f3ff.svg)}em[data-emoji=":muscle:"]:before,em[data-emoji=muscle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4aa.svg)}em[data-emoji=":muscle_tone1:"]:before,em[data-emoji=muscle_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4aa-1f3fb.svg)}em[data-emoji=":muscle_tone2:"]:before,em[data-emoji=muscle_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4aa-1f3fc.svg)}em[data-emoji=":muscle_tone3:"]:before,em[data-emoji=muscle_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4aa-1f3fd.svg)}em[data-emoji=":muscle_tone4:"]:before,em[data-emoji=muscle_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4aa-1f3fe.svg)}em[data-emoji=":muscle_tone5:"]:before,em[data-emoji=muscle_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4aa-1f3ff.svg)}em[data-emoji=":mechanical_arm:"]:before,em[data-emoji=mechanical_arm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9be.svg)}em[data-emoji=":middle_finger:"]:before,em[data-emoji=middle_finger]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f595.svg)}em[data-emoji=":middle_finger_tone1:"]:before,em[data-emoji=middle_finger_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f595-1f3fb.svg)}em[data-emoji=":middle_finger_tone2:"]:before,em[data-emoji=middle_finger_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f595-1f3fc.svg)}em[data-emoji=":middle_finger_tone3:"]:before,em[data-emoji=middle_finger_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f595-1f3fd.svg)}em[data-emoji=":middle_finger_tone4:"]:before,em[data-emoji=middle_finger_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f595-1f3fe.svg)}em[data-emoji=":middle_finger_tone5:"]:before,em[data-emoji=middle_finger_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f595-1f3ff.svg)}em[data-emoji=":writing_hand:"]:before,em[data-emoji=writing_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270d.svg)}em[data-emoji=":writing_hand_tone1:"]:before,em[data-emoji=writing_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270d-1f3fb.svg)}em[data-emoji=":writing_hand_tone2:"]:before,em[data-emoji=writing_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270d-1f3fc.svg)}em[data-emoji=":writing_hand_tone3:"]:before,em[data-emoji=writing_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270d-1f3fd.svg)}em[data-emoji=":writing_hand_tone4:"]:before,em[data-emoji=writing_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270d-1f3fe.svg)}em[data-emoji=":writing_hand_tone5:"]:before,em[data-emoji=writing_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/270d-1f3ff.svg)}em[data-emoji=":pray:"]:before,em[data-emoji=pray]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64f.svg)}em[data-emoji=":pray_tone1:"]:before,em[data-emoji=pray_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64f-1f3fb.svg)}em[data-emoji=":pray_tone2:"]:before,em[data-emoji=pray_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64f-1f3fc.svg)}em[data-emoji=":pray_tone3:"]:before,em[data-emoji=pray_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64f-1f3fd.svg)}em[data-emoji=":pray_tone4:"]:before,em[data-emoji=pray_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64f-1f3fe.svg)}em[data-emoji=":pray_tone5:"]:before,em[data-emoji=pray_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64f-1f3ff.svg)}em[data-emoji=":foot:"]:before,em[data-emoji=foot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b6.svg)}em[data-emoji=":foot_tone1:"]:before,em[data-emoji=foot_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b6-1f3fb.svg)}em[data-emoji=":foot_tone2:"]:before,em[data-emoji=foot_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b6-1f3fc.svg)}em[data-emoji=":foot_tone3:"]:before,em[data-emoji=foot_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b6-1f3fd.svg)}em[data-emoji=":foot_tone4:"]:before,em[data-emoji=foot_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b6-1f3fe.svg)}em[data-emoji=":foot_tone5:"]:before,em[data-emoji=foot_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b6-1f3ff.svg)}em[data-emoji=":leg:"]:before,em[data-emoji=leg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b5.svg)}em[data-emoji=":leg_tone1:"]:before,em[data-emoji=leg_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b5-1f3fb.svg)}em[data-emoji=":leg_tone2:"]:before,em[data-emoji=leg_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b5-1f3fc.svg)}em[data-emoji=":leg_tone3:"]:before,em[data-emoji=leg_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b5-1f3fd.svg)}em[data-emoji=":leg_tone4:"]:before,em[data-emoji=leg_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b5-1f3fe.svg)}em[data-emoji=":leg_tone5:"]:before,em[data-emoji=leg_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b5-1f3ff.svg)}em[data-emoji=":mechanical_leg:"]:before,em[data-emoji=mechanical_leg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bf.svg)}em[data-emoji=":lipstick:"]:before,em[data-emoji=lipstick]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f484.svg)}em[data-emoji=":kiss:"]:before,em[data-emoji=kiss]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f48b.svg)}em[data-emoji=":lips:"]:before,em[data-emoji=lips]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f444.svg)}em[data-emoji=":tongue:"]:before,em[data-emoji=tongue]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f445.svg)}em[data-emoji=":tooth:"]:before,em[data-emoji=tooth]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b7.svg)}em[data-emoji=":bone:"]:before,em[data-emoji=bone]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b4.svg)}em[data-emoji=":ear:"]:before,em[data-emoji=ear]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f442.svg)}em[data-emoji=":ear_tone1:"]:before,em[data-emoji=ear_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f442-1f3fb.svg)}em[data-emoji=":ear_tone2:"]:before,em[data-emoji=ear_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f442-1f3fc.svg)}em[data-emoji=":ear_tone3:"]:before,em[data-emoji=ear_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f442-1f3fd.svg)}em[data-emoji=":ear_tone4:"]:before,em[data-emoji=ear_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f442-1f3fe.svg)}em[data-emoji=":ear_tone5:"]:before,em[data-emoji=ear_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f442-1f3ff.svg)}em[data-emoji=":ear_with_hearing_aid:"]:before,em[data-emoji=ear_with_hearing_aid]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bb.svg)}em[data-emoji=":ear_with_hearing_aid_tone1:"]:before,em[data-emoji=ear_with_hearing_aid_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bb-1f3fb.svg)}em[data-emoji=":ear_with_hearing_aid_tone2:"]:before,em[data-emoji=ear_with_hearing_aid_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bb-1f3fc.svg)}em[data-emoji=":ear_with_hearing_aid_tone3:"]:before,em[data-emoji=ear_with_hearing_aid_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bb-1f3fd.svg)}em[data-emoji=":ear_with_hearing_aid_tone4:"]:before,em[data-emoji=ear_with_hearing_aid_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bb-1f3fe.svg)}em[data-emoji=":ear_with_hearing_aid_tone5:"]:before,em[data-emoji=ear_with_hearing_aid_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bb-1f3ff.svg)}em[data-emoji=":nose:"]:before,em[data-emoji=nose]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f443.svg)}em[data-emoji=":nose_tone1:"]:before,em[data-emoji=nose_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f443-1f3fb.svg)}em[data-emoji=":nose_tone2:"]:before,em[data-emoji=nose_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f443-1f3fc.svg)}em[data-emoji=":nose_tone3:"]:before,em[data-emoji=nose_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f443-1f3fd.svg)}em[data-emoji=":nose_tone4:"]:before,em[data-emoji=nose_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f443-1f3fe.svg)}em[data-emoji=":nose_tone5:"]:before,em[data-emoji=nose_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f443-1f3ff.svg)}em[data-emoji=":footprints:"]:before,em[data-emoji=footprints]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f463.svg)}em[data-emoji=":eye:"]:before,em[data-emoji=eye]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f441.svg)}em[data-emoji=":eyes:"]:before,em[data-emoji=eyes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f440.svg)}em[data-emoji=":brain:"]:before,em[data-emoji=brain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e0.svg)}em[data-emoji=":speaking_head:"]:before,em[data-emoji=speaking_head]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5e3.svg)}em[data-emoji=":bust_in_silhouette:"]:before,em[data-emoji=bust_in_silhouette]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f464.svg)}em[data-emoji=":busts_in_silhouette:"]:before,em[data-emoji=busts_in_silhouette]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f465.svg)}em[data-emoji=":baby:"]:before,em[data-emoji=baby]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f476.svg)}em[data-emoji=":baby_tone1:"]:before,em[data-emoji=baby_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f476-1f3fb.svg)}em[data-emoji=":baby_tone2:"]:before,em[data-emoji=baby_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f476-1f3fc.svg)}em[data-emoji=":baby_tone3:"]:before,em[data-emoji=baby_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f476-1f3fd.svg)}em[data-emoji=":baby_tone4:"]:before,em[data-emoji=baby_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f476-1f3fe.svg)}em[data-emoji=":baby_tone5:"]:before,em[data-emoji=baby_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f476-1f3ff.svg)}em[data-emoji=":girl:"]:before,em[data-emoji=girl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f467.svg)}em[data-emoji=":girl_tone1:"]:before,em[data-emoji=girl_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f467-1f3fb.svg)}em[data-emoji=":girl_tone2:"]:before,em[data-emoji=girl_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f467-1f3fc.svg)}em[data-emoji=":girl_tone3:"]:before,em[data-emoji=girl_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f467-1f3fd.svg)}em[data-emoji=":girl_tone4:"]:before,em[data-emoji=girl_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f467-1f3fe.svg)}em[data-emoji=":girl_tone5:"]:before,em[data-emoji=girl_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f467-1f3ff.svg)}em[data-emoji=":child:"]:before,em[data-emoji=child]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d2.svg)}em[data-emoji=":child_tone1:"]:before,em[data-emoji=child_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d2-1f3fb.svg)}em[data-emoji=":child_tone2:"]:before,em[data-emoji=child_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d2-1f3fc.svg)}em[data-emoji=":child_tone3:"]:before,em[data-emoji=child_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d2-1f3fd.svg)}em[data-emoji=":child_tone4:"]:before,em[data-emoji=child_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d2-1f3fe.svg)}em[data-emoji=":child_tone5:"]:before,em[data-emoji=child_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d2-1f3ff.svg)}em[data-emoji=":boy:"]:before,em[data-emoji=boy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f466.svg)}em[data-emoji=":boy_tone1:"]:before,em[data-emoji=boy_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f466-1f3fb.svg)}em[data-emoji=":boy_tone2:"]:before,em[data-emoji=boy_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f466-1f3fc.svg)}em[data-emoji=":boy_tone3:"]:before,em[data-emoji=boy_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f466-1f3fd.svg)}em[data-emoji=":boy_tone4:"]:before,em[data-emoji=boy_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f466-1f3fe.svg)}em[data-emoji=":boy_tone5:"]:before,em[data-emoji=boy_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f466-1f3ff.svg)}em[data-emoji=":woman:"]:before,em[data-emoji=woman]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469.svg)}em[data-emoji=":woman_tone1:"]:before,em[data-emoji=woman_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb.svg)}em[data-emoji=":woman_tone2:"]:before,em[data-emoji=woman_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc.svg)}em[data-emoji=":woman_tone3:"]:before,em[data-emoji=woman_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd.svg)}em[data-emoji=":woman_tone4:"]:before,em[data-emoji=woman_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe.svg)}em[data-emoji=":woman_tone5:"]:before,em[data-emoji=woman_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff.svg)}em[data-emoji=":adult:"]:before,em[data-emoji=adult]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1.svg)}em[data-emoji=":adult_tone1:"]:before,em[data-emoji=adult_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fb.svg)}em[data-emoji=":adult_tone2:"]:before,em[data-emoji=adult_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fc.svg)}em[data-emoji=":adult_tone3:"]:before,em[data-emoji=adult_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fd.svg)}em[data-emoji=":adult_tone4:"]:before,em[data-emoji=adult_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fe.svg)}em[data-emoji=":adult_tone5:"]:before,em[data-emoji=adult_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3ff.svg)}em[data-emoji=":man:"]:before,em[data-emoji=man]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468.svg)}em[data-emoji=":man_tone1:"]:before,em[data-emoji=man_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb.svg)}em[data-emoji=":man_tone2:"]:before,em[data-emoji=man_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc.svg)}em[data-emoji=":man_tone3:"]:before,em[data-emoji=man_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd.svg)}em[data-emoji=":man_tone4:"]:before,em[data-emoji=man_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe.svg)}em[data-emoji=":man_tone5:"]:before,em[data-emoji=man_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff.svg)}em[data-emoji=":woman_curly_haired:"]:before,em[data-emoji=woman_curly_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f9b1.svg)}em[data-emoji=":woman_curly_haired_tone1:"]:before,em[data-emoji=woman_curly_haired_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f9b1.svg)}em[data-emoji=":woman_curly_haired_tone2:"]:before,em[data-emoji=woman_curly_haired_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f9b1.svg)}em[data-emoji=":woman_curly_haired_tone3:"]:before,em[data-emoji=woman_curly_haired_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f9b1.svg)}em[data-emoji=":woman_curly_haired_tone4:"]:before,em[data-emoji=woman_curly_haired_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f9b1.svg)}em[data-emoji=":woman_curly_haired_tone5:"]:before,em[data-emoji=woman_curly_haired_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f9b1.svg)}em[data-emoji=":man_curly_haired:"]:before,em[data-emoji=man_curly_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f9b1.svg)}em[data-emoji=":man_curly_haired_tone1:"]:before,em[data-emoji=man_curly_haired_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f9b1.svg)}em[data-emoji=":man_curly_haired_tone2:"]:before,em[data-emoji=man_curly_haired_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f9b1.svg)}em[data-emoji=":man_curly_haired_tone3:"]:before,em[data-emoji=man_curly_haired_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f9b1.svg)}em[data-emoji=":man_curly_haired_tone4:"]:before,em[data-emoji=man_curly_haired_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f9b1.svg)}em[data-emoji=":man_curly_haired_tone5:"]:before,em[data-emoji=man_curly_haired_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f9b1.svg)}em[data-emoji=":woman_red_haired:"]:before,em[data-emoji=woman_red_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f9b0.svg)}em[data-emoji=":woman_red_haired_tone1:"]:before,em[data-emoji=woman_red_haired_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f9b0.svg)}em[data-emoji=":woman_red_haired_tone2:"]:before,em[data-emoji=woman_red_haired_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f9b0.svg)}em[data-emoji=":woman_red_haired_tone3:"]:before,em[data-emoji=woman_red_haired_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f9b0.svg)}em[data-emoji=":woman_red_haired_tone4:"]:before,em[data-emoji=woman_red_haired_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f9b0.svg)}em[data-emoji=":woman_red_haired_tone5:"]:before,em[data-emoji=woman_red_haired_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f9b0.svg)}em[data-emoji=":man_red_haired:"]:before,em[data-emoji=man_red_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f9b0.svg)}em[data-emoji=":man_red_haired_tone1:"]:before,em[data-emoji=man_red_haired_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f9b0.svg)}em[data-emoji=":man_red_haired_tone2:"]:before,em[data-emoji=man_red_haired_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f9b0.svg)}em[data-emoji=":man_red_haired_tone3:"]:before,em[data-emoji=man_red_haired_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f9b0.svg)}em[data-emoji=":man_red_haired_tone4:"]:before,em[data-emoji=man_red_haired_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f9b0.svg)}em[data-emoji=":man_red_haired_tone5:"]:before,em[data-emoji=man_red_haired_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f9b0.svg)}em[data-emoji=":blond-haired_woman:"]:before,em[data-emoji=blond-haired_woman]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-200d-2640-fe0f.svg)}em[data-emoji=":blond-haired_woman_tone1:"]:before,em[data-emoji=blond-haired_woman_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":blond-haired_woman_tone2:"]:before,em[data-emoji=blond-haired_woman_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":blond-haired_woman_tone3:"]:before,em[data-emoji=blond-haired_woman_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":blond-haired_woman_tone4:"]:before,em[data-emoji=blond-haired_woman_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":blond-haired_woman_tone5:"]:before,em[data-emoji=blond-haired_woman_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":blond_haired_person:"]:before,em[data-emoji=blond_haired_person]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471.svg)}em[data-emoji=":blond_haired_person_tone1:"]:before,em[data-emoji=blond_haired_person_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fb.svg)}em[data-emoji=":blond_haired_person_tone2:"]:before,em[data-emoji=blond_haired_person_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fc.svg)}em[data-emoji=":blond_haired_person_tone3:"]:before,em[data-emoji=blond_haired_person_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fd.svg)}em[data-emoji=":blond_haired_person_tone4:"]:before,em[data-emoji=blond_haired_person_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fe.svg)}em[data-emoji=":blond_haired_person_tone5:"]:before,em[data-emoji=blond_haired_person_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3ff.svg)}em[data-emoji=":blond-haired_man:"]:before,em[data-emoji=blond-haired_man]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-200d-2642-fe0f.svg)}em[data-emoji=":blond-haired_man_tone1:"]:before,em[data-emoji=blond-haired_man_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":blond-haired_man_tone2:"]:before,em[data-emoji=blond-haired_man_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":blond-haired_man_tone3:"]:before,em[data-emoji=blond-haired_man_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":blond-haired_man_tone4:"]:before,em[data-emoji=blond-haired_man_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":blond-haired_man_tone5:"]:before,em[data-emoji=blond-haired_man_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f471-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":woman_white_haired:"]:before,em[data-emoji=woman_white_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f9b3.svg)}em[data-emoji=":woman_white_haired_tone1:"]:before,em[data-emoji=woman_white_haired_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f9b3.svg)}em[data-emoji=":woman_white_haired_tone2:"]:before,em[data-emoji=woman_white_haired_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f9b3.svg)}em[data-emoji=":woman_white_haired_tone3:"]:before,em[data-emoji=woman_white_haired_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f9b3.svg)}em[data-emoji=":woman_white_haired_tone4:"]:before,em[data-emoji=woman_white_haired_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f9b3.svg)}em[data-emoji=":woman_white_haired_tone5:"]:before,em[data-emoji=woman_white_haired_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f9b3.svg)}em[data-emoji=":man_white_haired:"]:before,em[data-emoji=man_white_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f9b3.svg)}em[data-emoji=":man_white_haired_tone1:"]:before,em[data-emoji=man_white_haired_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f9b3.svg)}em[data-emoji=":man_white_haired_tone2:"]:before,em[data-emoji=man_white_haired_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f9b3.svg)}em[data-emoji=":man_white_haired_tone3:"]:before,em[data-emoji=man_white_haired_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f9b3.svg)}em[data-emoji=":man_white_haired_tone4:"]:before,em[data-emoji=man_white_haired_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f9b3.svg)}em[data-emoji=":man_white_haired_tone5:"]:before,em[data-emoji=man_white_haired_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f9b3.svg)}em[data-emoji=":woman_bald:"]:before,em[data-emoji=woman_bald]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f9b2.svg)}em[data-emoji=":woman_bald_tone1:"]:before,em[data-emoji=woman_bald_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f9b2.svg)}em[data-emoji=":woman_bald_tone2:"]:before,em[data-emoji=woman_bald_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f9b2.svg)}em[data-emoji=":woman_bald_tone3:"]:before,em[data-emoji=woman_bald_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f9b2.svg)}em[data-emoji=":woman_bald_tone4:"]:before,em[data-emoji=woman_bald_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f9b2.svg)}em[data-emoji=":woman_bald_tone5:"]:before,em[data-emoji=woman_bald_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f9b2.svg)}em[data-emoji=":man_bald:"]:before,em[data-emoji=man_bald]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f9b2.svg)}em[data-emoji=":man_bald_tone1:"]:before,em[data-emoji=man_bald_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f9b2.svg)}em[data-emoji=":man_bald_tone2:"]:before,em[data-emoji=man_bald_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f9b2.svg)}em[data-emoji=":man_bald_tone3:"]:before,em[data-emoji=man_bald_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f9b2.svg)}em[data-emoji=":man_bald_tone4:"]:before,em[data-emoji=man_bald_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f9b2.svg)}em[data-emoji=":man_bald_tone5:"]:before,em[data-emoji=man_bald_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f9b2.svg)}em[data-emoji=":bearded_person:"]:before,em[data-emoji=bearded_person]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d4.svg)}em[data-emoji=":bearded_person_tone1:"]:before,em[data-emoji=bearded_person_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d4-1f3fb.svg)}em[data-emoji=":bearded_person_tone2:"]:before,em[data-emoji=bearded_person_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d4-1f3fc.svg)}em[data-emoji=":bearded_person_tone3:"]:before,em[data-emoji=bearded_person_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d4-1f3fd.svg)}em[data-emoji=":bearded_person_tone4:"]:before,em[data-emoji=bearded_person_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d4-1f3fe.svg)}em[data-emoji=":bearded_person_tone5:"]:before,em[data-emoji=bearded_person_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d4-1f3ff.svg)}em[data-emoji=":older_woman:"]:before,em[data-emoji=older_woman]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f475.svg)}em[data-emoji=":older_woman_tone1:"]:before,em[data-emoji=older_woman_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f475-1f3fb.svg)}em[data-emoji=":older_woman_tone2:"]:before,em[data-emoji=older_woman_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f475-1f3fc.svg)}em[data-emoji=":older_woman_tone3:"]:before,em[data-emoji=older_woman_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f475-1f3fd.svg)}em[data-emoji=":older_woman_tone4:"]:before,em[data-emoji=older_woman_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f475-1f3fe.svg)}em[data-emoji=":older_woman_tone5:"]:before,em[data-emoji=older_woman_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f475-1f3ff.svg)}em[data-emoji=":older_adult:"]:before,em[data-emoji=older_adult]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d3.svg)}em[data-emoji=":older_adult_tone1:"]:before,em[data-emoji=older_adult_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d3-1f3fb.svg)}em[data-emoji=":older_adult_tone2:"]:before,em[data-emoji=older_adult_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d3-1f3fc.svg)}em[data-emoji=":older_adult_tone3:"]:before,em[data-emoji=older_adult_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d3-1f3fd.svg)}em[data-emoji=":older_adult_tone4:"]:before,em[data-emoji=older_adult_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d3-1f3fe.svg)}em[data-emoji=":older_adult_tone5:"]:before,em[data-emoji=older_adult_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d3-1f3ff.svg)}em[data-emoji=":older_man:"]:before,em[data-emoji=older_man]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f474.svg)}em[data-emoji=":older_man_tone1:"]:before,em[data-emoji=older_man_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f474-1f3fb.svg)}em[data-emoji=":older_man_tone2:"]:before,em[data-emoji=older_man_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f474-1f3fc.svg)}em[data-emoji=":older_man_tone3:"]:before,em[data-emoji=older_man_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f474-1f3fd.svg)}em[data-emoji=":older_man_tone4:"]:before,em[data-emoji=older_man_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f474-1f3fe.svg)}em[data-emoji=":older_man_tone5:"]:before,em[data-emoji=older_man_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f474-1f3ff.svg)}em[data-emoji=":man_with_chinese_cap:"]:before,em[data-emoji=man_with_chinese_cap]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f472.svg)}em[data-emoji=":man_with_chinese_cap_tone1:"]:before,em[data-emoji=man_with_chinese_cap_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f472-1f3fb.svg)}em[data-emoji=":man_with_chinese_cap_tone2:"]:before,em[data-emoji=man_with_chinese_cap_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f472-1f3fc.svg)}em[data-emoji=":man_with_chinese_cap_tone3:"]:before,em[data-emoji=man_with_chinese_cap_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f472-1f3fd.svg)}em[data-emoji=":man_with_chinese_cap_tone4:"]:before,em[data-emoji=man_with_chinese_cap_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f472-1f3fe.svg)}em[data-emoji=":man_with_chinese_cap_tone5:"]:before,em[data-emoji=man_with_chinese_cap_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f472-1f3ff.svg)}em[data-emoji=":person_wearing_turban:"]:before,em[data-emoji=person_wearing_turban]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473.svg)}em[data-emoji=":person_wearing_turban_tone1:"]:before,em[data-emoji=person_wearing_turban_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fb.svg)}em[data-emoji=":person_wearing_turban_tone2:"]:before,em[data-emoji=person_wearing_turban_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fc.svg)}em[data-emoji=":person_wearing_turban_tone3:"]:before,em[data-emoji=person_wearing_turban_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fd.svg)}em[data-emoji=":person_wearing_turban_tone4:"]:before,em[data-emoji=person_wearing_turban_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fe.svg)}em[data-emoji=":person_wearing_turban_tone5:"]:before,em[data-emoji=person_wearing_turban_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3ff.svg)}em[data-emoji=":woman_wearing_turban:"]:before,em[data-emoji=woman_wearing_turban]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-200d-2640-fe0f.svg)}em[data-emoji=":woman_wearing_turban_tone1:"]:before,em[data-emoji=woman_wearing_turban_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_wearing_turban_tone2:"]:before,em[data-emoji=woman_wearing_turban_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_wearing_turban_tone3:"]:before,em[data-emoji=woman_wearing_turban_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_wearing_turban_tone4:"]:before,em[data-emoji=woman_wearing_turban_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_wearing_turban_tone5:"]:before,em[data-emoji=woman_wearing_turban_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_wearing_turban:"]:before,em[data-emoji=man_wearing_turban]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-200d-2642-fe0f.svg)}em[data-emoji=":man_wearing_turban_tone1:"]:before,em[data-emoji=man_wearing_turban_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_wearing_turban_tone2:"]:before,em[data-emoji=man_wearing_turban_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_wearing_turban_tone3:"]:before,em[data-emoji=man_wearing_turban_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_wearing_turban_tone4:"]:before,em[data-emoji=man_wearing_turban_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_wearing_turban_tone5:"]:before,em[data-emoji=man_wearing_turban_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f473-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":woman_with_headscarf:"]:before,em[data-emoji=woman_with_headscarf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d5.svg)}em[data-emoji=":woman_with_headscarf_tone1:"]:before,em[data-emoji=woman_with_headscarf_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d5-1f3fb.svg)}em[data-emoji=":woman_with_headscarf_tone2:"]:before,em[data-emoji=woman_with_headscarf_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d5-1f3fc.svg)}em[data-emoji=":woman_with_headscarf_tone3:"]:before,em[data-emoji=woman_with_headscarf_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d5-1f3fd.svg)}em[data-emoji=":woman_with_headscarf_tone4:"]:before,em[data-emoji=woman_with_headscarf_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d5-1f3fe.svg)}em[data-emoji=":woman_with_headscarf_tone5:"]:before,em[data-emoji=woman_with_headscarf_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d5-1f3ff.svg)}em[data-emoji=":police_officer:"]:before,em[data-emoji=police_officer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e.svg)}em[data-emoji=":police_officer_tone1:"]:before,em[data-emoji=police_officer_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fb.svg)}em[data-emoji=":police_officer_tone2:"]:before,em[data-emoji=police_officer_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fc.svg)}em[data-emoji=":police_officer_tone3:"]:before,em[data-emoji=police_officer_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fd.svg)}em[data-emoji=":police_officer_tone4:"]:before,em[data-emoji=police_officer_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fe.svg)}em[data-emoji=":police_officer_tone5:"]:before,em[data-emoji=police_officer_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3ff.svg)}em[data-emoji=":woman_police_officer:"]:before,em[data-emoji=woman_police_officer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-200d-2640-fe0f.svg)}em[data-emoji=":woman_police_officer_tone1:"]:before,em[data-emoji=woman_police_officer_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_police_officer_tone2:"]:before,em[data-emoji=woman_police_officer_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_police_officer_tone3:"]:before,em[data-emoji=woman_police_officer_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_police_officer_tone4:"]:before,em[data-emoji=woman_police_officer_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_police_officer_tone5:"]:before,em[data-emoji=woman_police_officer_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_police_officer:"]:before,em[data-emoji=man_police_officer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-200d-2642-fe0f.svg)}em[data-emoji=":man_police_officer_tone1:"]:before,em[data-emoji=man_police_officer_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_police_officer_tone2:"]:before,em[data-emoji=man_police_officer_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_police_officer_tone3:"]:before,em[data-emoji=man_police_officer_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_police_officer_tone4:"]:before,em[data-emoji=man_police_officer_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_police_officer_tone5:"]:before,em[data-emoji=man_police_officer_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46e-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":construction_worker:"]:before,em[data-emoji=construction_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477.svg)}em[data-emoji=":construction_worker_tone1:"]:before,em[data-emoji=construction_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fb.svg)}em[data-emoji=":construction_worker_tone2:"]:before,em[data-emoji=construction_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fc.svg)}em[data-emoji=":construction_worker_tone3:"]:before,em[data-emoji=construction_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fd.svg)}em[data-emoji=":construction_worker_tone4:"]:before,em[data-emoji=construction_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fe.svg)}em[data-emoji=":construction_worker_tone5:"]:before,em[data-emoji=construction_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3ff.svg)}em[data-emoji=":woman_construction_worker:"]:before,em[data-emoji=woman_construction_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-200d-2640-fe0f.svg)}em[data-emoji=":woman_construction_worker_tone1:"]:before,em[data-emoji=woman_construction_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_construction_worker_tone2:"]:before,em[data-emoji=woman_construction_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_construction_worker_tone3:"]:before,em[data-emoji=woman_construction_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_construction_worker_tone4:"]:before,em[data-emoji=woman_construction_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_construction_worker_tone5:"]:before,em[data-emoji=woman_construction_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_construction_worker:"]:before,em[data-emoji=man_construction_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-200d-2642-fe0f.svg)}em[data-emoji=":man_construction_worker_tone1:"]:before,em[data-emoji=man_construction_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_construction_worker_tone2:"]:before,em[data-emoji=man_construction_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_construction_worker_tone3:"]:before,em[data-emoji=man_construction_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_construction_worker_tone4:"]:before,em[data-emoji=man_construction_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_construction_worker_tone5:"]:before,em[data-emoji=man_construction_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f477-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":guard:"]:before,em[data-emoji=guard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482.svg)}em[data-emoji=":guard_tone1:"]:before,em[data-emoji=guard_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fb.svg)}em[data-emoji=":guard_tone2:"]:before,em[data-emoji=guard_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fc.svg)}em[data-emoji=":guard_tone3:"]:before,em[data-emoji=guard_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fd.svg)}em[data-emoji=":guard_tone4:"]:before,em[data-emoji=guard_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fe.svg)}em[data-emoji=":guard_tone5:"]:before,em[data-emoji=guard_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3ff.svg)}em[data-emoji=":woman_guard:"]:before,em[data-emoji=woman_guard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-200d-2640-fe0f.svg)}em[data-emoji=":woman_guard_tone1:"]:before,em[data-emoji=woman_guard_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_guard_tone2:"]:before,em[data-emoji=woman_guard_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_guard_tone3:"]:before,em[data-emoji=woman_guard_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_guard_tone4:"]:before,em[data-emoji=woman_guard_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_guard_tone5:"]:before,em[data-emoji=woman_guard_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_guard:"]:before,em[data-emoji=man_guard]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-200d-2642-fe0f.svg)}em[data-emoji=":man_guard_tone1:"]:before,em[data-emoji=man_guard_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_guard_tone2:"]:before,em[data-emoji=man_guard_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_guard_tone3:"]:before,em[data-emoji=man_guard_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_guard_tone4:"]:before,em[data-emoji=man_guard_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_guard_tone5:"]:before,em[data-emoji=man_guard_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f482-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":detective:"]:before,em[data-emoji=detective]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575.svg)}em[data-emoji=":detective_tone1:"]:before,em[data-emoji=detective_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fb.svg)}em[data-emoji=":detective_tone2:"]:before,em[data-emoji=detective_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fc.svg)}em[data-emoji=":detective_tone3:"]:before,em[data-emoji=detective_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fd.svg)}em[data-emoji=":detective_tone4:"]:before,em[data-emoji=detective_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fe.svg)}em[data-emoji=":detective_tone5:"]:before,em[data-emoji=detective_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3ff.svg)}em[data-emoji=":woman_detective:"]:before,em[data-emoji=woman_detective]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-fe0f-200d-2640-fe0f.svg)}em[data-emoji=":woman_detective_tone1:"]:before,em[data-emoji=woman_detective_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_detective_tone2:"]:before,em[data-emoji=woman_detective_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_detective_tone3:"]:before,em[data-emoji=woman_detective_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_detective_tone4:"]:before,em[data-emoji=woman_detective_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_detective_tone5:"]:before,em[data-emoji=woman_detective_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_detective:"]:before,em[data-emoji=man_detective]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-fe0f-200d-2642-fe0f.svg)}em[data-emoji=":man_detective_tone1:"]:before,em[data-emoji=man_detective_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_detective_tone2:"]:before,em[data-emoji=man_detective_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_detective_tone3:"]:before,em[data-emoji=man_detective_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_detective_tone4:"]:before,em[data-emoji=man_detective_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_detective_tone5:"]:before,em[data-emoji=man_detective_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f575-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":woman_health_worker:"]:before,em[data-emoji=woman_health_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-2695-fe0f.svg)}em[data-emoji=":woman_health_worker_tone1:"]:before,em[data-emoji=woman_health_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-2695-fe0f.svg)}em[data-emoji=":woman_health_worker_tone2:"]:before,em[data-emoji=woman_health_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-2695-fe0f.svg)}em[data-emoji=":woman_health_worker_tone3:"]:before,em[data-emoji=woman_health_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-2695-fe0f.svg)}em[data-emoji=":woman_health_worker_tone4:"]:before,em[data-emoji=woman_health_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-2695-fe0f.svg)}em[data-emoji=":woman_health_worker_tone5:"]:before,em[data-emoji=woman_health_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-2695-fe0f.svg)}em[data-emoji=":man_health_worker:"]:before,em[data-emoji=man_health_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-2695-fe0f.svg)}em[data-emoji=":man_health_worker_tone1:"]:before,em[data-emoji=man_health_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-2695-fe0f.svg)}em[data-emoji=":man_health_worker_tone2:"]:before,em[data-emoji=man_health_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-2695-fe0f.svg)}em[data-emoji=":man_health_worker_tone3:"]:before,em[data-emoji=man_health_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-2695-fe0f.svg)}em[data-emoji=":man_health_worker_tone4:"]:before,em[data-emoji=man_health_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-2695-fe0f.svg)}em[data-emoji=":man_health_worker_tone5:"]:before,em[data-emoji=man_health_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-2695-fe0f.svg)}em[data-emoji=":woman_farmer:"]:before,em[data-emoji=woman_farmer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f33e.svg)}em[data-emoji=":woman_farmer_tone1:"]:before,em[data-emoji=woman_farmer_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f33e.svg)}em[data-emoji=":woman_farmer_tone2:"]:before,em[data-emoji=woman_farmer_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f33e.svg)}em[data-emoji=":woman_farmer_tone3:"]:before,em[data-emoji=woman_farmer_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f33e.svg)}em[data-emoji=":woman_farmer_tone4:"]:before,em[data-emoji=woman_farmer_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f33e.svg)}em[data-emoji=":woman_farmer_tone5:"]:before,em[data-emoji=woman_farmer_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f33e.svg)}em[data-emoji=":man_farmer:"]:before,em[data-emoji=man_farmer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f33e.svg)}em[data-emoji=":man_farmer_tone1:"]:before,em[data-emoji=man_farmer_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f33e.svg)}em[data-emoji=":man_farmer_tone2:"]:before,em[data-emoji=man_farmer_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f33e.svg)}em[data-emoji=":man_farmer_tone3:"]:before,em[data-emoji=man_farmer_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f33e.svg)}em[data-emoji=":man_farmer_tone4:"]:before,em[data-emoji=man_farmer_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f33e.svg)}em[data-emoji=":man_farmer_tone5:"]:before,em[data-emoji=man_farmer_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f33e.svg)}em[data-emoji=":woman_cook:"]:before,em[data-emoji=woman_cook]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f373.svg)}em[data-emoji=":woman_cook_tone1:"]:before,em[data-emoji=woman_cook_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f373.svg)}em[data-emoji=":woman_cook_tone2:"]:before,em[data-emoji=woman_cook_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f373.svg)}em[data-emoji=":woman_cook_tone3:"]:before,em[data-emoji=woman_cook_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f373.svg)}em[data-emoji=":woman_cook_tone4:"]:before,em[data-emoji=woman_cook_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f373.svg)}em[data-emoji=":woman_cook_tone5:"]:before,em[data-emoji=woman_cook_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f373.svg)}em[data-emoji=":man_cook:"]:before,em[data-emoji=man_cook]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f373.svg)}em[data-emoji=":man_cook_tone1:"]:before,em[data-emoji=man_cook_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f373.svg)}em[data-emoji=":man_cook_tone2:"]:before,em[data-emoji=man_cook_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f373.svg)}em[data-emoji=":man_cook_tone3:"]:before,em[data-emoji=man_cook_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f373.svg)}em[data-emoji=":man_cook_tone4:"]:before,em[data-emoji=man_cook_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f373.svg)}em[data-emoji=":man_cook_tone5:"]:before,em[data-emoji=man_cook_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f373.svg)}em[data-emoji=":woman_student:"]:before,em[data-emoji=woman_student]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f393.svg)}em[data-emoji=":woman_student_tone1:"]:before,em[data-emoji=woman_student_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f393.svg)}em[data-emoji=":woman_student_tone2:"]:before,em[data-emoji=woman_student_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f393.svg)}em[data-emoji=":woman_student_tone3:"]:before,em[data-emoji=woman_student_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f393.svg)}em[data-emoji=":woman_student_tone4:"]:before,em[data-emoji=woman_student_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f393.svg)}em[data-emoji=":woman_student_tone5:"]:before,em[data-emoji=woman_student_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f393.svg)}em[data-emoji=":man_student:"]:before,em[data-emoji=man_student]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f393.svg)}em[data-emoji=":man_student_tone1:"]:before,em[data-emoji=man_student_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f393.svg)}em[data-emoji=":man_student_tone2:"]:before,em[data-emoji=man_student_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f393.svg)}em[data-emoji=":man_student_tone3:"]:before,em[data-emoji=man_student_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f393.svg)}em[data-emoji=":man_student_tone4:"]:before,em[data-emoji=man_student_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f393.svg)}em[data-emoji=":man_student_tone5:"]:before,em[data-emoji=man_student_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f393.svg)}em[data-emoji=":woman_singer:"]:before,em[data-emoji=woman_singer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f3a4.svg)}em[data-emoji=":woman_singer_tone1:"]:before,em[data-emoji=woman_singer_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f3a4.svg)}em[data-emoji=":woman_singer_tone2:"]:before,em[data-emoji=woman_singer_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f3a4.svg)}em[data-emoji=":woman_singer_tone3:"]:before,em[data-emoji=woman_singer_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f3a4.svg)}em[data-emoji=":woman_singer_tone4:"]:before,em[data-emoji=woman_singer_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f3a4.svg)}em[data-emoji=":woman_singer_tone5:"]:before,em[data-emoji=woman_singer_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f3a4.svg)}em[data-emoji=":man_singer:"]:before,em[data-emoji=man_singer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f3a4.svg)}em[data-emoji=":man_singer_tone1:"]:before,em[data-emoji=man_singer_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f3a4.svg)}em[data-emoji=":man_singer_tone2:"]:before,em[data-emoji=man_singer_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f3a4.svg)}em[data-emoji=":man_singer_tone3:"]:before,em[data-emoji=man_singer_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f3a4.svg)}em[data-emoji=":man_singer_tone4:"]:before,em[data-emoji=man_singer_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f3a4.svg)}em[data-emoji=":man_singer_tone5:"]:before,em[data-emoji=man_singer_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f3a4.svg)}em[data-emoji=":woman_teacher:"]:before,em[data-emoji=woman_teacher]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f3eb.svg)}em[data-emoji=":woman_teacher_tone1:"]:before,em[data-emoji=woman_teacher_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f3eb.svg)}em[data-emoji=":woman_teacher_tone2:"]:before,em[data-emoji=woman_teacher_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f3eb.svg)}em[data-emoji=":woman_teacher_tone3:"]:before,em[data-emoji=woman_teacher_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f3eb.svg)}em[data-emoji=":woman_teacher_tone4:"]:before,em[data-emoji=woman_teacher_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f3eb.svg)}em[data-emoji=":woman_teacher_tone5:"]:before,em[data-emoji=woman_teacher_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f3eb.svg)}em[data-emoji=":man_teacher:"]:before,em[data-emoji=man_teacher]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f3eb.svg)}em[data-emoji=":man_teacher_tone1:"]:before,em[data-emoji=man_teacher_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f3eb.svg)}em[data-emoji=":man_teacher_tone2:"]:before,em[data-emoji=man_teacher_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f3eb.svg)}em[data-emoji=":man_teacher_tone3:"]:before,em[data-emoji=man_teacher_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f3eb.svg)}em[data-emoji=":man_teacher_tone4:"]:before,em[data-emoji=man_teacher_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f3eb.svg)}em[data-emoji=":man_teacher_tone5:"]:before,em[data-emoji=man_teacher_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f3eb.svg)}em[data-emoji=":woman_factory_worker:"]:before,em[data-emoji=woman_factory_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f3ed.svg)}em[data-emoji=":woman_factory_worker_tone1:"]:before,em[data-emoji=woman_factory_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f3ed.svg)}em[data-emoji=":woman_factory_worker_tone2:"]:before,em[data-emoji=woman_factory_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f3ed.svg)}em[data-emoji=":woman_factory_worker_tone3:"]:before,em[data-emoji=woman_factory_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f3ed.svg)}em[data-emoji=":woman_factory_worker_tone4:"]:before,em[data-emoji=woman_factory_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f3ed.svg)}em[data-emoji=":woman_factory_worker_tone5:"]:before,em[data-emoji=woman_factory_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f3ed.svg)}em[data-emoji=":man_factory_worker:"]:before,em[data-emoji=man_factory_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f3ed.svg)}em[data-emoji=":man_factory_worker_tone1:"]:before,em[data-emoji=man_factory_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f3ed.svg)}em[data-emoji=":man_factory_worker_tone2:"]:before,em[data-emoji=man_factory_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f3ed.svg)}em[data-emoji=":man_factory_worker_tone3:"]:before,em[data-emoji=man_factory_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f3ed.svg)}em[data-emoji=":man_factory_worker_tone4:"]:before,em[data-emoji=man_factory_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f3ed.svg)}em[data-emoji=":man_factory_worker_tone5:"]:before,em[data-emoji=man_factory_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f3ed.svg)}em[data-emoji=":woman_technologist:"]:before,em[data-emoji=woman_technologist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f4bb.svg)}em[data-emoji=":woman_technologist_tone1:"]:before,em[data-emoji=woman_technologist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f4bb.svg)}em[data-emoji=":woman_technologist_tone2:"]:before,em[data-emoji=woman_technologist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f4bb.svg)}em[data-emoji=":woman_technologist_tone3:"]:before,em[data-emoji=woman_technologist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f4bb.svg)}em[data-emoji=":woman_technologist_tone4:"]:before,em[data-emoji=woman_technologist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f4bb.svg)}em[data-emoji=":woman_technologist_tone5:"]:before,em[data-emoji=woman_technologist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f4bb.svg)}em[data-emoji=":man_technologist:"]:before,em[data-emoji=man_technologist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f4bb.svg)}em[data-emoji=":man_technologist_tone1:"]:before,em[data-emoji=man_technologist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f4bb.svg)}em[data-emoji=":man_technologist_tone2:"]:before,em[data-emoji=man_technologist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f4bb.svg)}em[data-emoji=":man_technologist_tone3:"]:before,em[data-emoji=man_technologist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f4bb.svg)}em[data-emoji=":man_technologist_tone4:"]:before,em[data-emoji=man_technologist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f4bb.svg)}em[data-emoji=":man_technologist_tone5:"]:before,em[data-emoji=man_technologist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f4bb.svg)}em[data-emoji=":woman_office_worker:"]:before,em[data-emoji=woman_office_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f4bc.svg)}em[data-emoji=":woman_office_worker_tone1:"]:before,em[data-emoji=woman_office_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f4bc.svg)}em[data-emoji=":woman_office_worker_tone2:"]:before,em[data-emoji=woman_office_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f4bc.svg)}em[data-emoji=":woman_office_worker_tone3:"]:before,em[data-emoji=woman_office_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f4bc.svg)}em[data-emoji=":woman_office_worker_tone4:"]:before,em[data-emoji=woman_office_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f4bc.svg)}em[data-emoji=":woman_office_worker_tone5:"]:before,em[data-emoji=woman_office_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f4bc.svg)}em[data-emoji=":man_office_worker:"]:before,em[data-emoji=man_office_worker]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f4bc.svg)}em[data-emoji=":man_office_worker_tone1:"]:before,em[data-emoji=man_office_worker_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f4bc.svg)}em[data-emoji=":man_office_worker_tone2:"]:before,em[data-emoji=man_office_worker_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f4bc.svg)}em[data-emoji=":man_office_worker_tone3:"]:before,em[data-emoji=man_office_worker_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f4bc.svg)}em[data-emoji=":man_office_worker_tone4:"]:before,em[data-emoji=man_office_worker_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f4bc.svg)}em[data-emoji=":man_office_worker_tone5:"]:before,em[data-emoji=man_office_worker_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f4bc.svg)}em[data-emoji=":woman_mechanic:"]:before,em[data-emoji=woman_mechanic]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f527.svg)}em[data-emoji=":woman_mechanic_tone1:"]:before,em[data-emoji=woman_mechanic_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f527.svg)}em[data-emoji=":woman_mechanic_tone2:"]:before,em[data-emoji=woman_mechanic_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f527.svg)}em[data-emoji=":woman_mechanic_tone3:"]:before,em[data-emoji=woman_mechanic_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f527.svg)}em[data-emoji=":woman_mechanic_tone4:"]:before,em[data-emoji=woman_mechanic_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f527.svg)}em[data-emoji=":woman_mechanic_tone5:"]:before,em[data-emoji=woman_mechanic_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f527.svg)}em[data-emoji=":man_mechanic:"]:before,em[data-emoji=man_mechanic]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f527.svg)}em[data-emoji=":man_mechanic_tone1:"]:before,em[data-emoji=man_mechanic_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f527.svg)}em[data-emoji=":man_mechanic_tone2:"]:before,em[data-emoji=man_mechanic_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f527.svg)}em[data-emoji=":man_mechanic_tone3:"]:before,em[data-emoji=man_mechanic_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f527.svg)}em[data-emoji=":man_mechanic_tone4:"]:before,em[data-emoji=man_mechanic_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f527.svg)}em[data-emoji=":man_mechanic_tone5:"]:before,em[data-emoji=man_mechanic_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f527.svg)}em[data-emoji=":woman_scientist:"]:before,em[data-emoji=woman_scientist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f52c.svg)}em[data-emoji=":woman_scientist_tone1:"]:before,em[data-emoji=woman_scientist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f52c.svg)}em[data-emoji=":woman_scientist_tone2:"]:before,em[data-emoji=woman_scientist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f52c.svg)}em[data-emoji=":woman_scientist_tone3:"]:before,em[data-emoji=woman_scientist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f52c.svg)}em[data-emoji=":woman_scientist_tone4:"]:before,em[data-emoji=woman_scientist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f52c.svg)}em[data-emoji=":woman_scientist_tone5:"]:before,em[data-emoji=woman_scientist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f52c.svg)}em[data-emoji=":man_scientist:"]:before,em[data-emoji=man_scientist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f52c.svg)}em[data-emoji=":man_scientist_tone1:"]:before,em[data-emoji=man_scientist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f52c.svg)}em[data-emoji=":man_scientist_tone2:"]:before,em[data-emoji=man_scientist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f52c.svg)}em[data-emoji=":man_scientist_tone3:"]:before,em[data-emoji=man_scientist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f52c.svg)}em[data-emoji=":man_scientist_tone4:"]:before,em[data-emoji=man_scientist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f52c.svg)}em[data-emoji=":man_scientist_tone5:"]:before,em[data-emoji=man_scientist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f52c.svg)}em[data-emoji=":woman_artist:"]:before,em[data-emoji=woman_artist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f3a8.svg)}em[data-emoji=":woman_artist_tone1:"]:before,em[data-emoji=woman_artist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f3a8.svg)}em[data-emoji=":woman_artist_tone2:"]:before,em[data-emoji=woman_artist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f3a8.svg)}em[data-emoji=":woman_artist_tone3:"]:before,em[data-emoji=woman_artist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f3a8.svg)}em[data-emoji=":woman_artist_tone4:"]:before,em[data-emoji=woman_artist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f3a8.svg)}em[data-emoji=":woman_artist_tone5:"]:before,em[data-emoji=woman_artist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f3a8.svg)}em[data-emoji=":man_artist:"]:before,em[data-emoji=man_artist]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f3a8.svg)}em[data-emoji=":man_artist_tone1:"]:before,em[data-emoji=man_artist_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f3a8.svg)}em[data-emoji=":man_artist_tone2:"]:before,em[data-emoji=man_artist_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f3a8.svg)}em[data-emoji=":man_artist_tone3:"]:before,em[data-emoji=man_artist_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f3a8.svg)}em[data-emoji=":man_artist_tone4:"]:before,em[data-emoji=man_artist_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f3a8.svg)}em[data-emoji=":man_artist_tone5:"]:before,em[data-emoji=man_artist_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f3a8.svg)}em[data-emoji=":woman_firefighter:"]:before,em[data-emoji=woman_firefighter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f692.svg)}em[data-emoji=":woman_firefighter_tone1:"]:before,em[data-emoji=woman_firefighter_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f692.svg)}em[data-emoji=":woman_firefighter_tone2:"]:before,em[data-emoji=woman_firefighter_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f692.svg)}em[data-emoji=":woman_firefighter_tone3:"]:before,em[data-emoji=woman_firefighter_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f692.svg)}em[data-emoji=":woman_firefighter_tone4:"]:before,em[data-emoji=woman_firefighter_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f692.svg)}em[data-emoji=":woman_firefighter_tone5:"]:before,em[data-emoji=woman_firefighter_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f692.svg)}em[data-emoji=":man_firefighter:"]:before,em[data-emoji=man_firefighter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f692.svg)}em[data-emoji=":man_firefighter_tone1:"]:before,em[data-emoji=man_firefighter_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f692.svg)}em[data-emoji=":man_firefighter_tone2:"]:before,em[data-emoji=man_firefighter_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f692.svg)}em[data-emoji=":man_firefighter_tone3:"]:before,em[data-emoji=man_firefighter_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f692.svg)}em[data-emoji=":man_firefighter_tone4:"]:before,em[data-emoji=man_firefighter_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f692.svg)}em[data-emoji=":man_firefighter_tone5:"]:before,em[data-emoji=man_firefighter_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f692.svg)}em[data-emoji=":woman_pilot:"]:before,em[data-emoji=woman_pilot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-2708-fe0f.svg)}em[data-emoji=":woman_pilot_tone1:"]:before,em[data-emoji=woman_pilot_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-2708-fe0f.svg)}em[data-emoji=":woman_pilot_tone2:"]:before,em[data-emoji=woman_pilot_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-2708-fe0f.svg)}em[data-emoji=":woman_pilot_tone3:"]:before,em[data-emoji=woman_pilot_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-2708-fe0f.svg)}em[data-emoji=":woman_pilot_tone4:"]:before,em[data-emoji=woman_pilot_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-2708-fe0f.svg)}em[data-emoji=":woman_pilot_tone5:"]:before,em[data-emoji=woman_pilot_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-2708-fe0f.svg)}em[data-emoji=":man_pilot:"]:before,em[data-emoji=man_pilot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-2708-fe0f.svg)}em[data-emoji=":man_pilot_tone1:"]:before,em[data-emoji=man_pilot_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-2708-fe0f.svg)}em[data-emoji=":man_pilot_tone2:"]:before,em[data-emoji=man_pilot_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-2708-fe0f.svg)}em[data-emoji=":man_pilot_tone3:"]:before,em[data-emoji=man_pilot_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-2708-fe0f.svg)}em[data-emoji=":man_pilot_tone4:"]:before,em[data-emoji=man_pilot_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-2708-fe0f.svg)}em[data-emoji=":man_pilot_tone5:"]:before,em[data-emoji=man_pilot_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-2708-fe0f.svg)}em[data-emoji=":woman_astronaut:"]:before,em[data-emoji=woman_astronaut]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f680.svg)}em[data-emoji=":woman_astronaut_tone1:"]:before,em[data-emoji=woman_astronaut_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f680.svg)}em[data-emoji=":woman_astronaut_tone2:"]:before,em[data-emoji=woman_astronaut_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f680.svg)}em[data-emoji=":woman_astronaut_tone3:"]:before,em[data-emoji=woman_astronaut_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f680.svg)}em[data-emoji=":woman_astronaut_tone4:"]:before,em[data-emoji=woman_astronaut_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f680.svg)}em[data-emoji=":woman_astronaut_tone5:"]:before,em[data-emoji=woman_astronaut_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f680.svg)}em[data-emoji=":man_astronaut:"]:before,em[data-emoji=man_astronaut]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f680.svg)}em[data-emoji=":man_astronaut_tone1:"]:before,em[data-emoji=man_astronaut_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f680.svg)}em[data-emoji=":man_astronaut_tone2:"]:before,em[data-emoji=man_astronaut_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f680.svg)}em[data-emoji=":man_astronaut_tone3:"]:before,em[data-emoji=man_astronaut_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f680.svg)}em[data-emoji=":man_astronaut_tone4:"]:before,em[data-emoji=man_astronaut_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f680.svg)}em[data-emoji=":man_astronaut_tone5:"]:before,em[data-emoji=man_astronaut_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f680.svg)}em[data-emoji=":woman_judge:"]:before,em[data-emoji=woman_judge]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-2696-fe0f.svg)}em[data-emoji=":woman_judge_tone1:"]:before,em[data-emoji=woman_judge_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-2696-fe0f.svg)}em[data-emoji=":woman_judge_tone2:"]:before,em[data-emoji=woman_judge_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-2696-fe0f.svg)}em[data-emoji=":woman_judge_tone3:"]:before,em[data-emoji=woman_judge_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-2696-fe0f.svg)}em[data-emoji=":woman_judge_tone4:"]:before,em[data-emoji=woman_judge_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-2696-fe0f.svg)}em[data-emoji=":woman_judge_tone5:"]:before,em[data-emoji=woman_judge_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-2696-fe0f.svg)}em[data-emoji=":man_judge:"]:before,em[data-emoji=man_judge]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-2696-fe0f.svg)}em[data-emoji=":man_judge_tone1:"]:before,em[data-emoji=man_judge_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-2696-fe0f.svg)}em[data-emoji=":man_judge_tone2:"]:before,em[data-emoji=man_judge_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-2696-fe0f.svg)}em[data-emoji=":man_judge_tone3:"]:before,em[data-emoji=man_judge_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-2696-fe0f.svg)}em[data-emoji=":man_judge_tone4:"]:before,em[data-emoji=man_judge_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-2696-fe0f.svg)}em[data-emoji=":man_judge_tone5:"]:before,em[data-emoji=man_judge_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-2696-fe0f.svg)}em[data-emoji=":bride_with_veil:"]:before,em[data-emoji=bride_with_veil]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f470.svg)}em[data-emoji=":bride_with_veil_tone1:"]:before,em[data-emoji=bride_with_veil_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f470-1f3fb.svg)}em[data-emoji=":bride_with_veil_tone2:"]:before,em[data-emoji=bride_with_veil_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f470-1f3fc.svg)}em[data-emoji=":bride_with_veil_tone3:"]:before,em[data-emoji=bride_with_veil_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f470-1f3fd.svg)}em[data-emoji=":bride_with_veil_tone4:"]:before,em[data-emoji=bride_with_veil_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f470-1f3fe.svg)}em[data-emoji=":bride_with_veil_tone5:"]:before,em[data-emoji=bride_with_veil_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f470-1f3ff.svg)}em[data-emoji=":man_in_tuxedo:"]:before,em[data-emoji=man_in_tuxedo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f935.svg)}em[data-emoji=":man_in_tuxedo_tone1:"]:before,em[data-emoji=man_in_tuxedo_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f935-1f3fb.svg)}em[data-emoji=":man_in_tuxedo_tone2:"]:before,em[data-emoji=man_in_tuxedo_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f935-1f3fc.svg)}em[data-emoji=":man_in_tuxedo_tone3:"]:before,em[data-emoji=man_in_tuxedo_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f935-1f3fd.svg)}em[data-emoji=":man_in_tuxedo_tone4:"]:before,em[data-emoji=man_in_tuxedo_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f935-1f3fe.svg)}em[data-emoji=":man_in_tuxedo_tone5:"]:before,em[data-emoji=man_in_tuxedo_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f935-1f3ff.svg)}em[data-emoji=":princess:"]:before,em[data-emoji=princess]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f478.svg)}em[data-emoji=":princess_tone1:"]:before,em[data-emoji=princess_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f478-1f3fb.svg)}em[data-emoji=":princess_tone2:"]:before,em[data-emoji=princess_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f478-1f3fc.svg)}em[data-emoji=":princess_tone3:"]:before,em[data-emoji=princess_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f478-1f3fd.svg)}em[data-emoji=":princess_tone4:"]:before,em[data-emoji=princess_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f478-1f3fe.svg)}em[data-emoji=":princess_tone5:"]:before,em[data-emoji=princess_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f478-1f3ff.svg)}em[data-emoji=":prince:"]:before,em[data-emoji=prince]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f934.svg)}em[data-emoji=":prince_tone1:"]:before,em[data-emoji=prince_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f934-1f3fb.svg)}em[data-emoji=":prince_tone2:"]:before,em[data-emoji=prince_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f934-1f3fc.svg)}em[data-emoji=":prince_tone3:"]:before,em[data-emoji=prince_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f934-1f3fd.svg)}em[data-emoji=":prince_tone4:"]:before,em[data-emoji=prince_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f934-1f3fe.svg)}em[data-emoji=":prince_tone5:"]:before,em[data-emoji=prince_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f934-1f3ff.svg)}em[data-emoji=":superhero:"]:before,em[data-emoji=superhero]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8.svg)}em[data-emoji=":superhero_tone1:"]:before,em[data-emoji=superhero_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fb.svg)}em[data-emoji=":superhero_tone2:"]:before,em[data-emoji=superhero_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fc.svg)}em[data-emoji=":superhero_tone3:"]:before,em[data-emoji=superhero_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fd.svg)}em[data-emoji=":superhero_tone4:"]:before,em[data-emoji=superhero_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fe.svg)}em[data-emoji=":superhero_tone5:"]:before,em[data-emoji=superhero_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3ff.svg)}em[data-emoji=":woman_superhero:"]:before,em[data-emoji=woman_superhero]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-200d-2640-fe0f.svg)}em[data-emoji=":woman_superhero_tone1:"]:before,em[data-emoji=woman_superhero_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_superhero_tone2:"]:before,em[data-emoji=woman_superhero_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_superhero_tone3:"]:before,em[data-emoji=woman_superhero_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_superhero_tone4:"]:before,em[data-emoji=woman_superhero_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_superhero_tone5:"]:before,em[data-emoji=woman_superhero_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_superhero:"]:before,em[data-emoji=man_superhero]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-200d-2642-fe0f.svg)}em[data-emoji=":man_superhero_tone1:"]:before,em[data-emoji=man_superhero_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_superhero_tone2:"]:before,em[data-emoji=man_superhero_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_superhero_tone3:"]:before,em[data-emoji=man_superhero_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_superhero_tone4:"]:before,em[data-emoji=man_superhero_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_superhero_tone5:"]:before,em[data-emoji=man_superhero_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b8-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":supervillain:"]:before,em[data-emoji=supervillain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9.svg)}em[data-emoji=":supervillain_tone1:"]:before,em[data-emoji=supervillain_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fb.svg)}em[data-emoji=":supervillain_tone2:"]:before,em[data-emoji=supervillain_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fc.svg)}em[data-emoji=":supervillain_tone3:"]:before,em[data-emoji=supervillain_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fd.svg)}em[data-emoji=":supervillain_tone4:"]:before,em[data-emoji=supervillain_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fe.svg)}em[data-emoji=":supervillain_tone5:"]:before,em[data-emoji=supervillain_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3ff.svg)}em[data-emoji=":woman_supervillain:"]:before,em[data-emoji=woman_supervillain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-200d-2640-fe0f.svg)}em[data-emoji=":woman_supervillain_tone1:"]:before,em[data-emoji=woman_supervillain_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_supervillain_tone2:"]:before,em[data-emoji=woman_supervillain_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_supervillain_tone3:"]:before,em[data-emoji=woman_supervillain_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_supervillain_tone4:"]:before,em[data-emoji=woman_supervillain_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_supervillain_tone5:"]:before,em[data-emoji=woman_supervillain_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_supervillain:"]:before,em[data-emoji=man_supervillain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-200d-2642-fe0f.svg)}em[data-emoji=":man_supervillain_tone1:"]:before,em[data-emoji=man_supervillain_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_supervillain_tone2:"]:before,em[data-emoji=man_supervillain_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_supervillain_tone3:"]:before,em[data-emoji=man_supervillain_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_supervillain_tone4:"]:before,em[data-emoji=man_supervillain_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_supervillain_tone5:"]:before,em[data-emoji=man_supervillain_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b9-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":mrs_claus:"]:before,em[data-emoji=mrs_claus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f936.svg)}em[data-emoji=":mrs_claus_tone1:"]:before,em[data-emoji=mrs_claus_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f936-1f3fb.svg)}em[data-emoji=":mrs_claus_tone2:"]:before,em[data-emoji=mrs_claus_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f936-1f3fc.svg)}em[data-emoji=":mrs_claus_tone3:"]:before,em[data-emoji=mrs_claus_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f936-1f3fd.svg)}em[data-emoji=":mrs_claus_tone4:"]:before,em[data-emoji=mrs_claus_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f936-1f3fe.svg)}em[data-emoji=":mrs_claus_tone5:"]:before,em[data-emoji=mrs_claus_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f936-1f3ff.svg)}em[data-emoji=":santa:"]:before,em[data-emoji=santa]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f385.svg)}em[data-emoji=":santa_tone1:"]:before,em[data-emoji=santa_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f385-1f3fb.svg)}em[data-emoji=":santa_tone2:"]:before,em[data-emoji=santa_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f385-1f3fc.svg)}em[data-emoji=":santa_tone3:"]:before,em[data-emoji=santa_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f385-1f3fd.svg)}em[data-emoji=":santa_tone4:"]:before,em[data-emoji=santa_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f385-1f3fe.svg)}em[data-emoji=":santa_tone5:"]:before,em[data-emoji=santa_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f385-1f3ff.svg)}em[data-emoji=":mage:"]:before,em[data-emoji=mage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9.svg)}em[data-emoji=":mage_tone1:"]:before,em[data-emoji=mage_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fb.svg)}em[data-emoji=":mage_tone2:"]:before,em[data-emoji=mage_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fc.svg)}em[data-emoji=":mage_tone3:"]:before,em[data-emoji=mage_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fd.svg)}em[data-emoji=":mage_tone4:"]:before,em[data-emoji=mage_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fe.svg)}em[data-emoji=":mage_tone5:"]:before,em[data-emoji=mage_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3ff.svg)}em[data-emoji=":woman_mage:"]:before,em[data-emoji=woman_mage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-200d-2640-fe0f.svg)}em[data-emoji=":woman_mage_tone1:"]:before,em[data-emoji=woman_mage_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_mage_tone2:"]:before,em[data-emoji=woman_mage_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_mage_tone3:"]:before,em[data-emoji=woman_mage_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_mage_tone4:"]:before,em[data-emoji=woman_mage_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_mage_tone5:"]:before,em[data-emoji=woman_mage_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_mage:"]:before,em[data-emoji=man_mage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-200d-2642-fe0f.svg)}em[data-emoji=":man_mage_tone1:"]:before,em[data-emoji=man_mage_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_mage_tone2:"]:before,em[data-emoji=man_mage_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_mage_tone3:"]:before,em[data-emoji=man_mage_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_mage_tone4:"]:before,em[data-emoji=man_mage_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_mage_tone5:"]:before,em[data-emoji=man_mage_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d9-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":elf:"]:before,em[data-emoji=elf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd.svg)}em[data-emoji=":elf_tone1:"]:before,em[data-emoji=elf_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fb.svg)}em[data-emoji=":elf_tone2:"]:before,em[data-emoji=elf_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fc.svg)}em[data-emoji=":elf_tone3:"]:before,em[data-emoji=elf_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fd.svg)}em[data-emoji=":elf_tone4:"]:before,em[data-emoji=elf_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fe.svg)}em[data-emoji=":elf_tone5:"]:before,em[data-emoji=elf_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3ff.svg)}em[data-emoji=":woman_elf:"]:before,em[data-emoji=woman_elf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-200d-2640-fe0f.svg)}em[data-emoji=":woman_elf_tone1:"]:before,em[data-emoji=woman_elf_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_elf_tone2:"]:before,em[data-emoji=woman_elf_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_elf_tone3:"]:before,em[data-emoji=woman_elf_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_elf_tone4:"]:before,em[data-emoji=woman_elf_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_elf_tone5:"]:before,em[data-emoji=woman_elf_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_elf:"]:before,em[data-emoji=man_elf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-200d-2642-fe0f.svg)}em[data-emoji=":man_elf_tone1:"]:before,em[data-emoji=man_elf_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_elf_tone2:"]:before,em[data-emoji=man_elf_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_elf_tone3:"]:before,em[data-emoji=man_elf_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_elf_tone4:"]:before,em[data-emoji=man_elf_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_elf_tone5:"]:before,em[data-emoji=man_elf_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dd-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":vampire:"]:before,em[data-emoji=vampire]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db.svg)}em[data-emoji=":vampire_tone1:"]:before,em[data-emoji=vampire_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fb.svg)}em[data-emoji=":vampire_tone2:"]:before,em[data-emoji=vampire_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fc.svg)}em[data-emoji=":vampire_tone3:"]:before,em[data-emoji=vampire_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fd.svg)}em[data-emoji=":vampire_tone4:"]:before,em[data-emoji=vampire_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fe.svg)}em[data-emoji=":vampire_tone5:"]:before,em[data-emoji=vampire_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3ff.svg)}em[data-emoji=":woman_vampire:"]:before,em[data-emoji=woman_vampire]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-200d-2640-fe0f.svg)}em[data-emoji=":woman_vampire_tone1:"]:before,em[data-emoji=woman_vampire_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_vampire_tone2:"]:before,em[data-emoji=woman_vampire_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_vampire_tone3:"]:before,em[data-emoji=woman_vampire_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_vampire_tone4:"]:before,em[data-emoji=woman_vampire_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_vampire_tone5:"]:before,em[data-emoji=woman_vampire_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_vampire:"]:before,em[data-emoji=man_vampire]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-200d-2642-fe0f.svg)}em[data-emoji=":man_vampire_tone1:"]:before,em[data-emoji=man_vampire_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_vampire_tone2:"]:before,em[data-emoji=man_vampire_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_vampire_tone3:"]:before,em[data-emoji=man_vampire_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_vampire_tone4:"]:before,em[data-emoji=man_vampire_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_vampire_tone5:"]:before,em[data-emoji=man_vampire_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9db-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":zombie:"]:before,em[data-emoji=zombie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9df.svg)}em[data-emoji=":woman_zombie:"]:before,em[data-emoji=woman_zombie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9df-200d-2640-fe0f.svg)}em[data-emoji=":man_zombie:"]:before,em[data-emoji=man_zombie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9df-200d-2642-fe0f.svg)}em[data-emoji=":genie:"]:before,em[data-emoji=genie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9de.svg)}em[data-emoji=":woman_genie:"]:before,em[data-emoji=woman_genie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9de-200d-2640-fe0f.svg)}em[data-emoji=":man_genie:"]:before,em[data-emoji=man_genie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9de-200d-2642-fe0f.svg)}em[data-emoji=":merperson:"]:before,em[data-emoji=merperson]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc.svg)}em[data-emoji=":merperson_tone1:"]:before,em[data-emoji=merperson_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fb.svg)}em[data-emoji=":merperson_tone2:"]:before,em[data-emoji=merperson_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fc.svg)}em[data-emoji=":merperson_tone3:"]:before,em[data-emoji=merperson_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fd.svg)}em[data-emoji=":merperson_tone4:"]:before,em[data-emoji=merperson_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fe.svg)}em[data-emoji=":merperson_tone5:"]:before,em[data-emoji=merperson_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3ff.svg)}em[data-emoji=":mermaid:"]:before,em[data-emoji=mermaid]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-200d-2640-fe0f.svg)}em[data-emoji=":mermaid_tone1:"]:before,em[data-emoji=mermaid_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":mermaid_tone2:"]:before,em[data-emoji=mermaid_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":mermaid_tone3:"]:before,em[data-emoji=mermaid_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":mermaid_tone4:"]:before,em[data-emoji=mermaid_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":mermaid_tone5:"]:before,em[data-emoji=mermaid_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":merman:"]:before,em[data-emoji=merman]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-200d-2642-fe0f.svg)}em[data-emoji=":merman_tone1:"]:before,em[data-emoji=merman_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":merman_tone2:"]:before,em[data-emoji=merman_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":merman_tone3:"]:before,em[data-emoji=merman_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":merman_tone4:"]:before,em[data-emoji=merman_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":merman_tone5:"]:before,em[data-emoji=merman_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9dc-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":fairy:"]:before,em[data-emoji=fairy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da.svg)}em[data-emoji=":fairy_tone1:"]:before,em[data-emoji=fairy_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fb.svg)}em[data-emoji=":fairy_tone2:"]:before,em[data-emoji=fairy_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fc.svg)}em[data-emoji=":fairy_tone3:"]:before,em[data-emoji=fairy_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fd.svg)}em[data-emoji=":fairy_tone4:"]:before,em[data-emoji=fairy_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fe.svg)}em[data-emoji=":fairy_tone5:"]:before,em[data-emoji=fairy_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3ff.svg)}em[data-emoji=":woman_fairy:"]:before,em[data-emoji=woman_fairy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-200d-2640-fe0f.svg)}em[data-emoji=":woman_fairy_tone1:"]:before,em[data-emoji=woman_fairy_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_fairy_tone2:"]:before,em[data-emoji=woman_fairy_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_fairy_tone3:"]:before,em[data-emoji=woman_fairy_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_fairy_tone4:"]:before,em[data-emoji=woman_fairy_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_fairy_tone5:"]:before,em[data-emoji=woman_fairy_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_fairy:"]:before,em[data-emoji=man_fairy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-200d-2642-fe0f.svg)}em[data-emoji=":man_fairy_tone1:"]:before,em[data-emoji=man_fairy_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_fairy_tone2:"]:before,em[data-emoji=man_fairy_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_fairy_tone3:"]:before,em[data-emoji=man_fairy_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_fairy_tone4:"]:before,em[data-emoji=man_fairy_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_fairy_tone5:"]:before,em[data-emoji=man_fairy_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9da-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":angel:"]:before,em[data-emoji=angel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47c.svg)}em[data-emoji=":angel_tone1:"]:before,em[data-emoji=angel_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47c-1f3fb.svg)}em[data-emoji=":angel_tone2:"]:before,em[data-emoji=angel_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47c-1f3fc.svg)}em[data-emoji=":angel_tone3:"]:before,em[data-emoji=angel_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47c-1f3fd.svg)}em[data-emoji=":angel_tone4:"]:before,em[data-emoji=angel_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47c-1f3fe.svg)}em[data-emoji=":angel_tone5:"]:before,em[data-emoji=angel_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f47c-1f3ff.svg)}em[data-emoji=":pregnant_woman:"]:before,em[data-emoji=pregnant_woman]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f930.svg)}em[data-emoji=":pregnant_woman_tone1:"]:before,em[data-emoji=pregnant_woman_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f930-1f3fb.svg)}em[data-emoji=":pregnant_woman_tone2:"]:before,em[data-emoji=pregnant_woman_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f930-1f3fc.svg)}em[data-emoji=":pregnant_woman_tone3:"]:before,em[data-emoji=pregnant_woman_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f930-1f3fd.svg)}em[data-emoji=":pregnant_woman_tone4:"]:before,em[data-emoji=pregnant_woman_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f930-1f3fe.svg)}em[data-emoji=":pregnant_woman_tone5:"]:before,em[data-emoji=pregnant_woman_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f930-1f3ff.svg)}em[data-emoji=":breast_feeding:"]:before,em[data-emoji=breast_feeding]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f931.svg)}em[data-emoji=":breast_feeding_tone1:"]:before,em[data-emoji=breast_feeding_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f931-1f3fb.svg)}em[data-emoji=":breast_feeding_tone2:"]:before,em[data-emoji=breast_feeding_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f931-1f3fc.svg)}em[data-emoji=":breast_feeding_tone3:"]:before,em[data-emoji=breast_feeding_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f931-1f3fd.svg)}em[data-emoji=":breast_feeding_tone4:"]:before,em[data-emoji=breast_feeding_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f931-1f3fe.svg)}em[data-emoji=":breast_feeding_tone5:"]:before,em[data-emoji=breast_feeding_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f931-1f3ff.svg)}em[data-emoji=":person_bowing:"]:before,em[data-emoji=person_bowing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647.svg)}em[data-emoji=":person_bowing_tone1:"]:before,em[data-emoji=person_bowing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fb.svg)}em[data-emoji=":person_bowing_tone2:"]:before,em[data-emoji=person_bowing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fc.svg)}em[data-emoji=":person_bowing_tone3:"]:before,em[data-emoji=person_bowing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fd.svg)}em[data-emoji=":person_bowing_tone4:"]:before,em[data-emoji=person_bowing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fe.svg)}em[data-emoji=":person_bowing_tone5:"]:before,em[data-emoji=person_bowing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3ff.svg)}em[data-emoji=":woman_bowing:"]:before,em[data-emoji=woman_bowing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-200d-2640-fe0f.svg)}em[data-emoji=":woman_bowing_tone1:"]:before,em[data-emoji=woman_bowing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_bowing_tone2:"]:before,em[data-emoji=woman_bowing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_bowing_tone3:"]:before,em[data-emoji=woman_bowing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_bowing_tone4:"]:before,em[data-emoji=woman_bowing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_bowing_tone5:"]:before,em[data-emoji=woman_bowing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_bowing:"]:before,em[data-emoji=man_bowing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-200d-2642-fe0f.svg)}em[data-emoji=":man_bowing_tone1:"]:before,em[data-emoji=man_bowing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_bowing_tone2:"]:before,em[data-emoji=man_bowing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_bowing_tone3:"]:before,em[data-emoji=man_bowing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_bowing_tone4:"]:before,em[data-emoji=man_bowing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_bowing_tone5:"]:before,em[data-emoji=man_bowing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f647-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_tipping_hand:"]:before,em[data-emoji=person_tipping_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481.svg)}em[data-emoji=":person_tipping_hand_tone1:"]:before,em[data-emoji=person_tipping_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fb.svg)}em[data-emoji=":person_tipping_hand_tone2:"]:before,em[data-emoji=person_tipping_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fc.svg)}em[data-emoji=":person_tipping_hand_tone3:"]:before,em[data-emoji=person_tipping_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fd.svg)}em[data-emoji=":person_tipping_hand_tone4:"]:before,em[data-emoji=person_tipping_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fe.svg)}em[data-emoji=":person_tipping_hand_tone5:"]:before,em[data-emoji=person_tipping_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3ff.svg)}em[data-emoji=":woman_tipping_hand:"]:before,em[data-emoji=woman_tipping_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-200d-2640-fe0f.svg)}em[data-emoji=":woman_tipping_hand_tone1:"]:before,em[data-emoji=woman_tipping_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_tipping_hand_tone2:"]:before,em[data-emoji=woman_tipping_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_tipping_hand_tone3:"]:before,em[data-emoji=woman_tipping_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_tipping_hand_tone4:"]:before,em[data-emoji=woman_tipping_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_tipping_hand_tone5:"]:before,em[data-emoji=woman_tipping_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_tipping_hand:"]:before,em[data-emoji=man_tipping_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-200d-2642-fe0f.svg)}em[data-emoji=":man_tipping_hand_tone1:"]:before,em[data-emoji=man_tipping_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_tipping_hand_tone2:"]:before,em[data-emoji=man_tipping_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_tipping_hand_tone3:"]:before,em[data-emoji=man_tipping_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_tipping_hand_tone4:"]:before,em[data-emoji=man_tipping_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_tipping_hand_tone5:"]:before,em[data-emoji=man_tipping_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f481-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_gesturing_no:"]:before,em[data-emoji=person_gesturing_no]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645.svg)}em[data-emoji=":person_gesturing_no_tone1:"]:before,em[data-emoji=person_gesturing_no_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fb.svg)}em[data-emoji=":person_gesturing_no_tone2:"]:before,em[data-emoji=person_gesturing_no_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fc.svg)}em[data-emoji=":person_gesturing_no_tone3:"]:before,em[data-emoji=person_gesturing_no_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fd.svg)}em[data-emoji=":person_gesturing_no_tone4:"]:before,em[data-emoji=person_gesturing_no_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fe.svg)}em[data-emoji=":person_gesturing_no_tone5:"]:before,em[data-emoji=person_gesturing_no_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3ff.svg)}em[data-emoji=":woman_gesturing_no:"]:before,em[data-emoji=woman_gesturing_no]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_no_tone1:"]:before,em[data-emoji=woman_gesturing_no_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_no_tone2:"]:before,em[data-emoji=woman_gesturing_no_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_no_tone3:"]:before,em[data-emoji=woman_gesturing_no_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_no_tone4:"]:before,em[data-emoji=woman_gesturing_no_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_no_tone5:"]:before,em[data-emoji=woman_gesturing_no_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_gesturing_no:"]:before,em[data-emoji=man_gesturing_no]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_no_tone1:"]:before,em[data-emoji=man_gesturing_no_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_no_tone2:"]:before,em[data-emoji=man_gesturing_no_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_no_tone3:"]:before,em[data-emoji=man_gesturing_no_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_no_tone4:"]:before,em[data-emoji=man_gesturing_no_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_no_tone5:"]:before,em[data-emoji=man_gesturing_no_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f645-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_gesturing_ok:"]:before,em[data-emoji=person_gesturing_ok]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646.svg)}em[data-emoji=":person_gesturing_ok_tone1:"]:before,em[data-emoji=person_gesturing_ok_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fb.svg)}em[data-emoji=":person_gesturing_ok_tone2:"]:before,em[data-emoji=person_gesturing_ok_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fc.svg)}em[data-emoji=":person_gesturing_ok_tone3:"]:before,em[data-emoji=person_gesturing_ok_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fd.svg)}em[data-emoji=":person_gesturing_ok_tone4:"]:before,em[data-emoji=person_gesturing_ok_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fe.svg)}em[data-emoji=":person_gesturing_ok_tone5:"]:before,em[data-emoji=person_gesturing_ok_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3ff.svg)}em[data-emoji=":woman_gesturing_ok:"]:before,em[data-emoji=woman_gesturing_ok]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_ok_tone1:"]:before,em[data-emoji=woman_gesturing_ok_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_ok_tone2:"]:before,em[data-emoji=woman_gesturing_ok_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_ok_tone3:"]:before,em[data-emoji=woman_gesturing_ok_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_ok_tone4:"]:before,em[data-emoji=woman_gesturing_ok_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_gesturing_ok_tone5:"]:before,em[data-emoji=woman_gesturing_ok_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_gesturing_ok:"]:before,em[data-emoji=man_gesturing_ok]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_ok_tone1:"]:before,em[data-emoji=man_gesturing_ok_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_ok_tone2:"]:before,em[data-emoji=man_gesturing_ok_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_ok_tone3:"]:before,em[data-emoji=man_gesturing_ok_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_ok_tone4:"]:before,em[data-emoji=man_gesturing_ok_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_gesturing_ok_tone5:"]:before,em[data-emoji=man_gesturing_ok_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f646-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_raising_hand:"]:before,em[data-emoji=person_raising_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b.svg)}em[data-emoji=":person_raising_hand_tone1:"]:before,em[data-emoji=person_raising_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fb.svg)}em[data-emoji=":person_raising_hand_tone2:"]:before,em[data-emoji=person_raising_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fc.svg)}em[data-emoji=":person_raising_hand_tone3:"]:before,em[data-emoji=person_raising_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fd.svg)}em[data-emoji=":person_raising_hand_tone4:"]:before,em[data-emoji=person_raising_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fe.svg)}em[data-emoji=":person_raising_hand_tone5:"]:before,em[data-emoji=person_raising_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3ff.svg)}em[data-emoji=":woman_raising_hand:"]:before,em[data-emoji=woman_raising_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-200d-2640-fe0f.svg)}em[data-emoji=":woman_raising_hand_tone1:"]:before,em[data-emoji=woman_raising_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_raising_hand_tone2:"]:before,em[data-emoji=woman_raising_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_raising_hand_tone3:"]:before,em[data-emoji=woman_raising_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_raising_hand_tone4:"]:before,em[data-emoji=woman_raising_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_raising_hand_tone5:"]:before,em[data-emoji=woman_raising_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_raising_hand:"]:before,em[data-emoji=man_raising_hand]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-200d-2642-fe0f.svg)}em[data-emoji=":man_raising_hand_tone1:"]:before,em[data-emoji=man_raising_hand_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_raising_hand_tone2:"]:before,em[data-emoji=man_raising_hand_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_raising_hand_tone3:"]:before,em[data-emoji=man_raising_hand_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_raising_hand_tone4:"]:before,em[data-emoji=man_raising_hand_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_raising_hand_tone5:"]:before,em[data-emoji=man_raising_hand_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64b-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":deaf_person:"]:before,em[data-emoji=deaf_person]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf.svg)}em[data-emoji=":deaf_person_tone1:"]:before,em[data-emoji=deaf_person_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fb.svg)}em[data-emoji=":deaf_person_tone2:"]:before,em[data-emoji=deaf_person_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fc.svg)}em[data-emoji=":deaf_person_tone3:"]:before,em[data-emoji=deaf_person_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fd.svg)}em[data-emoji=":deaf_person_tone4:"]:before,em[data-emoji=deaf_person_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fe.svg)}em[data-emoji=":deaf_person_tone5:"]:before,em[data-emoji=deaf_person_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3ff.svg)}em[data-emoji=":deaf_woman:"]:before,em[data-emoji=deaf_woman]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-200d-2640-fe0f.svg)}em[data-emoji=":deaf_woman_tone1:"]:before,em[data-emoji=deaf_woman_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":deaf_woman_tone2:"]:before,em[data-emoji=deaf_woman_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":deaf_woman_tone3:"]:before,em[data-emoji=deaf_woman_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":deaf_woman_tone4:"]:before,em[data-emoji=deaf_woman_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":deaf_woman_tone5:"]:before,em[data-emoji=deaf_woman_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":deaf_man:"]:before,em[data-emoji=deaf_man]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-200d-2642-fe0f.svg)}em[data-emoji=":deaf_man_tone1:"]:before,em[data-emoji=deaf_man_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":deaf_man_tone2:"]:before,em[data-emoji=deaf_man_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":deaf_man_tone3:"]:before,em[data-emoji=deaf_man_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":deaf_man_tone4:"]:before,em[data-emoji=deaf_man_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":deaf_man_tone5:"]:before,em[data-emoji=deaf_man_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cf-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_facepalming:"]:before,em[data-emoji=person_facepalming]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926.svg)}em[data-emoji=":person_facepalming_tone1:"]:before,em[data-emoji=person_facepalming_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fb.svg)}em[data-emoji=":person_facepalming_tone2:"]:before,em[data-emoji=person_facepalming_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fc.svg)}em[data-emoji=":person_facepalming_tone3:"]:before,em[data-emoji=person_facepalming_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fd.svg)}em[data-emoji=":person_facepalming_tone4:"]:before,em[data-emoji=person_facepalming_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fe.svg)}em[data-emoji=":person_facepalming_tone5:"]:before,em[data-emoji=person_facepalming_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3ff.svg)}em[data-emoji=":woman_facepalming:"]:before,em[data-emoji=woman_facepalming]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-200d-2640-fe0f.svg)}em[data-emoji=":woman_facepalming_tone1:"]:before,em[data-emoji=woman_facepalming_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_facepalming_tone2:"]:before,em[data-emoji=woman_facepalming_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_facepalming_tone3:"]:before,em[data-emoji=woman_facepalming_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_facepalming_tone4:"]:before,em[data-emoji=woman_facepalming_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_facepalming_tone5:"]:before,em[data-emoji=woman_facepalming_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_facepalming:"]:before,em[data-emoji=man_facepalming]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-200d-2642-fe0f.svg)}em[data-emoji=":man_facepalming_tone1:"]:before,em[data-emoji=man_facepalming_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_facepalming_tone2:"]:before,em[data-emoji=man_facepalming_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_facepalming_tone3:"]:before,em[data-emoji=man_facepalming_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_facepalming_tone4:"]:before,em[data-emoji=man_facepalming_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_facepalming_tone5:"]:before,em[data-emoji=man_facepalming_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f926-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_shrugging:"]:before,em[data-emoji=person_shrugging]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937.svg)}em[data-emoji=":person_shrugging_tone1:"]:before,em[data-emoji=person_shrugging_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fb.svg)}em[data-emoji=":person_shrugging_tone2:"]:before,em[data-emoji=person_shrugging_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fc.svg)}em[data-emoji=":person_shrugging_tone3:"]:before,em[data-emoji=person_shrugging_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fd.svg)}em[data-emoji=":person_shrugging_tone4:"]:before,em[data-emoji=person_shrugging_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fe.svg)}em[data-emoji=":person_shrugging_tone5:"]:before,em[data-emoji=person_shrugging_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3ff.svg)}em[data-emoji=":woman_shrugging:"]:before,em[data-emoji=woman_shrugging]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-200d-2640-fe0f.svg)}em[data-emoji=":woman_shrugging_tone1:"]:before,em[data-emoji=woman_shrugging_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_shrugging_tone2:"]:before,em[data-emoji=woman_shrugging_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_shrugging_tone3:"]:before,em[data-emoji=woman_shrugging_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_shrugging_tone4:"]:before,em[data-emoji=woman_shrugging_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_shrugging_tone5:"]:before,em[data-emoji=woman_shrugging_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_shrugging:"]:before,em[data-emoji=man_shrugging]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-200d-2642-fe0f.svg)}em[data-emoji=":man_shrugging_tone1:"]:before,em[data-emoji=man_shrugging_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_shrugging_tone2:"]:before,em[data-emoji=man_shrugging_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_shrugging_tone3:"]:before,em[data-emoji=man_shrugging_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_shrugging_tone4:"]:before,em[data-emoji=man_shrugging_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_shrugging_tone5:"]:before,em[data-emoji=man_shrugging_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f937-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_pouting:"]:before,em[data-emoji=person_pouting]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e.svg)}em[data-emoji=":person_pouting_tone1:"]:before,em[data-emoji=person_pouting_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fb.svg)}em[data-emoji=":person_pouting_tone2:"]:before,em[data-emoji=person_pouting_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fc.svg)}em[data-emoji=":person_pouting_tone3:"]:before,em[data-emoji=person_pouting_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fd.svg)}em[data-emoji=":person_pouting_tone4:"]:before,em[data-emoji=person_pouting_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fe.svg)}em[data-emoji=":person_pouting_tone5:"]:before,em[data-emoji=person_pouting_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3ff.svg)}em[data-emoji=":woman_pouting:"]:before,em[data-emoji=woman_pouting]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-200d-2640-fe0f.svg)}em[data-emoji=":woman_pouting_tone1:"]:before,em[data-emoji=woman_pouting_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_pouting_tone2:"]:before,em[data-emoji=woman_pouting_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_pouting_tone3:"]:before,em[data-emoji=woman_pouting_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_pouting_tone4:"]:before,em[data-emoji=woman_pouting_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_pouting_tone5:"]:before,em[data-emoji=woman_pouting_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_pouting:"]:before,em[data-emoji=man_pouting]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-200d-2642-fe0f.svg)}em[data-emoji=":man_pouting_tone1:"]:before,em[data-emoji=man_pouting_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_pouting_tone2:"]:before,em[data-emoji=man_pouting_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_pouting_tone3:"]:before,em[data-emoji=man_pouting_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_pouting_tone4:"]:before,em[data-emoji=man_pouting_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_pouting_tone5:"]:before,em[data-emoji=man_pouting_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64e-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_frowning:"]:before,em[data-emoji=person_frowning]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d.svg)}em[data-emoji=":person_frowning_tone1:"]:before,em[data-emoji=person_frowning_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fb.svg)}em[data-emoji=":person_frowning_tone2:"]:before,em[data-emoji=person_frowning_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fc.svg)}em[data-emoji=":person_frowning_tone3:"]:before,em[data-emoji=person_frowning_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fd.svg)}em[data-emoji=":person_frowning_tone4:"]:before,em[data-emoji=person_frowning_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fe.svg)}em[data-emoji=":person_frowning_tone5:"]:before,em[data-emoji=person_frowning_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3ff.svg)}em[data-emoji=":woman_frowning:"]:before,em[data-emoji=woman_frowning]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-200d-2640-fe0f.svg)}em[data-emoji=":woman_frowning_tone1:"]:before,em[data-emoji=woman_frowning_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_frowning_tone2:"]:before,em[data-emoji=woman_frowning_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_frowning_tone3:"]:before,em[data-emoji=woman_frowning_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_frowning_tone4:"]:before,em[data-emoji=woman_frowning_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_frowning_tone5:"]:before,em[data-emoji=woman_frowning_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_frowning:"]:before,em[data-emoji=man_frowning]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-200d-2642-fe0f.svg)}em[data-emoji=":man_frowning_tone1:"]:before,em[data-emoji=man_frowning_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_frowning_tone2:"]:before,em[data-emoji=man_frowning_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_frowning_tone3:"]:before,em[data-emoji=man_frowning_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_frowning_tone4:"]:before,em[data-emoji=man_frowning_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_frowning_tone5:"]:before,em[data-emoji=man_frowning_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f64d-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_getting_haircut:"]:before,em[data-emoji=person_getting_haircut]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487.svg)}em[data-emoji=":person_getting_haircut_tone1:"]:before,em[data-emoji=person_getting_haircut_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fb.svg)}em[data-emoji=":person_getting_haircut_tone2:"]:before,em[data-emoji=person_getting_haircut_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fc.svg)}em[data-emoji=":person_getting_haircut_tone3:"]:before,em[data-emoji=person_getting_haircut_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fd.svg)}em[data-emoji=":person_getting_haircut_tone4:"]:before,em[data-emoji=person_getting_haircut_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fe.svg)}em[data-emoji=":person_getting_haircut_tone5:"]:before,em[data-emoji=person_getting_haircut_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3ff.svg)}em[data-emoji=":woman_getting_haircut:"]:before,em[data-emoji=woman_getting_haircut]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_haircut_tone1:"]:before,em[data-emoji=woman_getting_haircut_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_haircut_tone2:"]:before,em[data-emoji=woman_getting_haircut_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_haircut_tone3:"]:before,em[data-emoji=woman_getting_haircut_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_haircut_tone4:"]:before,em[data-emoji=woman_getting_haircut_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_haircut_tone5:"]:before,em[data-emoji=woman_getting_haircut_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_getting_haircut:"]:before,em[data-emoji=man_getting_haircut]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_haircut_tone1:"]:before,em[data-emoji=man_getting_haircut_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_haircut_tone2:"]:before,em[data-emoji=man_getting_haircut_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_haircut_tone3:"]:before,em[data-emoji=man_getting_haircut_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_haircut_tone4:"]:before,em[data-emoji=man_getting_haircut_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_haircut_tone5:"]:before,em[data-emoji=man_getting_haircut_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f487-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_getting_massage:"]:before,em[data-emoji=person_getting_massage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486.svg)}em[data-emoji=":person_getting_massage_tone1:"]:before,em[data-emoji=person_getting_massage_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fb.svg)}em[data-emoji=":person_getting_massage_tone2:"]:before,em[data-emoji=person_getting_massage_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fc.svg)}em[data-emoji=":person_getting_massage_tone3:"]:before,em[data-emoji=person_getting_massage_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fd.svg)}em[data-emoji=":person_getting_massage_tone4:"]:before,em[data-emoji=person_getting_massage_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fe.svg)}em[data-emoji=":person_getting_massage_tone5:"]:before,em[data-emoji=person_getting_massage_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3ff.svg)}em[data-emoji=":woman_getting_face_massage:"]:before,em[data-emoji=woman_getting_face_massage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_face_massage_tone1:"]:before,em[data-emoji=woman_getting_face_massage_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_face_massage_tone2:"]:before,em[data-emoji=woman_getting_face_massage_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_face_massage_tone3:"]:before,em[data-emoji=woman_getting_face_massage_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_face_massage_tone4:"]:before,em[data-emoji=woman_getting_face_massage_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_getting_face_massage_tone5:"]:before,em[data-emoji=woman_getting_face_massage_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_getting_face_massage:"]:before,em[data-emoji=man_getting_face_massage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_face_massage_tone1:"]:before,em[data-emoji=man_getting_face_massage_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_face_massage_tone2:"]:before,em[data-emoji=man_getting_face_massage_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_face_massage_tone3:"]:before,em[data-emoji=man_getting_face_massage_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_face_massage_tone4:"]:before,em[data-emoji=man_getting_face_massage_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_getting_face_massage_tone5:"]:before,em[data-emoji=man_getting_face_massage_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f486-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_in_steamy_room:"]:before,em[data-emoji=person_in_steamy_room]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6.svg)}em[data-emoji=":person_in_steamy_room_tone1:"]:before,em[data-emoji=person_in_steamy_room_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fb.svg)}em[data-emoji=":person_in_steamy_room_tone2:"]:before,em[data-emoji=person_in_steamy_room_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fc.svg)}em[data-emoji=":person_in_steamy_room_tone3:"]:before,em[data-emoji=person_in_steamy_room_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fd.svg)}em[data-emoji=":person_in_steamy_room_tone4:"]:before,em[data-emoji=person_in_steamy_room_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fe.svg)}em[data-emoji=":person_in_steamy_room_tone5:"]:before,em[data-emoji=person_in_steamy_room_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3ff.svg)}em[data-emoji=":woman_in_steamy_room:"]:before,em[data-emoji=woman_in_steamy_room]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_steamy_room_tone1:"]:before,em[data-emoji=woman_in_steamy_room_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_steamy_room_tone2:"]:before,em[data-emoji=woman_in_steamy_room_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_steamy_room_tone3:"]:before,em[data-emoji=woman_in_steamy_room_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_steamy_room_tone4:"]:before,em[data-emoji=woman_in_steamy_room_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_in_steamy_room_tone5:"]:before,em[data-emoji=woman_in_steamy_room_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_in_steamy_room:"]:before,em[data-emoji=man_in_steamy_room]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-200d-2642-fe0f.svg)}em[data-emoji=":man_in_steamy_room_tone1:"]:before,em[data-emoji=man_in_steamy_room_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_in_steamy_room_tone2:"]:before,em[data-emoji=man_in_steamy_room_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_in_steamy_room_tone3:"]:before,em[data-emoji=man_in_steamy_room_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_in_steamy_room_tone4:"]:before,em[data-emoji=man_in_steamy_room_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_in_steamy_room_tone5:"]:before,em[data-emoji=man_in_steamy_room_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d6-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":nail_care:"]:before,em[data-emoji=nail_care]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f485.svg)}em[data-emoji=":nail_care_tone1:"]:before,em[data-emoji=nail_care_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f485-1f3fb.svg)}em[data-emoji=":nail_care_tone2:"]:before,em[data-emoji=nail_care_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f485-1f3fc.svg)}em[data-emoji=":nail_care_tone3:"]:before,em[data-emoji=nail_care_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f485-1f3fd.svg)}em[data-emoji=":nail_care_tone4:"]:before,em[data-emoji=nail_care_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f485-1f3fe.svg)}em[data-emoji=":nail_care_tone5:"]:before,em[data-emoji=nail_care_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f485-1f3ff.svg)}em[data-emoji=":selfie:"]:before,em[data-emoji=selfie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f933.svg)}em[data-emoji=":selfie_tone1:"]:before,em[data-emoji=selfie_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f933-1f3fb.svg)}em[data-emoji=":selfie_tone2:"]:before,em[data-emoji=selfie_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f933-1f3fc.svg)}em[data-emoji=":selfie_tone3:"]:before,em[data-emoji=selfie_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f933-1f3fd.svg)}em[data-emoji=":selfie_tone4:"]:before,em[data-emoji=selfie_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f933-1f3fe.svg)}em[data-emoji=":selfie_tone5:"]:before,em[data-emoji=selfie_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f933-1f3ff.svg)}em[data-emoji=":dancer:"]:before,em[data-emoji=dancer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f483.svg)}em[data-emoji=":dancer_tone1:"]:before,em[data-emoji=dancer_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f483-1f3fb.svg)}em[data-emoji=":dancer_tone2:"]:before,em[data-emoji=dancer_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f483-1f3fc.svg)}em[data-emoji=":dancer_tone3:"]:before,em[data-emoji=dancer_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f483-1f3fd.svg)}em[data-emoji=":dancer_tone4:"]:before,em[data-emoji=dancer_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f483-1f3fe.svg)}em[data-emoji=":dancer_tone5:"]:before,em[data-emoji=dancer_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f483-1f3ff.svg)}em[data-emoji=":man_dancing:"]:before,em[data-emoji=man_dancing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f57a.svg)}em[data-emoji=":man_dancing_tone1:"]:before,em[data-emoji=man_dancing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f57a-1f3fb.svg)}em[data-emoji=":man_dancing_tone2:"]:before,em[data-emoji=man_dancing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f57a-1f3fc.svg)}em[data-emoji=":man_dancing_tone3:"]:before,em[data-emoji=man_dancing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f57a-1f3fd.svg)}em[data-emoji=":man_dancing_tone5:"]:before,em[data-emoji=man_dancing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f57a-1f3ff.svg)}em[data-emoji=":man_dancing_tone4:"]:before,em[data-emoji=man_dancing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f57a-1f3fe.svg)}em[data-emoji=":people_with_bunny_ears_partying:"]:before,em[data-emoji=people_with_bunny_ears_partying]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46f.svg)}em[data-emoji=":women_with_bunny_ears_partying:"]:before,em[data-emoji=women_with_bunny_ears_partying]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46f-200d-2640-fe0f.svg)}em[data-emoji=":men_with_bunny_ears_partying:"]:before,em[data-emoji=men_with_bunny_ears_partying]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46f-200d-2642-fe0f.svg)}em[data-emoji=":levitate:"]:before,em[data-emoji=levitate]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f574.svg)}em[data-emoji=":levitate_tone1:"]:before,em[data-emoji=levitate_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f574-1f3fb.svg)}em[data-emoji=":levitate_tone2:"]:before,em[data-emoji=levitate_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f574-1f3fc.svg)}em[data-emoji=":levitate_tone3:"]:before,em[data-emoji=levitate_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f574-1f3fd.svg)}em[data-emoji=":levitate_tone4:"]:before,em[data-emoji=levitate_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f574-1f3fe.svg)}em[data-emoji=":levitate_tone5:"]:before,em[data-emoji=levitate_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f574-1f3ff.svg)}em[data-emoji=":person_walking:"]:before,em[data-emoji=person_walking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6.svg)}em[data-emoji=":person_walking_tone1:"]:before,em[data-emoji=person_walking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fb.svg)}em[data-emoji=":person_walking_tone2:"]:before,em[data-emoji=person_walking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fc.svg)}em[data-emoji=":person_walking_tone3:"]:before,em[data-emoji=person_walking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fd.svg)}em[data-emoji=":person_walking_tone4:"]:before,em[data-emoji=person_walking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fe.svg)}em[data-emoji=":person_walking_tone5:"]:before,em[data-emoji=person_walking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3ff.svg)}em[data-emoji=":woman_walking:"]:before,em[data-emoji=woman_walking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-200d-2640-fe0f.svg)}em[data-emoji=":woman_walking_tone1:"]:before,em[data-emoji=woman_walking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_walking_tone2:"]:before,em[data-emoji=woman_walking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_walking_tone3:"]:before,em[data-emoji=woman_walking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_walking_tone4:"]:before,em[data-emoji=woman_walking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_walking_tone5:"]:before,em[data-emoji=woman_walking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_walking:"]:before,em[data-emoji=man_walking]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-200d-2642-fe0f.svg)}em[data-emoji=":man_walking_tone1:"]:before,em[data-emoji=man_walking_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_walking_tone2:"]:before,em[data-emoji=man_walking_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_walking_tone3:"]:before,em[data-emoji=man_walking_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_walking_tone4:"]:before,em[data-emoji=man_walking_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_walking_tone5:"]:before,em[data-emoji=man_walking_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b6-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_running:"]:before,em[data-emoji=person_running]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3.svg)}em[data-emoji=":person_running_tone1:"]:before,em[data-emoji=person_running_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fb.svg)}em[data-emoji=":person_running_tone2:"]:before,em[data-emoji=person_running_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fc.svg)}em[data-emoji=":person_running_tone3:"]:before,em[data-emoji=person_running_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fd.svg)}em[data-emoji=":person_running_tone4:"]:before,em[data-emoji=person_running_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fe.svg)}em[data-emoji=":person_running_tone5:"]:before,em[data-emoji=person_running_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3ff.svg)}em[data-emoji=":woman_running:"]:before,em[data-emoji=woman_running]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-200d-2640-fe0f.svg)}em[data-emoji=":woman_running_tone1:"]:before,em[data-emoji=woman_running_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_running_tone2:"]:before,em[data-emoji=woman_running_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_running_tone3:"]:before,em[data-emoji=woman_running_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_running_tone4:"]:before,em[data-emoji=woman_running_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_running_tone5:"]:before,em[data-emoji=woman_running_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_running:"]:before,em[data-emoji=man_running]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-200d-2642-fe0f.svg)}em[data-emoji=":man_running_tone1:"]:before,em[data-emoji=man_running_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_running_tone2:"]:before,em[data-emoji=man_running_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_running_tone3:"]:before,em[data-emoji=man_running_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_running_tone4:"]:before,em[data-emoji=man_running_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_running_tone5:"]:before,em[data-emoji=man_running_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c3-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_standing:"]:before,em[data-emoji=person_standing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd.svg)}em[data-emoji=":person_standing_tone1:"]:before,em[data-emoji=person_standing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fb.svg)}em[data-emoji=":person_standing_tone2:"]:before,em[data-emoji=person_standing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fc.svg)}em[data-emoji=":person_standing_tone3:"]:before,em[data-emoji=person_standing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fd.svg)}em[data-emoji=":person_standing_tone4:"]:before,em[data-emoji=person_standing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fe.svg)}em[data-emoji=":person_standing_tone5:"]:before,em[data-emoji=person_standing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3ff.svg)}em[data-emoji=":woman_standing:"]:before,em[data-emoji=woman_standing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-200d-2640-fe0f.svg)}em[data-emoji=":woman_standing_tone1:"]:before,em[data-emoji=woman_standing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_standing_tone2:"]:before,em[data-emoji=woman_standing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_standing_tone3:"]:before,em[data-emoji=woman_standing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_standing_tone4:"]:before,em[data-emoji=woman_standing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_standing_tone5:"]:before,em[data-emoji=woman_standing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_standing:"]:before,em[data-emoji=man_standing]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-200d-2642-fe0f.svg)}em[data-emoji=":man_standing_tone1:"]:before,em[data-emoji=man_standing_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_standing_tone2:"]:before,em[data-emoji=man_standing_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_standing_tone3:"]:before,em[data-emoji=man_standing_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_standing_tone4:"]:before,em[data-emoji=man_standing_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_standing_tone5:"]:before,em[data-emoji=man_standing_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9cd-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":person_kneeling:"]:before,em[data-emoji=person_kneeling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce.svg)}em[data-emoji=":person_kneeling_tone1:"]:before,em[data-emoji=person_kneeling_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fb.svg)}em[data-emoji=":person_kneeling_tone2:"]:before,em[data-emoji=person_kneeling_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fc.svg)}em[data-emoji=":person_kneeling_tone3:"]:before,em[data-emoji=person_kneeling_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fd.svg)}em[data-emoji=":person_kneeling_tone4:"]:before,em[data-emoji=person_kneeling_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fe.svg)}em[data-emoji=":person_kneeling_tone5:"]:before,em[data-emoji=person_kneeling_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3ff.svg)}em[data-emoji=":woman_kneeling:"]:before,em[data-emoji=woman_kneeling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-200d-2640-fe0f.svg)}em[data-emoji=":woman_kneeling_tone1:"]:before,em[data-emoji=woman_kneeling_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fb-200d-2640-fe0f.svg)}em[data-emoji=":woman_kneeling_tone2:"]:before,em[data-emoji=woman_kneeling_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fc-200d-2640-fe0f.svg)}em[data-emoji=":woman_kneeling_tone3:"]:before,em[data-emoji=woman_kneeling_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fd-200d-2640-fe0f.svg)}em[data-emoji=":woman_kneeling_tone4:"]:before,em[data-emoji=woman_kneeling_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fe-200d-2640-fe0f.svg)}em[data-emoji=":woman_kneeling_tone5:"]:before,em[data-emoji=woman_kneeling_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3ff-200d-2640-fe0f.svg)}em[data-emoji=":man_kneeling:"]:before,em[data-emoji=man_kneeling]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-200d-2642-fe0f.svg)}em[data-emoji=":man_kneeling_tone1:"]:before,em[data-emoji=man_kneeling_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fb-200d-2642-fe0f.svg)}em[data-emoji=":man_kneeling_tone2:"]:before,em[data-emoji=man_kneeling_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fc-200d-2642-fe0f.svg)}em[data-emoji=":man_kneeling_tone3:"]:before,em[data-emoji=man_kneeling_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fd-200d-2642-fe0f.svg)}em[data-emoji=":man_kneeling_tone4:"]:before,em[data-emoji=man_kneeling_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3fe-200d-2642-fe0f.svg)}em[data-emoji=":man_kneeling_tone5:"]:before,em[data-emoji=man_kneeling_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ce-1f3ff-200d-2642-fe0f.svg)}em[data-emoji=":woman_with_probing_cane:"]:before,em[data-emoji=woman_with_probing_cane]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f9af.svg)}em[data-emoji=":woman_with_probing_cane_tone1:"]:before,em[data-emoji=woman_with_probing_cane_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f9af.svg)}em[data-emoji=":woman_with_probing_cane_tone2:"]:before,em[data-emoji=woman_with_probing_cane_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f9af.svg)}em[data-emoji=":woman_with_probing_cane_tone3:"]:before,em[data-emoji=woman_with_probing_cane_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f9af.svg)}em[data-emoji=":woman_with_probing_cane_tone4:"]:before,em[data-emoji=woman_with_probing_cane_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f9af.svg)}em[data-emoji=":woman_with_probing_cane_tone5:"]:before,em[data-emoji=woman_with_probing_cane_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f9af.svg)}em[data-emoji=":man_with_probing_cane:"]:before,em[data-emoji=man_with_probing_cane]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f9af.svg)}em[data-emoji=":man_with_probing_cane_tone1:"]:before,em[data-emoji=man_with_probing_cane_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f9af.svg)}em[data-emoji=":man_with_probing_cane_tone2:"]:before,em[data-emoji=man_with_probing_cane_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f9af.svg)}em[data-emoji=":man_with_probing_cane_tone3:"]:before,em[data-emoji=man_with_probing_cane_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f9af.svg)}em[data-emoji=":man_with_probing_cane_tone4:"]:before,em[data-emoji=man_with_probing_cane_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f9af.svg)}em[data-emoji=":man_with_probing_cane_tone5:"]:before,em[data-emoji=man_with_probing_cane_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f9af.svg)}em[data-emoji=":woman_in_motorized_wheelchair:"]:before,em[data-emoji=woman_in_motorized_wheelchair]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f9bc.svg)}em[data-emoji=":woman_in_motorized_wheelchair_tone1:"]:before,em[data-emoji=woman_in_motorized_wheelchair_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f9bc.svg)}em[data-emoji=":woman_in_motorized_wheelchair_tone2:"]:before,em[data-emoji=woman_in_motorized_wheelchair_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f9bc.svg)}em[data-emoji=":woman_in_motorized_wheelchair_tone3:"]:before,em[data-emoji=woman_in_motorized_wheelchair_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f9bc.svg)}em[data-emoji=":woman_in_motorized_wheelchair_tone4:"]:before,em[data-emoji=woman_in_motorized_wheelchair_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f9bc.svg)}em[data-emoji=":woman_in_motorized_wheelchair_tone5:"]:before,em[data-emoji=woman_in_motorized_wheelchair_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f9bc.svg)}em[data-emoji=":man_in_motorized_wheelchair:"]:before,em[data-emoji=man_in_motorized_wheelchair]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f9bc.svg)}em[data-emoji=":man_in_motorized_wheelchair_tone1:"]:before,em[data-emoji=man_in_motorized_wheelchair_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f9bc.svg)}em[data-emoji=":man_in_motorized_wheelchair_tone2:"]:before,em[data-emoji=man_in_motorized_wheelchair_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f9bc.svg)}em[data-emoji=":man_in_motorized_wheelchair_tone3:"]:before,em[data-emoji=man_in_motorized_wheelchair_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f9bc.svg)}em[data-emoji=":man_in_motorized_wheelchair_tone4:"]:before,em[data-emoji=man_in_motorized_wheelchair_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f9bc.svg)}em[data-emoji=":man_in_motorized_wheelchair_tone5:"]:before,em[data-emoji=man_in_motorized_wheelchair_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f9bc.svg)}em[data-emoji=":woman_in_manual_wheelchair:"]:before,em[data-emoji=woman_in_manual_wheelchair]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f9bd.svg)}em[data-emoji=":woman_in_manual_wheelchair_tone1:"]:before,em[data-emoji=woman_in_manual_wheelchair_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f9bd.svg)}em[data-emoji=":woman_in_manual_wheelchair_tone2:"]:before,em[data-emoji=woman_in_manual_wheelchair_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f9bd.svg)}em[data-emoji=":woman_in_manual_wheelchair_tone3:"]:before,em[data-emoji=woman_in_manual_wheelchair_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f9bd.svg)}em[data-emoji=":woman_in_manual_wheelchair_tone4:"]:before,em[data-emoji=woman_in_manual_wheelchair_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f9bd.svg)}em[data-emoji=":woman_in_manual_wheelchair_tone5:"]:before,em[data-emoji=woman_in_manual_wheelchair_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f9bd.svg)}em[data-emoji=":man_in_manual_wheelchair:"]:before,em[data-emoji=man_in_manual_wheelchair]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f9bd.svg)}em[data-emoji=":man_in_manual_wheelchair_tone1:"]:before,em[data-emoji=man_in_manual_wheelchair_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fb-200d-1f9bd.svg)}em[data-emoji=":man_in_manual_wheelchair_tone2:"]:before,em[data-emoji=man_in_manual_wheelchair_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f9bd.svg)}em[data-emoji=":man_in_manual_wheelchair_tone3:"]:before,em[data-emoji=man_in_manual_wheelchair_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f9bd.svg)}em[data-emoji=":man_in_manual_wheelchair_tone4:"]:before,em[data-emoji=man_in_manual_wheelchair_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f9bd.svg)}em[data-emoji=":man_in_manual_wheelchair_tone5:"]:before,em[data-emoji=man_in_manual_wheelchair_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f9bd.svg)}em[data-emoji=":people_holding_hands:"]:before,em[data-emoji=people_holding_hands]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-200d-1f91d-200d-1f9d1.svg)}em[data-emoji=":people_holding_hands_tone1:"]:before,em[data-emoji=people_holding_hands_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fb-200d-1f91d-200d-1f9d1-1f3fb.svg)}em[data-emoji=":people_holding_hands_tone2:"]:before,em[data-emoji=people_holding_hands_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fc.svg)}em[data-emoji=":people_holding_hands_tone2_tone1:"]:before,em[data-emoji=people_holding_hands_tone2_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fc-200d-1f91d-200d-1f9d1-1f3fb.svg)}em[data-emoji=":people_holding_hands_tone3:"]:before,em[data-emoji=people_holding_hands_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fd.svg)}em[data-emoji=":people_holding_hands_tone3_tone1:"]:before,em[data-emoji=people_holding_hands_tone3_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fb.svg)}em[data-emoji=":people_holding_hands_tone3_tone2:"]:before,em[data-emoji=people_holding_hands_tone3_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fd-200d-1f91d-200d-1f9d1-1f3fc.svg)}em[data-emoji=":people_holding_hands_tone4:"]:before,em[data-emoji=people_holding_hands_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fe.svg)}em[data-emoji=":people_holding_hands_tone4_tone1:"]:before,em[data-emoji=people_holding_hands_tone4_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fb.svg)}em[data-emoji=":people_holding_hands_tone4_tone2:"]:before,em[data-emoji=people_holding_hands_tone4_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fc.svg)}em[data-emoji=":people_holding_hands_tone4_tone3:"]:before,em[data-emoji=people_holding_hands_tone4_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3fe-200d-1f91d-200d-1f9d1-1f3fd.svg)}em[data-emoji=":people_holding_hands_tone5:"]:before,em[data-emoji=people_holding_hands_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3ff.svg)}em[data-emoji=":people_holding_hands_tone5_tone1:"]:before,em[data-emoji=people_holding_hands_tone5_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fb.svg)}em[data-emoji=":people_holding_hands_tone5_tone2:"]:before,em[data-emoji=people_holding_hands_tone5_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fc.svg)}em[data-emoji=":people_holding_hands_tone5_tone3:"]:before,em[data-emoji=people_holding_hands_tone5_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fd.svg)}em[data-emoji=":people_holding_hands_tone5_tone4:"]:before,em[data-emoji=people_holding_hands_tone5_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9d1-1f3ff-200d-1f91d-200d-1f9d1-1f3fe.svg)}em[data-emoji=":couple:"]:before,em[data-emoji=couple]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46b.svg)}em[data-emoji=":woman_and_man_holding_hands_tone1:"]:before,em[data-emoji=woman_and_man_holding_hands_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46b-1f3fb.svg)}em[data-emoji=":woman_and_man_holding_hands_tone1_tone2:"]:before,em[data-emoji=woman_and_man_holding_hands_tone1_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fc.svg)}em[data-emoji=":woman_and_man_holding_hands_tone1_tone3:"]:before,em[data-emoji=woman_and_man_holding_hands_tone1_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fd.svg)}em[data-emoji=":woman_and_man_holding_hands_tone1_tone4:"]:before,em[data-emoji=woman_and_man_holding_hands_tone1_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f91d-200d-1f468-1f3fe.svg)}em[data-emoji=":woman_and_man_holding_hands_tone1_tone5:"]:before,em[data-emoji=woman_and_man_holding_hands_tone1_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fb-200d-1f91d-200d-1f468-1f3ff.svg)}em[data-emoji=":woman_and_man_holding_hands_tone2:"]:before,em[data-emoji=woman_and_man_holding_hands_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46b-1f3fc.svg)}em[data-emoji=":woman_and_man_holding_hands_tone2_tone1:"]:before,em[data-emoji=woman_and_man_holding_hands_tone2_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fb.svg)}em[data-emoji=":woman_and_man_holding_hands_tone2_tone3:"]:before,em[data-emoji=woman_and_man_holding_hands_tone2_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fd.svg)}em[data-emoji=":woman_and_man_holding_hands_tone2_tone4:"]:before,em[data-emoji=woman_and_man_holding_hands_tone2_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f91d-200d-1f468-1f3fe.svg)}em[data-emoji=":woman_and_man_holding_hands_tone2_tone5:"]:before,em[data-emoji=woman_and_man_holding_hands_tone2_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f91d-200d-1f468-1f3ff.svg)}em[data-emoji=":woman_and_man_holding_hands_tone3:"]:before,em[data-emoji=woman_and_man_holding_hands_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46b-1f3fd.svg)}em[data-emoji=":woman_and_man_holding_hands_tone3_tone1:"]:before,em[data-emoji=woman_and_man_holding_hands_tone3_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fb.svg)}em[data-emoji=":woman_and_man_holding_hands_tone3_tone2:"]:before,em[data-emoji=woman_and_man_holding_hands_tone3_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fc.svg)}em[data-emoji=":woman_and_man_holding_hands_tone3_tone4:"]:before,em[data-emoji=woman_and_man_holding_hands_tone3_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f91d-200d-1f468-1f3fe.svg)}em[data-emoji=":woman_and_man_holding_hands_tone3_tone5:"]:before,em[data-emoji=woman_and_man_holding_hands_tone3_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f91d-200d-1f468-1f3ff.svg)}em[data-emoji=":woman_and_man_holding_hands_tone4:"]:before,em[data-emoji=woman_and_man_holding_hands_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46b-1f3fe.svg)}em[data-emoji=":woman_and_man_holding_hands_tone4_tone1:"]:before,em[data-emoji=woman_and_man_holding_hands_tone4_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fb.svg)}em[data-emoji=":woman_and_man_holding_hands_tone4_tone2:"]:before,em[data-emoji=woman_and_man_holding_hands_tone4_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fc.svg)}em[data-emoji=":woman_and_man_holding_hands_tone4_tone3:"]:before,em[data-emoji=woman_and_man_holding_hands_tone4_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f91d-200d-1f468-1f3fd.svg)}em[data-emoji=":woman_and_man_holding_hands_tone4_tone5:"]:before,em[data-emoji=woman_and_man_holding_hands_tone4_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f91d-200d-1f468-1f3ff.svg)}em[data-emoji=":woman_and_man_holding_hands_tone5:"]:before,em[data-emoji=woman_and_man_holding_hands_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46b-1f3ff.svg)}em[data-emoji=":woman_and_man_holding_hands_tone5_tone1:"]:before,em[data-emoji=woman_and_man_holding_hands_tone5_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fb.svg)}em[data-emoji=":woman_and_man_holding_hands_tone5_tone2:"]:before,em[data-emoji=woman_and_man_holding_hands_tone5_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fc.svg)}em[data-emoji=":woman_and_man_holding_hands_tone5_tone3:"]:before,em[data-emoji=woman_and_man_holding_hands_tone5_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fd.svg)}em[data-emoji=":woman_and_man_holding_hands_tone5_tone4:"]:before,em[data-emoji=woman_and_man_holding_hands_tone5_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f91d-200d-1f468-1f3fe.svg)}em[data-emoji=":two_women_holding_hands:"]:before,em[data-emoji=two_women_holding_hands]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46d.svg)}em[data-emoji=":women_holding_hands_tone1:"]:before,em[data-emoji=women_holding_hands_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46d-1f3fb.svg)}em[data-emoji=":women_holding_hands_tone2:"]:before,em[data-emoji=women_holding_hands_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46d-1f3fc.svg)}em[data-emoji=":women_holding_hands_tone2_tone1:"]:before,em[data-emoji=women_holding_hands_tone2_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fc-200d-1f91d-200d-1f469-1f3fb.svg)}em[data-emoji=":women_holding_hands_tone3:"]:before,em[data-emoji=women_holding_hands_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46d-1f3fd.svg)}em[data-emoji=":women_holding_hands_tone3_tone1:"]:before,em[data-emoji=women_holding_hands_tone3_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fb.svg)}em[data-emoji=":women_holding_hands_tone3_tone2:"]:before,em[data-emoji=women_holding_hands_tone3_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fd-200d-1f91d-200d-1f469-1f3fc.svg)}em[data-emoji=":women_holding_hands_tone4:"]:before,em[data-emoji=women_holding_hands_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46d-1f3fe.svg)}em[data-emoji=":women_holding_hands_tone4_tone1:"]:before,em[data-emoji=women_holding_hands_tone4_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fb.svg)}em[data-emoji=":women_holding_hands_tone4_tone2:"]:before,em[data-emoji=women_holding_hands_tone4_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fc.svg)}em[data-emoji=":women_holding_hands_tone4_tone3:"]:before,em[data-emoji=women_holding_hands_tone4_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3fe-200d-1f91d-200d-1f469-1f3fd.svg)}em[data-emoji=":women_holding_hands_tone5:"]:before,em[data-emoji=women_holding_hands_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46d-1f3ff.svg)}em[data-emoji=":women_holding_hands_tone5_tone1:"]:before,em[data-emoji=women_holding_hands_tone5_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fb.svg)}em[data-emoji=":women_holding_hands_tone5_tone2:"]:before,em[data-emoji=women_holding_hands_tone5_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fc.svg)}em[data-emoji=":women_holding_hands_tone5_tone3:"]:before,em[data-emoji=women_holding_hands_tone5_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fd.svg)}em[data-emoji=":women_holding_hands_tone5_tone4:"]:before,em[data-emoji=women_holding_hands_tone5_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-1f3ff-200d-1f91d-200d-1f469-1f3fe.svg)}em[data-emoji=":two_men_holding_hands:"]:before,em[data-emoji=two_men_holding_hands]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46c.svg)}em[data-emoji=":men_holding_hands_tone1:"]:before,em[data-emoji=men_holding_hands_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46c-1f3fb.svg)}em[data-emoji=":men_holding_hands_tone2:"]:before,em[data-emoji=men_holding_hands_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46c-1f3fc.svg)}em[data-emoji=":men_holding_hands_tone2_tone1:"]:before,em[data-emoji=men_holding_hands_tone2_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fc-200d-1f91d-200d-1f468-1f3fb.svg)}em[data-emoji=":men_holding_hands_tone3:"]:before,em[data-emoji=men_holding_hands_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46c-1f3fd.svg)}em[data-emoji=":men_holding_hands_tone3_tone1:"]:before,em[data-emoji=men_holding_hands_tone3_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fb.svg)}em[data-emoji=":men_holding_hands_tone3_tone2:"]:before,em[data-emoji=men_holding_hands_tone3_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fd-200d-1f91d-200d-1f468-1f3fc.svg)}em[data-emoji=":men_holding_hands_tone4:"]:before,em[data-emoji=men_holding_hands_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46c-1f3fe.svg)}em[data-emoji=":men_holding_hands_tone4_tone1:"]:before,em[data-emoji=men_holding_hands_tone4_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fb.svg)}em[data-emoji=":men_holding_hands_tone4_tone2:"]:before,em[data-emoji=men_holding_hands_tone4_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fc.svg)}em[data-emoji=":men_holding_hands_tone4_tone3:"]:before,em[data-emoji=men_holding_hands_tone4_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3fe-200d-1f91d-200d-1f468-1f3fd.svg)}em[data-emoji=":men_holding_hands_tone5:"]:before,em[data-emoji=men_holding_hands_tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46c-1f3ff.svg)}em[data-emoji=":men_holding_hands_tone5_tone1:"]:before,em[data-emoji=men_holding_hands_tone5_tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fb.svg)}em[data-emoji=":men_holding_hands_tone5_tone2:"]:before,em[data-emoji=men_holding_hands_tone5_tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fc.svg)}em[data-emoji=":men_holding_hands_tone5_tone3:"]:before,em[data-emoji=men_holding_hands_tone5_tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fd.svg)}em[data-emoji=":men_holding_hands_tone5_tone4:"]:before,em[data-emoji=men_holding_hands_tone5_tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-1f3ff-200d-1f91d-200d-1f468-1f3fe.svg)}em[data-emoji=":couple_with_heart:"]:before,em[data-emoji=couple_with_heart]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f491.svg)}em[data-emoji=":couple_with_heart_woman_man:"]:before,em[data-emoji=couple_with_heart_woman_man]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-2764-fe0f-200d-1f468.svg)}em[data-emoji=":couple_ww:"]:before,em[data-emoji=couple_ww]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-2764-fe0f-200d-1f469.svg)}em[data-emoji=":couple_mm:"]:before,em[data-emoji=couple_mm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-2764-fe0f-200d-1f468.svg)}em[data-emoji=":couplekiss:"]:before,em[data-emoji=couplekiss]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f48f.svg)}em[data-emoji=":kiss_woman_man:"]:before,em[data-emoji=kiss_woman_man]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-2764-fe0f-200d-1f48b-200d-1f468.svg)}em[data-emoji=":kiss_ww:"]:before,em[data-emoji=kiss_ww]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.svg)}em[data-emoji=":kiss_mm:"]:before,em[data-emoji=kiss_mm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.svg)}em[data-emoji=":family:"]:before,em[data-emoji=family]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f46a.svg)}em[data-emoji=":family_man_woman_boy:"]:before,em[data-emoji=family_man_woman_boy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f469-200d-1f466.svg)}em[data-emoji=":family_mwg:"]:before,em[data-emoji=family_mwg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f469-200d-1f467.svg)}em[data-emoji=":family_mwgb:"]:before,em[data-emoji=family_mwgb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f469-200d-1f467-200d-1f466.svg)}em[data-emoji=":family_mwbb:"]:before,em[data-emoji=family_mwbb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f469-200d-1f466-200d-1f466.svg)}em[data-emoji=":family_mwgg:"]:before,em[data-emoji=family_mwgg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f469-200d-1f467-200d-1f467.svg)}em[data-emoji=":family_wwb:"]:before,em[data-emoji=family_wwb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f469-200d-1f466.svg)}em[data-emoji=":family_wwg:"]:before,em[data-emoji=family_wwg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f469-200d-1f467.svg)}em[data-emoji=":family_wwgb:"]:before,em[data-emoji=family_wwgb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f469-200d-1f467-200d-1f466.svg)}em[data-emoji=":family_wwbb:"]:before,em[data-emoji=family_wwbb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f469-200d-1f466-200d-1f466.svg)}em[data-emoji=":family_wwgg:"]:before,em[data-emoji=family_wwgg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f469-200d-1f467-200d-1f467.svg)}em[data-emoji=":family_mmb:"]:before,em[data-emoji=family_mmb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f468-200d-1f466.svg)}em[data-emoji=":family_mmg:"]:before,em[data-emoji=family_mmg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f468-200d-1f467.svg)}em[data-emoji=":family_mmgb:"]:before,em[data-emoji=family_mmgb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f468-200d-1f467-200d-1f466.svg)}em[data-emoji=":family_mmbb:"]:before,em[data-emoji=family_mmbb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f468-200d-1f466-200d-1f466.svg)}em[data-emoji=":family_mmgg:"]:before,em[data-emoji=family_mmgg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f468-200d-1f467-200d-1f467.svg)}em[data-emoji=":family_woman_boy:"]:before,em[data-emoji=family_woman_boy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f466.svg)}em[data-emoji=":family_woman_girl:"]:before,em[data-emoji=family_woman_girl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f467.svg)}em[data-emoji=":family_woman_girl_boy:"]:before,em[data-emoji=family_woman_girl_boy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f467-200d-1f466.svg)}em[data-emoji=":family_woman_boy_boy:"]:before,em[data-emoji=family_woman_boy_boy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f466-200d-1f466.svg)}em[data-emoji=":family_woman_girl_girl:"]:before,em[data-emoji=family_woman_girl_girl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f469-200d-1f467-200d-1f467.svg)}em[data-emoji=":family_man_boy:"]:before,em[data-emoji=family_man_boy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f466.svg)}em[data-emoji=":family_man_girl:"]:before,em[data-emoji=family_man_girl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f467.svg)}em[data-emoji=":family_man_girl_boy:"]:before,em[data-emoji=family_man_girl_boy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f467-200d-1f466.svg)}em[data-emoji=":family_man_boy_boy:"]:before,em[data-emoji=family_man_boy_boy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f466-200d-1f466.svg)}em[data-emoji=":family_man_girl_girl:"]:before,em[data-emoji=family_man_girl_girl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f468-200d-1f467-200d-1f467.svg)}em[data-emoji=":yarn:"]:before,em[data-emoji=yarn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f6.svg)}em[data-emoji=":thread:"]:before,em[data-emoji=thread]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f5.svg)}em[data-emoji=":coat:"]:before,em[data-emoji=coat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e5.svg)}em[data-emoji=":lab_coat:"]:before,em[data-emoji=lab_coat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f97c.svg)}em[data-emoji=":safety_vest:"]:before,em[data-emoji=safety_vest]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9ba.svg)}em[data-emoji=":womans_clothes:"]:before,em[data-emoji=womans_clothes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f45a.svg)}em[data-emoji=":shirt:"]:before,em[data-emoji=shirt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f455.svg)}em[data-emoji=":jeans:"]:before,em[data-emoji=jeans]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f456.svg)}em[data-emoji=":shorts:"]:before,em[data-emoji=shorts]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa73.svg)}em[data-emoji=":necktie:"]:before,em[data-emoji=necktie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f454.svg)}em[data-emoji=":dress:"]:before,em[data-emoji=dress]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f457.svg)}em[data-emoji=":bikini:"]:before,em[data-emoji=bikini]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f459.svg)}em[data-emoji=":one_piece_swimsuit:"]:before,em[data-emoji=one_piece_swimsuit]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa71.svg)}em[data-emoji=":kimono:"]:before,em[data-emoji=kimono]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f458.svg)}em[data-emoji=":sari:"]:before,em[data-emoji=sari]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f97b.svg)}em[data-emoji=":womans_flat_shoe:"]:before,em[data-emoji=womans_flat_shoe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f97f.svg)}em[data-emoji=":high_heel:"]:before,em[data-emoji=high_heel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f460.svg)}em[data-emoji=":sandal:"]:before,em[data-emoji=sandal]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f461.svg)}em[data-emoji=":boot:"]:before,em[data-emoji=boot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f462.svg)}em[data-emoji=":ballet_shoes:"]:before,em[data-emoji=ballet_shoes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa70.svg)}em[data-emoji=":mans_shoe:"]:before,em[data-emoji=mans_shoe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f45e.svg)}em[data-emoji=":athletic_shoe:"]:before,em[data-emoji=athletic_shoe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f45f.svg)}em[data-emoji=":hiking_boot:"]:before,em[data-emoji=hiking_boot]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f97e.svg)}em[data-emoji=":briefs:"]:before,em[data-emoji=briefs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1fa72.svg)}em[data-emoji=":socks:"]:before,em[data-emoji=socks]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e6.svg)}em[data-emoji=":gloves:"]:before,em[data-emoji=gloves]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e4.svg)}em[data-emoji=":scarf:"]:before,em[data-emoji=scarf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e3.svg)}em[data-emoji=":tophat:"]:before,em[data-emoji=tophat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a9.svg)}em[data-emoji=":billed_cap:"]:before,em[data-emoji=billed_cap]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9e2.svg)}em[data-emoji=":womans_hat:"]:before,em[data-emoji=womans_hat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f452.svg)}em[data-emoji=":mortar_board:"]:before,em[data-emoji=mortar_board]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f393.svg)}em[data-emoji=":helmet_with_cross:"]:before,em[data-emoji=helmet_with_cross]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26d1.svg)}em[data-emoji=":crown:"]:before,em[data-emoji=crown]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f451.svg)}em[data-emoji=":ring:"]:before,em[data-emoji=ring]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f48d.svg)}em[data-emoji=":pouch:"]:before,em[data-emoji=pouch]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f45d.svg)}em[data-emoji=":purse:"]:before,em[data-emoji=purse]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f45b.svg)}em[data-emoji=":handbag:"]:before,em[data-emoji=handbag]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f45c.svg)}em[data-emoji=":briefcase:"]:before,em[data-emoji=briefcase]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4bc.svg)}em[data-emoji=":school_satchel:"]:before,em[data-emoji=school_satchel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f392.svg)}em[data-emoji=":luggage:"]:before,em[data-emoji=luggage]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9f3.svg)}em[data-emoji=":eyeglasses:"]:before,em[data-emoji=eyeglasses]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f453.svg)}em[data-emoji=":dark_sunglasses:"]:before,em[data-emoji=dark_sunglasses]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f576.svg)}em[data-emoji=":goggles:"]:before,em[data-emoji=goggles]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f97d.svg)}em[data-emoji=":diving_mask:"]:before,em[data-emoji=diving_mask]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f93f.svg)}em[data-emoji=":closed_umbrella:"]:before,em[data-emoji=closed_umbrella]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f302.svg)}em[data-emoji=":curly_haired:"]:before,em[data-emoji=curly_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b1.svg)}em[data-emoji=":red_haired:"]:before,em[data-emoji=red_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b0.svg)}em[data-emoji=":white_haired:"]:before,em[data-emoji=white_haired]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b3.svg)}em[data-emoji=":bald:"]:before,em[data-emoji=bald]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9b2.svg)}em[data-emoji=":red_car:"]:before,em[data-emoji=red_car]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f697.svg)}em[data-emoji=":taxi:"]:before,em[data-emoji=taxi]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f695.svg)}em[data-emoji=":blue_car:"]:before,em[data-emoji=blue_car]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f699.svg)}em[data-emoji=":bus:"]:before,em[data-emoji=bus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f68c.svg)}em[data-emoji=":trolleybus:"]:before,em[data-emoji=trolleybus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f68e.svg)}em[data-emoji=":race_car:"]:before,em[data-emoji=race_car]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ce.svg)}em[data-emoji=":police_car:"]:before,em[data-emoji=police_car]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f693.svg)}em[data-emoji=":ambulance:"]:before,em[data-emoji=ambulance]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f691.svg)}em[data-emoji=":fire_engine:"]:before,em[data-emoji=fire_engine]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f692.svg)}em[data-emoji=":minibus:"]:before,em[data-emoji=minibus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f690.svg)}em[data-emoji=":truck:"]:before,em[data-emoji=truck]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f69a.svg)}em[data-emoji=":articulated_lorry:"]:before,em[data-emoji=articulated_lorry]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f69b.svg)}em[data-emoji=":tractor:"]:before,em[data-emoji=tractor]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f69c.svg)}em[data-emoji=":auto_rickshaw:"]:before,em[data-emoji=auto_rickshaw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6fa.svg)}em[data-emoji=":motor_scooter:"]:before,em[data-emoji=motor_scooter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6f5.svg)}em[data-emoji=":motorcycle:"]:before,em[data-emoji=motorcycle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3cd.svg)}em[data-emoji=":scooter:"]:before,em[data-emoji=scooter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6f4.svg)}em[data-emoji=":bike:"]:before,em[data-emoji=bike]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6b2.svg)}em[data-emoji=":motorized_wheelchair:"]:before,em[data-emoji=motorized_wheelchair]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bc.svg)}em[data-emoji=":manual_wheelchair:"]:before,em[data-emoji=manual_wheelchair]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f9bd.svg)}em[data-emoji=":rotating_light:"]:before,em[data-emoji=rotating_light]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a8.svg)}em[data-emoji=":oncoming_police_car:"]:before,em[data-emoji=oncoming_police_car]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f694.svg)}em[data-emoji=":oncoming_bus:"]:before,em[data-emoji=oncoming_bus]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f68d.svg)}em[data-emoji=":oncoming_automobile:"]:before,em[data-emoji=oncoming_automobile]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f698.svg)}em[data-emoji=":oncoming_taxi:"]:before,em[data-emoji=oncoming_taxi]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f696.svg)}em[data-emoji=":aerial_tramway:"]:before,em[data-emoji=aerial_tramway]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a1.svg)}em[data-emoji=":mountain_cableway:"]:before,em[data-emoji=mountain_cableway]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a0.svg)}em[data-emoji=":suspension_railway:"]:before,em[data-emoji=suspension_railway]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f69f.svg)}em[data-emoji=":railway_car:"]:before,em[data-emoji=railway_car]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f683.svg)}em[data-emoji=":train:"]:before,em[data-emoji=train]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f68b.svg)}em[data-emoji=":mountain_railway:"]:before,em[data-emoji=mountain_railway]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f69e.svg)}em[data-emoji=":monorail:"]:before,em[data-emoji=monorail]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f69d.svg)}em[data-emoji=":bullettrain_side:"]:before,em[data-emoji=bullettrain_side]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f684.svg)}em[data-emoji=":bullettrain_front:"]:before,em[data-emoji=bullettrain_front]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f685.svg)}em[data-emoji=":light_rail:"]:before,em[data-emoji=light_rail]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f688.svg)}em[data-emoji=":steam_locomotive:"]:before,em[data-emoji=steam_locomotive]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f682.svg)}em[data-emoji=":train2:"]:before,em[data-emoji=train2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f686.svg)}em[data-emoji=":metro:"]:before,em[data-emoji=metro]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f687.svg)}em[data-emoji=":tram:"]:before,em[data-emoji=tram]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f68a.svg)}em[data-emoji=":station:"]:before,em[data-emoji=station]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f689.svg)}em[data-emoji=":airplane_departure:"]:before,em[data-emoji=airplane_departure]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6eb.svg)}em[data-emoji=":airplane_arriving:"]:before,em[data-emoji=airplane_arriving]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6ec.svg)}em[data-emoji=":airplane_small:"]:before,em[data-emoji=airplane_small]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6e9.svg)}em[data-emoji=":seat:"]:before,em[data-emoji=seat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f4ba.svg)}em[data-emoji=":satellite_orbital:"]:before,em[data-emoji=satellite_orbital]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6f0.svg)}em[data-emoji=":rocket:"]:before,em[data-emoji=rocket]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f680.svg)}em[data-emoji=":flying_saucer:"]:before,em[data-emoji=flying_saucer]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6f8.svg)}em[data-emoji=":helicopter:"]:before,em[data-emoji=helicopter]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f681.svg)}em[data-emoji=":canoe:"]:before,em[data-emoji=canoe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6f6.svg)}em[data-emoji=":sailboat:"]:before,em[data-emoji=sailboat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f5.svg)}em[data-emoji=":speedboat:"]:before,em[data-emoji=speedboat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a4.svg)}em[data-emoji=":motorboat:"]:before,em[data-emoji=motorboat]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6e5.svg)}em[data-emoji=":cruise_ship:"]:before,em[data-emoji=cruise_ship]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6f3.svg)}em[data-emoji=":ferry:"]:before,em[data-emoji=ferry]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f4.svg)}em[data-emoji=":ship:"]:before,em[data-emoji=ship]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a2.svg)}em[data-emoji=":fuelpump:"]:before,em[data-emoji=fuelpump]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26fd.svg)}em[data-emoji=":construction:"]:before,em[data-emoji=construction]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a7.svg)}em[data-emoji=":vertical_traffic_light:"]:before,em[data-emoji=vertical_traffic_light]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a6.svg)}em[data-emoji=":traffic_light:"]:before,em[data-emoji=traffic_light]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a5.svg)}em[data-emoji=":busstop:"]:before,em[data-emoji=busstop]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f68f.svg)}em[data-emoji=":map:"]:before,em[data-emoji=map]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5fa.svg)}em[data-emoji=":moyai:"]:before,em[data-emoji=moyai]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5ff.svg)}em[data-emoji=":statue_of_liberty:"]:before,em[data-emoji=statue_of_liberty]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5fd.svg)}em[data-emoji=":tokyo_tower:"]:before,em[data-emoji=tokyo_tower]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5fc.svg)}em[data-emoji=":european_castle:"]:before,em[data-emoji=european_castle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f0.svg)}em[data-emoji=":japanese_castle:"]:before,em[data-emoji=japanese_castle]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ef.svg)}em[data-emoji=":stadium:"]:before,em[data-emoji=stadium]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3df.svg)}em[data-emoji=":ferris_wheel:"]:before,em[data-emoji=ferris_wheel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a1.svg)}em[data-emoji=":roller_coaster:"]:before,em[data-emoji=roller_coaster]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a2.svg)}em[data-emoji=":carousel_horse:"]:before,em[data-emoji=carousel_horse]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3a0.svg)}em[data-emoji=":fountain:"]:before,em[data-emoji=fountain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f2.svg)}em[data-emoji=":beach_umbrella:"]:before,em[data-emoji=beach_umbrella]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f1.svg)}em[data-emoji=":beach:"]:before,em[data-emoji=beach]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d6.svg)}em[data-emoji=":island:"]:before,em[data-emoji=island]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3dd.svg)}em[data-emoji=":desert:"]:before,em[data-emoji=desert]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3dc.svg)}em[data-emoji=":volcano:"]:before,em[data-emoji=volcano]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f30b.svg)}em[data-emoji=":mountain:"]:before,em[data-emoji=mountain]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26f0.svg)}em[data-emoji=":mountain_snow:"]:before,em[data-emoji=mountain_snow]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d4.svg)}em[data-emoji=":mount_fuji:"]:before,em[data-emoji=mount_fuji]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5fb.svg)}em[data-emoji=":camping:"]:before,em[data-emoji=camping]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d5.svg)}em[data-emoji=":tent:"]:before,em[data-emoji=tent]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26fa.svg)}em[data-emoji=":house:"]:before,em[data-emoji=house]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e0.svg)}em[data-emoji=":house_with_garden:"]:before,em[data-emoji=house_with_garden]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e1.svg)}em[data-emoji=":homes:"]:before,em[data-emoji=homes]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d8.svg)}em[data-emoji=":house_abandoned:"]:before,em[data-emoji=house_abandoned]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3da.svg)}em[data-emoji=":construction_site:"]:before,em[data-emoji=construction_site]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d7.svg)}em[data-emoji=":factory:"]:before,em[data-emoji=factory]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ed.svg)}em[data-emoji=":office:"]:before,em[data-emoji=office]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e2.svg)}em[data-emoji=":department_store:"]:before,em[data-emoji=department_store]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ec.svg)}em[data-emoji=":post_office:"]:before,em[data-emoji=post_office]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e3.svg)}em[data-emoji=":european_post_office:"]:before,em[data-emoji=european_post_office]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e4.svg)}em[data-emoji=":hospital:"]:before,em[data-emoji=hospital]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e5.svg)}em[data-emoji=":bank:"]:before,em[data-emoji=bank]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e6.svg)}em[data-emoji=":hotel:"]:before,em[data-emoji=hotel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e8.svg)}em[data-emoji=":convenience_store:"]:before,em[data-emoji=convenience_store]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ea.svg)}em[data-emoji=":school:"]:before,em[data-emoji=school]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3eb.svg)}em[data-emoji=":love_hotel:"]:before,em[data-emoji=love_hotel]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3e9.svg)}em[data-emoji=":wedding:"]:before,em[data-emoji=wedding]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f492.svg)}em[data-emoji=":classical_building:"]:before,em[data-emoji=classical_building]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3db.svg)}em[data-emoji=":church:"]:before,em[data-emoji=church]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26ea.svg)}em[data-emoji=":mosque:"]:before,em[data-emoji=mosque]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f54c.svg)}em[data-emoji=":hindu_temple:"]:before,em[data-emoji=hindu_temple]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6d5.svg)}em[data-emoji=":synagogue:"]:before,em[data-emoji=synagogue]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f54d.svg)}em[data-emoji=":kaaba:"]:before,em[data-emoji=kaaba]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f54b.svg)}em[data-emoji=":shinto_shrine:"]:before,em[data-emoji=shinto_shrine]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/26e9.svg)}em[data-emoji=":railway_track:"]:before,em[data-emoji=railway_track]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6e4.svg)}em[data-emoji=":motorway:"]:before,em[data-emoji=motorway]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6e3.svg)}em[data-emoji=":japan:"]:before,em[data-emoji=japan]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f5fe.svg)}em[data-emoji=":rice_scene:"]:before,em[data-emoji=rice_scene]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f391.svg)}em[data-emoji=":park:"]:before,em[data-emoji=park]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3de.svg)}em[data-emoji=":sunrise:"]:before,em[data-emoji=sunrise]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f305.svg)}em[data-emoji=":sunrise_over_mountains:"]:before,em[data-emoji=sunrise_over_mountains]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f304.svg)}em[data-emoji=":stars:"]:before,em[data-emoji=stars]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f320.svg)}em[data-emoji=":sparkler:"]:before,em[data-emoji=sparkler]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f387.svg)}em[data-emoji=":fireworks:"]:before,em[data-emoji=fireworks]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f386.svg)}em[data-emoji=":city_sunset:"]:before,em[data-emoji=city_sunset]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f307.svg)}em[data-emoji=":city_dusk:"]:before,em[data-emoji=city_dusk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f306.svg)}em[data-emoji=":cityscape:"]:before,em[data-emoji=cityscape]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3d9.svg)}em[data-emoji=":night_with_stars:"]:before,em[data-emoji=night_with_stars]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f303.svg)}em[data-emoji=":milky_way:"]:before,em[data-emoji=milky_way]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f30c.svg)}em[data-emoji=":bridge_at_night:"]:before,em[data-emoji=bridge_at_night]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f309.svg)}em[data-emoji=":foggy:"]:before,em[data-emoji=foggy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f301.svg)}em[data-emoji=":regional_indicator_z:"]:before,em[data-emoji=regional_indicator_z]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ff.svg)}em[data-emoji=":regional_indicator_y:"]:before,em[data-emoji=regional_indicator_y]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fe.svg)}em[data-emoji=":regional_indicator_x:"]:before,em[data-emoji=regional_indicator_x]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fd.svg)}em[data-emoji=":regional_indicator_w:"]:before,em[data-emoji=regional_indicator_w]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fc.svg)}em[data-emoji=":regional_indicator_v:"]:before,em[data-emoji=regional_indicator_v]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fb.svg)}em[data-emoji=":regional_indicator_u:"]:before,em[data-emoji=regional_indicator_u]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fa.svg)}em[data-emoji=":regional_indicator_t:"]:before,em[data-emoji=regional_indicator_t]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9.svg)}em[data-emoji=":regional_indicator_s:"]:before,em[data-emoji=regional_indicator_s]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8.svg)}em[data-emoji=":regional_indicator_r:"]:before,em[data-emoji=regional_indicator_r]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f7.svg)}em[data-emoji=":regional_indicator_q:"]:before,em[data-emoji=regional_indicator_q]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f6.svg)}em[data-emoji=":regional_indicator_p:"]:before,em[data-emoji=regional_indicator_p]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5.svg)}em[data-emoji=":regional_indicator_o:"]:before,em[data-emoji=regional_indicator_o]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f4.svg)}em[data-emoji=":regional_indicator_n:"]:before,em[data-emoji=regional_indicator_n]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3.svg)}em[data-emoji=":regional_indicator_m:"]:before,em[data-emoji=regional_indicator_m]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2.svg)}em[data-emoji=":regional_indicator_l:"]:before,em[data-emoji=regional_indicator_l]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1.svg)}em[data-emoji=":regional_indicator_k:"]:before,em[data-emoji=regional_indicator_k]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0.svg)}em[data-emoji=":regional_indicator_j:"]:before,em[data-emoji=regional_indicator_j]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ef.svg)}em[data-emoji=":regional_indicator_i:"]:before,em[data-emoji=regional_indicator_i]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee.svg)}em[data-emoji=":regional_indicator_h:"]:before,em[data-emoji=regional_indicator_h]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ed.svg)}em[data-emoji=":regional_indicator_g:"]:before,em[data-emoji=regional_indicator_g]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec.svg)}em[data-emoji=":regional_indicator_f:"]:before,em[data-emoji=regional_indicator_f]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1eb.svg)}em[data-emoji=":regional_indicator_e:"]:before,em[data-emoji=regional_indicator_e]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea.svg)}em[data-emoji=":regional_indicator_d:"]:before,em[data-emoji=regional_indicator_d]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e9.svg)}em[data-emoji=":regional_indicator_c:"]:before,em[data-emoji=regional_indicator_c]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8.svg)}em[data-emoji=":regional_indicator_b:"]:before,em[data-emoji=regional_indicator_b]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7.svg)}em[data-emoji=":regional_indicator_a:"]:before,em[data-emoji=regional_indicator_a]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6.svg)}em[data-emoji=":flag_white:"]:before,em[data-emoji=flag_white]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f3.svg)}em[data-emoji=":flag_black:"]:before,em[data-emoji=flag_black]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f4.svg)}em[data-emoji=":checkered_flag:"]:before,em[data-emoji=checkered_flag]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3c1.svg)}em[data-emoji=":triangular_flag_on_post:"]:before,em[data-emoji=triangular_flag_on_post]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f6a9.svg)}em[data-emoji=":rainbow_flag:"]:before,em[data-emoji=rainbow_flag]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f3-fe0f-200d-1f308.svg)}em[data-emoji=":pirate_flag:"]:before,em[data-emoji=pirate_flag]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f4-200d-2620-fe0f.svg)}em[data-emoji=":flag_af:"]:before,em[data-emoji=flag_af]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1eb.svg)}em[data-emoji=":flag_ax:"]:before,em[data-emoji=flag_ax]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1fd.svg)}em[data-emoji=":flag_al:"]:before,em[data-emoji=flag_al]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1f1.svg)}em[data-emoji=":flag_dz:"]:before,em[data-emoji=flag_dz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e9-1f1ff.svg)}em[data-emoji=":flag_as:"]:before,em[data-emoji=flag_as]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1f8.svg)}em[data-emoji=":flag_ad:"]:before,em[data-emoji=flag_ad]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1e9.svg)}em[data-emoji=":flag_ao:"]:before,em[data-emoji=flag_ao]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1f4.svg)}em[data-emoji=":flag_ai:"]:before,em[data-emoji=flag_ai]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1ee.svg)}em[data-emoji=":flag_aq:"]:before,em[data-emoji=flag_aq]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1f6.svg)}em[data-emoji=":flag_ag:"]:before,em[data-emoji=flag_ag]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1ec.svg)}em[data-emoji=":flag_ar:"]:before,em[data-emoji=flag_ar]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1f7.svg)}em[data-emoji=":flag_am:"]:before,em[data-emoji=flag_am]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1f2.svg)}em[data-emoji=":flag_aw:"]:before,em[data-emoji=flag_aw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1fc.svg)}em[data-emoji=":flag_au:"]:before,em[data-emoji=flag_au]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1fa.svg)}em[data-emoji=":flag_at:"]:before,em[data-emoji=flag_at]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1f9.svg)}em[data-emoji=":flag_az:"]:before,em[data-emoji=flag_az]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1ff.svg)}em[data-emoji=":flag_bs:"]:before,em[data-emoji=flag_bs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1f8.svg)}em[data-emoji=":flag_bh:"]:before,em[data-emoji=flag_bh]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1ed.svg)}em[data-emoji=":flag_bd:"]:before,em[data-emoji=flag_bd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1e9.svg)}em[data-emoji=":flag_bb:"]:before,em[data-emoji=flag_bb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1e7.svg)}em[data-emoji=":flag_by:"]:before,em[data-emoji=flag_by]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1fe.svg)}em[data-emoji=":flag_be:"]:before,em[data-emoji=flag_be]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1ea.svg)}em[data-emoji=":flag_bz:"]:before,em[data-emoji=flag_bz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1ff.svg)}em[data-emoji=":flag_bj:"]:before,em[data-emoji=flag_bj]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1ef.svg)}em[data-emoji=":flag_bm:"]:before,em[data-emoji=flag_bm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1f2.svg)}em[data-emoji=":flag_bt:"]:before,em[data-emoji=flag_bt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1f9.svg)}em[data-emoji=":flag_bo:"]:before,em[data-emoji=flag_bo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1f4.svg)}em[data-emoji=":flag_ba:"]:before,em[data-emoji=flag_ba]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1e6.svg)}em[data-emoji=":flag_bw:"]:before,em[data-emoji=flag_bw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1fc.svg)}em[data-emoji=":flag_br:"]:before,em[data-emoji=flag_br]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1f7.svg)}em[data-emoji=":flag_io:"]:before,em[data-emoji=flag_io]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1f4.svg)}em[data-emoji=":flag_vg:"]:before,em[data-emoji=flag_vg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fb-1f1ec.svg)}em[data-emoji=":flag_bn:"]:before,em[data-emoji=flag_bn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1f3.svg)}em[data-emoji=":flag_bg:"]:before,em[data-emoji=flag_bg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1ec.svg)}em[data-emoji=":flag_bf:"]:before,em[data-emoji=flag_bf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1eb.svg)}em[data-emoji=":flag_bi:"]:before,em[data-emoji=flag_bi]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1ee.svg)}em[data-emoji=":flag_kh:"]:before,em[data-emoji=flag_kh]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1ed.svg)}em[data-emoji=":flag_cm:"]:before,em[data-emoji=flag_cm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1f2.svg)}em[data-emoji=":flag_ca:"]:before,em[data-emoji=flag_ca]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1e6.svg)}em[data-emoji=":flag_ic:"]:before,em[data-emoji=flag_ic]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1e8.svg)}em[data-emoji=":flag_cv:"]:before,em[data-emoji=flag_cv]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1fb.svg)}em[data-emoji=":flag_bq:"]:before,em[data-emoji=flag_bq]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1f6.svg)}em[data-emoji=":flag_ky:"]:before,em[data-emoji=flag_ky]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1fe.svg)}em[data-emoji=":flag_cf:"]:before,em[data-emoji=flag_cf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1eb.svg)}em[data-emoji=":flag_td:"]:before,em[data-emoji=flag_td]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1e9.svg)}em[data-emoji=":flag_cl:"]:before,em[data-emoji=flag_cl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1f1.svg)}em[data-emoji=":flag_cn:"]:before,em[data-emoji=flag_cn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1f3.svg)}em[data-emoji=":flag_cx:"]:before,em[data-emoji=flag_cx]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1fd.svg)}em[data-emoji=":flag_cc:"]:before,em[data-emoji=flag_cc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1e8.svg)}em[data-emoji=":flag_co:"]:before,em[data-emoji=flag_co]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1f4.svg)}em[data-emoji=":flag_km:"]:before,em[data-emoji=flag_km]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1f2.svg)}em[data-emoji=":flag_cg:"]:before,em[data-emoji=flag_cg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1ec.svg)}em[data-emoji=":flag_cd:"]:before,em[data-emoji=flag_cd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1e9.svg)}em[data-emoji=":flag_ck:"]:before,em[data-emoji=flag_ck]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1f0.svg)}em[data-emoji=":flag_cr:"]:before,em[data-emoji=flag_cr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1f7.svg)}em[data-emoji=":flag_ci:"]:before,em[data-emoji=flag_ci]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1ee.svg)}em[data-emoji=":flag_hr:"]:before,em[data-emoji=flag_hr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ed-1f1f7.svg)}em[data-emoji=":flag_cu:"]:before,em[data-emoji=flag_cu]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1fa.svg)}em[data-emoji=":flag_cw:"]:before,em[data-emoji=flag_cw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1fc.svg)}em[data-emoji=":flag_cy:"]:before,em[data-emoji=flag_cy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1fe.svg)}em[data-emoji=":flag_cz:"]:before,em[data-emoji=flag_cz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1ff.svg)}em[data-emoji=":flag_dk:"]:before,em[data-emoji=flag_dk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e9-1f1f0.svg)}em[data-emoji=":flag_dj:"]:before,em[data-emoji=flag_dj]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e9-1f1ef.svg)}em[data-emoji=":flag_dm:"]:before,em[data-emoji=flag_dm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e9-1f1f2.svg)}em[data-emoji=":flag_do:"]:before,em[data-emoji=flag_do]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e9-1f1f4.svg)}em[data-emoji=":flag_ec:"]:before,em[data-emoji=flag_ec]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1e8.svg)}em[data-emoji=":flag_eg:"]:before,em[data-emoji=flag_eg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1ec.svg)}em[data-emoji=":flag_sv:"]:before,em[data-emoji=flag_sv]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1fb.svg)}em[data-emoji=":flag_gq:"]:before,em[data-emoji=flag_gq]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1f6.svg)}em[data-emoji=":flag_er:"]:before,em[data-emoji=flag_er]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1f7.svg)}em[data-emoji=":flag_ee:"]:before,em[data-emoji=flag_ee]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1ea.svg)}em[data-emoji=":flag_et:"]:before,em[data-emoji=flag_et]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1f9.svg)}em[data-emoji=":flag_eu:"]:before,em[data-emoji=flag_eu]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1fa.svg)}em[data-emoji=":flag_fk:"]:before,em[data-emoji=flag_fk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1eb-1f1f0.svg)}em[data-emoji=":flag_fo:"]:before,em[data-emoji=flag_fo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1eb-1f1f4.svg)}em[data-emoji=":flag_fj:"]:before,em[data-emoji=flag_fj]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1eb-1f1ef.svg)}em[data-emoji=":flag_fi:"]:before,em[data-emoji=flag_fi]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1eb-1f1ee.svg)}em[data-emoji=":flag_fr:"]:before,em[data-emoji=flag_fr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1eb-1f1f7.svg)}em[data-emoji=":flag_gf:"]:before,em[data-emoji=flag_gf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1eb.svg)}em[data-emoji=":flag_pf:"]:before,em[data-emoji=flag_pf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1eb.svg)}em[data-emoji=":flag_tf:"]:before,em[data-emoji=flag_tf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1eb.svg)}em[data-emoji=":flag_ga:"]:before,em[data-emoji=flag_ga]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1e6.svg)}em[data-emoji=":flag_gm:"]:before,em[data-emoji=flag_gm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1f2.svg)}em[data-emoji=":flag_ge:"]:before,em[data-emoji=flag_ge]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1ea.svg)}em[data-emoji=":flag_de:"]:before,em[data-emoji=flag_de]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e9-1f1ea.svg)}em[data-emoji=":flag_gh:"]:before,em[data-emoji=flag_gh]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1ed.svg)}em[data-emoji=":flag_gi:"]:before,em[data-emoji=flag_gi]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1ee.svg)}em[data-emoji=":flag_gr:"]:before,em[data-emoji=flag_gr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1f7.svg)}em[data-emoji=":flag_gl:"]:before,em[data-emoji=flag_gl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1f1.svg)}em[data-emoji=":flag_gd:"]:before,em[data-emoji=flag_gd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1e9.svg)}em[data-emoji=":flag_gp:"]:before,em[data-emoji=flag_gp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1f5.svg)}em[data-emoji=":flag_gu:"]:before,em[data-emoji=flag_gu]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1fa.svg)}em[data-emoji=":flag_gt:"]:before,em[data-emoji=flag_gt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1f9.svg)}em[data-emoji=":flag_gg:"]:before,em[data-emoji=flag_gg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1ec.svg)}em[data-emoji=":flag_gn:"]:before,em[data-emoji=flag_gn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1f3.svg)}em[data-emoji=":flag_gw:"]:before,em[data-emoji=flag_gw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1fc.svg)}em[data-emoji=":flag_gy:"]:before,em[data-emoji=flag_gy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1fe.svg)}em[data-emoji=":flag_ht:"]:before,em[data-emoji=flag_ht]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ed-1f1f9.svg)}em[data-emoji=":flag_hn:"]:before,em[data-emoji=flag_hn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ed-1f1f3.svg)}em[data-emoji=":flag_hk:"]:before,em[data-emoji=flag_hk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ed-1f1f0.svg)}em[data-emoji=":flag_hu:"]:before,em[data-emoji=flag_hu]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ed-1f1fa.svg)}em[data-emoji=":flag_is:"]:before,em[data-emoji=flag_is]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1f8.svg)}em[data-emoji=":flag_in:"]:before,em[data-emoji=flag_in]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1f3.svg)}em[data-emoji=":flag_id:"]:before,em[data-emoji=flag_id]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1e9.svg)}em[data-emoji=":flag_ir:"]:before,em[data-emoji=flag_ir]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1f7.svg)}em[data-emoji=":flag_iq:"]:before,em[data-emoji=flag_iq]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1f6.svg)}em[data-emoji=":flag_ie:"]:before,em[data-emoji=flag_ie]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1ea.svg)}em[data-emoji=":flag_im:"]:before,em[data-emoji=flag_im]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1f2.svg)}em[data-emoji=":flag_il:"]:before,em[data-emoji=flag_il]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1f1.svg)}em[data-emoji=":flag_it:"]:before,em[data-emoji=flag_it]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ee-1f1f9.svg)}em[data-emoji=":flag_jm:"]:before,em[data-emoji=flag_jm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ef-1f1f2.svg)}em[data-emoji=":flag_jp:"]:before,em[data-emoji=flag_jp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ef-1f1f5.svg)}em[data-emoji=":crossed_flags:"]:before,em[data-emoji=crossed_flags]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f38c.svg)}em[data-emoji=":flag_je:"]:before,em[data-emoji=flag_je]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ef-1f1ea.svg)}em[data-emoji=":flag_jo:"]:before,em[data-emoji=flag_jo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ef-1f1f4.svg)}em[data-emoji=":flag_kz:"]:before,em[data-emoji=flag_kz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1ff.svg)}em[data-emoji=":flag_ke:"]:before,em[data-emoji=flag_ke]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1ea.svg)}em[data-emoji=":flag_ki:"]:before,em[data-emoji=flag_ki]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1ee.svg)}em[data-emoji=":flag_xk:"]:before,em[data-emoji=flag_xk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fd-1f1f0.svg)}em[data-emoji=":flag_kw:"]:before,em[data-emoji=flag_kw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1fc.svg)}em[data-emoji=":flag_kg:"]:before,em[data-emoji=flag_kg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1ec.svg)}em[data-emoji=":flag_la:"]:before,em[data-emoji=flag_la]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1e6.svg)}em[data-emoji=":flag_lv:"]:before,em[data-emoji=flag_lv]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1fb.svg)}em[data-emoji=":flag_lb:"]:before,em[data-emoji=flag_lb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1e7.svg)}em[data-emoji=":flag_ls:"]:before,em[data-emoji=flag_ls]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1f8.svg)}em[data-emoji=":flag_lr:"]:before,em[data-emoji=flag_lr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1f7.svg)}em[data-emoji=":flag_ly:"]:before,em[data-emoji=flag_ly]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1fe.svg)}em[data-emoji=":flag_li:"]:before,em[data-emoji=flag_li]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1ee.svg)}em[data-emoji=":flag_lt:"]:before,em[data-emoji=flag_lt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1f9.svg)}em[data-emoji=":flag_lu:"]:before,em[data-emoji=flag_lu]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1fa.svg)}em[data-emoji=":flag_mo:"]:before,em[data-emoji=flag_mo]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f4.svg)}em[data-emoji=":flag_mk:"]:before,em[data-emoji=flag_mk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f0.svg)}em[data-emoji=":flag_mg:"]:before,em[data-emoji=flag_mg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1ec.svg)}em[data-emoji=":flag_mw:"]:before,em[data-emoji=flag_mw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1fc.svg)}em[data-emoji=":flag_my:"]:before,em[data-emoji=flag_my]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1fe.svg)}em[data-emoji=":flag_mv:"]:before,em[data-emoji=flag_mv]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1fb.svg)}em[data-emoji=":flag_ml:"]:before,em[data-emoji=flag_ml]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f1.svg)}em[data-emoji=":flag_mt:"]:before,em[data-emoji=flag_mt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f9.svg)}em[data-emoji=":flag_mh:"]:before,em[data-emoji=flag_mh]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1ed.svg)}em[data-emoji=":flag_mq:"]:before,em[data-emoji=flag_mq]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f6.svg)}em[data-emoji=":flag_mr:"]:before,em[data-emoji=flag_mr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f7.svg)}em[data-emoji=":flag_mu:"]:before,em[data-emoji=flag_mu]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1fa.svg)}em[data-emoji=":flag_yt:"]:before,em[data-emoji=flag_yt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fe-1f1f9.svg)}em[data-emoji=":flag_mx:"]:before,em[data-emoji=flag_mx]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1fd.svg)}em[data-emoji=":flag_fm:"]:before,em[data-emoji=flag_fm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1eb-1f1f2.svg)}em[data-emoji=":flag_md:"]:before,em[data-emoji=flag_md]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1e9.svg)}em[data-emoji=":flag_mc:"]:before,em[data-emoji=flag_mc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1e8.svg)}em[data-emoji=":flag_mn:"]:before,em[data-emoji=flag_mn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f3.svg)}em[data-emoji=":flag_me:"]:before,em[data-emoji=flag_me]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1ea.svg)}em[data-emoji=":flag_ms:"]:before,em[data-emoji=flag_ms]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f8.svg)}em[data-emoji=":flag_ma:"]:before,em[data-emoji=flag_ma]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1e6.svg)}em[data-emoji=":flag_mz:"]:before,em[data-emoji=flag_mz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1ff.svg)}em[data-emoji=":flag_mm:"]:before,em[data-emoji=flag_mm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f2.svg)}em[data-emoji=":flag_na:"]:before,em[data-emoji=flag_na]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1e6.svg)}em[data-emoji=":flag_nr:"]:before,em[data-emoji=flag_nr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1f7.svg)}em[data-emoji=":flag_np:"]:before,em[data-emoji=flag_np]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1f5.svg)}em[data-emoji=":flag_nl:"]:before,em[data-emoji=flag_nl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1f1.svg)}em[data-emoji=":flag_nc:"]:before,em[data-emoji=flag_nc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1e8.svg)}em[data-emoji=":flag_nz:"]:before,em[data-emoji=flag_nz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1ff.svg)}em[data-emoji=":flag_ni:"]:before,em[data-emoji=flag_ni]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1ee.svg)}em[data-emoji=":flag_ne:"]:before,em[data-emoji=flag_ne]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1ea.svg)}em[data-emoji=":flag_ng:"]:before,em[data-emoji=flag_ng]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1ec.svg)}em[data-emoji=":flag_nu:"]:before,em[data-emoji=flag_nu]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1fa.svg)}em[data-emoji=":flag_nf:"]:before,em[data-emoji=flag_nf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1eb.svg)}em[data-emoji=":flag_kp:"]:before,em[data-emoji=flag_kp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1f5.svg)}em[data-emoji=":flag_mp:"]:before,em[data-emoji=flag_mp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1f5.svg)}em[data-emoji=":flag_no:"]:before,em[data-emoji=flag_no]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f3-1f1f4.svg)}em[data-emoji=":flag_om:"]:before,em[data-emoji=flag_om]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f4-1f1f2.svg)}em[data-emoji=":flag_pk:"]:before,em[data-emoji=flag_pk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1f0.svg)}em[data-emoji=":flag_pw:"]:before,em[data-emoji=flag_pw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1fc.svg)}em[data-emoji=":flag_ps:"]:before,em[data-emoji=flag_ps]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1f8.svg)}em[data-emoji=":flag_pa:"]:before,em[data-emoji=flag_pa]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1e6.svg)}em[data-emoji=":flag_pg:"]:before,em[data-emoji=flag_pg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1ec.svg)}em[data-emoji=":flag_py:"]:before,em[data-emoji=flag_py]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1fe.svg)}em[data-emoji=":flag_pe:"]:before,em[data-emoji=flag_pe]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1ea.svg)}em[data-emoji=":flag_ph:"]:before,em[data-emoji=flag_ph]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1ed.svg)}em[data-emoji=":flag_pn:"]:before,em[data-emoji=flag_pn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1f3.svg)}em[data-emoji=":flag_pl:"]:before,em[data-emoji=flag_pl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1f1.svg)}em[data-emoji=":flag_pt:"]:before,em[data-emoji=flag_pt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1f9.svg)}em[data-emoji=":flag_pr:"]:before,em[data-emoji=flag_pr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1f7.svg)}em[data-emoji=":flag_qa:"]:before,em[data-emoji=flag_qa]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f6-1f1e6.svg)}em[data-emoji=":flag_re:"]:before,em[data-emoji=flag_re]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f7-1f1ea.svg)}em[data-emoji=":flag_ro:"]:before,em[data-emoji=flag_ro]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f7-1f1f4.svg)}em[data-emoji=":flag_ru:"]:before,em[data-emoji=flag_ru]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f7-1f1fa.svg)}em[data-emoji=":flag_rw:"]:before,em[data-emoji=flag_rw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f7-1f1fc.svg)}em[data-emoji=":flag_ws:"]:before,em[data-emoji=flag_ws]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fc-1f1f8.svg)}em[data-emoji=":flag_sm:"]:before,em[data-emoji=flag_sm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1f2.svg)}em[data-emoji=":flag_st:"]:before,em[data-emoji=flag_st]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1f9.svg)}em[data-emoji=":flag_sa:"]:before,em[data-emoji=flag_sa]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1e6.svg)}em[data-emoji=":flag_sn:"]:before,em[data-emoji=flag_sn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1f3.svg)}em[data-emoji=":flag_rs:"]:before,em[data-emoji=flag_rs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f7-1f1f8.svg)}em[data-emoji=":flag_sc:"]:before,em[data-emoji=flag_sc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1e8.svg)}em[data-emoji=":flag_sl:"]:before,em[data-emoji=flag_sl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1f1.svg)}em[data-emoji=":flag_sg:"]:before,em[data-emoji=flag_sg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1ec.svg)}em[data-emoji=":flag_sx:"]:before,em[data-emoji=flag_sx]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1fd.svg)}em[data-emoji=":flag_sk:"]:before,em[data-emoji=flag_sk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1f0.svg)}em[data-emoji=":flag_si:"]:before,em[data-emoji=flag_si]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1ee.svg)}em[data-emoji=":flag_gs:"]:before,em[data-emoji=flag_gs]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1f8.svg)}em[data-emoji=":flag_sb:"]:before,em[data-emoji=flag_sb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1e7.svg)}em[data-emoji=":flag_so:"]:before,em[data-emoji=flag_so]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1f4.svg)}em[data-emoji=":flag_za:"]:before,em[data-emoji=flag_za]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ff-1f1e6.svg)}em[data-emoji=":flag_kr:"]:before,em[data-emoji=flag_kr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1f7.svg)}em[data-emoji=":flag_ss:"]:before,em[data-emoji=flag_ss]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1f8.svg)}em[data-emoji=":flag_es:"]:before,em[data-emoji=flag_es]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1f8.svg)}em[data-emoji=":flag_lk:"]:before,em[data-emoji=flag_lk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1f0.svg)}em[data-emoji=":flag_bl:"]:before,em[data-emoji=flag_bl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1f1.svg)}em[data-emoji=":flag_sh:"]:before,em[data-emoji=flag_sh]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1ed.svg)}em[data-emoji=":flag_kn:"]:before,em[data-emoji=flag_kn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f0-1f1f3.svg)}em[data-emoji=":flag_lc:"]:before,em[data-emoji=flag_lc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f1-1f1e8.svg)}em[data-emoji=":flag_pm:"]:before,em[data-emoji=flag_pm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f5-1f1f2.svg)}em[data-emoji=":flag_vc:"]:before,em[data-emoji=flag_vc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fb-1f1e8.svg)}em[data-emoji=":flag_sd:"]:before,em[data-emoji=flag_sd]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1e9.svg)}em[data-emoji=":flag_sr:"]:before,em[data-emoji=flag_sr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1f7.svg)}em[data-emoji=":flag_sz:"]:before,em[data-emoji=flag_sz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1ff.svg)}em[data-emoji=":flag_se:"]:before,em[data-emoji=flag_se]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1ea.svg)}em[data-emoji=":flag_ch:"]:before,em[data-emoji=flag_ch]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1ed.svg)}em[data-emoji=":flag_sy:"]:before,em[data-emoji=flag_sy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1fe.svg)}em[data-emoji=":flag_tw:"]:before,em[data-emoji=flag_tw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1fc.svg)}em[data-emoji=":flag_tj:"]:before,em[data-emoji=flag_tj]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1ef.svg)}em[data-emoji=":flag_tz:"]:before,em[data-emoji=flag_tz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1ff.svg)}em[data-emoji=":flag_th:"]:before,em[data-emoji=flag_th]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1ed.svg)}em[data-emoji=":flag_tl:"]:before,em[data-emoji=flag_tl]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1f1.svg)}em[data-emoji=":flag_tg:"]:before,em[data-emoji=flag_tg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1ec.svg)}em[data-emoji=":flag_tk:"]:before,em[data-emoji=flag_tk]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1f0.svg)}em[data-emoji=":flag_to:"]:before,em[data-emoji=flag_to]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1f4.svg)}em[data-emoji=":flag_tt:"]:before,em[data-emoji=flag_tt]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1f9.svg)}em[data-emoji=":flag_tn:"]:before,em[data-emoji=flag_tn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1f3.svg)}em[data-emoji=":flag_tr:"]:before,em[data-emoji=flag_tr]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1f7.svg)}em[data-emoji=":flag_tm:"]:before,em[data-emoji=flag_tm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1f2.svg)}em[data-emoji=":flag_tc:"]:before,em[data-emoji=flag_tc]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1e8.svg)}em[data-emoji=":flag_vi:"]:before,em[data-emoji=flag_vi]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fb-1f1ee.svg)}em[data-emoji=":flag_tv:"]:before,em[data-emoji=flag_tv]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1fb.svg)}em[data-emoji=":flag_ug:"]:before,em[data-emoji=flag_ug]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fa-1f1ec.svg)}em[data-emoji=":flag_ua:"]:before,em[data-emoji=flag_ua]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fa-1f1e6.svg)}em[data-emoji=":flag_ae:"]:before,em[data-emoji=flag_ae]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1ea.svg)}em[data-emoji=":flag_gb:"]:before,em[data-emoji=flag_gb]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ec-1f1e7.svg)}em[data-emoji=":england:"]:before,em[data-emoji=england]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.svg)}em[data-emoji=":scotland:"]:before,em[data-emoji=scotland]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.svg)}em[data-emoji=":wales:"]:before,em[data-emoji=wales]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.svg)}em[data-emoji=":flag_us:"]:before,em[data-emoji=flag_us]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fa-1f1f8.svg)}em[data-emoji=":flag_uy:"]:before,em[data-emoji=flag_uy]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fa-1f1fe.svg)}em[data-emoji=":flag_uz:"]:before,em[data-emoji=flag_uz]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fa-1f1ff.svg)}em[data-emoji=":flag_vu:"]:before,em[data-emoji=flag_vu]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fb-1f1fa.svg)}em[data-emoji=":flag_va:"]:before,em[data-emoji=flag_va]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fb-1f1e6.svg)}em[data-emoji=":flag_ve:"]:before,em[data-emoji=flag_ve]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fb-1f1ea.svg)}em[data-emoji=":flag_vn:"]:before,em[data-emoji=flag_vn]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fb-1f1f3.svg)}em[data-emoji=":flag_wf:"]:before,em[data-emoji=flag_wf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fc-1f1eb.svg)}em[data-emoji=":flag_eh:"]:before,em[data-emoji=flag_eh]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1ed.svg)}em[data-emoji=":flag_ye:"]:before,em[data-emoji=flag_ye]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fe-1f1ea.svg)}em[data-emoji=":flag_zm:"]:before,em[data-emoji=flag_zm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ff-1f1f2.svg)}em[data-emoji=":flag_zw:"]:before,em[data-emoji=flag_zw]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ff-1f1fc.svg)}em[data-emoji=":flag_ac:"]:before,em[data-emoji=flag_ac]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e6-1f1e8.svg)}em[data-emoji=":flag_bv:"]:before,em[data-emoji=flag_bv]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e7-1f1fb.svg)}em[data-emoji=":flag_cp:"]:before,em[data-emoji=flag_cp]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e8-1f1f5.svg)}em[data-emoji=":flag_ea:"]:before,em[data-emoji=flag_ea]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ea-1f1e6.svg)}em[data-emoji=":flag_dg:"]:before,em[data-emoji=flag_dg]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1e9-1f1ec.svg)}em[data-emoji=":flag_hm:"]:before,em[data-emoji=flag_hm]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1ed-1f1f2.svg)}em[data-emoji=":flag_mf:"]:before,em[data-emoji=flag_mf]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f2-1f1eb.svg)}em[data-emoji=":flag_sj:"]:before,em[data-emoji=flag_sj]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f8-1f1ef.svg)}em[data-emoji=":flag_ta:"]:before,em[data-emoji=flag_ta]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1f9-1f1e6.svg)}em[data-emoji=":flag_um:"]:before,em[data-emoji=flag_um]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fa-1f1f2.svg)}em[data-emoji=":united_nations:"]:before,em[data-emoji=united_nations]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f1fa-1f1f3.svg)}em[data-emoji=":tone1:"]:before,em[data-emoji=tone1]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3fb.svg)}em[data-emoji=":tone2:"]:before,em[data-emoji=tone2]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3fc.svg)}em[data-emoji=":tone3:"]:before,em[data-emoji=tone3]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3fd.svg)}em[data-emoji=":tone4:"]:before,em[data-emoji=tone4]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3fe.svg)}em[data-emoji=":tone5:"]:before,em[data-emoji=tone5]:before{background-image:url(https://twemoji.maxcdn.com/v/latest/svg/1f3ff.svg)}/*! + * # Fomantic-UI - Flag + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */i.flag:not(.icon){display:inline-block;width:16px;height:11px;line-height:11px;vertical-align:baseline;margin:0 .5em 0 0;text-decoration:inherit;speak:none;-webkit-font-smoothing:antialiased;-webkit-backface-visibility:hidden;backface-visibility:hidden}i.flag:not(.icon):before{display:inline-block;content:'';background:url(themes/default/assets/images/flags.png) no-repeat -108px -1976px;width:16px;height:11px}i.flag.ad:before,i.flag.andorra:before{background-position:0 0}i.flag.ae:before,i.flag.uae:before,i.flag.united.arab.emirates:before{background-position:0 -26px}i.flag.af:before,i.flag.afghanistan:before{background-position:0 -52px}i.flag.ag:before,i.flag.antigua:before{background-position:0 -78px}i.flag.ai:before,i.flag.anguilla:before{background-position:0 -104px}i.flag.al:before,i.flag.albania:before{background-position:0 -130px}i.flag.am:before,i.flag.armenia:before{background-position:0 -156px}i.flag.an:before,i.flag.netherlands.antilles:before{background-position:0 -182px}i.flag.angola:before,i.flag.ao:before{background-position:0 -208px}i.flag.ar:before,i.flag.argentina:before{background-position:0 -234px}i.flag.american.samoa:before,i.flag.as:before{background-position:0 -260px}i.flag.at:before,i.flag.austria:before{background-position:0 -286px}i.flag.au:before,i.flag.australia:before{background-position:0 -312px}i.flag.aruba:before,i.flag.aw:before{background-position:0 -338px}i.flag.aland.islands:before,i.flag.ax:before{background-position:0 -364px}i.flag.az:before,i.flag.azerbaijan:before{background-position:0 -390px}i.flag.ba:before,i.flag.bosnia:before{background-position:0 -416px}i.flag.barbados:before,i.flag.bb:before{background-position:0 -442px}i.flag.bangladesh:before,i.flag.bd:before{background-position:0 -468px}i.flag.be:before,i.flag.belgium:before{background-position:0 -494px}i.flag.bf:before,i.flag.burkina.faso:before{background-position:0 -520px}i.flag.bg:before,i.flag.bulgaria:before{background-position:0 -546px}i.flag.bahrain:before,i.flag.bh:before{background-position:0 -572px}i.flag.bi:before,i.flag.burundi:before{background-position:0 -598px}i.flag.benin:before,i.flag.bj:before{background-position:0 -624px}i.flag.bermuda:before,i.flag.bm:before{background-position:0 -650px}i.flag.bn:before,i.flag.brunei:before{background-position:0 -676px}i.flag.bo:before,i.flag.bolivia:before{background-position:0 -702px}i.flag.br:before,i.flag.brazil:before{background-position:0 -728px}i.flag.bahamas:before,i.flag.bs:before{background-position:0 -754px}i.flag.bhutan:before,i.flag.bt:before{background-position:0 -780px}i.flag.bouvet.island:before,i.flag.bv:before{background-position:0 -806px}i.flag.botswana:before,i.flag.bw:before{background-position:0 -832px}i.flag.belarus:before,i.flag.by:before{background-position:0 -858px}i.flag.belize:before,i.flag.bz:before{background-position:0 -884px}i.flag.ca:before,i.flag.canada:before{background-position:0 -910px}i.flag.cc:before,i.flag.cocos.islands:before{background-position:0 -962px}i.flag.cd:before,i.flag.congo:before{background-position:0 -988px}i.flag.central.african.republic:before,i.flag.cf:before{background-position:0 -1014px}i.flag.cg:before,i.flag.congo.brazzaville:before{background-position:0 -1040px}i.flag.ch:before,i.flag.switzerland:before{background-position:0 -1066px}i.flag.ci:before,i.flag.cote.divoire:before{background-position:0 -1092px}i.flag.ck:before,i.flag.cook.islands:before{background-position:0 -1118px}i.flag.chile:before,i.flag.cl:before{background-position:0 -1144px}i.flag.cameroon:before,i.flag.cm:before{background-position:0 -1170px}i.flag.china:before,i.flag.cn:before{background-position:0 -1196px}i.flag.co:before,i.flag.colombia:before{background-position:0 -1222px}i.flag.costa.rica:before,i.flag.cr:before{background-position:0 -1248px}i.flag.cs:before,i.flag.serbia:before{background-position:0 -1274px}i.flag.cu:before,i.flag.cuba:before{background-position:0 -1300px}i.flag.cape.verde:before,i.flag.cv:before{background-position:0 -1326px}i.flag.christmas.island:before,i.flag.cx:before{background-position:0 -1352px}i.flag.cy:before,i.flag.cyprus:before{background-position:0 -1378px}i.flag.cz:before,i.flag.czech.republic:before{background-position:0 -1404px}i.flag.de:before,i.flag.germany:before{background-position:0 -1430px}i.flag.dj:before,i.flag.djibouti:before{background-position:0 -1456px}i.flag.denmark:before,i.flag.dk:before{background-position:0 -1482px}i.flag.dm:before,i.flag.dominica:before{background-position:0 -1508px}i.flag.do:before,i.flag.dominican.republic:before{background-position:0 -1534px}i.flag.algeria:before,i.flag.dz:before{background-position:0 -1560px}i.flag.ec:before,i.flag.ecuador:before{background-position:0 -1586px}i.flag.ee:before,i.flag.estonia:before{background-position:0 -1612px}i.flag.eg:before,i.flag.egypt:before{background-position:0 -1638px}i.flag.eh:before,i.flag.western.sahara:before{background-position:0 -1664px}i.flag.england:before,i.flag.gb.eng:before{background-position:0 -1690px}i.flag.er:before,i.flag.eritrea:before{background-position:0 -1716px}i.flag.es:before,i.flag.spain:before{background-position:0 -1742px}i.flag.et:before,i.flag.ethiopia:before{background-position:0 -1768px}i.flag.eu:before,i.flag.european.union:before{background-position:0 -1794px}i.flag.fi:before,i.flag.finland:before{background-position:0 -1846px}i.flag.fiji:before,i.flag.fj:before{background-position:0 -1872px}i.flag.falkland.islands:before,i.flag.fk:before{background-position:0 -1898px}i.flag.fm:before,i.flag.micronesia:before{background-position:0 -1924px}i.flag.faroe.islands:before,i.flag.fo:before{background-position:0 -1950px}i.flag.fr:before,i.flag.france:before{background-position:0 -1976px}i.flag.ga:before,i.flag.gabon:before{background-position:-36px 0}i.flag.gb:before,i.flag.uk:before,i.flag.united.kingdom:before{background-position:-36px -26px}i.flag.gd:before,i.flag.grenada:before{background-position:-36px -52px}i.flag.ge:before,i.flag.georgia:before{background-position:-36px -78px}i.flag.french.guiana:before,i.flag.gf:before{background-position:-36px -104px}i.flag.gh:before,i.flag.ghana:before{background-position:-36px -130px}i.flag.gi:before,i.flag.gibraltar:before{background-position:-36px -156px}i.flag.gl:before,i.flag.greenland:before{background-position:-36px -182px}i.flag.gambia:before,i.flag.gm:before{background-position:-36px -208px}i.flag.gn:before,i.flag.guinea:before{background-position:-36px -234px}i.flag.gp:before,i.flag.guadeloupe:before{background-position:-36px -260px}i.flag.equatorial.guinea:before,i.flag.gq:before{background-position:-36px -286px}i.flag.gr:before,i.flag.greece:before{background-position:-36px -312px}i.flag.gs:before,i.flag.sandwich.islands:before{background-position:-36px -338px}i.flag.gt:before,i.flag.guatemala:before{background-position:-36px -364px}i.flag.gu:before,i.flag.guam:before{background-position:-36px -390px}i.flag.guinea-bissau:before,i.flag.gw:before{background-position:-36px -416px}i.flag.guyana:before,i.flag.gy:before{background-position:-36px -442px}i.flag.hk:before,i.flag.hong.kong:before{background-position:-36px -468px}i.flag.heard.island:before,i.flag.hm:before{background-position:-36px -494px}i.flag.hn:before,i.flag.honduras:before{background-position:-36px -520px}i.flag.croatia:before,i.flag.hr:before{background-position:-36px -546px}i.flag.haiti:before,i.flag.ht:before{background-position:-36px -572px}i.flag.hu:before,i.flag.hungary:before{background-position:-36px -598px}i.flag.id:before,i.flag.indonesia:before{background-position:-36px -624px}i.flag.ie:before,i.flag.ireland:before{background-position:-36px -650px}i.flag.il:before,i.flag.israel:before{background-position:-36px -676px}i.flag.in:before,i.flag.india:before{background-position:-36px -702px}i.flag.indian.ocean.territory:before,i.flag.io:before{background-position:-36px -728px}i.flag.iq:before,i.flag.iraq:before{background-position:-36px -754px}i.flag.ir:before,i.flag.iran:before{background-position:-36px -780px}i.flag.iceland:before,i.flag.is:before{background-position:-36px -806px}i.flag.it:before,i.flag.italy:before{background-position:-36px -832px}i.flag.jamaica:before,i.flag.jm:before{background-position:-36px -858px}i.flag.jo:before,i.flag.jordan:before{background-position:-36px -884px}i.flag.japan:before,i.flag.jp:before{background-position:-36px -910px}i.flag.ke:before,i.flag.kenya:before{background-position:-36px -936px}i.flag.kg:before,i.flag.kyrgyzstan:before{background-position:-36px -962px}i.flag.cambodia:before,i.flag.kh:before{background-position:-36px -988px}i.flag.ki:before,i.flag.kiribati:before{background-position:-36px -1014px}i.flag.comoros:before,i.flag.km:before{background-position:-36px -1040px}i.flag.kn:before,i.flag.saint.kitts.and.nevis:before{background-position:-36px -1066px}i.flag.kp:before,i.flag.north.korea:before{background-position:-36px -1092px}i.flag.kr:before,i.flag.south.korea:before{background-position:-36px -1118px}i.flag.kuwait:before,i.flag.kw:before{background-position:-36px -1144px}i.flag.cayman.islands:before,i.flag.ky:before{background-position:-36px -1170px}i.flag.kazakhstan:before,i.flag.kz:before{background-position:-36px -1196px}i.flag.la:before,i.flag.laos:before{background-position:-36px -1222px}i.flag.lb:before,i.flag.lebanon:before{background-position:-36px -1248px}i.flag.lc:before,i.flag.saint.lucia:before{background-position:-36px -1274px}i.flag.li:before,i.flag.liechtenstein:before{background-position:-36px -1300px}i.flag.lk:before,i.flag.sri.lanka:before{background-position:-36px -1326px}i.flag.liberia:before,i.flag.lr:before{background-position:-36px -1352px}i.flag.lesotho:before,i.flag.ls:before{background-position:-36px -1378px}i.flag.lithuania:before,i.flag.lt:before{background-position:-36px -1404px}i.flag.lu:before,i.flag.luxembourg:before{background-position:-36px -1430px}i.flag.latvia:before,i.flag.lv:before{background-position:-36px -1456px}i.flag.libya:before,i.flag.ly:before{background-position:-36px -1482px}i.flag.ma:before,i.flag.morocco:before{background-position:-36px -1508px}i.flag.mc:before,i.flag.monaco:before{background-position:-36px -1534px}i.flag.md:before,i.flag.moldova:before{background-position:-36px -1560px}i.flag.me:before,i.flag.montenegro:before{background-position:-36px -1586px}i.flag.madagascar:before,i.flag.mg:before{background-position:-36px -1613px}i.flag.marshall.islands:before,i.flag.mh:before{background-position:-36px -1639px}i.flag.macedonia:before,i.flag.mk:before{background-position:-36px -1665px}i.flag.mali:before,i.flag.ml:before{background-position:-36px -1691px}i.flag.burma:before,i.flag.mm:before,i.flag.myanmar:before{background-position:-36px -1717px}i.flag.mn:before,i.flag.mongolia:before{background-position:-36px -1743px}i.flag.macau:before,i.flag.mo:before{background-position:-36px -1769px}i.flag.mp:before,i.flag.northern.mariana.islands:before{background-position:-36px -1795px}i.flag.martinique:before,i.flag.mq:before{background-position:-36px -1821px}i.flag.mauritania:before,i.flag.mr:before{background-position:-36px -1847px}i.flag.montserrat:before,i.flag.ms:before{background-position:-36px -1873px}i.flag.malta:before,i.flag.mt:before{background-position:-36px -1899px}i.flag.mauritius:before,i.flag.mu:before{background-position:-36px -1925px}i.flag.maldives:before,i.flag.mv:before{background-position:-36px -1951px}i.flag.malawi:before,i.flag.mw:before{background-position:-36px -1977px}i.flag.mexico:before,i.flag.mx:before{background-position:-72px 0}i.flag.malaysia:before,i.flag.my:before{background-position:-72px -26px}i.flag.mozambique:before,i.flag.mz:before{background-position:-72px -52px}i.flag.na:before,i.flag.namibia:before{background-position:-72px -78px}i.flag.nc:before,i.flag.new.caledonia:before{background-position:-72px -104px}i.flag.ne:before,i.flag.niger:before{background-position:-72px -130px}i.flag.nf:before,i.flag.norfolk.island:before{background-position:-72px -156px}i.flag.ng:before,i.flag.nigeria:before{background-position:-72px -182px}i.flag.ni:before,i.flag.nicaragua:before{background-position:-72px -208px}i.flag.netherlands:before,i.flag.nl:before{background-position:-72px -234px}i.flag.no:before,i.flag.norway:before{background-position:-72px -260px}i.flag.nepal:before,i.flag.np:before{background-position:-72px -286px}i.flag.nauru:before,i.flag.nr:before{background-position:-72px -312px}i.flag.niue:before,i.flag.nu:before{background-position:-72px -338px}i.flag.new.zealand:before,i.flag.nz:before{background-position:-72px -364px}i.flag.om:before,i.flag.oman:before{background-position:-72px -390px}i.flag.pa:before,i.flag.panama:before{background-position:-72px -416px}i.flag.pe:before,i.flag.peru:before{background-position:-72px -442px}i.flag.french.polynesia:before,i.flag.pf:before{background-position:-72px -468px}i.flag.new.guinea:before,i.flag.pg:before{background-position:-72px -494px}i.flag.ph:before,i.flag.philippines:before{background-position:-72px -520px}i.flag.pakistan:before,i.flag.pk:before{background-position:-72px -546px}i.flag.pl:before,i.flag.poland:before{background-position:-72px -572px}i.flag.pm:before,i.flag.saint.pierre:before{background-position:-72px -598px}i.flag.pitcairn.islands:before,i.flag.pn:before{background-position:-72px -624px}i.flag.pr:before,i.flag.puerto.rico:before{background-position:-72px -650px}i.flag.palestine:before,i.flag.ps:before{background-position:-72px -676px}i.flag.portugal:before,i.flag.pt:before{background-position:-72px -702px}i.flag.palau:before,i.flag.pw:before{background-position:-72px -728px}i.flag.paraguay:before,i.flag.py:before{background-position:-72px -754px}i.flag.qa:before,i.flag.qatar:before{background-position:-72px -780px}i.flag.re:before,i.flag.reunion:before{background-position:-72px -806px}i.flag.ro:before,i.flag.romania:before{background-position:-72px -832px}i.flag.rs:before,i.flag.serbia:before{background-position:-72px -858px}i.flag.ru:before,i.flag.russia:before{background-position:-72px -884px}i.flag.rw:before,i.flag.rwanda:before{background-position:-72px -910px}i.flag.sa:before,i.flag.saudi.arabia:before{background-position:-72px -936px}i.flag.sb:before,i.flag.solomon.islands:before{background-position:-72px -962px}i.flag.sc:before,i.flag.seychelles:before{background-position:-72px -988px}i.flag.gb.sct:before,i.flag.scotland:before{background-position:-72px -1014px}i.flag.sd:before,i.flag.sudan:before{background-position:-72px -1040px}i.flag.se:before,i.flag.sweden:before{background-position:-72px -1066px}i.flag.sg:before,i.flag.singapore:before{background-position:-72px -1092px}i.flag.saint.helena:before,i.flag.sh:before{background-position:-72px -1118px}i.flag.si:before,i.flag.slovenia:before{background-position:-72px -1144px}i.flag.jan.mayen:before,i.flag.sj:before,i.flag.svalbard:before{background-position:-72px -1170px}i.flag.sk:before,i.flag.slovakia:before{background-position:-72px -1196px}i.flag.sierra.leone:before,i.flag.sl:before{background-position:-72px -1222px}i.flag.san.marino:before,i.flag.sm:before{background-position:-72px -1248px}i.flag.senegal:before,i.flag.sn:before{background-position:-72px -1274px}i.flag.so:before,i.flag.somalia:before{background-position:-72px -1300px}i.flag.sr:before,i.flag.suriname:before{background-position:-72px -1326px}i.flag.sao.tome:before,i.flag.st:before{background-position:-72px -1352px}i.flag.el.salvador:before,i.flag.sv:before{background-position:-72px -1378px}i.flag.sy:before,i.flag.syria:before{background-position:-72px -1404px}i.flag.swaziland:before,i.flag.sz:before{background-position:-72px -1430px}i.flag.caicos.islands:before,i.flag.tc:before{background-position:-72px -1456px}i.flag.chad:before,i.flag.td:before{background-position:-72px -1482px}i.flag.french.territories:before,i.flag.tf:before{background-position:-72px -1508px}i.flag.tg:before,i.flag.togo:before{background-position:-72px -1534px}i.flag.th:before,i.flag.thailand:before{background-position:-72px -1560px}i.flag.tajikistan:before,i.flag.tj:before{background-position:-72px -1586px}i.flag.tk:before,i.flag.tokelau:before{background-position:-72px -1612px}i.flag.timorleste:before,i.flag.tl:before{background-position:-72px -1638px}i.flag.tm:before,i.flag.turkmenistan:before{background-position:-72px -1664px}i.flag.tn:before,i.flag.tunisia:before{background-position:-72px -1690px}i.flag.to:before,i.flag.tonga:before{background-position:-72px -1716px}i.flag.tr:before,i.flag.turkey:before{background-position:-72px -1742px}i.flag.trinidad:before,i.flag.tt:before{background-position:-72px -1768px}i.flag.tuvalu:before,i.flag.tv:before{background-position:-72px -1794px}i.flag.taiwan:before,i.flag.tw:before{background-position:-72px -1820px}i.flag.tanzania:before,i.flag.tz:before{background-position:-72px -1846px}i.flag.ua:before,i.flag.ukraine:before{background-position:-72px -1872px}i.flag.ug:before,i.flag.uganda:before{background-position:-72px -1898px}i.flag.um:before,i.flag.us.minor.islands:before{background-position:-72px -1924px}i.flag.america:before,i.flag.united.states:before,i.flag.us:before{background-position:-72px -1950px}i.flag.uruguay:before,i.flag.uy:before{background-position:-72px -1976px}i.flag.uz:before,i.flag.uzbekistan:before{background-position:-108px 0}i.flag.va:before,i.flag.vatican.city:before{background-position:-108px -26px}i.flag.saint.vincent:before,i.flag.vc:before{background-position:-108px -52px}i.flag.ve:before,i.flag.venezuela:before{background-position:-108px -78px}i.flag.british.virgin.islands:before,i.flag.vg:before{background-position:-108px -104px}i.flag.us.virgin.islands:before,i.flag.vi:before{background-position:-108px -130px}i.flag.vietnam:before,i.flag.vn:before{background-position:-108px -156px}i.flag.vanuatu:before,i.flag.vu:before{background-position:-108px -182px}i.flag.gb.wls:before,i.flag.wales:before{background-position:-108px -208px}i.flag.wallis.and.futuna:before,i.flag.wf:before{background-position:-108px -234px}i.flag.samoa:before,i.flag.ws:before{background-position:-108px -260px}i.flag.ye:before,i.flag.yemen:before{background-position:-108px -286px}i.flag.mayotte:before,i.flag.yt:before{background-position:-108px -312px}i.flag.south.africa:before,i.flag.za:before{background-position:-108px -338px}i.flag.zambia:before,i.flag.zm:before{background-position:-108px -364px}i.flag.zimbabwe:before,i.flag.zw:before{background-position:-108px -390px}/*! + * # Fomantic-UI - Header + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.header{border:none;margin:calc(2rem - .1428571428571429em) 0 1rem;padding:0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;line-height:1.28571429em;text-transform:none;color:rgba(0,0,0,.87)}.ui.header:first-child{margin-top:-.14285714em}.ui.header:last-child{margin-bottom:0}.ui.header .sub.header{display:block;font-weight:400;padding:0;margin:0;font-size:1rem;line-height:1.2em;color:rgba(0,0,0,.6)}.ui.header>.icon{display:table-cell;opacity:1;font-size:1.5em;padding-top:0;vertical-align:middle}.ui.header .icon:only-child{display:inline-block;padding:0;margin-right:.75rem}.ui.header>.image:not(.icon),.ui.header>img{display:inline-block;margin-top:.14285714em;width:2.5em;height:auto;vertical-align:middle}.ui.header>.image:not(.icon):only-child,.ui.header>img:only-child{margin-right:.75rem}.ui.header .content{display:inline-block;vertical-align:top}.ui.header>.image+.content,.ui.header>img+.content{padding-left:.75rem;vertical-align:middle}.ui.header>.icon+.content{padding-left:.75rem;display:table-cell;vertical-align:middle}.ui.header .ui.label{font-size:'';margin-left:.5rem;vertical-align:middle}.ui.header+p{margin-top:0}h1.ui.header{font-size:2rem}h1.ui.header .sub.header{font-size:1.14285714rem}h2.ui.header{font-size:1.71428571rem}h2.ui.header .sub.header{font-size:1.14285714rem}h3.ui.header{font-size:1.28571429rem}h3.ui.header .sub.header{font-size:1rem}h4.ui.header{font-size:1.07142857rem}h4.ui.header .sub.header{font-size:1rem}h5.ui.header{font-size:1rem}h5.ui.header .sub.header{font-size:.92857143rem}h6.ui.header{font-size:.85714286rem}h6.ui.header .sub.header{font-size:.92857143rem}.ui.mini.header{font-size:.85714286em}.ui.mini.header .sub.header{font-size:.92857143rem}.ui.mini.sub.header{font-size:.78571429em}.ui.tiny.header{font-size:1em}.ui.tiny.header .sub.header{font-size:.92857143rem}.ui.tiny.sub.header{font-size:.78571429em}.ui.small.header{font-size:1.07142857em}.ui.small.header .sub.header{font-size:1rem}.ui.small.sub.header{font-size:.78571429em}.ui.large.header{font-size:1.71428571em}.ui.large.header .sub.header{font-size:1.14285714rem}.ui.large.sub.header{font-size:.92857143em}.ui.big.header{font-size:1.85714286em}.ui.big.header .sub.header{font-size:1.14285714rem}.ui.big.sub.header{font-size:1em}.ui.huge.header{font-size:2em;min-height:1em}.ui.huge.header .sub.header{font-size:1.14285714rem}.ui.huge.sub.header{font-size:1em}.ui.massive.header{font-size:2.28571429em;min-height:1em}.ui.massive.header .sub.header{font-size:1.42857143rem}.ui.massive.sub.header{font-size:1.14285714em}.ui.sub.header{padding:0;margin-bottom:.14285714rem;font-weight:700;font-size:.85714286em;text-transform:uppercase;color:''}.ui.icon.header{display:inline-block;text-align:center;margin:2rem 0 1rem}.ui.icon.header:after{content:'';display:block;height:0;clear:both;visibility:hidden}.ui.icon.header:first-child{margin-top:0}.ui.icon.header .icon{float:none;display:block;width:auto;height:auto;line-height:1;padding:0;font-size:3em;margin:0 auto .5rem;opacity:1}.ui.icon.header .corner.icon{font-size:calc(3em * .45)}.ui.icon.header .content{display:block;padding:0}.ui.icon.header .circular.icon{font-size:2em}.ui.icon.header .square.icon{font-size:2em}.ui.block.icon.header .icon{margin-bottom:0}.ui.icon.header.aligned{margin-left:auto;margin-right:auto;display:block}.ui.disabled.header{opacity:.45}.ui.inverted.header{color:#fff}.ui.inverted.header .sub.header{color:rgba(255,255,255,.8)}.ui.inverted.attached.header{background:#1b1c1d;-webkit-box-shadow:none;box-shadow:none;border-color:transparent}.ui.inverted.block.header{background:#545454 -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:#545454 -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:#545454 linear-gradient(transparent,rgba(0,0,0,.05));-webkit-box-shadow:none;box-shadow:none;border-bottom:none}.ui.primary.header{color:#2185d0}a.ui.primary.header:hover{color:#1678c2}.ui.primary.dividing.header{border-bottom:2px solid #2185d0}.ui.inverted.primary.header.header.header{color:#54c8ff}a.ui.inverted.primary.header.header.header:hover{color:#21b8ff}.ui.inverted.primary.dividing.header{border-bottom:2px solid #54c8ff}.ui.secondary.header{color:#1b1c1d}a.ui.secondary.header:hover{color:#27292a}.ui.secondary.dividing.header{border-bottom:2px solid #1b1c1d}.ui.inverted.secondary.header.header.header{color:#545454}a.ui.inverted.secondary.header.header.header:hover{color:#6e6e6e}.ui.inverted.secondary.dividing.header{border-bottom:2px solid #545454}.ui.red.header{color:#db2828}a.ui.red.header:hover{color:#d01919}.ui.red.dividing.header{border-bottom:2px solid #db2828}.ui.inverted.red.header.header.header{color:#ff695e}a.ui.inverted.red.header.header.header:hover{color:#ff392b}.ui.inverted.red.dividing.header{border-bottom:2px solid #ff695e}.ui.orange.header{color:#f2711c}a.ui.orange.header:hover{color:#f26202}.ui.orange.dividing.header{border-bottom:2px solid #f2711c}.ui.inverted.orange.header.header.header{color:#ff851b}a.ui.inverted.orange.header.header.header:hover{color:#e76b00}.ui.inverted.orange.dividing.header{border-bottom:2px solid #ff851b}.ui.yellow.header{color:#fbbd08}a.ui.yellow.header:hover{color:#eaae00}.ui.yellow.dividing.header{border-bottom:2px solid #fbbd08}.ui.inverted.yellow.header.header.header{color:#ffe21f}a.ui.inverted.yellow.header.header.header:hover{color:#ebcd00}.ui.inverted.yellow.dividing.header{border-bottom:2px solid #ffe21f}.ui.olive.header{color:#b5cc18}a.ui.olive.header:hover{color:#a7bd0d}.ui.olive.dividing.header{border-bottom:2px solid #b5cc18}.ui.inverted.olive.header.header.header{color:#d9e778}a.ui.inverted.olive.header.header.header:hover{color:#d2e745}.ui.inverted.olive.dividing.header{border-bottom:2px solid #d9e778}.ui.green.header{color:#21ba45}a.ui.green.header:hover{color:#16ab39}.ui.green.dividing.header{border-bottom:2px solid #21ba45}.ui.inverted.green.header.header.header{color:#2ecc40}a.ui.inverted.green.header.header.header:hover{color:#1ea92e}.ui.inverted.green.dividing.header{border-bottom:2px solid #2ecc40}.ui.teal.header{color:#00b5ad}a.ui.teal.header:hover{color:#009c95}.ui.teal.dividing.header{border-bottom:2px solid #00b5ad}.ui.inverted.teal.header.header.header{color:#6dffff}a.ui.inverted.teal.header.header.header:hover{color:#3affff}.ui.inverted.teal.dividing.header{border-bottom:2px solid #6dffff}.ui.blue.header{color:#2185d0}a.ui.blue.header:hover{color:#1678c2}.ui.blue.dividing.header{border-bottom:2px solid #2185d0}.ui.inverted.blue.header.header.header{color:#54c8ff}a.ui.inverted.blue.header.header.header:hover{color:#21b8ff}.ui.inverted.blue.dividing.header{border-bottom:2px solid #54c8ff}.ui.violet.header{color:#6435c9}a.ui.violet.header:hover{color:#5829bb}.ui.violet.dividing.header{border-bottom:2px solid #6435c9}.ui.inverted.violet.header.header.header{color:#a291fb}a.ui.inverted.violet.header.header.header:hover{color:#745aff}.ui.inverted.violet.dividing.header{border-bottom:2px solid #a291fb}.ui.purple.header{color:#a333c8}a.ui.purple.header:hover{color:#9627ba}.ui.purple.dividing.header{border-bottom:2px solid #a333c8}.ui.inverted.purple.header.header.header{color:#dc73ff}a.ui.inverted.purple.header.header.header:hover{color:#cf40ff}.ui.inverted.purple.dividing.header{border-bottom:2px solid #dc73ff}.ui.pink.header{color:#e03997}a.ui.pink.header:hover{color:#e61a8d}.ui.pink.dividing.header{border-bottom:2px solid #e03997}.ui.inverted.pink.header.header.header{color:#ff8edf}a.ui.inverted.pink.header.header.header:hover{color:#ff5bd1}.ui.inverted.pink.dividing.header{border-bottom:2px solid #ff8edf}.ui.brown.header{color:#a5673f}a.ui.brown.header:hover{color:#975b33}.ui.brown.dividing.header{border-bottom:2px solid #a5673f}.ui.inverted.brown.header.header.header{color:#d67c1c}a.ui.inverted.brown.header.header.header:hover{color:#b0620f}.ui.inverted.brown.dividing.header{border-bottom:2px solid #d67c1c}.ui.grey.header{color:#767676}a.ui.grey.header:hover{color:#838383}.ui.grey.dividing.header{border-bottom:2px solid #767676}.ui.inverted.grey.header.header.header{color:#dcddde}a.ui.inverted.grey.header.header.header:hover{color:#c2c4c5}.ui.inverted.grey.dividing.header{border-bottom:2px solid #dcddde}.ui.black.header{color:#1b1c1d}a.ui.black.header:hover{color:#27292a}.ui.black.dividing.header{border-bottom:2px solid #1b1c1d}.ui.inverted.black.header.header.header{color:#545454}a.ui.inverted.black.header.header.header:hover{color:#000}.ui.inverted.black.dividing.header{border-bottom:2px solid #545454}.ui.left.aligned.header{text-align:left}.ui.right.aligned.header{text-align:right}.ui.center.aligned.header,.ui.centered.header{text-align:center}.ui.justified.header{text-align:justify}.ui.justified.header:after{display:inline-block;content:'';width:100%}.ui.floated.header,.ui[class*="left floated"].header{float:left;margin-top:0;margin-right:.5em}.ui[class*="right floated"].header{float:right;margin-top:0;margin-left:.5em}.ui.fitted.header{padding:0}.ui.dividing.header{padding-bottom:.21428571rem;border-bottom:1px solid rgba(34,36,38,.15)}.ui.dividing.header .sub.header{padding-bottom:.21428571rem}.ui.dividing.header .icon{margin-bottom:0}.ui.inverted.dividing.header{border-bottom-color:rgba(255,255,255,.1)}.ui.block.header{background:#f3f4f5;padding:.78571429rem 1rem;-webkit-box-shadow:none;box-shadow:none;border:1px solid #d4d4d5;border-radius:.28571429rem}.ui.block.header:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6){font-size:1rem}.ui.mini.block.header{font-size:.78571429rem}.ui.tiny.block.header{font-size:.85714286rem}.ui.small.block.header{font-size:.92857143rem}.ui.large.block.header{font-size:1.14285714rem}.ui.big.block.header{font-size:1.28571429rem}.ui.huge.block.header{font-size:1.42857143rem}.ui.massive.block.header{font-size:1.71428571rem}.ui.attached.header{background:#fff;padding:.78571429rem 1rem;margin:0 -1px 0 -1px;-webkit-box-shadow:none;box-shadow:none;border:1px solid #d4d4d5;border-radius:0}.ui.attached.block.header{background:#f3f4f5}.ui.attached:not(.top).header{border-top:none}.ui.top.attached.header{border-radius:.28571429rem .28571429rem 0 0}.ui.bottom.attached.header{border-radius:0 0 .28571429rem .28571429rem}.ui.attached.header:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6){font-size:1em}.ui.mini.attached.header{font-size:.78571429em}.ui.tiny.attached.header{font-size:.85714286em}.ui.small.attached.header{font-size:.92857143em}.ui.large.attached.header{font-size:1.14285714em}.ui.big.attached.header{font-size:1.28571429em}.ui.huge.attached.header{font-size:1.42857143em}.ui.massive.attached.header{font-size:1.71428571em}.ui.header:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6){font-size:1.28571429em}/*! + * # Fomantic-UI - Icon + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */@font-face{font-family:Icons;src:url(themes/default/assets/fonts/icons.eot);src:url(themes/default/assets/fonts/icons.eot?#iefix) format('embedded-opentype'),url(themes/default/assets/fonts/icons.woff2) format('woff2'),url(themes/default/assets/fonts/icons.woff) format('woff'),url(themes/default/assets/fonts/icons.ttf) format('truetype'),url(themes/default/assets/fonts/icons.svg#icons) format('svg');font-style:normal;font-weight:400;font-variant:normal;text-decoration:inherit;text-transform:none}i.icon{display:inline-block;opacity:1;margin:0 .25rem 0 0;width:1.18em;height:1em;font-family:Icons;font-style:normal;font-weight:400;text-decoration:inherit;text-align:center;speak:none;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;-webkit-backface-visibility:hidden;backface-visibility:hidden}i.icon:before{background:0 0!important}i.icon.loading{height:1em;line-height:1;-webkit-animation:loader 2s linear infinite;animation:loader 2s linear infinite}i.emphasized.icon:not(.disabled),i.emphasized.icons:not(.disabled),i.icon:active,i.icon:hover,i.icons:active,i.icons:hover{opacity:1}i.disabled.icon,i.disabled.icons{opacity:.45;cursor:default;pointer-events:none}i.fitted.icon{width:auto;margin:0!important}i.link.icon:not(.disabled),i.link.icons:not(.disabled){cursor:pointer;opacity:.8;-webkit-transition:opacity .1s ease;transition:opacity .1s ease}i.link.icon:hover,i.link.icons:hover{opacity:1}i.circular.icon{border-radius:500em!important;line-height:1!important;padding:.5em 0!important;-webkit-box-shadow:0 0 0 .1em rgba(0,0,0,.1) inset;box-shadow:0 0 0 .1em rgba(0,0,0,.1) inset;width:2em!important;height:2em!important}i.circular.inverted.icon{border:none;-webkit-box-shadow:none;box-shadow:none}i.flipped.icon,i.horizontally.flipped.icon{-webkit-transform:scale(-1,1);transform:scale(-1,1)}i.vertically.flipped.icon{-webkit-transform:scale(1,-1);transform:scale(1,-1)}i.clockwise.rotated.icon,i.right.rotated.icon,i.rotated.icon{-webkit-transform:rotate(90deg);transform:rotate(90deg)}i.counterclockwise.rotated.icon,i.left.rotated.icon{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}i.halfway.rotated.icon{-webkit-transform:rotate(180deg);transform:rotate(180deg)}i.clockwise.rotated.flipped.icon,i.right.rotated.flipped.icon,i.rotated.flipped.icon{-webkit-transform:scale(-1,1) rotate(90deg);transform:scale(-1,1) rotate(90deg)}i.counterclockwise.rotated.flipped.icon,i.left.rotated.flipped.icon{-webkit-transform:scale(-1,1) rotate(-90deg);transform:scale(-1,1) rotate(-90deg)}i.halfway.rotated.flipped.icon{-webkit-transform:scale(-1,1) rotate(180deg);transform:scale(-1,1) rotate(180deg)}i.clockwise.rotated.vertically.flipped.icon,i.right.rotated.vertically.flipped.icon,i.rotated.vertically.flipped.icon{-webkit-transform:scale(1,-1) rotate(90deg);transform:scale(1,-1) rotate(90deg)}i.counterclockwise.rotated.vertically.flipped.icon,i.left.rotated.vertically.flipped.icon{-webkit-transform:scale(1,-1) rotate(-90deg);transform:scale(1,-1) rotate(-90deg)}i.halfway.rotated.vertically.flipped.icon{-webkit-transform:scale(1,-1) rotate(180deg);transform:scale(1,-1) rotate(180deg)}i.bordered.icon{line-height:1;vertical-align:baseline;width:2em;height:2em;padding:.5em 0!important;-webkit-box-shadow:0 0 0 .1em rgba(0,0,0,.1) inset;box-shadow:0 0 0 .1em rgba(0,0,0,.1) inset}i.bordered.inverted.icon{border:none;-webkit-box-shadow:none;box-shadow:none}i.inverted.bordered.icon,i.inverted.circular.icon{background-color:#1b1c1d;color:#fff}i.inverted.icon{color:#fff}i.primary.icon.icon.icon.icon{color:#2185d0}i.inverted.primary.icon.icon.icon.icon{color:#54c8ff}i.inverted.bordered.primary.icon.icon.icon.icon,i.inverted.circular.primary.icon.icon.icon.icon{background-color:#2185d0;color:#fff}i.secondary.icon.icon.icon.icon{color:#1b1c1d}i.inverted.secondary.icon.icon.icon.icon{color:#545454}i.inverted.bordered.secondary.icon.icon.icon.icon,i.inverted.circular.secondary.icon.icon.icon.icon{background-color:#1b1c1d;color:#fff}i.red.icon.icon.icon.icon{color:#db2828}i.inverted.red.icon.icon.icon.icon{color:#ff695e}i.inverted.bordered.red.icon.icon.icon.icon,i.inverted.circular.red.icon.icon.icon.icon{background-color:#db2828;color:#fff}i.orange.icon.icon.icon.icon{color:#f2711c}i.inverted.orange.icon.icon.icon.icon{color:#ff851b}i.inverted.bordered.orange.icon.icon.icon.icon,i.inverted.circular.orange.icon.icon.icon.icon{background-color:#f2711c;color:#fff}i.yellow.icon.icon.icon.icon{color:#fbbd08}i.inverted.yellow.icon.icon.icon.icon{color:#ffe21f}i.inverted.bordered.yellow.icon.icon.icon.icon,i.inverted.circular.yellow.icon.icon.icon.icon{background-color:#fbbd08;color:#fff}i.olive.icon.icon.icon.icon{color:#b5cc18}i.inverted.olive.icon.icon.icon.icon{color:#d9e778}i.inverted.bordered.olive.icon.icon.icon.icon,i.inverted.circular.olive.icon.icon.icon.icon{background-color:#b5cc18;color:#fff}i.green.icon.icon.icon.icon{color:#21ba45}i.inverted.green.icon.icon.icon.icon{color:#2ecc40}i.inverted.bordered.green.icon.icon.icon.icon,i.inverted.circular.green.icon.icon.icon.icon{background-color:#21ba45;color:#fff}i.teal.icon.icon.icon.icon{color:#00b5ad}i.inverted.teal.icon.icon.icon.icon{color:#6dffff}i.inverted.bordered.teal.icon.icon.icon.icon,i.inverted.circular.teal.icon.icon.icon.icon{background-color:#00b5ad;color:#fff}i.blue.icon.icon.icon.icon{color:#2185d0}i.inverted.blue.icon.icon.icon.icon{color:#54c8ff}i.inverted.bordered.blue.icon.icon.icon.icon,i.inverted.circular.blue.icon.icon.icon.icon{background-color:#2185d0;color:#fff}i.violet.icon.icon.icon.icon{color:#6435c9}i.inverted.violet.icon.icon.icon.icon{color:#a291fb}i.inverted.bordered.violet.icon.icon.icon.icon,i.inverted.circular.violet.icon.icon.icon.icon{background-color:#6435c9;color:#fff}i.purple.icon.icon.icon.icon{color:#a333c8}i.inverted.purple.icon.icon.icon.icon{color:#dc73ff}i.inverted.bordered.purple.icon.icon.icon.icon,i.inverted.circular.purple.icon.icon.icon.icon{background-color:#a333c8;color:#fff}i.pink.icon.icon.icon.icon{color:#e03997}i.inverted.pink.icon.icon.icon.icon{color:#ff8edf}i.inverted.bordered.pink.icon.icon.icon.icon,i.inverted.circular.pink.icon.icon.icon.icon{background-color:#e03997;color:#fff}i.brown.icon.icon.icon.icon{color:#a5673f}i.inverted.brown.icon.icon.icon.icon{color:#d67c1c}i.inverted.bordered.brown.icon.icon.icon.icon,i.inverted.circular.brown.icon.icon.icon.icon{background-color:#a5673f;color:#fff}i.grey.icon.icon.icon.icon{color:#767676}i.inverted.grey.icon.icon.icon.icon{color:#dcddde}i.inverted.bordered.grey.icon.icon.icon.icon,i.inverted.circular.grey.icon.icon.icon.icon{background-color:#767676;color:#fff}i.black.icon.icon.icon.icon{color:#1b1c1d}i.inverted.black.icon.icon.icon.icon{color:#545454}i.inverted.bordered.black.icon.icon.icon.icon,i.inverted.circular.black.icon.icon.icon.icon{background-color:#1b1c1d;color:#fff}i.icon,i.icons{font-size:1em}i.mini.mini.mini.icon,i.mini.mini.mini.icons{line-height:1;vertical-align:middle;font-size:.4em}i.tiny.tiny.tiny.icon,i.tiny.tiny.tiny.icons{line-height:1;vertical-align:middle;font-size:.5em}i.small.small.small.icon,i.small.small.small.icons{line-height:1;vertical-align:middle;font-size:.75em}i.large.large.large.icon,i.large.large.large.icons{line-height:1;vertical-align:middle;font-size:1.5em}i.big.big.big.icon,i.big.big.big.icons{line-height:1;vertical-align:middle;font-size:2em}i.huge.huge.huge.icon,i.huge.huge.huge.icons{line-height:1;vertical-align:middle;font-size:4em}i.massive.massive.massive.icon,i.massive.massive.massive.icons{line-height:1;vertical-align:middle;font-size:8em}i.icons{display:inline-block;position:relative;line-height:1}i.icons .icon{position:absolute;top:50%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);margin:0}i.icons .icon:first-child{position:static;width:auto;height:auto;vertical-align:top;-webkit-transform:none;transform:none}i.icons .corner.icon{top:auto;left:auto;right:0;bottom:0;-webkit-transform:none;transform:none;font-size:.45em;text-shadow:-1px -1px 0 #fff,1px -1px 0 #fff,-1px 1px 0 #fff,1px 1px 0 #fff}i.icons .icon.corner[class*="top right"]{top:0;left:auto;right:0;bottom:auto}i.icons .icon.corner[class*="top left"]{top:0;left:0;right:auto;bottom:auto}i.icons .icon.corner[class*="bottom left"]{top:auto;left:0;right:auto;bottom:0}i.icons .icon.corner[class*="bottom right"]{top:auto;left:auto;right:0;bottom:0}i.icons .inverted.corner.icon{text-shadow:-1px -1px 0 #1b1c1d,1px -1px 0 #1b1c1d,-1px 1px 0 #1b1c1d,1px 1px 0 #1b1c1d}i.icon.linkedin.in:before{content:"\f0e1"}i.icon.zoom.in:before{content:"\f00e"}i.icon.zoom.out:before{content:"\f010"}i.icon.sign.in:before{content:"\f2f6"}i.icon.in.cart:before{content:"\f218"}i.icon.log.out:before{content:"\f2f5"}i.icon.sign.out:before{content:"\f2f5"}i.icon.ad:before{content:"\f641"}i.icon.address.book:before{content:"\f2b9"}i.icon.address.card:before{content:"\f2bb"}i.icon.adjust:before{content:"\f042"}i.icon.air.freshener:before{content:"\f5d0"}i.icon.align.center:before{content:"\f037"}i.icon.align.justify:before{content:"\f039"}i.icon.align.left:before{content:"\f036"}i.icon.align.right:before{content:"\f038"}i.icon.allergies:before{content:"\f461"}i.icon.ambulance:before{content:"\f0f9"}i.icon.american.sign.language.interpreting:before{content:"\f2a3"}i.icon.anchor:before{content:"\f13d"}i.icon.angle.double.down:before{content:"\f103"}i.icon.angle.double.left:before{content:"\f100"}i.icon.angle.double.right:before{content:"\f101"}i.icon.angle.double.up:before{content:"\f102"}i.icon.angle.down:before{content:"\f107"}i.icon.angle.left:before{content:"\f104"}i.icon.angle.right:before{content:"\f105"}i.icon.angle.up:before{content:"\f106"}i.icon.angry:before{content:"\f556"}i.icon.ankh:before{content:"\f644"}i.icon.archive:before{content:"\f187"}i.icon.archway:before{content:"\f557"}i.icon.arrow.alternate.circle.down:before{content:"\f358"}i.icon.arrow.alternate.circle.left:before{content:"\f359"}i.icon.arrow.alternate.circle.right:before{content:"\f35a"}i.icon.arrow.alternate.circle.up:before{content:"\f35b"}i.icon.arrow.circle.down:before{content:"\f0ab"}i.icon.arrow.circle.left:before{content:"\f0a8"}i.icon.arrow.circle.right:before{content:"\f0a9"}i.icon.arrow.circle.up:before{content:"\f0aa"}i.icon.arrow.left:before{content:"\f060"}i.icon.arrow.right:before{content:"\f061"}i.icon.arrow.up:before{content:"\f062"}i.icon.arrow.down:before{content:"\f063"}i.icon.arrows.alternate:before{content:"\f0b2"}i.icon.arrows.alternate.horizontal:before{content:"\f337"}i.icon.arrows.alternate.vertical:before{content:"\f338"}i.icon.assistive.listening.systems:before{content:"\f2a2"}i.icon.asterisk:before{content:"\f069"}i.icon.at:before{content:"\f1fa"}i.icon.atlas:before{content:"\f558"}i.icon.atom:before{content:"\f5d2"}i.icon.audio.description:before{content:"\f29e"}i.icon.award:before{content:"\f559"}i.icon.baby:before{content:"\f77c"}i.icon.baby.carriage:before{content:"\f77d"}i.icon.backspace:before{content:"\f55a"}i.icon.backward:before{content:"\f04a"}i.icon.bacon:before{content:"\f7e5"}i.icon.bahai:before{content:"\f666"}i.icon.balance.scale:before{content:"\f24e"}i.icon.balance.scale.left:before{content:"\f515"}i.icon.balance.scale.right:before{content:"\f516"}i.icon.ban:before{content:"\f05e"}i.icon.band.aid:before{content:"\f462"}i.icon.barcode:before{content:"\f02a"}i.icon.bars:before{content:"\f0c9"}i.icon.baseball.ball:before{content:"\f433"}i.icon.basketball.ball:before{content:"\f434"}i.icon.bath:before{content:"\f2cd"}i.icon.battery.empty:before{content:"\f244"}i.icon.battery.full:before{content:"\f240"}i.icon.battery.half:before{content:"\f242"}i.icon.battery.quarter:before{content:"\f243"}i.icon.battery.three.quarters:before{content:"\f241"}i.icon.bed:before{content:"\f236"}i.icon.beer:before{content:"\f0fc"}i.icon.bell:before{content:"\f0f3"}i.icon.bell.slash:before{content:"\f1f6"}i.icon.bezier.curve:before{content:"\f55b"}i.icon.bible:before{content:"\f647"}i.icon.bicycle:before{content:"\f206"}i.icon.biking:before{content:"\f84a"}i.icon.binoculars:before{content:"\f1e5"}i.icon.biohazard:before{content:"\f780"}i.icon.birthday.cake:before{content:"\f1fd"}i.icon.blender:before{content:"\f517"}i.icon.blender.phone:before{content:"\f6b6"}i.icon.blind:before{content:"\f29d"}i.icon.blog:before{content:"\f781"}i.icon.bold:before{content:"\f032"}i.icon.bolt:before{content:"\f0e7"}i.icon.bomb:before{content:"\f1e2"}i.icon.bone:before{content:"\f5d7"}i.icon.bong:before{content:"\f55c"}i.icon.book:before{content:"\f02d"}i.icon.book.dead:before{content:"\f6b7"}i.icon.book.medical:before{content:"\f7e6"}i.icon.book.open:before{content:"\f518"}i.icon.book.reader:before{content:"\f5da"}i.icon.bookmark:before{content:"\f02e"}i.icon.border.all:before{content:"\f84c"}i.icon.border.none:before{content:"\f850"}i.icon.border.style:before{content:"\f853"}i.icon.bowling.ball:before{content:"\f436"}i.icon.box:before{content:"\f466"}i.icon.box.open:before{content:"\f49e"}i.icon.boxes:before{content:"\f468"}i.icon.braille:before{content:"\f2a1"}i.icon.brain:before{content:"\f5dc"}i.icon.bread.slice:before{content:"\f7ec"}i.icon.briefcase:before{content:"\f0b1"}i.icon.briefcase.medical:before{content:"\f469"}i.icon.broadcast.tower:before{content:"\f519"}i.icon.broom:before{content:"\f51a"}i.icon.brush:before{content:"\f55d"}i.icon.bug:before{content:"\f188"}i.icon.building:before{content:"\f1ad"}i.icon.bullhorn:before{content:"\f0a1"}i.icon.bullseye:before{content:"\f140"}i.icon.burn:before{content:"\f46a"}i.icon.bus:before{content:"\f207"}i.icon.bus.alternate:before{content:"\f55e"}i.icon.business.time:before{content:"\f64a"}i.icon.calculator:before{content:"\f1ec"}i.icon.calendar:before{content:"\f133"}i.icon.calendar.alternate:before{content:"\f073"}i.icon.calendar.check:before{content:"\f274"}i.icon.calendar.day:before{content:"\f783"}i.icon.calendar.minus:before{content:"\f272"}i.icon.calendar.plus:before{content:"\f271"}i.icon.calendar.times:before{content:"\f273"}i.icon.calendar.week:before{content:"\f784"}i.icon.camera:before{content:"\f030"}i.icon.camera.retro:before{content:"\f083"}i.icon.campground:before{content:"\f6bb"}i.icon.candy.cane:before{content:"\f786"}i.icon.cannabis:before{content:"\f55f"}i.icon.capsules:before{content:"\f46b"}i.icon.car:before{content:"\f1b9"}i.icon.car.alternate:before{content:"\f5de"}i.icon.car.battery:before{content:"\f5df"}i.icon.car.crash:before{content:"\f5e1"}i.icon.car.side:before{content:"\f5e4"}i.icon.caravan:before{content:"\f8ff"}i.icon.caret.down:before{content:"\f0d7"}i.icon.caret.left:before{content:"\f0d9"}i.icon.caret.right:before{content:"\f0da"}i.icon.caret.square.down:before{content:"\f150"}i.icon.caret.square.left:before{content:"\f191"}i.icon.caret.square.right:before{content:"\f152"}i.icon.caret.square.up:before{content:"\f151"}i.icon.caret.up:before{content:"\f0d8"}i.icon.carrot:before{content:"\f787"}i.icon.cart.arrow.down:before{content:"\f218"}i.icon.cart.plus:before{content:"\f217"}i.icon.cash.register:before{content:"\f788"}i.icon.cat:before{content:"\f6be"}i.icon.certificate:before{content:"\f0a3"}i.icon.chair:before{content:"\f6c0"}i.icon.chalkboard:before{content:"\f51b"}i.icon.chalkboard.teacher:before{content:"\f51c"}i.icon.charging.station:before{content:"\f5e7"}i.icon.chart.area:before{content:"\f1fe"}i.icon.chart.bar:before{content:"\f080"}i.icon.chart.line:before{content:"\f201"}i.icon.chart.pie:before{content:"\f200"}i.icon.check:before{content:"\f00c"}i.icon.check.circle:before{content:"\f058"}i.icon.check.double:before{content:"\f560"}i.icon.check.square:before{content:"\f14a"}i.icon.cheese:before{content:"\f7ef"}i.icon.chess:before{content:"\f439"}i.icon.chess.bishop:before{content:"\f43a"}i.icon.chess.board:before{content:"\f43c"}i.icon.chess.king:before{content:"\f43f"}i.icon.chess.knight:before{content:"\f441"}i.icon.chess.pawn:before{content:"\f443"}i.icon.chess.queen:before{content:"\f445"}i.icon.chess.rook:before{content:"\f447"}i.icon.chevron.circle.down:before{content:"\f13a"}i.icon.chevron.circle.left:before{content:"\f137"}i.icon.chevron.circle.right:before{content:"\f138"}i.icon.chevron.circle.up:before{content:"\f139"}i.icon.chevron.down:before{content:"\f078"}i.icon.chevron.left:before{content:"\f053"}i.icon.chevron.right:before{content:"\f054"}i.icon.chevron.up:before{content:"\f077"}i.icon.child:before{content:"\f1ae"}i.icon.church:before{content:"\f51d"}i.icon.circle:before{content:"\f111"}i.icon.circle.notch:before{content:"\f1ce"}i.icon.city:before{content:"\f64f"}i.icon.clinic.medical:before{content:"\f7f2"}i.icon.clipboard:before{content:"\f328"}i.icon.clipboard.check:before{content:"\f46c"}i.icon.clipboard.list:before{content:"\f46d"}i.icon.clock:before{content:"\f017"}i.icon.clone:before{content:"\f24d"}i.icon.closed.captioning:before{content:"\f20a"}i.icon.cloud:before{content:"\f0c2"}i.icon.cloud.download.alternate:before{content:"\f381"}i.icon.cloud.meatball:before{content:"\f73b"}i.icon.cloud.moon:before{content:"\f6c3"}i.icon.cloud.moon.rain:before{content:"\f73c"}i.icon.cloud.rain:before{content:"\f73d"}i.icon.cloud.showers.heavy:before{content:"\f740"}i.icon.cloud.sun:before{content:"\f6c4"}i.icon.cloud.sun.rain:before{content:"\f743"}i.icon.cloud.upload.alternate:before{content:"\f382"}i.icon.cocktail:before{content:"\f561"}i.icon.code:before{content:"\f121"}i.icon.code.branch:before{content:"\f126"}i.icon.coffee:before{content:"\f0f4"}i.icon.cog:before{content:"\f013"}i.icon.cogs:before{content:"\f085"}i.icon.coins:before{content:"\f51e"}i.icon.columns:before{content:"\f0db"}i.icon.comment:before{content:"\f075"}i.icon.comment.alternate:before{content:"\f27a"}i.icon.comment.dollar:before{content:"\f651"}i.icon.comment.dots:before{content:"\f4ad"}i.icon.comment.medical:before{content:"\f7f5"}i.icon.comment.slash:before{content:"\f4b3"}i.icon.comments:before{content:"\f086"}i.icon.comments.dollar:before{content:"\f653"}i.icon.compact.disc:before{content:"\f51f"}i.icon.compass:before{content:"\f14e"}i.icon.compress:before{content:"\f066"}i.icon.compress.alternate:before{content:"\f422"}i.icon.compress.arrows.alternate:before{content:"\f78c"}i.icon.concierge.bell:before{content:"\f562"}i.icon.cookie:before{content:"\f563"}i.icon.cookie.bite:before{content:"\f564"}i.icon.copy:before{content:"\f0c5"}i.icon.copyright:before{content:"\f1f9"}i.icon.couch:before{content:"\f4b8"}i.icon.credit.card:before{content:"\f09d"}i.icon.crop:before{content:"\f125"}i.icon.crop.alternate:before{content:"\f565"}i.icon.cross:before{content:"\f654"}i.icon.crosshairs:before{content:"\f05b"}i.icon.crow:before{content:"\f520"}i.icon.crown:before{content:"\f521"}i.icon.crutch:before{content:"\f7f7"}i.icon.cube:before{content:"\f1b2"}i.icon.cubes:before{content:"\f1b3"}i.icon.cut:before{content:"\f0c4"}i.icon.database:before{content:"\f1c0"}i.icon.deaf:before{content:"\f2a4"}i.icon.democrat:before{content:"\f747"}i.icon.desktop:before{content:"\f108"}i.icon.dharmachakra:before{content:"\f655"}i.icon.diagnoses:before{content:"\f470"}i.icon.dice:before{content:"\f522"}i.icon.dice.d20:before{content:"\f6cf"}i.icon.dice.d6:before{content:"\f6d1"}i.icon.dice.five:before{content:"\f523"}i.icon.dice.four:before{content:"\f524"}i.icon.dice.one:before{content:"\f525"}i.icon.dice.six:before{content:"\f526"}i.icon.dice.three:before{content:"\f527"}i.icon.dice.two:before{content:"\f528"}i.icon.digital.tachograph:before{content:"\f566"}i.icon.directions:before{content:"\f5eb"}i.icon.divide:before{content:"\f529"}i.icon.dizzy:before{content:"\f567"}i.icon.dna:before{content:"\f471"}i.icon.dog:before{content:"\f6d3"}i.icon.dollar.sign:before{content:"\f155"}i.icon.dolly:before{content:"\f472"}i.icon.dolly.flatbed:before{content:"\f474"}i.icon.donate:before{content:"\f4b9"}i.icon.door.closed:before{content:"\f52a"}i.icon.door.open:before{content:"\f52b"}i.icon.dot.circle:before{content:"\f192"}i.icon.dove:before{content:"\f4ba"}i.icon.download:before{content:"\f019"}i.icon.drafting.compass:before{content:"\f568"}i.icon.dragon:before{content:"\f6d5"}i.icon.draw.polygon:before{content:"\f5ee"}i.icon.drum:before{content:"\f569"}i.icon.drum.steelpan:before{content:"\f56a"}i.icon.drumstick.bite:before{content:"\f6d7"}i.icon.dumbbell:before{content:"\f44b"}i.icon.dumpster:before{content:"\f793"}i.icon.dumpster.fire:before{content:"\f794"}i.icon.dungeon:before{content:"\f6d9"}i.icon.edit:before{content:"\f044"}i.icon.egg:before{content:"\f7fb"}i.icon.eject:before{content:"\f052"}i.icon.ellipsis.horizontal:before{content:"\f141"}i.icon.ellipsis.vertical:before{content:"\f142"}i.icon.envelope:before{content:"\f0e0"}i.icon.envelope.open:before{content:"\f2b6"}i.icon.envelope.open.text:before{content:"\f658"}i.icon.envelope.square:before{content:"\f199"}i.icon.equals:before{content:"\f52c"}i.icon.eraser:before{content:"\f12d"}i.icon.ethernet:before{content:"\f796"}i.icon.euro.sign:before{content:"\f153"}i.icon.exchange.alternate:before{content:"\f362"}i.icon.exclamation:before{content:"\f12a"}i.icon.exclamation.circle:before{content:"\f06a"}i.icon.exclamation.triangle:before{content:"\f071"}i.icon.expand:before{content:"\f065"}i.icon.expand.alternate:before{content:"\f424"}i.icon.expand.arrows.alternate:before{content:"\f31e"}i.icon.external.alternate:before{content:"\f35d"}i.icon.external.link.square.alternate:before{content:"\f360"}i.icon.eye:before{content:"\f06e"}i.icon.eye.dropper:before{content:"\f1fb"}i.icon.eye.slash:before{content:"\f070"}i.icon.fan:before{content:"\f863"}i.icon.fast.backward:before{content:"\f049"}i.icon.fast.forward:before{content:"\f050"}i.icon.fax:before{content:"\f1ac"}i.icon.feather:before{content:"\f52d"}i.icon.feather.alternate:before{content:"\f56b"}i.icon.female:before{content:"\f182"}i.icon.fighter.jet:before{content:"\f0fb"}i.icon.file:before{content:"\f15b"}i.icon.file.alternate:before{content:"\f15c"}i.icon.file.archive:before{content:"\f1c6"}i.icon.file.audio:before{content:"\f1c7"}i.icon.file.code:before{content:"\f1c9"}i.icon.file.contract:before{content:"\f56c"}i.icon.file.csv:before{content:"\f6dd"}i.icon.file.download:before{content:"\f56d"}i.icon.file.excel:before{content:"\f1c3"}i.icon.file.export:before{content:"\f56e"}i.icon.file.image:before{content:"\f1c5"}i.icon.file.import:before{content:"\f56f"}i.icon.file.invoice:before{content:"\f570"}i.icon.file.invoice.dollar:before{content:"\f571"}i.icon.file.medical:before{content:"\f477"}i.icon.file.medical.alternate:before{content:"\f478"}i.icon.file.pdf:before{content:"\f1c1"}i.icon.file.powerpoint:before{content:"\f1c4"}i.icon.file.prescription:before{content:"\f572"}i.icon.file.signature:before{content:"\f573"}i.icon.file.upload:before{content:"\f574"}i.icon.file.video:before{content:"\f1c8"}i.icon.file.word:before{content:"\f1c2"}i.icon.fill:before{content:"\f575"}i.icon.fill.drip:before{content:"\f576"}i.icon.film:before{content:"\f008"}i.icon.filter:before{content:"\f0b0"}i.icon.fingerprint:before{content:"\f577"}i.icon.fire:before{content:"\f06d"}i.icon.fire.alternate:before{content:"\f7e4"}i.icon.fire.extinguisher:before{content:"\f134"}i.icon.first.aid:before{content:"\f479"}i.icon.fish:before{content:"\f578"}i.icon.fist.raised:before{content:"\f6de"}i.icon.flag:before{content:"\f024"}i.icon.flag.checkered:before{content:"\f11e"}i.icon.flag.usa:before{content:"\f74d"}i.icon.flask:before{content:"\f0c3"}i.icon.flushed:before{content:"\f579"}i.icon.folder:before{content:"\f07b"}i.icon.folder.minus:before{content:"\f65d"}i.icon.folder.open:before{content:"\f07c"}i.icon.folder.plus:before{content:"\f65e"}i.icon.font:before{content:"\f031"}i.icon.football.ball:before{content:"\f44e"}i.icon.forward:before{content:"\f04e"}i.icon.frog:before{content:"\f52e"}i.icon.frown:before{content:"\f119"}i.icon.frown.open:before{content:"\f57a"}i.icon.fruit-apple:before{content:"\f5d1"}i.icon.funnel.dollar:before{content:"\f662"}i.icon.futbol:before{content:"\f1e3"}i.icon.gamepad:before{content:"\f11b"}i.icon.gas.pump:before{content:"\f52f"}i.icon.gavel:before{content:"\f0e3"}i.icon.gem:before{content:"\f3a5"}i.icon.genderless:before{content:"\f22d"}i.icon.ghost:before{content:"\f6e2"}i.icon.gift:before{content:"\f06b"}i.icon.gifts:before{content:"\f79c"}i.icon.glass.cheers:before{content:"\f79f"}i.icon.glass.martini:before{content:"\f000"}i.icon.glass.martini.alternate:before{content:"\f57b"}i.icon.glass.whiskey:before{content:"\f7a0"}i.icon.glasses:before{content:"\f530"}i.icon.globe:before{content:"\f0ac"}i.icon.globe.africa:before{content:"\f57c"}i.icon.globe.americas:before{content:"\f57d"}i.icon.globe.asia:before{content:"\f57e"}i.icon.globe.europe:before{content:"\f7a2"}i.icon.golf.ball:before{content:"\f450"}i.icon.gopuram:before{content:"\f664"}i.icon.graduation.cap:before{content:"\f19d"}i.icon.greater.than:before{content:"\f531"}i.icon.greater.than.equal:before{content:"\f532"}i.icon.grimace:before{content:"\f57f"}i.icon.grin:before{content:"\f580"}i.icon.grin.alternate:before{content:"\f581"}i.icon.grin.beam:before{content:"\f582"}i.icon.grin.beam.sweat:before{content:"\f583"}i.icon.grin.hearts:before{content:"\f584"}i.icon.grin.squint:before{content:"\f585"}i.icon.grin.squint.tears:before{content:"\f586"}i.icon.grin.stars:before{content:"\f587"}i.icon.grin.tears:before{content:"\f588"}i.icon.grin.tongue:before{content:"\f589"}i.icon.grin.tongue.squint:before{content:"\f58a"}i.icon.grin.tongue.wink:before{content:"\f58b"}i.icon.grin.wink:before{content:"\f58c"}i.icon.grip.horizontal:before{content:"\f58d"}i.icon.grip.lines:before{content:"\f7a4"}i.icon.grip.lines.vertical:before{content:"\f7a5"}i.icon.grip.vertical:before{content:"\f58e"}i.icon.guitar:before{content:"\f7a6"}i.icon.h.square:before{content:"\f0fd"}i.icon.hamburger:before{content:"\f805"}i.icon.hammer:before{content:"\f6e3"}i.icon.hamsa:before{content:"\f665"}i.icon.hand.holding:before{content:"\f4bd"}i.icon.hand.holding.heart:before{content:"\f4be"}i.icon.hand.holding.usd:before{content:"\f4c0"}i.icon.hand.lizard:before{content:"\f258"}i.icon.hand.middle.finger:before{content:"\f806"}i.icon.hand.paper:before{content:"\f256"}i.icon.hand.peace:before{content:"\f25b"}i.icon.hand.point.down:before{content:"\f0a7"}i.icon.hand.point.left:before{content:"\f0a5"}i.icon.hand.point.right:before{content:"\f0a4"}i.icon.hand.point.up:before{content:"\f0a6"}i.icon.hand.pointer:before{content:"\f25a"}i.icon.hand.rock:before{content:"\f255"}i.icon.hand.scissors:before{content:"\f257"}i.icon.hand.spock:before{content:"\f259"}i.icon.hands:before{content:"\f4c2"}i.icon.hands.helping:before{content:"\f4c4"}i.icon.handshake:before{content:"\f2b5"}i.icon.hanukiah:before{content:"\f6e6"}i.icon.hard.hat:before{content:"\f807"}i.icon.hashtag:before{content:"\f292"}i.icon.hat.cowboy:before{content:"\f8c0"}i.icon.hat.cowboy.side:before{content:"\f8c1"}i.icon.hat.wizard:before{content:"\f6e8"}i.icon.hdd:before{content:"\f0a0"}i.icon.heading:before{content:"\f1dc"}i.icon.headphones:before{content:"\f025"}i.icon.headphones.alternate:before{content:"\f58f"}i.icon.headset:before{content:"\f590"}i.icon.heart:before{content:"\f004"}i.icon.heart.broken:before{content:"\f7a9"}i.icon.heartbeat:before{content:"\f21e"}i.icon.helicopter:before{content:"\f533"}i.icon.highlighter:before{content:"\f591"}i.icon.hiking:before{content:"\f6ec"}i.icon.hippo:before{content:"\f6ed"}i.icon.history:before{content:"\f1da"}i.icon.hockey.puck:before{content:"\f453"}i.icon.holly.berry:before{content:"\f7aa"}i.icon.home:before{content:"\f015"}i.icon.horse:before{content:"\f6f0"}i.icon.horse.head:before{content:"\f7ab"}i.icon.hospital:before{content:"\f0f8"}i.icon.hospital.alternate:before{content:"\f47d"}i.icon.hospital.symbol:before{content:"\f47e"}i.icon.hot.tub:before{content:"\f593"}i.icon.hotdog:before{content:"\f80f"}i.icon.hotel:before{content:"\f594"}i.icon.hourglass:before{content:"\f254"}i.icon.hourglass.end:before{content:"\f253"}i.icon.hourglass.half:before{content:"\f252"}i.icon.hourglass.start:before{content:"\f251"}i.icon.house.damage:before{content:"\f6f1"}i.icon.hryvnia:before{content:"\f6f2"}i.icon.i.cursor:before{content:"\f246"}i.icon.ice.cream:before{content:"\f810"}i.icon.icicles:before{content:"\f7ad"}i.icon.icons:before{content:"\f86d"}i.icon.id.badge:before{content:"\f2c1"}i.icon.id.card:before{content:"\f2c2"}i.icon.id.card.alternate:before{content:"\f47f"}i.icon.igloo:before{content:"\f7ae"}i.icon.image:before{content:"\f03e"}i.icon.images:before{content:"\f302"}i.icon.inbox:before{content:"\f01c"}i.icon.indent:before{content:"\f03c"}i.icon.industry:before{content:"\f275"}i.icon.infinity:before{content:"\f534"}i.icon.info:before{content:"\f129"}i.icon.info.circle:before{content:"\f05a"}i.icon.italic:before{content:"\f033"}i.icon.jedi:before{content:"\f669"}i.icon.joint:before{content:"\f595"}i.icon.journal.whills:before{content:"\f66a"}i.icon.kaaba:before{content:"\f66b"}i.icon.key:before{content:"\f084"}i.icon.keyboard:before{content:"\f11c"}i.icon.khanda:before{content:"\f66d"}i.icon.kiss:before{content:"\f596"}i.icon.kiss.beam:before{content:"\f597"}i.icon.kiss.wink.heart:before{content:"\f598"}i.icon.kiwi.bird:before{content:"\f535"}i.icon.landmark:before{content:"\f66f"}i.icon.language:before{content:"\f1ab"}i.icon.laptop:before{content:"\f109"}i.icon.laptop.code:before{content:"\f5fc"}i.icon.laptop.medical:before{content:"\f812"}i.icon.laugh:before{content:"\f599"}i.icon.laugh.beam:before{content:"\f59a"}i.icon.laugh.squint:before{content:"\f59b"}i.icon.laugh.wink:before{content:"\f59c"}i.icon.layer.group:before{content:"\f5fd"}i.icon.leaf:before{content:"\f06c"}i.icon.lemon:before{content:"\f094"}i.icon.less.than:before{content:"\f536"}i.icon.less.than.equal:before{content:"\f537"}i.icon.level.down.alternate:before{content:"\f3be"}i.icon.level.up.alternate:before{content:"\f3bf"}i.icon.life.ring:before{content:"\f1cd"}i.icon.lightbulb:before{content:"\f0eb"}i.icon.linkify:before{content:"\f0c1"}i.icon.lira.sign:before{content:"\f195"}i.icon.list:before{content:"\f03a"}i.icon.list.alternate:before{content:"\f022"}i.icon.list.ol:before{content:"\f0cb"}i.icon.list.ul:before{content:"\f0ca"}i.icon.location.arrow:before{content:"\f124"}i.icon.lock:before{content:"\f023"}i.icon.lock.open:before{content:"\f3c1"}i.icon.long.arrow.alternate.down:before{content:"\f309"}i.icon.long.arrow.alternate.left:before{content:"\f30a"}i.icon.long.arrow.alternate.right:before{content:"\f30b"}i.icon.long.arrow.alternate.up:before{content:"\f30c"}i.icon.low.vision:before{content:"\f2a8"}i.icon.luggage.cart:before{content:"\f59d"}i.icon.magic:before{content:"\f0d0"}i.icon.magnet:before{content:"\f076"}i.icon.mail.bulk:before{content:"\f674"}i.icon.male:before{content:"\f183"}i.icon.map:before{content:"\f279"}i.icon.map.marked:before{content:"\f59f"}i.icon.map.marked.alternate:before{content:"\f5a0"}i.icon.map.marker:before{content:"\f041"}i.icon.map.marker.alternate:before{content:"\f3c5"}i.icon.map.pin:before{content:"\f276"}i.icon.map.signs:before{content:"\f277"}i.icon.marker:before{content:"\f5a1"}i.icon.mars:before{content:"\f222"}i.icon.mars.double:before{content:"\f227"}i.icon.mars.stroke:before{content:"\f229"}i.icon.mars.stroke.horizontal:before{content:"\f22b"}i.icon.mars.stroke.vertical:before{content:"\f22a"}i.icon.mask:before{content:"\f6fa"}i.icon.medal:before{content:"\f5a2"}i.icon.medkit:before{content:"\f0fa"}i.icon.meh:before{content:"\f11a"}i.icon.meh.blank:before{content:"\f5a4"}i.icon.meh.rolling.eyes:before{content:"\f5a5"}i.icon.memory:before{content:"\f538"}i.icon.menorah:before{content:"\f676"}i.icon.mercury:before{content:"\f223"}i.icon.meteor:before{content:"\f753"}i.icon.microchip:before{content:"\f2db"}i.icon.microphone:before{content:"\f130"}i.icon.microphone.alternate:before{content:"\f3c9"}i.icon.microphone.alternate.slash:before{content:"\f539"}i.icon.microphone.slash:before{content:"\f131"}i.icon.microscope:before{content:"\f610"}i.icon.minus:before{content:"\f068"}i.icon.minus.circle:before{content:"\f056"}i.icon.minus.square:before{content:"\f146"}i.icon.mitten:before{content:"\f7b5"}i.icon.mobile:before{content:"\f10b"}i.icon.mobile.alternate:before{content:"\f3cd"}i.icon.money.bill:before{content:"\f0d6"}i.icon.money.bill.alternate:before{content:"\f3d1"}i.icon.money.bill.wave:before{content:"\f53a"}i.icon.money.bill.wave.alternate:before{content:"\f53b"}i.icon.money.check:before{content:"\f53c"}i.icon.money.check.alternate:before{content:"\f53d"}i.icon.monument:before{content:"\f5a6"}i.icon.moon:before{content:"\f186"}i.icon.mortar.pestle:before{content:"\f5a7"}i.icon.mosque:before{content:"\f678"}i.icon.motorcycle:before{content:"\f21c"}i.icon.mountain:before{content:"\f6fc"}i.icon.mouse:before{content:"\f8cc"}i.icon.mouse.pointer:before{content:"\f245"}i.icon.mug.hot:before{content:"\f7b6"}i.icon.music:before{content:"\f001"}i.icon.network.wired:before{content:"\f6ff"}i.icon.neuter:before{content:"\f22c"}i.icon.newspaper:before{content:"\f1ea"}i.icon.not.equal:before{content:"\f53e"}i.icon.notes.medical:before{content:"\f481"}i.icon.object.group:before{content:"\f247"}i.icon.object.ungroup:before{content:"\f248"}i.icon.oil.can:before{content:"\f613"}i.icon.om:before{content:"\f679"}i.icon.otter:before{content:"\f700"}i.icon.outdent:before{content:"\f03b"}i.icon.pager:before{content:"\f815"}i.icon.paint.brush:before{content:"\f1fc"}i.icon.paint.roller:before{content:"\f5aa"}i.icon.palette:before{content:"\f53f"}i.icon.pallet:before{content:"\f482"}i.icon.paper.plane:before{content:"\f1d8"}i.icon.paperclip:before{content:"\f0c6"}i.icon.parachute.box:before{content:"\f4cd"}i.icon.paragraph:before{content:"\f1dd"}i.icon.parking:before{content:"\f540"}i.icon.passport:before{content:"\f5ab"}i.icon.pastafarianism:before{content:"\f67b"}i.icon.paste:before{content:"\f0ea"}i.icon.pause:before{content:"\f04c"}i.icon.pause.circle:before{content:"\f28b"}i.icon.paw:before{content:"\f1b0"}i.icon.peace:before{content:"\f67c"}i.icon.pen:before{content:"\f304"}i.icon.pen.alternate:before{content:"\f305"}i.icon.pen.fancy:before{content:"\f5ac"}i.icon.pen.nib:before{content:"\f5ad"}i.icon.pen.square:before{content:"\f14b"}i.icon.pencil.alternate:before{content:"\f303"}i.icon.pencil.ruler:before{content:"\f5ae"}i.icon.people.carry:before{content:"\f4ce"}i.icon.pepper.hot:before{content:"\f816"}i.icon.percent:before{content:"\f295"}i.icon.percentage:before{content:"\f541"}i.icon.person.booth:before{content:"\f756"}i.icon.phone:before{content:"\f095"}i.icon.phone.alternate:before{content:"\f879"}i.icon.phone.slash:before{content:"\f3dd"}i.icon.phone.square:before{content:"\f098"}i.icon.phone.square.alternate:before{content:"\f87b"}i.icon.phone.volume:before{content:"\f2a0"}i.icon.photo.video:before{content:"\f87c"}i.icon.piggy.bank:before{content:"\f4d3"}i.icon.pills:before{content:"\f484"}i.icon.pizza.slice:before{content:"\f818"}i.icon.place.of.worship:before{content:"\f67f"}i.icon.plane:before{content:"\f072"}i.icon.plane.arrival:before{content:"\f5af"}i.icon.plane.departure:before{content:"\f5b0"}i.icon.play:before{content:"\f04b"}i.icon.play.circle:before{content:"\f144"}i.icon.plug:before{content:"\f1e6"}i.icon.plus:before{content:"\f067"}i.icon.plus.circle:before{content:"\f055"}i.icon.plus.square:before{content:"\f0fe"}i.icon.podcast:before{content:"\f2ce"}i.icon.poll:before{content:"\f681"}i.icon.poll.horizontal:before{content:"\f682"}i.icon.poo:before{content:"\f2fe"}i.icon.poo.storm:before{content:"\f75a"}i.icon.poop:before{content:"\f619"}i.icon.portrait:before{content:"\f3e0"}i.icon.pound.sign:before{content:"\f154"}i.icon.power.off:before{content:"\f011"}i.icon.pray:before{content:"\f683"}i.icon.praying.hands:before{content:"\f684"}i.icon.prescription:before{content:"\f5b1"}i.icon.prescription.bottle:before{content:"\f485"}i.icon.prescription.bottle.alternate:before{content:"\f486"}i.icon.print:before{content:"\f02f"}i.icon.procedures:before{content:"\f487"}i.icon.project.diagram:before{content:"\f542"}i.icon.puzzle.piece:before{content:"\f12e"}i.icon.qrcode:before{content:"\f029"}i.icon.question:before{content:"\f128"}i.icon.question.circle:before{content:"\f059"}i.icon.quidditch:before{content:"\f458"}i.icon.quote.left:before{content:"\f10d"}i.icon.quote.right:before{content:"\f10e"}i.icon.quran:before{content:"\f687"}i.icon.radiation:before{content:"\f7b9"}i.icon.radiation.alternate:before{content:"\f7ba"}i.icon.rainbow:before{content:"\f75b"}i.icon.random:before{content:"\f074"}i.icon.receipt:before{content:"\f543"}i.icon.record.vinyl:before{content:"\f8d9"}i.icon.recycle:before{content:"\f1b8"}i.icon.redo:before{content:"\f01e"}i.icon.redo.alternate:before{content:"\f2f9"}i.icon.registered:before{content:"\f25d"}i.icon.remove.format:before{content:"\f87d"}i.icon.reply:before{content:"\f3e5"}i.icon.reply.all:before{content:"\f122"}i.icon.republican:before{content:"\f75e"}i.icon.restroom:before{content:"\f7bd"}i.icon.retweet:before{content:"\f079"}i.icon.ribbon:before{content:"\f4d6"}i.icon.ring:before{content:"\f70b"}i.icon.road:before{content:"\f018"}i.icon.robot:before{content:"\f544"}i.icon.rocket:before{content:"\f135"}i.icon.route:before{content:"\f4d7"}i.icon.rss:before{content:"\f09e"}i.icon.rss.square:before{content:"\f143"}i.icon.ruble.sign:before{content:"\f158"}i.icon.ruler:before{content:"\f545"}i.icon.ruler.combined:before{content:"\f546"}i.icon.ruler.horizontal:before{content:"\f547"}i.icon.ruler.vertical:before{content:"\f548"}i.icon.running:before{content:"\f70c"}i.icon.rupee.sign:before{content:"\f156"}i.icon.sad.cry:before{content:"\f5b3"}i.icon.sad.tear:before{content:"\f5b4"}i.icon.satellite:before{content:"\f7bf"}i.icon.satellite.dish:before{content:"\f7c0"}i.icon.save:before{content:"\f0c7"}i.icon.school:before{content:"\f549"}i.icon.screwdriver:before{content:"\f54a"}i.icon.scroll:before{content:"\f70e"}i.icon.sd.card:before{content:"\f7c2"}i.icon.search:before{content:"\f002"}i.icon.search.dollar:before{content:"\f688"}i.icon.search.location:before{content:"\f689"}i.icon.search.minus:before{content:"\f010"}i.icon.search.plus:before{content:"\f00e"}i.icon.seedling:before{content:"\f4d8"}i.icon.server:before{content:"\f233"}i.icon.shapes:before{content:"\f61f"}i.icon.share:before{content:"\f064"}i.icon.share.alternate:before{content:"\f1e0"}i.icon.share.alternate.square:before{content:"\f1e1"}i.icon.share.square:before{content:"\f14d"}i.icon.shekel.sign:before{content:"\f20b"}i.icon.shield.alternate:before{content:"\f3ed"}i.icon.ship:before{content:"\f21a"}i.icon.shipping.fast:before{content:"\f48b"}i.icon.shoe.prints:before{content:"\f54b"}i.icon.shopping.bag:before{content:"\f290"}i.icon.shopping.basket:before{content:"\f291"}i.icon.shopping.cart:before{content:"\f07a"}i.icon.shower:before{content:"\f2cc"}i.icon.shuttle.van:before{content:"\f5b6"}i.icon.sign:before{content:"\f4d9"}i.icon.sign.in.alternate:before{content:"\f2f6"}i.icon.sign.language:before{content:"\f2a7"}i.icon.sign.out.alternate:before{content:"\f2f5"}i.icon.signal:before{content:"\f012"}i.icon.signature:before{content:"\f5b7"}i.icon.sim.card:before{content:"\f7c4"}i.icon.sitemap:before{content:"\f0e8"}i.icon.skating:before{content:"\f7c5"}i.icon.skiing:before{content:"\f7c9"}i.icon.skiing.nordic:before{content:"\f7ca"}i.icon.skull:before{content:"\f54c"}i.icon.skull.crossbones:before{content:"\f714"}i.icon.slash:before{content:"\f715"}i.icon.sleigh:before{content:"\f7cc"}i.icon.sliders.horizontal:before{content:"\f1de"}i.icon.smile:before{content:"\f118"}i.icon.smile.beam:before{content:"\f5b8"}i.icon.smile.wink:before{content:"\f4da"}i.icon.smog:before{content:"\f75f"}i.icon.smoking:before{content:"\f48d"}i.icon.smoking.ban:before{content:"\f54d"}i.icon.sms:before{content:"\f7cd"}i.icon.snowboarding:before{content:"\f7ce"}i.icon.snowflake:before{content:"\f2dc"}i.icon.snowman:before{content:"\f7d0"}i.icon.snowplow:before{content:"\f7d2"}i.icon.socks:before{content:"\f696"}i.icon.solar.panel:before{content:"\f5ba"}i.icon.sort:before{content:"\f0dc"}i.icon.sort.alphabet.down:before{content:"\f15d"}i.icon.sort.alphabet.down.alternate:before{content:"\f881"}i.icon.sort.alphabet.up:before{content:"\f15e"}i.icon.sort.alphabet.up.alternate:before{content:"\f882"}i.icon.sort.amount.down:before{content:"\f160"}i.icon.sort.amount.down.alternate:before{content:"\f884"}i.icon.sort.amount.up:before{content:"\f161"}i.icon.sort.amount.up.alternate:before{content:"\f885"}i.icon.sort.down:before{content:"\f0dd"}i.icon.sort.numeric.down:before{content:"\f162"}i.icon.sort.numeric.down.alternate:before{content:"\f886"}i.icon.sort.numeric.up:before{content:"\f163"}i.icon.sort.numeric.up.alternate:before{content:"\f887"}i.icon.sort.up:before{content:"\f0de"}i.icon.spa:before{content:"\f5bb"}i.icon.space.shuttle:before{content:"\f197"}i.icon.spell.check:before{content:"\f891"}i.icon.spider:before{content:"\f717"}i.icon.spinner:before{content:"\f110"}i.icon.splotch:before{content:"\f5bc"}i.icon.spray.can:before{content:"\f5bd"}i.icon.square:before{content:"\f0c8"}i.icon.square.full:before{content:"\f45c"}i.icon.square.root.alternate:before{content:"\f698"}i.icon.stamp:before{content:"\f5bf"}i.icon.star:before{content:"\f005"}i.icon.star.and.crescent:before{content:"\f699"}i.icon.star.half:before{content:"\f089"}i.icon.star.half.alternate:before{content:"\f5c0"}i.icon.star.of.david:before{content:"\f69a"}i.icon.star.of.life:before{content:"\f621"}i.icon.step.backward:before{content:"\f048"}i.icon.step.forward:before{content:"\f051"}i.icon.stethoscope:before{content:"\f0f1"}i.icon.sticky.note:before{content:"\f249"}i.icon.stop:before{content:"\f04d"}i.icon.stop.circle:before{content:"\f28d"}i.icon.stopwatch:before{content:"\f2f2"}i.icon.store:before{content:"\f54e"}i.icon.store.alternate:before{content:"\f54f"}i.icon.stream:before{content:"\f550"}i.icon.street.view:before{content:"\f21d"}i.icon.strikethrough:before{content:"\f0cc"}i.icon.stroopwafel:before{content:"\f551"}i.icon.subscript:before{content:"\f12c"}i.icon.subway:before{content:"\f239"}i.icon.suitcase:before{content:"\f0f2"}i.icon.suitcase.rolling:before{content:"\f5c1"}i.icon.sun:before{content:"\f185"}i.icon.superscript:before{content:"\f12b"}i.icon.surprise:before{content:"\f5c2"}i.icon.swatchbook:before{content:"\f5c3"}i.icon.swimmer:before{content:"\f5c4"}i.icon.swimming.pool:before{content:"\f5c5"}i.icon.synagogue:before{content:"\f69b"}i.icon.sync:before{content:"\f021"}i.icon.sync.alternate:before{content:"\f2f1"}i.icon.syringe:before{content:"\f48e"}i.icon.table:before{content:"\f0ce"}i.icon.table.tennis:before{content:"\f45d"}i.icon.tablet:before{content:"\f10a"}i.icon.tablet.alternate:before{content:"\f3fa"}i.icon.tablets:before{content:"\f490"}i.icon.tachometer.alternate:before{content:"\f3fd"}i.icon.tag:before{content:"\f02b"}i.icon.tags:before{content:"\f02c"}i.icon.tape:before{content:"\f4db"}i.icon.tasks:before{content:"\f0ae"}i.icon.taxi:before{content:"\f1ba"}i.icon.teeth:before{content:"\f62e"}i.icon.teeth.open:before{content:"\f62f"}i.icon.temperature.high:before{content:"\f769"}i.icon.temperature.low:before{content:"\f76b"}i.icon.tenge:before{content:"\f7d7"}i.icon.terminal:before{content:"\f120"}i.icon.text.height:before{content:"\f034"}i.icon.text.width:before{content:"\f035"}i.icon.th:before{content:"\f00a"}i.icon.th.large:before{content:"\f009"}i.icon.th.list:before{content:"\f00b"}i.icon.theater.masks:before{content:"\f630"}i.icon.thermometer:before{content:"\f491"}i.icon.thermometer.empty:before{content:"\f2cb"}i.icon.thermometer.full:before{content:"\f2c7"}i.icon.thermometer.half:before{content:"\f2c9"}i.icon.thermometer.quarter:before{content:"\f2ca"}i.icon.thermometer.three.quarters:before{content:"\f2c8"}i.icon.thumbs.down:before{content:"\f165"}i.icon.thumbs.up:before{content:"\f164"}i.icon.thumbtack:before{content:"\f08d"}i.icon.ticket.alternate:before{content:"\f3ff"}i.icon.times:before{content:"\f00d"}i.icon.times.circle:before{content:"\f057"}i.icon.tint:before{content:"\f043"}i.icon.tint.slash:before{content:"\f5c7"}i.icon.tired:before{content:"\f5c8"}i.icon.toggle.off:before{content:"\f204"}i.icon.toggle.on:before{content:"\f205"}i.icon.toilet:before{content:"\f7d8"}i.icon.toilet.paper:before{content:"\f71e"}i.icon.toolbox:before{content:"\f552"}i.icon.tools:before{content:"\f7d9"}i.icon.tooth:before{content:"\f5c9"}i.icon.torah:before{content:"\f6a0"}i.icon.torii.gate:before{content:"\f6a1"}i.icon.tractor:before{content:"\f722"}i.icon.trademark:before{content:"\f25c"}i.icon.traffic.light:before{content:"\f637"}i.icon.trailer:before{content:"\f941"}i.icon.train:before{content:"\f238"}i.icon.tram:before{content:"\f7da"}i.icon.transgender:before{content:"\f224"}i.icon.transgender.alternate:before{content:"\f225"}i.icon.trash:before{content:"\f1f8"}i.icon.trash.alternate:before{content:"\f2ed"}i.icon.trash.restore:before{content:"\f829"}i.icon.trash.restore.alternate:before{content:"\f82a"}i.icon.tree:before{content:"\f1bb"}i.icon.trophy:before{content:"\f091"}i.icon.truck:before{content:"\f0d1"}i.icon.truck.monster:before{content:"\f63b"}i.icon.truck.moving:before{content:"\f4df"}i.icon.truck.packing:before{content:"\f4de"}i.icon.truck.pickup:before{content:"\f63c"}i.icon.tshirt:before{content:"\f553"}i.icon.tty:before{content:"\f1e4"}i.icon.tv:before{content:"\f26c"}i.icon.umbrella:before{content:"\f0e9"}i.icon.umbrella.beach:before{content:"\f5ca"}i.icon.underline:before{content:"\f0cd"}i.icon.undo:before{content:"\f0e2"}i.icon.undo.alternate:before{content:"\f2ea"}i.icon.universal.access:before{content:"\f29a"}i.icon.university:before{content:"\f19c"}i.icon.unlink:before{content:"\f127"}i.icon.unlock:before{content:"\f09c"}i.icon.unlock.alternate:before{content:"\f13e"}i.icon.upload:before{content:"\f093"}i.icon.user:before{content:"\f007"}i.icon.user.alternate:before{content:"\f406"}i.icon.user.alternate.slash:before{content:"\f4fa"}i.icon.user.astronaut:before{content:"\f4fb"}i.icon.user.check:before{content:"\f4fc"}i.icon.user.circle:before{content:"\f2bd"}i.icon.user.clock:before{content:"\f4fd"}i.icon.user.cog:before{content:"\f4fe"}i.icon.user.edit:before{content:"\f4ff"}i.icon.user.friends:before{content:"\f500"}i.icon.user.graduate:before{content:"\f501"}i.icon.user.injured:before{content:"\f728"}i.icon.user.lock:before{content:"\f502"}i.icon.user.md:before{content:"\f0f0"}i.icon.user.minus:before{content:"\f503"}i.icon.user.ninja:before{content:"\f504"}i.icon.user.nurse:before{content:"\f82f"}i.icon.user.plus:before{content:"\f234"}i.icon.user.secret:before{content:"\f21b"}i.icon.user.shield:before{content:"\f505"}i.icon.user.slash:before{content:"\f506"}i.icon.user.tag:before{content:"\f507"}i.icon.user.tie:before{content:"\f508"}i.icon.user.times:before{content:"\f235"}i.icon.users:before{content:"\f0c0"}i.icon.users.cog:before{content:"\f509"}i.icon.utensil.spoon:before{content:"\f2e5"}i.icon.utensils:before{content:"\f2e7"}i.icon.vector.square:before{content:"\f5cb"}i.icon.venus:before{content:"\f221"}i.icon.venus.double:before{content:"\f226"}i.icon.venus.mars:before{content:"\f228"}i.icon.vial:before{content:"\f492"}i.icon.vials:before{content:"\f493"}i.icon.video:before{content:"\f03d"}i.icon.video.slash:before{content:"\f4e2"}i.icon.vihara:before{content:"\f6a7"}i.icon.voicemail:before{content:"\f897"}i.icon.volleyball.ball:before{content:"\f45f"}i.icon.volume.down:before{content:"\f027"}i.icon.volume.mute:before{content:"\f6a9"}i.icon.volume.off:before{content:"\f026"}i.icon.volume.up:before{content:"\f028"}i.icon.vote.yea:before{content:"\f772"}i.icon.vr.cardboard:before{content:"\f729"}i.icon.walking:before{content:"\f554"}i.icon.wallet:before{content:"\f555"}i.icon.warehouse:before{content:"\f494"}i.icon.water:before{content:"\f773"}i.icon.wave.square:before{content:"\f83e"}i.icon.weight:before{content:"\f496"}i.icon.weight.hanging:before{content:"\f5cd"}i.icon.wheelchair:before{content:"\f193"}i.icon.wifi:before{content:"\f1eb"}i.icon.wind:before{content:"\f72e"}i.icon.window.close:before{content:"\f410"}i.icon.window.maximize:before{content:"\f2d0"}i.icon.window.minimize:before{content:"\f2d1"}i.icon.window.restore:before{content:"\f2d2"}i.icon.wine.bottle:before{content:"\f72f"}i.icon.wine.glass:before{content:"\f4e3"}i.icon.wine.glass.alternate:before{content:"\f5ce"}i.icon.won.sign:before{content:"\f159"}i.icon.wrench:before{content:"\f0ad"}i.icon.x.ray:before{content:"\f497"}i.icon.yen.sign:before{content:"\f157"}i.icon.yin.yang:before{content:"\f6ad"}i.icon.add:before{content:"\f067"}i.icon.add.circle:before{content:"\f055"}i.icon.add.square:before{content:"\f0fe"}i.icon.add.to.calendar:before{content:"\f271"}i.icon.add.to.cart:before{content:"\f217"}i.icon.add.user:before{content:"\f234"}i.icon.alarm:before{content:"\f0f3"}i.icon.alarm.mute:before{content:"\f1f6"}i.icon.ald:before{content:"\f2a2"}i.icon.als:before{content:"\f2a2"}i.icon.announcement:before{content:"\f0a1"}i.icon.area.chart:before{content:"\f1fe"}i.icon.area.graph:before{content:"\f1fe"}i.icon.arrow.down.cart:before{content:"\f218"}i.icon.asexual:before{content:"\f22d"}i.icon.asl:before{content:"\f2a3"}i.icon.asl.interpreting:before{content:"\f2a3"}i.icon.assistive.listening.devices:before{content:"\f2a2"}i.icon.attach:before{content:"\f0c6"}i.icon.attention:before{content:"\f06a"}i.icon.balance:before{content:"\f24e"}i.icon.bar:before{content:"\f0fc"}i.icon.bathtub:before{content:"\f2cd"}i.icon.battery.four:before{content:"\f240"}i.icon.battery.high:before{content:"\f241"}i.icon.battery.low:before{content:"\f243"}i.icon.battery.medium:before{content:"\f242"}i.icon.battery.one:before{content:"\f243"}i.icon.battery.three:before{content:"\f241"}i.icon.battery.two:before{content:"\f242"}i.icon.battery.zero:before{content:"\f244"}i.icon.birthday:before{content:"\f1fd"}i.icon.block.layout:before{content:"\f009"}i.icon.broken.chain:before{content:"\f127"}i.icon.browser:before{content:"\f022"}i.icon.call:before{content:"\f095"}i.icon.call.square:before{content:"\f098"}i.icon.cancel:before{content:"\f00d"}i.icon.cart:before{content:"\f07a"}i.icon.cc:before{content:"\f20a"}i.icon.chain:before{content:"\f0c1"}i.icon.chat:before{content:"\f075"}i.icon.checked.calendar:before{content:"\f274"}i.icon.checkmark:before{content:"\f00c"}i.icon.checkmark.box:before{content:"\f14a"}i.icon.chess.rock:before{content:"\f447"}i.icon.circle.notched:before{content:"\f1ce"}i.icon.circle.thin:before{content:"\f111"}i.icon.close:before{content:"\f00d"}i.icon.cloud.download:before{content:"\f381"}i.icon.cloud.upload:before{content:"\f382"}i.icon.cny:before{content:"\f157"}i.icon.cocktail:before{content:"\f000"}i.icon.commenting:before{content:"\f27a"}i.icon.compose:before{content:"\f303"}i.icon.computer:before{content:"\f108"}i.icon.configure:before{content:"\f0ad"}i.icon.content:before{content:"\f0c9"}i.icon.conversation:before{content:"\f086"}i.icon.credit.card.alternative:before{content:"\f09d"}i.icon.currency:before{content:"\f3d1"}i.icon.dashboard:before{content:"\f3fd"}i.icon.deafness:before{content:"\f2a4"}i.icon.delete:before{content:"\f00d"}i.icon.delete.calendar:before{content:"\f273"}i.icon.detective:before{content:"\f21b"}i.icon.diamond:before{content:"\f3a5"}i.icon.discussions:before{content:"\f086"}i.icon.disk:before{content:"\f0a0"}i.icon.doctor:before{content:"\f0f0"}i.icon.dollar:before{content:"\f155"}i.icon.dont:before{content:"\f05e"}i.icon.drivers.license:before{content:"\f2c2"}i.icon.dropdown:before{content:"\f0d7"}i.icon.emergency:before{content:"\f0f9"}i.icon.erase:before{content:"\f12d"}i.icon.eur:before{content:"\f153"}i.icon.euro:before{content:"\f153"}i.icon.exchange:before{content:"\f362"}i.icon.external:before{content:"\f35d"}i.icon.external.share:before{content:"\f14d"}i.icon.external.square:before{content:"\f360"}i.icon.eyedropper:before{content:"\f1fb"}i.icon.factory:before{content:"\f275"}i.icon.favorite:before{content:"\f005"}i.icon.feed:before{content:"\f09e"}i.icon.female.homosexual:before{content:"\f226"}i.icon.file.text:before{content:"\f15c"}i.icon.find:before{content:"\f1e5"}i.icon.first.aid:before{content:"\f0fa"}i.icon.food:before{content:"\f2e7"}i.icon.fork:before{content:"\f126"}i.icon.game:before{content:"\f11b"}i.icon.gay:before{content:"\f227"}i.icon.gbp:before{content:"\f154"}i.icon.grab:before{content:"\f255"}i.icon.graduation:before{content:"\f19d"}i.icon.grid.layout:before{content:"\f00a"}i.icon.group:before{content:"\f0c0"}i.icon.h:before{content:"\f0fd"}i.icon.hamburger:before{content:"\f0c9"}i.icon.hand.victory:before{content:"\f25b"}i.icon.handicap:before{content:"\f193"}i.icon.hard.of.hearing:before{content:"\f2a4"}i.icon.header:before{content:"\f1dc"}i.icon.heart.empty:before{content:"\f004"}i.icon.help:before{content:"\f128"}i.icon.help.circle:before{content:"\f059"}i.icon.heterosexual:before{content:"\f228"}i.icon.hide:before{content:"\f070"}i.icon.hotel:before{content:"\f236"}i.icon.hourglass.four:before{content:"\f254"}i.icon.hourglass.full:before{content:"\f254"}i.icon.hourglass.one:before{content:"\f251"}i.icon.hourglass.three:before{content:"\f253"}i.icon.hourglass.two:before{content:"\f252"}i.icon.hourglass.zero:before{content:"\f253"}i.icon.idea:before{content:"\f0eb"}i.icon.ils:before{content:"\f20b"}i.icon.in.cart:before{content:"\f218"}i.icon.inr:before{content:"\f156"}i.icon.intergender:before{content:"\f224"}i.icon.intersex:before{content:"\f224"}i.icon.jpy:before{content:"\f157"}i.icon.krw:before{content:"\f159"}i.icon.lab:before{content:"\f0c3"}i.icon.law:before{content:"\f24e"}i.icon.legal:before{content:"\f0e3"}i.icon.lesbian:before{content:"\f226"}i.icon.level.down:before{content:"\f3be"}i.icon.level.up:before{content:"\f3bf"}i.icon.lightning:before{content:"\f0e7"}i.icon.like:before{content:"\f004"}i.icon.line.graph:before{content:"\f201"}i.icon.linkify:before{content:"\f0c1"}i.icon.lira:before{content:"\f195"}i.icon.list.layout:before{content:"\f00b"}i.icon.log.out:before{content:"\f2f5"}i.icon.magnify:before{content:"\f00e"}i.icon.mail:before{content:"\f0e0"}i.icon.mail.forward:before{content:"\f064"}i.icon.mail.square:before{content:"\f199"}i.icon.male.homosexual:before{content:"\f227"}i.icon.man:before{content:"\f222"}i.icon.marker:before{content:"\f041"}i.icon.mars.alternate:before{content:"\f229"}i.icon.mars.horizontal:before{content:"\f22b"}i.icon.mars.vertical:before{content:"\f22a"}i.icon.meanpath:before{content:"\f0c8"}i.icon.military:before{content:"\f0fb"}i.icon.money:before{content:"\f3d1"}i.icon.move:before{content:"\f0b2"}i.icon.mute:before{content:"\f131"}i.icon.non.binary.transgender:before{content:"\f223"}i.icon.numbered.list:before{content:"\f0cb"}i.icon.options:before{content:"\f1de"}i.icon.ordered.list:before{content:"\f0cb"}i.icon.other.gender:before{content:"\f229"}i.icon.other.gender.horizontal:before{content:"\f22b"}i.icon.other.gender.vertical:before{content:"\f22a"}i.icon.payment:before{content:"\f09d"}i.icon.pencil:before{content:"\f303"}i.icon.pencil.square:before{content:"\f14b"}i.icon.photo:before{content:"\f030"}i.icon.picture:before{content:"\f03e"}i.icon.pie.chart:before{content:"\f200"}i.icon.pie.graph:before{content:"\f200"}i.icon.pin:before{content:"\f08d"}i.icon.plus.cart:before{content:"\f217"}i.icon.point:before{content:"\f041"}i.icon.pointing.down:before{content:"\f0a7"}i.icon.pointing.left:before{content:"\f0a5"}i.icon.pointing.right:before{content:"\f0a4"}i.icon.pointing.up:before{content:"\f0a6"}i.icon.pound:before{content:"\f154"}i.icon.power:before{content:"\f011"}i.icon.power.cord:before{content:"\f1e6"}i.icon.privacy:before{content:"\f084"}i.icon.protect:before{content:"\f023"}i.icon.puzzle:before{content:"\f12e"}i.icon.r.circle:before{content:"\f25d"}i.icon.radio:before{content:"\f192"}i.icon.rain:before{content:"\f0e9"}i.icon.record:before{content:"\f03d"}i.icon.refresh:before{content:"\f021"}i.icon.remove:before{content:"\f00d"}i.icon.remove.bookmark:before{content:"\f02e"}i.icon.remove.circle:before{content:"\f057"}i.icon.remove.from.calendar:before{content:"\f272"}i.icon.remove.user:before{content:"\f235"}i.icon.repeat:before{content:"\f01e"}i.icon.resize.horizontal:before{content:"\f337"}i.icon.resize.vertical:before{content:"\f338"}i.icon.rmb:before{content:"\f157"}i.icon.rouble:before{content:"\f158"}i.icon.rub:before{content:"\f158"}i.icon.ruble:before{content:"\f158"}i.icon.rupee:before{content:"\f156"}i.icon.s15:before{content:"\f2cd"}i.icon.selected.radio:before{content:"\f192"}i.icon.send:before{content:"\f1d8"}i.icon.setting:before{content:"\f013"}i.icon.settings:before{content:"\f085"}i.icon.shekel:before{content:"\f20b"}i.icon.sheqel:before{content:"\f20b"}i.icon.shield:before{content:"\f3ed"}i.icon.shipping:before{content:"\f0d1"}i.icon.shop:before{content:"\f07a"}i.icon.shuffle:before{content:"\f074"}i.icon.shutdown:before{content:"\f011"}i.icon.sidebar:before{content:"\f0c9"}i.icon.sign.in:before{content:"\f2f6"}i.icon.sign.out:before{content:"\f2f5"}i.icon.signing:before{content:"\f2a7"}i.icon.signup:before{content:"\f044"}i.icon.sliders:before{content:"\f1de"}i.icon.soccer:before{content:"\f1e3"}i.icon.sort.alphabet.ascending:before{content:"\f15d"}i.icon.sort.alphabet.descending:before{content:"\f15e"}i.icon.sort.ascending:before{content:"\f0de"}i.icon.sort.content.ascending:before{content:"\f160"}i.icon.sort.content.descending:before{content:"\f161"}i.icon.sort.descending:before{content:"\f0dd"}i.icon.sort.numeric.ascending:before{content:"\f162"}i.icon.sort.numeric.descending:before{content:"\f163"}i.icon.sound:before{content:"\f025"}i.icon.spoon:before{content:"\f2e5"}i.icon.spy:before{content:"\f21b"}i.icon.star.empty:before{content:"\f005"}i.icon.star.half.empty:before{content:"\f089"}i.icon.star.half.full:before{content:"\f089"}i.icon.student:before{content:"\f19d"}i.icon.talk:before{content:"\f27a"}i.icon.target:before{content:"\f140"}i.icon.teletype:before{content:"\f1e4"}i.icon.television:before{content:"\f26c"}i.icon.text.cursor:before{content:"\f246"}i.icon.text.telephone:before{content:"\f1e4"}i.icon.theme:before{content:"\f043"}i.icon.thermometer:before{content:"\f2c7"}i.icon.thumb.tack:before{content:"\f08d"}i.icon.ticket:before{content:"\f3ff"}i.icon.time:before{content:"\f017"}i.icon.times.rectangle:before{content:"\f410"}i.icon.tm:before{content:"\f25c"}i.icon.toggle.down:before{content:"\f150"}i.icon.toggle.left:before{content:"\f191"}i.icon.toggle.right:before{content:"\f152"}i.icon.toggle.up:before{content:"\f151"}i.icon.translate:before{content:"\f1ab"}i.icon.travel:before{content:"\f0b1"}i.icon.treatment:before{content:"\f0f1"}i.icon.triangle.down:before{content:"\f0d7"}i.icon.triangle.left:before{content:"\f0d9"}i.icon.triangle.right:before{content:"\f0da"}i.icon.triangle.up:before{content:"\f0d8"}i.icon.try:before{content:"\f195"}i.icon.unhide:before{content:"\f06e"}i.icon.unlinkify:before{content:"\f127"}i.icon.unmute:before{content:"\f130"}i.icon.unordered.list:before{content:"\f0ca"}i.icon.usd:before{content:"\f155"}i.icon.user.cancel:before{content:"\f235"}i.icon.user.close:before{content:"\f235"}i.icon.user.delete:before{content:"\f235"}i.icon.user.doctor:before{content:"\f0f0"}i.icon.user.x:before{content:"\f235"}i.icon.vcard:before{content:"\f2bb"}i.icon.video.camera:before{content:"\f03d"}i.icon.video.play:before{content:"\f144"}i.icon.volume.control.phone:before{content:"\f2a0"}i.icon.wait:before{content:"\f017"}i.icon.warning:before{content:"\f12a"}i.icon.warning.circle:before{content:"\f06a"}i.icon.warning.sign:before{content:"\f071"}i.icon.wi.fi:before{content:"\f1eb"}i.icon.winner:before{content:"\f091"}i.icon.wizard:before{content:"\f0d0"}i.icon.woman:before{content:"\f221"}i.icon.won:before{content:"\f159"}i.icon.world:before{content:"\f0ac"}i.icon.write:before{content:"\f303"}i.icon.write.square:before{content:"\f14b"}i.icon.x:before{content:"\f00d"}i.icon.yen:before{content:"\f157"}i.icon.zip:before{content:"\f187"}i.icon.zoom:before{content:"\f00e"}i.icon.zoom.in:before{content:"\f00e"}i.icon.zoom.out:before{content:"\f010"}@font-face{font-family:outline-icons;src:url(themes/default/assets/fonts/outline-icons.eot);src:url(themes/default/assets/fonts/outline-icons.eot?#iefix) format('embedded-opentype'),url(themes/default/assets/fonts/outline-icons.woff2) format('woff2'),url(themes/default/assets/fonts/outline-icons.woff) format('woff'),url(themes/default/assets/fonts/outline-icons.ttf) format('truetype'),url(themes/default/assets/fonts/outline-icons.svg#icons) format('svg');font-style:normal;font-weight:400;font-variant:normal;text-decoration:inherit;text-transform:none}i.icon.outline{font-family:outline-icons}i.icon.address.book.outline:before{content:"\f2b9"}i.icon.address.card.outline:before{content:"\f2bb"}i.icon.angry.outline:before{content:"\f556"}i.icon.arrow.alternate.circle.down.outline:before{content:"\f358"}i.icon.arrow.alternate.circle.left.outline:before{content:"\f359"}i.icon.arrow.alternate.circle.right.outline:before{content:"\f35a"}i.icon.arrow.alternate.circle.up.outline:before{content:"\f35b"}i.icon.bell.outline:before{content:"\f0f3"}i.icon.bell.slash.outline:before{content:"\f1f6"}i.icon.bookmark.outline:before{content:"\f02e"}i.icon.building.outline:before{content:"\f1ad"}i.icon.calendar.alternate.outline:before{content:"\f073"}i.icon.calendar.check.outline:before{content:"\f274"}i.icon.calendar.minus.outline:before{content:"\f272"}i.icon.calendar.outline:before{content:"\f133"}i.icon.calendar.plus.outline:before{content:"\f271"}i.icon.calendar.times.outline:before{content:"\f273"}i.icon.caret.square.down.outline:before{content:"\f150"}i.icon.caret.square.left.outline:before{content:"\f191"}i.icon.caret.square.right.outline:before{content:"\f152"}i.icon.caret.square.up.outline:before{content:"\f151"}i.icon.chart.bar.outline:before{content:"\f080"}i.icon.check.circle.outline:before{content:"\f058"}i.icon.check.square.outline:before{content:"\f14a"}i.icon.circle.outline:before{content:"\f111"}i.icon.clipboard.outline:before{content:"\f328"}i.icon.clock.outline:before{content:"\f017"}i.icon.clone.outline:before{content:"\f24d"}i.icon.closed.captioning.outline:before{content:"\f20a"}i.icon.comment.alternate.outline:before{content:"\f27a"}i.icon.comment.dots.outline:before{content:"\f4ad"}i.icon.comment.outline:before{content:"\f075"}i.icon.comments.outline:before{content:"\f086"}i.icon.compass.outline:before{content:"\f14e"}i.icon.copy.outline:before{content:"\f0c5"}i.icon.copyright.outline:before{content:"\f1f9"}i.icon.credit.card.outline:before{content:"\f09d"}i.icon.dizzy.outline:before{content:"\f567"}i.icon.dot.circle.outline:before{content:"\f192"}i.icon.edit.outline:before{content:"\f044"}i.icon.envelope.open.outline:before{content:"\f2b6"}i.icon.envelope.outline:before{content:"\f0e0"}i.icon.eye.outline:before{content:"\f06e"}i.icon.eye.slash.outline:before{content:"\f070"}i.icon.file.alternate.outline:before{content:"\f15c"}i.icon.file.archive.outline:before{content:"\f1c6"}i.icon.file.audio.outline:before{content:"\f1c7"}i.icon.file.code.outline:before{content:"\f1c9"}i.icon.file.excel.outline:before{content:"\f1c3"}i.icon.file.image.outline:before{content:"\f1c5"}i.icon.file.outline:before{content:"\f15b"}i.icon.file.pdf.outline:before{content:"\f1c1"}i.icon.file.powerpoint.outline:before{content:"\f1c4"}i.icon.file.video.outline:before{content:"\f1c8"}i.icon.file.word.outline:before{content:"\f1c2"}i.icon.flag.outline:before{content:"\f024"}i.icon.flushed.outline:before{content:"\f579"}i.icon.folder.open.outline:before{content:"\f07c"}i.icon.folder.outline:before{content:"\f07b"}i.icon.frown.open.outline:before{content:"\f57a"}i.icon.frown.outline:before{content:"\f119"}i.icon.futbol.outline:before{content:"\f1e3"}i.icon.gem.outline:before{content:"\f3a5"}i.icon.grimace.outline:before{content:"\f57f"}i.icon.grin.alternate.outline:before{content:"\f581"}i.icon.grin.beam.outline:before{content:"\f582"}i.icon.grin.beam.sweat.outline:before{content:"\f583"}i.icon.grin.hearts.outline:before{content:"\f584"}i.icon.grin.outline:before{content:"\f580"}i.icon.grin.squint.outline:before{content:"\f585"}i.icon.grin.squint.tears.outline:before{content:"\f586"}i.icon.grin.stars.outline:before{content:"\f587"}i.icon.grin.tears.outline:before{content:"\f588"}i.icon.grin.tongue.outline:before{content:"\f589"}i.icon.grin.tongue.squint.outline:before{content:"\f58a"}i.icon.grin.tongue.wink.outline:before{content:"\f58b"}i.icon.grin.wink.outline:before{content:"\f58c"}i.icon.hand.lizard.outline:before{content:"\f258"}i.icon.hand.paper.outline:before{content:"\f256"}i.icon.hand.peace.outline:before{content:"\f25b"}i.icon.hand.point.down.outline:before{content:"\f0a7"}i.icon.hand.point.left.outline:before{content:"\f0a5"}i.icon.hand.point.right.outline:before{content:"\f0a4"}i.icon.hand.point.up.outline:before{content:"\f0a6"}i.icon.hand.pointer.outline:before{content:"\f25a"}i.icon.hand.rock.outline:before{content:"\f255"}i.icon.hand.scissors.outline:before{content:"\f257"}i.icon.hand.spock.outline:before{content:"\f259"}i.icon.handshake.outline:before{content:"\f2b5"}i.icon.hdd.outline:before{content:"\f0a0"}i.icon.heart.outline:before{content:"\f004"}i.icon.hospital.outline:before{content:"\f0f8"}i.icon.hourglass.outline:before{content:"\f254"}i.icon.id.badge.outline:before{content:"\f2c1"}i.icon.id.card.outline:before{content:"\f2c2"}i.icon.image.outline:before{content:"\f03e"}i.icon.images.outline:before{content:"\f302"}i.icon.keyboard.outline:before{content:"\f11c"}i.icon.kiss.beam.outline:before{content:"\f597"}i.icon.kiss.outline:before{content:"\f596"}i.icon.kiss.wink.heart.outline:before{content:"\f598"}i.icon.laugh.beam.outline:before{content:"\f59a"}i.icon.laugh.outline:before{content:"\f599"}i.icon.laugh.squint.outline:before{content:"\f59b"}i.icon.laugh.wink.outline:before{content:"\f59c"}i.icon.lemon.outline:before{content:"\f094"}i.icon.life.ring.outline:before{content:"\f1cd"}i.icon.lightbulb.outline:before{content:"\f0eb"}i.icon.list.alternate.outline:before{content:"\f022"}i.icon.map.outline:before{content:"\f279"}i.icon.meh.blank.outline:before{content:"\f5a4"}i.icon.meh.outline:before{content:"\f11a"}i.icon.meh.rolling.eyes.outline:before{content:"\f5a5"}i.icon.minus.square.outline:before{content:"\f146"}i.icon.money.bill.alternate.outline:before{content:"\f3d1"}i.icon.moon.outline:before{content:"\f186"}i.icon.newspaper.outline:before{content:"\f1ea"}i.icon.object.group.outline:before{content:"\f247"}i.icon.object.ungroup.outline:before{content:"\f248"}i.icon.paper.plane.outline:before{content:"\f1d8"}i.icon.pause.circle.outline:before{content:"\f28b"}i.icon.play.circle.outline:before{content:"\f144"}i.icon.plus.square.outline:before{content:"\f0fe"}i.icon.question.circle.outline:before{content:"\f059"}i.icon.registered.outline:before{content:"\f25d"}i.icon.sad.cry.outline:before{content:"\f5b3"}i.icon.sad.tear.outline:before{content:"\f5b4"}i.icon.save.outline:before{content:"\f0c7"}i.icon.share.square.outline:before{content:"\f14d"}i.icon.smile.beam.outline:before{content:"\f5b8"}i.icon.smile.outline:before{content:"\f118"}i.icon.smile.wink.outline:before{content:"\f4da"}i.icon.snowflake.outline:before{content:"\f2dc"}i.icon.square.outline:before{content:"\f0c8"}i.icon.star.half.outline:before{content:"\f089"}i.icon.star.outline:before{content:"\f005"}i.icon.sticky.note.outline:before{content:"\f249"}i.icon.stop.circle.outline:before{content:"\f28d"}i.icon.sun.outline:before{content:"\f185"}i.icon.surprise.outline:before{content:"\f5c2"}i.icon.thumbs.down.outline:before{content:"\f165"}i.icon.thumbs.up.outline:before{content:"\f164"}i.icon.times.circle.outline:before{content:"\f057"}i.icon.tired.outline:before{content:"\f5c8"}i.icon.trash.alternate.outline:before{content:"\f2ed"}i.icon.user.circle.outline:before{content:"\f2bd"}i.icon.user.outline:before{content:"\f007"}i.icon.window.close.outline:before{content:"\f410"}i.icon.window.maximize.outline:before{content:"\f2d0"}i.icon.window.minimize.outline:before{content:"\f2d1"}i.icon.window.restore.outline:before{content:"\f2d2"}@font-face{font-family:brand-icons;src:url(themes/default/assets/fonts/brand-icons.eot);src:url(themes/default/assets/fonts/brand-icons.eot?#iefix) format('embedded-opentype'),url(themes/default/assets/fonts/brand-icons.woff2) format('woff2'),url(themes/default/assets/fonts/brand-icons.woff) format('woff'),url(themes/default/assets/fonts/brand-icons.ttf) format('truetype'),url(themes/default/assets/fonts/brand-icons.svg#icons) format('svg');font-style:normal;font-weight:400;font-variant:normal;text-decoration:inherit;text-transform:none}i.icon.\35 00px:before{content:"\f26e";font-family:brand-icons}i.icon.accessible:before{content:"\f368";font-family:brand-icons}i.icon.accusoft:before{content:"\f369";font-family:brand-icons}i.icon.acquisitions.incorporated:before{content:"\f6af";font-family:brand-icons}i.icon.adn:before{content:"\f170";font-family:brand-icons}i.icon.adobe:before{content:"\f778";font-family:brand-icons}i.icon.adversal:before{content:"\f36a";font-family:brand-icons}i.icon.affiliatetheme:before{content:"\f36b";font-family:brand-icons}i.icon.airbnb:before{content:"\f834";font-family:brand-icons}i.icon.algolia:before{content:"\f36c";font-family:brand-icons}i.icon.alipay:before{content:"\f642";font-family:brand-icons}i.icon.amazon:before{content:"\f270";font-family:brand-icons}i.icon.amazon.pay:before{content:"\f42c";font-family:brand-icons}i.icon.amilia:before{content:"\f36d";font-family:brand-icons}i.icon.android:before{content:"\f17b";font-family:brand-icons}i.icon.angellist:before{content:"\f209";font-family:brand-icons}i.icon.angrycreative:before{content:"\f36e";font-family:brand-icons}i.icon.angular:before{content:"\f420";font-family:brand-icons}i.icon.app.store:before{content:"\f36f";font-family:brand-icons}i.icon.app.store.ios:before{content:"\f370";font-family:brand-icons}i.icon.apper:before{content:"\f371";font-family:brand-icons}i.icon.apple:before{content:"\f179";font-family:brand-icons}i.icon.apple.pay:before{content:"\f415";font-family:brand-icons}i.icon.artstation:before{content:"\f77a";font-family:brand-icons}i.icon.asymmetrik:before{content:"\f372";font-family:brand-icons}i.icon.atlassian:before{content:"\f77b";font-family:brand-icons}i.icon.audible:before{content:"\f373";font-family:brand-icons}i.icon.autoprefixer:before{content:"\f41c";font-family:brand-icons}i.icon.avianex:before{content:"\f374";font-family:brand-icons}i.icon.aviato:before{content:"\f421";font-family:brand-icons}i.icon.aws:before{content:"\f375";font-family:brand-icons}i.icon.bandcamp:before{content:"\f2d5";font-family:brand-icons}i.icon.battle.net:before{content:"\f835";font-family:brand-icons}i.icon.behance:before{content:"\f1b4";font-family:brand-icons}i.icon.behance.square:before{content:"\f1b5";font-family:brand-icons}i.icon.bimobject:before{content:"\f378";font-family:brand-icons}i.icon.bitbucket:before{content:"\f171";font-family:brand-icons}i.icon.bitcoin:before{content:"\f379";font-family:brand-icons}i.icon.bity:before{content:"\f37a";font-family:brand-icons}i.icon.black.tie:before{content:"\f27e";font-family:brand-icons}i.icon.blackberry:before{content:"\f37b";font-family:brand-icons}i.icon.blogger:before{content:"\f37c";font-family:brand-icons}i.icon.blogger.b:before{content:"\f37d";font-family:brand-icons}i.icon.bluetooth:before{content:"\f293";font-family:brand-icons}i.icon.bluetooth.b:before{content:"\f294";font-family:brand-icons}i.icon.bootstrap:before{content:"\f836";font-family:brand-icons}i.icon.btc:before{content:"\f15a";font-family:brand-icons}i.icon.buffer:before{content:"\f837";font-family:brand-icons}i.icon.buromobelexperte:before{content:"\f37f";font-family:brand-icons}i.icon.buy.n.large:before{content:"\f8a6";font-family:brand-icons}i.icon.buysellads:before{content:"\f20d";font-family:brand-icons}i.icon.canadian.maple.leaf:before{content:"\f785";font-family:brand-icons}i.icon.cc.amazon.pay:before{content:"\f42d";font-family:brand-icons}i.icon.cc.amex:before{content:"\f1f3";font-family:brand-icons}i.icon.cc.apple.pay:before{content:"\f416";font-family:brand-icons}i.icon.cc.diners.club:before{content:"\f24c";font-family:brand-icons}i.icon.cc.discover:before{content:"\f1f2";font-family:brand-icons}i.icon.cc.jcb:before{content:"\f24b";font-family:brand-icons}i.icon.cc.mastercard:before{content:"\f1f1";font-family:brand-icons}i.icon.cc.paypal:before{content:"\f1f4";font-family:brand-icons}i.icon.cc.stripe:before{content:"\f1f5";font-family:brand-icons}i.icon.cc.visa:before{content:"\f1f0";font-family:brand-icons}i.icon.centercode:before{content:"\f380";font-family:brand-icons}i.icon.centos:before{content:"\f789";font-family:brand-icons}i.icon.chrome:before{content:"\f268";font-family:brand-icons}i.icon.chromecast:before{content:"\f838";font-family:brand-icons}i.icon.cloudscale:before{content:"\f383";font-family:brand-icons}i.icon.cloudsmith:before{content:"\f384";font-family:brand-icons}i.icon.cloudversify:before{content:"\f385";font-family:brand-icons}i.icon.codepen:before{content:"\f1cb";font-family:brand-icons}i.icon.codiepie:before{content:"\f284";font-family:brand-icons}i.icon.confluence:before{content:"\f78d";font-family:brand-icons}i.icon.connectdevelop:before{content:"\f20e";font-family:brand-icons}i.icon.contao:before{content:"\f26d";font-family:brand-icons}i.icon.cotton.bureau:before{content:"\f89e";font-family:brand-icons}i.icon.cpanel:before{content:"\f388";font-family:brand-icons}i.icon.creative.commons:before{content:"\f25e";font-family:brand-icons}i.icon.creative.commons.by:before{content:"\f4e7";font-family:brand-icons}i.icon.creative.commons.nc:before{content:"\f4e8";font-family:brand-icons}i.icon.creative.commons.nc.eu:before{content:"\f4e9";font-family:brand-icons}i.icon.creative.commons.nc.jp:before{content:"\f4ea";font-family:brand-icons}i.icon.creative.commons.nd:before{content:"\f4eb";font-family:brand-icons}i.icon.creative.commons.pd:before{content:"\f4ec";font-family:brand-icons}i.icon.creative.commons.pd.alternate:before{content:"\f4ed";font-family:brand-icons}i.icon.creative.commons.remix:before{content:"\f4ee";font-family:brand-icons}i.icon.creative.commons.sa:before{content:"\f4ef";font-family:brand-icons}i.icon.creative.commons.sampling:before{content:"\f4f0";font-family:brand-icons}i.icon.creative.commons.sampling.plus:before{content:"\f4f1";font-family:brand-icons}i.icon.creative.commons.share:before{content:"\f4f2";font-family:brand-icons}i.icon.creative.commons.zero:before{content:"\f4f3";font-family:brand-icons}i.icon.critical.role:before{content:"\f6c9";font-family:brand-icons}i.icon.css3:before{content:"\f13c";font-family:brand-icons}i.icon.css3.alternate:before{content:"\f38b";font-family:brand-icons}i.icon.cuttlefish:before{content:"\f38c";font-family:brand-icons}i.icon.d.and.d:before{content:"\f38d";font-family:brand-icons}i.icon.d.and.d.beyond:before{content:"\f6ca";font-family:brand-icons}i.icon.dashcube:before{content:"\f210";font-family:brand-icons}i.icon.delicious:before{content:"\f1a5";font-family:brand-icons}i.icon.deploydog:before{content:"\f38e";font-family:brand-icons}i.icon.deskpro:before{content:"\f38f";font-family:brand-icons}i.icon.dev:before{content:"\f6cc";font-family:brand-icons}i.icon.deviantart:before{content:"\f1bd";font-family:brand-icons}i.icon.dhl:before{content:"\f790";font-family:brand-icons}i.icon.diaspora:before{content:"\f791";font-family:brand-icons}i.icon.digg:before{content:"\f1a6";font-family:brand-icons}i.icon.digital.ocean:before{content:"\f391";font-family:brand-icons}i.icon.discord:before{content:"\f392";font-family:brand-icons}i.icon.discourse:before{content:"\f393";font-family:brand-icons}i.icon.dochub:before{content:"\f394";font-family:brand-icons}i.icon.docker:before{content:"\f395";font-family:brand-icons}i.icon.draft2digital:before{content:"\f396";font-family:brand-icons}i.icon.dribbble:before{content:"\f17d";font-family:brand-icons}i.icon.dribbble.square:before{content:"\f397";font-family:brand-icons}i.icon.dropbox:before{content:"\f16b";font-family:brand-icons}i.icon.drupal:before{content:"\f1a9";font-family:brand-icons}i.icon.dyalog:before{content:"\f399";font-family:brand-icons}i.icon.earlybirds:before{content:"\f39a";font-family:brand-icons}i.icon.ebay:before{content:"\f4f4";font-family:brand-icons}i.icon.edge:before{content:"\f282";font-family:brand-icons}i.icon.elementor:before{content:"\f430";font-family:brand-icons}i.icon.ello:before{content:"\f5f1";font-family:brand-icons}i.icon.ember:before{content:"\f423";font-family:brand-icons}i.icon.empire:before{content:"\f1d1";font-family:brand-icons}i.icon.envira:before{content:"\f299";font-family:brand-icons}i.icon.erlang:before{content:"\f39d";font-family:brand-icons}i.icon.ethereum:before{content:"\f42e";font-family:brand-icons}i.icon.etsy:before{content:"\f2d7";font-family:brand-icons}i.icon.evernote:before{content:"\f839";font-family:brand-icons}i.icon.expeditedssl:before{content:"\f23e";font-family:brand-icons}i.icon.facebook:before{content:"\f09a";font-family:brand-icons}i.icon.facebook.f:before{content:"\f39e";font-family:brand-icons}i.icon.facebook.messenger:before{content:"\f39f";font-family:brand-icons}i.icon.facebook.square:before{content:"\f082";font-family:brand-icons}i.icon.fantasy.flight.games:before{content:"\f6dc";font-family:brand-icons}i.icon.fedex:before{content:"\f797";font-family:brand-icons}i.icon.fedora:before{content:"\f798";font-family:brand-icons}i.icon.figma:before{content:"\f799";font-family:brand-icons}i.icon.firefox:before{content:"\f269";font-family:brand-icons}i.icon.firefox.browser:before{content:"\f907";font-family:brand-icons}i.icon.first.order:before{content:"\f2b0";font-family:brand-icons}i.icon.first.order.alternate:before{content:"\f50a";font-family:brand-icons}i.icon.firstdraft:before{content:"\f3a1";font-family:brand-icons}i.icon.flickr:before{content:"\f16e";font-family:brand-icons}i.icon.flipboard:before{content:"\f44d";font-family:brand-icons}i.icon.fly:before{content:"\f417";font-family:brand-icons}i.icon.font.awesome:before{content:"\f2b4";font-family:brand-icons}i.icon.font.awesome.alternate:before{content:"\f35c";font-family:brand-icons}i.icon.font.awesome.flag:before{content:"\f425";font-family:brand-icons}i.icon.fonticons:before{content:"\f280";font-family:brand-icons}i.icon.fonticons.fi:before{content:"\f3a2";font-family:brand-icons}i.icon.fort.awesome:before{content:"\f286";font-family:brand-icons}i.icon.fort.awesome.alternate:before{content:"\f3a3";font-family:brand-icons}i.icon.forumbee:before{content:"\f211";font-family:brand-icons}i.icon.foursquare:before{content:"\f180";font-family:brand-icons}i.icon.free.code.camp:before{content:"\f2c5";font-family:brand-icons}i.icon.freebsd:before{content:"\f3a4";font-family:brand-icons}i.icon.fulcrum:before{content:"\f50b";font-family:brand-icons}i.icon.galactic.republic:before{content:"\f50c";font-family:brand-icons}i.icon.galactic.senate:before{content:"\f50d";font-family:brand-icons}i.icon.get.pocket:before{content:"\f265";font-family:brand-icons}i.icon.gg:before{content:"\f260";font-family:brand-icons}i.icon.gg.circle:before{content:"\f261";font-family:brand-icons}i.icon.git:before{content:"\f1d3";font-family:brand-icons}i.icon.git.alternate:before{content:"\f841";font-family:brand-icons}i.icon.git.square:before{content:"\f1d2";font-family:brand-icons}i.icon.github:before{content:"\f09b";font-family:brand-icons}i.icon.github.alternate:before{content:"\f113";font-family:brand-icons}i.icon.github.square:before{content:"\f092";font-family:brand-icons}i.icon.gitkraken:before{content:"\f3a6";font-family:brand-icons}i.icon.gitlab:before{content:"\f296";font-family:brand-icons}i.icon.gitter:before{content:"\f426";font-family:brand-icons}i.icon.glide:before{content:"\f2a5";font-family:brand-icons}i.icon.glide.g:before{content:"\f2a6";font-family:brand-icons}i.icon.gofore:before{content:"\f3a7";font-family:brand-icons}i.icon.goodreads:before{content:"\f3a8";font-family:brand-icons}i.icon.goodreads.g:before{content:"\f3a9";font-family:brand-icons}i.icon.google:before{content:"\f1a0";font-family:brand-icons}i.icon.google.drive:before{content:"\f3aa";font-family:brand-icons}i.icon.google.play:before{content:"\f3ab";font-family:brand-icons}i.icon.google.plus:before{content:"\f2b3";font-family:brand-icons}i.icon.google.plus.g:before{content:"\f0d5";font-family:brand-icons}i.icon.google.plus.square:before{content:"\f0d4";font-family:brand-icons}i.icon.google.wallet:before{content:"\f1ee";font-family:brand-icons}i.icon.gratipay:before{content:"\f184";font-family:brand-icons}i.icon.grav:before{content:"\f2d6";font-family:brand-icons}i.icon.gripfire:before{content:"\f3ac";font-family:brand-icons}i.icon.grunt:before{content:"\f3ad";font-family:brand-icons}i.icon.gulp:before{content:"\f3ae";font-family:brand-icons}i.icon.hacker.news:before{content:"\f1d4";font-family:brand-icons}i.icon.hacker.news.square:before{content:"\f3af";font-family:brand-icons}i.icon.hackerrank:before{content:"\f5f7";font-family:brand-icons}i.icon.hips:before{content:"\f452";font-family:brand-icons}i.icon.hire.a.helper:before{content:"\f3b0";font-family:brand-icons}i.icon.hooli:before{content:"\f427";font-family:brand-icons}i.icon.hornbill:before{content:"\f592";font-family:brand-icons}i.icon.hotjar:before{content:"\f3b1";font-family:brand-icons}i.icon.houzz:before{content:"\f27c";font-family:brand-icons}i.icon.html5:before{content:"\f13b";font-family:brand-icons}i.icon.hubspot:before{content:"\f3b2";font-family:brand-icons}i.icon.ideal:before{content:"\f913";font-family:brand-icons}i.icon.imdb:before{content:"\f2d8";font-family:brand-icons}i.icon.instagram:before{content:"\f16d";font-family:brand-icons}i.icon.intercom:before{content:"\f7af";font-family:brand-icons}i.icon.internet.explorer:before{content:"\f26b";font-family:brand-icons}i.icon.invision:before{content:"\f7b0";font-family:brand-icons}i.icon.ioxhost:before{content:"\f208";font-family:brand-icons}i.icon.itch.io:before{content:"\f83a";font-family:brand-icons}i.icon.itunes:before{content:"\f3b4";font-family:brand-icons}i.icon.itunes.note:before{content:"\f3b5";font-family:brand-icons}i.icon.java:before{content:"\f4e4";font-family:brand-icons}i.icon.jedi.order:before{content:"\f50e";font-family:brand-icons}i.icon.jenkins:before{content:"\f3b6";font-family:brand-icons}i.icon.jira:before{content:"\f7b1";font-family:brand-icons}i.icon.joget:before{content:"\f3b7";font-family:brand-icons}i.icon.joomla:before{content:"\f1aa";font-family:brand-icons}i.icon.js:before{content:"\f3b8";font-family:brand-icons}i.icon.js.square:before{content:"\f3b9";font-family:brand-icons}i.icon.jsfiddle:before{content:"\f1cc";font-family:brand-icons}i.icon.kaggle:before{content:"\f5fa";font-family:brand-icons}i.icon.keybase:before{content:"\f4f5";font-family:brand-icons}i.icon.keycdn:before{content:"\f3ba";font-family:brand-icons}i.icon.kickstarter:before{content:"\f3bb";font-family:brand-icons}i.icon.kickstarter.k:before{content:"\f3bc";font-family:brand-icons}i.icon.korvue:before{content:"\f42f";font-family:brand-icons}i.icon.laravel:before{content:"\f3bd";font-family:brand-icons}i.icon.lastfm:before{content:"\f202";font-family:brand-icons}i.icon.lastfm.square:before{content:"\f203";font-family:brand-icons}i.icon.leanpub:before{content:"\f212";font-family:brand-icons}i.icon.lesscss:before{content:"\f41d";font-family:brand-icons}i.icon.linechat:before{content:"\f3c0";font-family:brand-icons}i.icon.linkedin:before{content:"\f08c";font-family:brand-icons}i.icon.linkedin.in:before{content:"\f0e1";font-family:brand-icons}i.icon.linode:before{content:"\f2b8";font-family:brand-icons}i.icon.linux:before{content:"\f17c";font-family:brand-icons}i.icon.lyft:before{content:"\f3c3";font-family:brand-icons}i.icon.magento:before{content:"\f3c4";font-family:brand-icons}i.icon.mailchimp:before{content:"\f59e";font-family:brand-icons}i.icon.mandalorian:before{content:"\f50f";font-family:brand-icons}i.icon.markdown:before{content:"\f60f";font-family:brand-icons}i.icon.mastodon:before{content:"\f4f6";font-family:brand-icons}i.icon.maxcdn:before{content:"\f136";font-family:brand-icons}i.icon.mdb:before{content:"\f8ca";font-family:brand-icons}i.icon.medapps:before{content:"\f3c6";font-family:brand-icons}i.icon.medium:before{content:"\f23a";font-family:brand-icons}i.icon.medium.m:before{content:"\f3c7";font-family:brand-icons}i.icon.medrt:before{content:"\f3c8";font-family:brand-icons}i.icon.meetup:before{content:"\f2e0";font-family:brand-icons}i.icon.megaport:before{content:"\f5a3";font-family:brand-icons}i.icon.mendeley:before{content:"\f7b3";font-family:brand-icons}i.icon.microblog:before{content:"\f91a";font-family:brand-icons}i.icon.microsoft:before{content:"\f3ca";font-family:brand-icons}i.icon.mix:before{content:"\f3cb";font-family:brand-icons}i.icon.mixcloud:before{content:"\f289";font-family:brand-icons}i.icon.mizuni:before{content:"\f3cc";font-family:brand-icons}i.icon.modx:before{content:"\f285";font-family:brand-icons}i.icon.monero:before{content:"\f3d0";font-family:brand-icons}i.icon.napster:before{content:"\f3d2";font-family:brand-icons}i.icon.neos:before{content:"\f612";font-family:brand-icons}i.icon.nimblr:before{content:"\f5a8";font-family:brand-icons}i.icon.node:before{content:"\f419";font-family:brand-icons}i.icon.node.js:before{content:"\f3d3";font-family:brand-icons}i.icon.npm:before{content:"\f3d4";font-family:brand-icons}i.icon.ns8:before{content:"\f3d5";font-family:brand-icons}i.icon.nutritionix:before{content:"\f3d6";font-family:brand-icons}i.icon.odnoklassniki:before{content:"\f263";font-family:brand-icons}i.icon.odnoklassniki.square:before{content:"\f264";font-family:brand-icons}i.icon.old.republic:before{content:"\f510";font-family:brand-icons}i.icon.opencart:before{content:"\f23d";font-family:brand-icons}i.icon.openid:before{content:"\f19b";font-family:brand-icons}i.icon.opera:before{content:"\f26a";font-family:brand-icons}i.icon.optin.monster:before{content:"\f23c";font-family:brand-icons}i.icon.orcid:before{content:"\f8d2";font-family:brand-icons}i.icon.osi:before{content:"\f41a";font-family:brand-icons}i.icon.page4:before{content:"\f3d7";font-family:brand-icons}i.icon.pagelines:before{content:"\f18c";font-family:brand-icons}i.icon.palfed:before{content:"\f3d8";font-family:brand-icons}i.icon.patreon:before{content:"\f3d9";font-family:brand-icons}i.icon.paypal:before{content:"\f1ed";font-family:brand-icons}i.icon.penny.arcade:before{content:"\f704";font-family:brand-icons}i.icon.periscope:before{content:"\f3da";font-family:brand-icons}i.icon.phabricator:before{content:"\f3db";font-family:brand-icons}i.icon.phoenix.framework:before{content:"\f3dc";font-family:brand-icons}i.icon.phoenix.squadron:before{content:"\f511";font-family:brand-icons}i.icon.php:before{content:"\f457";font-family:brand-icons}i.icon.pied.piper:before{content:"\f2ae";font-family:brand-icons}i.icon.pied.piper.alternate:before{content:"\f1a8";font-family:brand-icons}i.icon.pied.piper.hat:before{content:"\f4e5";font-family:brand-icons}i.icon.pied.piper.pp:before{content:"\f1a7";font-family:brand-icons}i.icon.pied.piper.square:before{content:"\f91e";font-family:brand-icons}i.icon.pinterest:before{content:"\f0d2";font-family:brand-icons}i.icon.pinterest.p:before{content:"\f231";font-family:brand-icons}i.icon.pinterest.square:before{content:"\f0d3";font-family:brand-icons}i.icon.playstation:before{content:"\f3df";font-family:brand-icons}i.icon.product.hunt:before{content:"\f288";font-family:brand-icons}i.icon.pushed:before{content:"\f3e1";font-family:brand-icons}i.icon.python:before{content:"\f3e2";font-family:brand-icons}i.icon.qq:before{content:"\f1d6";font-family:brand-icons}i.icon.quinscape:before{content:"\f459";font-family:brand-icons}i.icon.quora:before{content:"\f2c4";font-family:brand-icons}i.icon.r.project:before{content:"\f4f7";font-family:brand-icons}i.icon.raspberry.pi:before{content:"\f7bb";font-family:brand-icons}i.icon.ravelry:before{content:"\f2d9";font-family:brand-icons}i.icon.react:before{content:"\f41b";font-family:brand-icons}i.icon.reacteurope:before{content:"\f75d";font-family:brand-icons}i.icon.readme:before{content:"\f4d5";font-family:brand-icons}i.icon.rebel:before{content:"\f1d0";font-family:brand-icons}i.icon.reddit:before{content:"\f1a1";font-family:brand-icons}i.icon.reddit.alien:before{content:"\f281";font-family:brand-icons}i.icon.reddit.square:before{content:"\f1a2";font-family:brand-icons}i.icon.redhat:before{content:"\f7bc";font-family:brand-icons}i.icon.redriver:before{content:"\f3e3";font-family:brand-icons}i.icon.redyeti:before{content:"\f69d";font-family:brand-icons}i.icon.renren:before{content:"\f18b";font-family:brand-icons}i.icon.replyd:before{content:"\f3e6";font-family:brand-icons}i.icon.researchgate:before{content:"\f4f8";font-family:brand-icons}i.icon.resolving:before{content:"\f3e7";font-family:brand-icons}i.icon.rev:before{content:"\f5b2";font-family:brand-icons}i.icon.rocketchat:before{content:"\f3e8";font-family:brand-icons}i.icon.rockrms:before{content:"\f3e9";font-family:brand-icons}i.icon.safari:before{content:"\f267";font-family:brand-icons}i.icon.salesforce:before{content:"\f83b";font-family:brand-icons}i.icon.sass:before{content:"\f41e";font-family:brand-icons}i.icon.schlix:before{content:"\f3ea";font-family:brand-icons}i.icon.scribd:before{content:"\f28a";font-family:brand-icons}i.icon.searchengin:before{content:"\f3eb";font-family:brand-icons}i.icon.sellcast:before{content:"\f2da";font-family:brand-icons}i.icon.sellsy:before{content:"\f213";font-family:brand-icons}i.icon.servicestack:before{content:"\f3ec";font-family:brand-icons}i.icon.shirtsinbulk:before{content:"\f214";font-family:brand-icons}i.icon.shopware:before{content:"\f5b5";font-family:brand-icons}i.icon.simplybuilt:before{content:"\f215";font-family:brand-icons}i.icon.sistrix:before{content:"\f3ee";font-family:brand-icons}i.icon.sith:before{content:"\f512";font-family:brand-icons}i.icon.sketch:before{content:"\f7c6";font-family:brand-icons}i.icon.skyatlas:before{content:"\f216";font-family:brand-icons}i.icon.skype:before{content:"\f17e";font-family:brand-icons}i.icon.slack:before{content:"\f198";font-family:brand-icons}i.icon.slack.hash:before{content:"\f3ef";font-family:brand-icons}i.icon.slideshare:before{content:"\f1e7";font-family:brand-icons}i.icon.snapchat:before{content:"\f2ab";font-family:brand-icons}i.icon.snapchat.ghost:before{content:"\f2ac";font-family:brand-icons}i.icon.snapchat.square:before{content:"\f2ad";font-family:brand-icons}i.icon.soundcloud:before{content:"\f1be";font-family:brand-icons}i.icon.sourcetree:before{content:"\f7d3";font-family:brand-icons}i.icon.speakap:before{content:"\f3f3";font-family:brand-icons}i.icon.speaker.deck:before{content:"\f83c";font-family:brand-icons}i.icon.spotify:before{content:"\f1bc";font-family:brand-icons}i.icon.squarespace:before{content:"\f5be";font-family:brand-icons}i.icon.stack.exchange:before{content:"\f18d";font-family:brand-icons}i.icon.stack.overflow:before{content:"\f16c";font-family:brand-icons}i.icon.stackpath:before{content:"\f842";font-family:brand-icons}i.icon.staylinked:before{content:"\f3f5";font-family:brand-icons}i.icon.steam:before{content:"\f1b6";font-family:brand-icons}i.icon.steam.square:before{content:"\f1b7";font-family:brand-icons}i.icon.steam.symbol:before{content:"\f3f6";font-family:brand-icons}i.icon.sticker.mule:before{content:"\f3f7";font-family:brand-icons}i.icon.strava:before{content:"\f428";font-family:brand-icons}i.icon.stripe:before{content:"\f429";font-family:brand-icons}i.icon.stripe.s:before{content:"\f42a";font-family:brand-icons}i.icon.studiovinari:before{content:"\f3f8";font-family:brand-icons}i.icon.stumbleupon:before{content:"\f1a4";font-family:brand-icons}i.icon.stumbleupon.circle:before{content:"\f1a3";font-family:brand-icons}i.icon.superpowers:before{content:"\f2dd";font-family:brand-icons}i.icon.supple:before{content:"\f3f9";font-family:brand-icons}i.icon.suse:before{content:"\f7d6";font-family:brand-icons}i.icon.swift:before{content:"\f8e1";font-family:brand-icons}i.icon.symfony:before{content:"\f83d";font-family:brand-icons}i.icon.teamspeak:before{content:"\f4f9";font-family:brand-icons}i.icon.telegram:before{content:"\f2c6";font-family:brand-icons}i.icon.telegram.plane:before{content:"\f3fe";font-family:brand-icons}i.icon.tencent.weibo:before{content:"\f1d5";font-family:brand-icons}i.icon.themeco:before{content:"\f5c6";font-family:brand-icons}i.icon.themeisle:before{content:"\f2b2";font-family:brand-icons}i.icon.think.peaks:before{content:"\f731";font-family:brand-icons}i.icon.trade.federation:before{content:"\f513";font-family:brand-icons}i.icon.trello:before{content:"\f181";font-family:brand-icons}i.icon.tripadvisor:before{content:"\f262";font-family:brand-icons}i.icon.tumblr:before{content:"\f173";font-family:brand-icons}i.icon.tumblr.square:before{content:"\f174";font-family:brand-icons}i.icon.twitch:before{content:"\f1e8";font-family:brand-icons}i.icon.twitter:before{content:"\f099";font-family:brand-icons}i.icon.twitter.square:before{content:"\f081";font-family:brand-icons}i.icon.typo3:before{content:"\f42b";font-family:brand-icons}i.icon.uber:before{content:"\f402";font-family:brand-icons}i.icon.ubuntu:before{content:"\f7df";font-family:brand-icons}i.icon.uikit:before{content:"\f403";font-family:brand-icons}i.icon.umbraco:before{content:"\f8e8";font-family:brand-icons}i.icon.uniregistry:before{content:"\f404";font-family:brand-icons}i.icon.unity:before{content:"\f949";font-family:brand-icons}i.icon.untappd:before{content:"\f405";font-family:brand-icons}i.icon.ups:before{content:"\f7e0";font-family:brand-icons}i.icon.usb:before{content:"\f287";font-family:brand-icons}i.icon.usps:before{content:"\f7e1";font-family:brand-icons}i.icon.ussunnah:before{content:"\f407";font-family:brand-icons}i.icon.vaadin:before{content:"\f408";font-family:brand-icons}i.icon.viacoin:before{content:"\f237";font-family:brand-icons}i.icon.viadeo:before{content:"\f2a9";font-family:brand-icons}i.icon.viadeo.square:before{content:"\f2aa";font-family:brand-icons}i.icon.viber:before{content:"\f409";font-family:brand-icons}i.icon.vimeo:before{content:"\f40a";font-family:brand-icons}i.icon.vimeo.square:before{content:"\f194";font-family:brand-icons}i.icon.vimeo.v:before{content:"\f27d";font-family:brand-icons}i.icon.vine:before{content:"\f1ca";font-family:brand-icons}i.icon.vk:before{content:"\f189";font-family:brand-icons}i.icon.vnv:before{content:"\f40b";font-family:brand-icons}i.icon.vuejs:before{content:"\f41f";font-family:brand-icons}i.icon.waze:before{content:"\f83f";font-family:brand-icons}i.icon.weebly:before{content:"\f5cc";font-family:brand-icons}i.icon.weibo:before{content:"\f18a";font-family:brand-icons}i.icon.weixin:before{content:"\f1d7";font-family:brand-icons}i.icon.whatsapp:before{content:"\f232";font-family:brand-icons}i.icon.whatsapp.square:before{content:"\f40c";font-family:brand-icons}i.icon.whmcs:before{content:"\f40d";font-family:brand-icons}i.icon.wikipedia.w:before{content:"\f266";font-family:brand-icons}i.icon.windows:before{content:"\f17a";font-family:brand-icons}i.icon.wix:before{content:"\f5cf";font-family:brand-icons}i.icon.wizards.of.the.coast:before{content:"\f730";font-family:brand-icons}i.icon.wolf.pack.battalion:before{content:"\f514";font-family:brand-icons}i.icon.wordpress:before{content:"\f19a";font-family:brand-icons}i.icon.wordpress.simple:before{content:"\f411";font-family:brand-icons}i.icon.wpbeginner:before{content:"\f297";font-family:brand-icons}i.icon.wpexplorer:before{content:"\f2de";font-family:brand-icons}i.icon.wpforms:before{content:"\f298";font-family:brand-icons}i.icon.wpressr:before{content:"\f3e4";font-family:brand-icons}i.icon.xbox:before{content:"\f412";font-family:brand-icons}i.icon.xing:before{content:"\f168";font-family:brand-icons}i.icon.xing.square:before{content:"\f169";font-family:brand-icons}i.icon.y.combinator:before{content:"\f23b";font-family:brand-icons}i.icon.yahoo:before{content:"\f19e";font-family:brand-icons}i.icon.yammer:before{content:"\f840";font-family:brand-icons}i.icon.yandex:before{content:"\f413";font-family:brand-icons}i.icon.yandex.international:before{content:"\f414";font-family:brand-icons}i.icon.yarn:before{content:"\f7e3";font-family:brand-icons}i.icon.yelp:before{content:"\f1e9";font-family:brand-icons}i.icon.yoast:before{content:"\f2b1";font-family:brand-icons}i.icon.youtube:before{content:"\f167";font-family:brand-icons}i.icon.youtube.square:before{content:"\f431";font-family:brand-icons}i.icon.zhihu:before{content:"\f63f";font-family:brand-icons}i.icon.american.express:before{content:"\f1f3";font-family:brand-icons}i.icon.american.express.card:before{content:"\f1f3";font-family:brand-icons}i.icon.amex:before{content:"\f1f3";font-family:brand-icons}i.icon.bitbucket.square:before{content:"\f171";font-family:brand-icons}i.icon.bluetooth.alternative:before{content:"\f294";font-family:brand-icons}i.icon.credit.card.amazon.pay:before{content:"\f42d";font-family:brand-icons}i.icon.credit.card.american.express:before{content:"\f1f3";font-family:brand-icons}i.icon.credit.card.diners.club:before{content:"\f24c";font-family:brand-icons}i.icon.credit.card.discover:before{content:"\f1f2";font-family:brand-icons}i.icon.credit.card.jcb:before{content:"\f24b";font-family:brand-icons}i.icon.credit.card.mastercard:before{content:"\f1f1";font-family:brand-icons}i.icon.credit.card.paypal:before{content:"\f1f4";font-family:brand-icons}i.icon.credit.card.stripe:before{content:"\f1f5";font-family:brand-icons}i.icon.credit.card.visa:before{content:"\f1f0";font-family:brand-icons}i.icon.diners.club:before{content:"\f24c";font-family:brand-icons}i.icon.diners.club.card:before{content:"\f24c";font-family:brand-icons}i.icon.discover:before{content:"\f1f2";font-family:brand-icons}i.icon.discover.card:before{content:"\f1f2";font-family:brand-icons}i.icon.disk.outline:before{content:"\f369";font-family:brand-icons}i.icon.dribble:before{content:"\f17d";font-family:brand-icons}i.icon.eercast:before{content:"\f2da";font-family:brand-icons}i.icon.envira.gallery:before{content:"\f299";font-family:brand-icons}i.icon.fa:before{content:"\f2b4";font-family:brand-icons}i.icon.facebook.official:before{content:"\f082";font-family:brand-icons}i.icon.five.hundred.pixels:before{content:"\f26e";font-family:brand-icons}i.icon.gittip:before{content:"\f184";font-family:brand-icons}i.icon.google.plus.circle:before{content:"\f2b3";font-family:brand-icons}i.icon.google.plus.official:before{content:"\f2b3";font-family:brand-icons}i.icon.japan.credit.bureau:before{content:"\f24b";font-family:brand-icons}i.icon.japan.credit.bureau.card:before{content:"\f24b";font-family:brand-icons}i.icon.jcb:before{content:"\f24b";font-family:brand-icons}i.icon.linkedin.square:before{content:"\f08c";font-family:brand-icons}i.icon.mastercard:before{content:"\f1f1";font-family:brand-icons}i.icon.mastercard.card:before{content:"\f1f1";font-family:brand-icons}i.icon.microsoft.edge:before{content:"\f282";font-family:brand-icons}i.icon.ms.edge:before{content:"\f282";font-family:brand-icons}i.icon.new.pied.piper:before{content:"\f2ae";font-family:brand-icons}i.icon.optinmonster:before{content:"\f23c";font-family:brand-icons}i.icon.paypal.card:before{content:"\f1f4";font-family:brand-icons}i.icon.pied.piper.hat:before{content:"\f2ae";font-family:brand-icons}i.icon.pocket:before{content:"\f265";font-family:brand-icons}i.icon.stripe.card:before{content:"\f1f5";font-family:brand-icons}i.icon.theme.isle:before{content:"\f2b2";font-family:brand-icons}i.icon.visa:before{content:"\f1f0";font-family:brand-icons}i.icon.visa.card:before{content:"\f1f0";font-family:brand-icons}i.icon.wechat:before{content:"\f1d7";font-family:brand-icons}i.icon.wikipedia:before{content:"\f266";font-family:brand-icons}i.icon.wordpress.beginner:before{content:"\f297";font-family:brand-icons}i.icon.wordpress.forms:before{content:"\f298";font-family:brand-icons}i.icon.yc:before{content:"\f23b";font-family:brand-icons}i.icon.ycombinator:before{content:"\f23b";font-family:brand-icons}i.icon.youtube.play:before{content:"\f167";font-family:brand-icons}/*! + * # Fomantic-UI - Image + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.image{position:relative;display:inline-block;vertical-align:middle;max-width:100%;background-color:transparent}img.ui.image{display:block}.ui.image img,.ui.image svg{display:block;max-width:100%;height:auto}.ui.hidden.images,.ui.ui.hidden.image{display:none}.ui.hidden.transition.image,.ui.hidden.transition.images{display:block;visibility:hidden}.ui.images>.hidden.transition{display:inline-block;visibility:hidden}.ui.disabled.image,.ui.disabled.images{cursor:default;opacity:.45}.ui.inline.image,.ui.inline.image img,.ui.inline.image svg{display:inline-block}.ui.top.aligned.image,.ui.top.aligned.image img,.ui.top.aligned.image svg,.ui.top.aligned.images .image{display:inline-block;vertical-align:top}.ui.middle.aligned.image,.ui.middle.aligned.image img,.ui.middle.aligned.image svg,.ui.middle.aligned.images .image{display:inline-block;vertical-align:middle}.ui.bottom.aligned.image,.ui.bottom.aligned.image img,.ui.bottom.aligned.image svg,.ui.bottom.aligned.images .image{display:inline-block;vertical-align:bottom}.ui.rounded.image,.ui.rounded.image>*,.ui.rounded.images .image,.ui.rounded.images .image>*{border-radius:.3125em}.ui.bordered.image img,.ui.bordered.image svg,.ui.bordered.images .image,.ui.bordered.images img,.ui.bordered.images svg,img.ui.bordered.image{border:1px solid rgba(0,0,0,.1)}.ui.circular.image,.ui.circular.images{overflow:hidden}.ui.circular.image,.ui.circular.image>*,.ui.circular.images .image,.ui.circular.images .image>*{border-radius:500rem}.ui.fluid.image,.ui.fluid.image img,.ui.fluid.image svg,.ui.fluid.images,.ui.fluid.images img,.ui.fluid.images svg{display:block;width:100%;height:auto}.ui.avatar.image,.ui.avatar.image img,.ui.avatar.image svg,.ui.avatar.images .image,.ui.avatar.images img,.ui.avatar.images svg{margin-right:.25em;display:inline-block;width:2em;height:2em;border-radius:500rem}.ui.spaced.image{display:inline-block!important;margin-left:.5em;margin-right:.5em}.ui[class*="left spaced"].image{margin-left:.5em;margin-right:0}.ui[class*="right spaced"].image{margin-left:0;margin-right:.5em}.ui.floated.image,.ui.floated.images{float:left;margin-right:1em;margin-bottom:1em}.ui.right.floated.image,.ui.right.floated.images{float:right;margin-right:0;margin-bottom:1em;margin-left:1em}.ui.floated.image:last-child,.ui.floated.images:last-child{margin-bottom:0}.ui.centered.image,.ui.centered.images{margin-left:auto;margin-right:auto}.ui.medium.image,.ui.medium.images .image,.ui.medium.images img,.ui.medium.images svg{width:300px;height:auto;font-size:1rem}.ui.mini.image,.ui.mini.images .image,.ui.mini.images img,.ui.mini.images svg{width:35px;height:auto;font-size:.78571429rem}.ui.tiny.image,.ui.tiny.images .image,.ui.tiny.images img,.ui.tiny.images svg{width:80px;height:auto;font-size:.85714286rem}.ui.small.image,.ui.small.images .image,.ui.small.images img,.ui.small.images svg{width:150px;height:auto;font-size:.92857143rem}.ui.large.image,.ui.large.images .image,.ui.large.images img,.ui.large.images svg{width:450px;height:auto;font-size:1.14285714rem}.ui.big.image,.ui.big.images .image,.ui.big.images img,.ui.big.images svg{width:600px;height:auto;font-size:1.28571429rem}.ui.huge.image,.ui.huge.images .image,.ui.huge.images img,.ui.huge.images svg{width:800px;height:auto;font-size:1.42857143rem}.ui.massive.image,.ui.massive.images .image,.ui.massive.images img,.ui.massive.images svg{width:960px;height:auto;font-size:1.71428571rem}.ui.images{font-size:0;margin:0 -.25rem 0}.ui.images .image,.ui.images>img,.ui.images>svg{display:inline-block;margin:0 .25rem .5rem}/*! + * # Fomantic-UI - Input + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.input{position:relative;font-weight:400;font-style:normal;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;color:rgba(0,0,0,.87)}.ui.input>input{margin:0;max-width:100%;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;outline:0;-webkit-tap-highlight-color:rgba(255,255,255,0);text-align:left;line-height:1.21428571em;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;padding:.67857143em 1em;background:#fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);border-radius:.28571429rem;-webkit-transition:border-color .1s ease,-webkit-box-shadow .1s ease;transition:border-color .1s ease,-webkit-box-shadow .1s ease;transition:box-shadow .1s ease,border-color .1s ease;transition:box-shadow .1s ease,border-color .1s ease,-webkit-box-shadow .1s ease;-webkit-box-shadow:none;box-shadow:none}.ui.input>input::-webkit-input-placeholder{color:rgba(191,191,191,.87)}.ui.input>input::-moz-placeholder{color:rgba(191,191,191,.87)}.ui.input>input:-ms-input-placeholder{color:rgba(191,191,191,.87)}.ui.disabled.input,.ui.input:not(.disabled) input[disabled]{opacity:.45}.ui.disabled.input>input,.ui.input:not(.disabled) input[disabled]{pointer-events:none}.ui.input.down input,.ui.input>input:active{border-color:rgba(0,0,0,.3);background:#fafafa;color:rgba(0,0,0,.87);-webkit-box-shadow:none;box-shadow:none}.ui.loading.loading.input>i.icon:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loading.loading.input>i.icon:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent}.ui.input.focus>input,.ui.input>input:focus{border-color:#85b7d9;background:#fff;color:rgba(0,0,0,.8);-webkit-box-shadow:none;box-shadow:none}.ui.input.focus>input::-webkit-input-placeholder,.ui.input>input:focus::-webkit-input-placeholder{color:rgba(115,115,115,.87)}.ui.input.focus>input::-moz-placeholder,.ui.input>input:focus::-moz-placeholder{color:rgba(115,115,115,.87)}.ui.input.focus>input:-ms-input-placeholder,.ui.input>input:focus:-ms-input-placeholder{color:rgba(115,115,115,.87)}.ui.input.error>input{background-color:#fff6f6;border-color:#e0b4b4;color:#9f3a38;-webkit-box-shadow:none;box-shadow:none}.ui.input.error>input::-webkit-input-placeholder{color:#e7bdbc}.ui.input.error>input::-moz-placeholder{color:#e7bdbc}.ui.input.error>input:-ms-input-placeholder{color:#e7bdbc}.ui.input.error>input:focus::-webkit-input-placeholder{color:#da9796}.ui.input.error>input:focus::-moz-placeholder{color:#da9796}.ui.input.error>input:focus:-ms-input-placeholder{color:#da9796}.ui.input.info>input{background-color:#f8ffff;border-color:#a9d5de;color:#276f86;-webkit-box-shadow:none;box-shadow:none}.ui.input.info>input::-webkit-input-placeholder{color:#98cfe1}.ui.input.info>input::-moz-placeholder{color:#98cfe1}.ui.input.info>input:-ms-input-placeholder{color:#98cfe1}.ui.input.info>input:focus::-webkit-input-placeholder{color:#70bdd6}.ui.input.info>input:focus::-moz-placeholder{color:#70bdd6}.ui.input.info>input:focus:-ms-input-placeholder{color:#70bdd6}.ui.input.success>input{background-color:#fcfff5;border-color:#a3c293;color:#2c662d;-webkit-box-shadow:none;box-shadow:none}.ui.input.success>input::-webkit-input-placeholder{color:#8fcf90}.ui.input.success>input::-moz-placeholder{color:#8fcf90}.ui.input.success>input:-ms-input-placeholder{color:#8fcf90}.ui.input.success>input:focus::-webkit-input-placeholder{color:#6cbf6d}.ui.input.success>input:focus::-moz-placeholder{color:#6cbf6d}.ui.input.success>input:focus:-ms-input-placeholder{color:#6cbf6d}.ui.input.warning>input{background-color:#fffaf3;border-color:#c9ba9b;color:#573a08;-webkit-box-shadow:none;box-shadow:none}.ui.input.warning>input::-webkit-input-placeholder{color:#edad3e}.ui.input.warning>input::-moz-placeholder{color:#edad3e}.ui.input.warning>input:-ms-input-placeholder{color:#edad3e}.ui.input.warning>input:focus::-webkit-input-placeholder{color:#e39715}.ui.input.warning>input:focus::-moz-placeholder{color:#e39715}.ui.input.warning>input:focus:-ms-input-placeholder{color:#e39715}.ui.transparent.input>input,.ui.transparent.input>textarea{border-color:transparent!important;background-color:transparent!important;padding:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-radius:0!important}.field .ui.transparent.input>textarea{padding:.67857143em 1em}:not(.field)>.ui.transparent.icon.input>i.icon{width:1.1em}:not(.field)>.ui.ui.ui.transparent.icon.input>input{padding-left:0;padding-right:2em}:not(.field)>.ui.ui.ui.transparent[class*="left icon"].input>input{padding-left:2em;padding-right:0}.ui.transparent.inverted.input{color:#fff}.ui.ui.transparent.inverted.input>input,.ui.ui.transparent.inverted.input>textarea{color:inherit}.ui.transparent.inverted.input>input::-webkit-input-placeholder{color:rgba(255,255,255,.5)}.ui.transparent.inverted.input>input::-moz-placeholder{color:rgba(255,255,255,.5)}.ui.transparent.inverted.input>input:-ms-input-placeholder{color:rgba(255,255,255,.5)}.ui.icon.input>i.icon{cursor:default;position:absolute;line-height:1;text-align:center;top:0;right:0;margin:0;height:100%;width:2.67142857em;opacity:.5;border-radius:0 .28571429rem .28571429rem 0;-webkit-transition:opacity .3s ease;transition:opacity .3s ease}.ui.icon.input>i.icon:not(.link){pointer-events:none}.ui.ui.ui.ui.icon.input>input,.ui.ui.ui.ui.icon.input>textarea{padding-right:2.67142857em}.ui.icon.input>i.icon:after,.ui.icon.input>i.icon:before{left:0;position:absolute;text-align:center;top:50%;width:100%;margin-top:-.5em}.ui.icon.input>i.link.icon{cursor:pointer}.ui.icon.input>i.circular.icon{top:.35em;right:.5em}.ui[class*="left icon"].input>i.icon{right:auto;left:1px;border-radius:.28571429rem 0 0 .28571429rem}.ui[class*="left icon"].input>i.circular.icon{right:auto;left:.5em}.ui.ui.ui.ui[class*="left icon"].input>input,.ui.ui.ui.ui[class*="left icon"].input>textarea{padding-left:2.67142857em;padding-right:1em}.ui.icon.input>input:focus~i.icon,.ui.icon.input>textarea:focus~i.icon{opacity:1}.ui.labeled.input>.label{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin:0;font-size:1em}.ui.labeled.input>.label:not(.corner){padding-top:.78571429em;padding-bottom:.78571429em}.ui.labeled.input:not([class*="corner labeled"]) .label:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.ui.labeled.input:not([class*="corner labeled"]) .label:first-child+input{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:transparent}.ui.labeled.input:not([class*="corner labeled"]) .label:first-child+input:focus{border-left-color:#85b7d9}.ui[class*="right labeled"].input>input{border-top-right-radius:0!important;border-bottom-right-radius:0!important;border-right-color:transparent!important}.ui[class*="right labeled"].input>input+.label{border-top-left-radius:0;border-bottom-left-radius:0}.ui[class*="right labeled"].input>input:focus{border-right-color:#85b7d9!important}.ui.labeled.input .corner.label{top:1px;right:1px;font-size:.64285714em;border-radius:0 .28571429rem 0 0}.ui[class*="corner labeled"]:not([class*="left corner labeled"]).labeled.input>input,.ui[class*="corner labeled"]:not([class*="left corner labeled"]).labeled.input>textarea{padding-right:2.5em!important}.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"])>input,.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"])>textarea{padding-right:3.25em!important}.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"])>.icon{margin-right:1.25em}.ui[class*="left corner labeled"].labeled.input>input,.ui[class*="left corner labeled"].labeled.input>textarea{padding-left:2.5em!important}.ui[class*="left corner labeled"].icon.input>input,.ui[class*="left corner labeled"].icon.input>textarea{padding-left:3.25em!important}.ui[class*="left corner labeled"].icon.input>.icon{margin-left:1.25em}.ui.icon.input>textarea~.icon{height:3em}:not(.field)>.ui.transparent.icon.input>textarea~.icon{height:1.3em}.ui.input>.ui.corner.label{top:1px;right:1px}.ui.input>.ui.left.corner.label{right:auto;left:1px}.ui.action.input.error>.ui.button,.ui.form>.field.error>.ui.action.input>.ui.button,.ui.form>.field.error>.ui.labeled.input:not([class*="corner labeled"])>.ui.label,.ui.labeled.input.error:not([class*="corner labeled"])>.ui.label{border-top:1px solid #e0b4b4;border-bottom:1px solid #e0b4b4}.ui.form>.field.error>.ui.labeled.input:not(.right):not([class*="corner labeled"])>.ui.label,.ui.form>.field.error>.ui.left.action.input>.ui.button,.ui.labeled.input.error:not(.right):not([class*="corner labeled"])>.ui.label,.ui.left.action.input.error>.ui.button{border-left:1px solid #e0b4b4}.ui.action.input.error:not(.left)>input+.ui.button,.ui.form>.field.error>.ui.action.input:not(.left)>input+.ui.button,.ui.form>.field.error>.ui.right.labeled.input:not([class*="corner labeled"])>input+.ui.label,.ui.right.labeled.input.error:not([class*="corner labeled"])>input+.ui.label{border-right:1px solid #e0b4b4}.ui.form>.field.error>.ui.right.labeled.input:not([class*="corner labeled"])>.ui.label:first-child,.ui.right.labeled.input.error:not([class*="corner labeled"])>.ui.label:first-child{border-left:1px solid #e0b4b4}.ui.action.input.info>.ui.button,.ui.form>.field.info>.ui.action.input>.ui.button,.ui.form>.field.info>.ui.labeled.input:not([class*="corner labeled"])>.ui.label,.ui.labeled.input.info:not([class*="corner labeled"])>.ui.label{border-top:1px solid #a9d5de;border-bottom:1px solid #a9d5de}.ui.form>.field.info>.ui.labeled.input:not(.right):not([class*="corner labeled"])>.ui.label,.ui.form>.field.info>.ui.left.action.input>.ui.button,.ui.labeled.input.info:not(.right):not([class*="corner labeled"])>.ui.label,.ui.left.action.input.info>.ui.button{border-left:1px solid #a9d5de}.ui.action.input.info:not(.left)>input+.ui.button,.ui.form>.field.info>.ui.action.input:not(.left)>input+.ui.button,.ui.form>.field.info>.ui.right.labeled.input:not([class*="corner labeled"])>input+.ui.label,.ui.right.labeled.input.info:not([class*="corner labeled"])>input+.ui.label{border-right:1px solid #a9d5de}.ui.form>.field.info>.ui.right.labeled.input:not([class*="corner labeled"])>.ui.label:first-child,.ui.right.labeled.input.info:not([class*="corner labeled"])>.ui.label:first-child{border-left:1px solid #a9d5de}.ui.action.input.success>.ui.button,.ui.form>.field.success>.ui.action.input>.ui.button,.ui.form>.field.success>.ui.labeled.input:not([class*="corner labeled"])>.ui.label,.ui.labeled.input.success:not([class*="corner labeled"])>.ui.label{border-top:1px solid #a3c293;border-bottom:1px solid #a3c293}.ui.form>.field.success>.ui.labeled.input:not(.right):not([class*="corner labeled"])>.ui.label,.ui.form>.field.success>.ui.left.action.input>.ui.button,.ui.labeled.input.success:not(.right):not([class*="corner labeled"])>.ui.label,.ui.left.action.input.success>.ui.button{border-left:1px solid #a3c293}.ui.action.input.success:not(.left)>input+.ui.button,.ui.form>.field.success>.ui.action.input:not(.left)>input+.ui.button,.ui.form>.field.success>.ui.right.labeled.input:not([class*="corner labeled"])>input+.ui.label,.ui.right.labeled.input.success:not([class*="corner labeled"])>input+.ui.label{border-right:1px solid #a3c293}.ui.form>.field.success>.ui.right.labeled.input:not([class*="corner labeled"])>.ui.label:first-child,.ui.right.labeled.input.success:not([class*="corner labeled"])>.ui.label:first-child{border-left:1px solid #a3c293}.ui.action.input.warning>.ui.button,.ui.form>.field.warning>.ui.action.input>.ui.button,.ui.form>.field.warning>.ui.labeled.input:not([class*="corner labeled"])>.ui.label,.ui.labeled.input.warning:not([class*="corner labeled"])>.ui.label{border-top:1px solid #c9ba9b;border-bottom:1px solid #c9ba9b}.ui.form>.field.warning>.ui.labeled.input:not(.right):not([class*="corner labeled"])>.ui.label,.ui.form>.field.warning>.ui.left.action.input>.ui.button,.ui.labeled.input.warning:not(.right):not([class*="corner labeled"])>.ui.label,.ui.left.action.input.warning>.ui.button{border-left:1px solid #c9ba9b}.ui.action.input.warning:not(.left)>input+.ui.button,.ui.form>.field.warning>.ui.action.input:not(.left)>input+.ui.button,.ui.form>.field.warning>.ui.right.labeled.input:not([class*="corner labeled"])>input+.ui.label,.ui.right.labeled.input.warning:not([class*="corner labeled"])>input+.ui.label{border-right:1px solid #c9ba9b}.ui.form>.field.warning>.ui.right.labeled.input:not([class*="corner labeled"])>.ui.label:first-child,.ui.right.labeled.input.warning:not([class*="corner labeled"])>.ui.label:first-child{border-left:1px solid #c9ba9b}.ui.action.input>.button,.ui.action.input>.buttons{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto}.ui.action.input>.button,.ui.action.input>.buttons>.button{padding-top:.78571429em;padding-bottom:.78571429em;margin:0}.ui[class*="left action"].input>input{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:transparent}.ui.action.input:not([class*="left action"])>input{border-top-right-radius:0;border-bottom-right-radius:0;border-right-color:transparent}.ui.action.input>.button:first-child,.ui.action.input>.buttons:first-child>.button,.ui.action.input>.dropdown:first-child{border-radius:.28571429rem 0 0 .28571429rem}.ui.action.input>.button:not(:first-child),.ui.action.input>.buttons:not(:first-child)>.button,.ui.action.input>.dropdown:not(:first-child){border-radius:0}.ui.action.input>.button:last-child,.ui.action.input>.buttons:last-child>.button,.ui.action.input>.dropdown:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.action.input:not([class*="left action"])>input:focus{border-right-color:#85b7d9}.ui.ui[class*="left action"].input>input:focus{border-left-color:#85b7d9}.ui.inverted.input>input{border:none}.ui.fluid.input{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.fluid.input>input{width:0!important}.ui.input{font-size:1em}.ui.mini.input{font-size:.78571429em}.ui.tiny.input{font-size:.85714286em}.ui.small.input{font-size:.92857143em}.ui.large.input{font-size:1.14285714em}.ui.big.input{font-size:1.28571429em}.ui.huge.input{font-size:1.42857143em}.ui.massive.input{font-size:1.71428571em}/*! + * # Fomantic-UI - Label + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.label{display:inline-block;line-height:1;vertical-align:baseline;margin:0 .14285714em;background-color:#e8e8e8;background-image:none;padding:.5833em .833em;color:rgba(0,0,0,.6);text-transform:none;font-weight:700;border:0 solid transparent;border-radius:.28571429rem;-webkit-transition:background .1s ease;transition:background .1s ease}.ui.label:first-child{margin-left:0}.ui.label:last-child{margin-right:0}a.ui.label{cursor:pointer}.ui.label>a{cursor:pointer;color:inherit;opacity:.5;-webkit-transition:.1s opacity ease;transition:.1s opacity ease}.ui.label>a:hover{opacity:1}.ui.label>img{width:auto!important;vertical-align:middle;height:2.1666em}.ui.label>.icon,.ui.left.icon.label>.icon{width:auto;margin:0 .75em 0 0}.ui.label>.detail{display:inline-block;vertical-align:top;font-weight:700;margin-left:1em;opacity:.8}.ui.label>.detail .icon{margin:0 .25em 0 0}.ui.label>.close.icon,.ui.label>.delete.icon{cursor:pointer;font-size:.92857143em;opacity:.5;-webkit-transition:background .1s ease;transition:background .1s ease}.ui.label>.close.icon:hover,.ui.label>.delete.icon:hover{opacity:1}.ui.label.left.icon>.close.icon,.ui.label.left.icon>.delete.icon{margin:0 .5em 0 0}.ui.label:not(.icon)>.close.icon,.ui.label:not(.icon)>.delete.icon{margin:0 0 0 .5em}.ui.icon.label>.icon{margin:0 auto}.ui.right.icon.label>.icon{margin:0 0 0 .75em}.ui.labels>.label{margin:0 .5em .5em 0}.ui.header>.ui.label{margin-top:-.29165em}.ui.attached.segment>.ui.top.left.attached.label,.ui.bottom.attached.segment>.ui.top.left.attached.label{border-top-left-radius:0}.ui.attached.segment>.ui.top.right.attached.label,.ui.bottom.attached.segment>.ui.top.right.attached.label{border-top-right-radius:0}.ui.top.attached.segment>.ui.bottom.left.attached.label{border-bottom-left-radius:0}.ui.top.attached.segment>.ui.bottom.right.attached.label{border-bottom-right-radius:0}.ui.top.attached.label+:not(.attached),.ui.top.attached.label~.ui.bottom.attached.label+:not(.attached){margin-top:2rem!important}.ui.bottom.attached.label~:last-child:not(.attached){margin-top:0;margin-bottom:2rem!important}.ui.image.label{width:auto!important;margin-top:0;margin-bottom:0;max-width:9999px;vertical-align:baseline;text-transform:none;background:#e8e8e8;padding:.5833em .833em .5833em .5em;border-radius:.28571429rem;-webkit-box-shadow:none;box-shadow:none}.ui.image.label img{display:inline-block;vertical-align:top;height:2.1666em;margin:-.5833em .5em -.5833em -.5em;border-radius:.28571429rem 0 0 .28571429rem}.ui.image.label .detail{background:rgba(0,0,0,.1);margin:-.5833em -.833em -.5833em .5em;padding:.5833em .833em;border-radius:0 .28571429rem .28571429rem 0}.ui.tag.label,.ui.tag.labels .label{margin-left:1em;position:relative;padding-left:1.5em;padding-right:1.5em;border-radius:0 .28571429rem .28571429rem 0;-webkit-transition:none;transition:none}.ui.tag.label:before,.ui.tag.labels .label:before{position:absolute;-webkit-transform:translateY(-50%) translateX(50%) rotate(-45deg);transform:translateY(-50%) translateX(50%) rotate(-45deg);top:50%;right:100%;content:'';background-color:inherit;background-image:none;width:1.56em;height:1.56em;-webkit-transition:none;transition:none}.ui.tag.label:after,.ui.tag.labels .label:after{position:absolute;content:'';top:50%;left:-.25em;margin-top:-.25em;background-color:#fff;width:.5em;height:.5em;-webkit-box-shadow:0 -1px 1px 0 rgba(0,0,0,.3);box-shadow:0 -1px 1px 0 rgba(0,0,0,.3);border-radius:500rem}.ui.basic.tag.label:before,.ui.basic.tag.labels .label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;right:calc(100% + 1px)}.ui.basic.tag.label:after,.ui.basic.tag.labels .label:after{-webkit-box-shadow:0 -1px 3px 0 rgba(0,0,0,.8);box-shadow:0 -1px 3px 0 rgba(0,0,0,.8)}.ui.corner.label{position:absolute;top:0;right:0;margin:0;padding:0;text-align:center;border-color:#e8e8e8;width:4em;height:4em;z-index:1;-webkit-transition:border-color .1s ease;transition:border-color .1s ease}.ui.corner.label{background-color:transparent!important}.ui.corner.label:after{position:absolute;content:"";right:0;top:0;z-index:-1;width:0;height:0;background-color:transparent;border-top:0 solid transparent;border-right:4em solid transparent;border-bottom:4em solid transparent;border-left:0 solid transparent;border-right-color:inherit;-webkit-transition:border-color .1s ease;transition:border-color .1s ease}.ui.corner.label .icon{cursor:inherit;position:absolute;top:.64285714em;left:auto;right:.57142857em;font-size:1.14285714em;margin:0}.ui.left.corner.label,.ui.left.corner.label:after{right:auto;left:0}.ui.left.corner.label:after{border-top:4em solid transparent;border-right:4em solid transparent;border-bottom:0 solid transparent;border-left:0 solid transparent;border-top-color:inherit}.ui.left.corner.label .icon{left:.57142857em;right:auto}.ui.segment>.ui.corner.label{top:-1px;right:-1px}.ui.segment>.ui.left.corner.label{right:auto;left:-1px}.ui.ribbon.label{position:relative;margin:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;border-radius:0 .28571429rem .28571429rem 0;border-color:rgba(0,0,0,.15)}.ui.ribbon.label:after{position:absolute;content:'';top:100%;left:0;background-color:transparent;border-style:solid;border-width:0 1.2em 1.2em 0;border-color:transparent;border-right-color:inherit;width:0;height:0}.ui.ribbon.label{left:calc(-1rem - 1.2em);margin-right:-1.2em;padding-left:calc(1rem + 1.2em);padding-right:1.2em}.ui[class*="right ribbon"].label{left:calc(100% + 1rem + 1.2em);padding-left:1.2em;padding-right:calc(1rem + 1.2em)}.ui.basic.ribbon.label{padding-top:calc(.5833em - 1px);padding-bottom:calc(.5833em - 1px)}.ui.basic.ribbon.label:not([class*="right ribbon"]){padding-left:calc(1rem + 1.2em - 1px);padding-right:calc(1.2em - 1px)}.ui.basic[class*="right ribbon"].label{padding-left:calc(1.2em - 1px);padding-right:calc(1rem + 1.2em - 1px)}.ui.basic.ribbon.label::after{top:calc(100% + 1px)}.ui.basic.ribbon.label:not([class*="right ribbon"])::after{left:-1px}.ui.basic[class*="right ribbon"].label::after{right:-1px}.ui[class*="right ribbon"].label{text-align:left;-webkit-transform:translateX(-100%);transform:translateX(-100%);border-radius:.28571429rem 0 0 .28571429rem}.ui[class*="right ribbon"].label:after{left:auto;right:0;border-style:solid;border-width:1.2em 1.2em 0 0;border-color:transparent;border-top-color:inherit}.ui.card .image>.ribbon.label,.ui.image>.ribbon.label{position:absolute;top:1rem}.ui.card .image>.ui.ribbon.label,.ui.image>.ui.ribbon.label{left:calc(.05rem - 1.2em)}.ui.card .image>.ui[class*="right ribbon"].label,.ui.image>.ui[class*="right ribbon"].label{left:calc(100% + -.05rem + 1.2em);padding-left:.833em}.ui.table td>.ui.ribbon.label{left:calc(-1em - 1.2em)}.ui.table td>.ui[class*="right ribbon"].label{left:calc(100% + 1em + 1.2em);padding-left:.833em}.ui.attached.label,.ui[class*="top attached"].label{width:100%;position:absolute;margin:0;top:0;left:0;padding:.75em 1em;border-radius:.21428571rem .21428571rem 0 0}.ui[class*="bottom attached"].label{top:auto;bottom:0;border-radius:0 0 .21428571rem .21428571rem}.ui[class*="top left attached"].label{width:auto;margin-top:0!important;border-radius:.21428571rem 0 .28571429rem 0}.ui[class*="top right attached"].label{width:auto;left:auto;right:0;border-radius:0 .21428571rem 0 .28571429rem}.ui[class*="bottom left attached"].label{width:auto;top:auto;bottom:0;border-radius:0 .28571429rem 0 .21428571rem}.ui[class*="bottom right attached"].label{top:auto;bottom:0;left:auto;right:0;width:auto;border-radius:.28571429rem 0 .21428571rem 0}.ui.label.disabled{opacity:.5}.ui.labels a.label:hover,a.ui.label:hover{background-color:#e0e0e0;border-color:#e0e0e0;background-image:none;color:rgba(0,0,0,.8)}.ui.labels a.label:hover:before,a.ui.label:hover:before{color:rgba(0,0,0,.8)}.ui.active.label{background-color:#d0d0d0;border-color:#d0d0d0;background-image:none;color:rgba(0,0,0,.95)}.ui.active.label:before{background-color:#d0d0d0;background-image:none;color:rgba(0,0,0,.95)}.ui.labels a.active.label:hover,a.ui.active.label:hover{background-color:#c8c8c8;border-color:#c8c8c8;background-image:none;color:rgba(0,0,0,.95)}.ui.labels a.active.label:hover:before,a.ui.active.label:hover:before{background-color:#c8c8c8;background-image:none;color:rgba(0,0,0,.95)}.ui.label.visible:not(.dropdown),.ui.labels.visible .label{display:inline-block!important}.ui.label.hidden,.ui.labels.hidden .label{display:none!important}.ui.basic.label,.ui.basic.labels .label{background:none #fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);-webkit-box-shadow:none;box-shadow:none}.ui.basic.labels a.label:hover,a.ui.basic.label:hover{text-decoration:none;background:none #fff;color:#1e70bf;-webkit-box-shadow:none;box-shadow:none}.ui.basic.pointing.label:before{border-color:inherit}.ui.fluid.labels>.label,.ui.label.fluid{width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.ui.inverted.label,.ui.inverted.labels .label{color:rgba(255,255,255,.9);background-color:#b5b5b5}.ui.inverted.corner.label{border-color:#b5b5b5}.ui.inverted.corner.label:hover{border-color:#e8e8e8;-webkit-transition:none;transition:none}.ui.inverted.basic.label,.ui.inverted.basic.label:hover,.ui.inverted.basic.labels .label{border-color:rgba(255,255,255,.5);background:#1b1c1d}.ui.inverted.basic.label:hover{color:#4183c4}.ui.primary.labels .label,.ui.ui.ui.primary.label{background-color:#2185d0;border-color:#2185d0;color:rgba(255,255,255,.9)}.ui.primary.labels a.label:hover,a.ui.ui.ui.primary.label:hover{background-color:#1678c2;border-color:#1678c2;color:#fff}.ui.ui.ui.primary.ribbon.label{border-color:#1a69a4}.ui.basic.labels .primary.label,.ui.ui.ui.basic.primary.label{background:none #fff;border-color:#2185d0;color:#2185d0}.ui.basic.labels a.primary.label:hover,a.ui.ui.ui.basic.primary.label:hover{background:none #fff;border-color:#1678c2;color:#1678c2}.ui.inverted.labels .primary.label,.ui.ui.ui.inverted.primary.label{background-color:#54c8ff;border-color:#54c8ff;color:#1b1c1d}.ui.inverted.labels a.primary.label:hover,a.ui.ui.ui.inverted.primary.label:hover{background-color:#21b8ff;border-color:#21b8ff;color:#1b1c1d}.ui.ui.ui.inverted.primary.ribbon.label{border-color:#21b8ff}.ui.inverted.basic.labels .primary.label,.ui.ui.ui.inverted.basic.primary.label{background-color:#1b1c1d;border-color:#54c8ff;color:#54c8ff}.ui.inverted.basic.labels a.primary.label:hover,a.ui.ui.ui.inverted.basic.primary.label:hover{border-color:#21b8ff;background-color:#1b1c1d;color:#21b8ff}.ui.inverted.basic.tag.labels .primary.label,.ui.ui.ui.inverted.primary.basic.tag.label{border:1px solid #54c8ff}.ui.inverted.basic.tag.labels .primary.label:before,.ui.ui.ui.inverted.primary.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.secondary.labels .label,.ui.ui.ui.secondary.label{background-color:#1b1c1d;border-color:#1b1c1d;color:rgba(255,255,255,.9)}.ui.secondary.labels a.label:hover,a.ui.ui.ui.secondary.label:hover{background-color:#27292a;border-color:#27292a;color:#fff}.ui.ui.ui.secondary.ribbon.label{border-color:#020203}.ui.basic.labels .secondary.label,.ui.ui.ui.basic.secondary.label{background:none #fff;border-color:#1b1c1d;color:#1b1c1d}.ui.basic.labels a.secondary.label:hover,a.ui.ui.ui.basic.secondary.label:hover{background:none #fff;border-color:#27292a;color:#27292a}.ui.inverted.labels .secondary.label,.ui.ui.ui.inverted.secondary.label{background-color:#545454;border-color:#545454;color:#1b1c1d}.ui.inverted.labels a.secondary.label:hover,a.ui.ui.ui.inverted.secondary.label:hover{background-color:#6e6e6e;border-color:#6e6e6e;color:#1b1c1d}.ui.ui.ui.inverted.secondary.ribbon.label{border-color:#3b3b3b}.ui.inverted.basic.labels .secondary.label,.ui.ui.ui.inverted.basic.secondary.label{background-color:#1b1c1d;border-color:#545454;color:#545454}.ui.inverted.basic.labels a.secondary.label:hover,a.ui.ui.ui.inverted.basic.secondary.label:hover{border-color:#6e6e6e;background-color:#1b1c1d;color:#6e6e6e}.ui.inverted.basic.tag.labels .secondary.label,.ui.ui.ui.inverted.secondary.basic.tag.label{border:1px solid #545454}.ui.inverted.basic.tag.labels .secondary.label:before,.ui.ui.ui.inverted.secondary.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.red.labels .label,.ui.ui.ui.red.label{background-color:#db2828;border-color:#db2828;color:#fff}.ui.red.labels a.label:hover,a.ui.ui.ui.red.label:hover{background-color:#d01919;border-color:#d01919;color:#fff}.ui.ui.ui.red.ribbon.label{border-color:#b21e1e}.ui.basic.labels .red.label,.ui.ui.ui.basic.red.label{background:none #fff;border-color:#db2828;color:#db2828}.ui.basic.labels a.red.label:hover,a.ui.ui.ui.basic.red.label:hover{background:none #fff;border-color:#d01919;color:#d01919}.ui.inverted.labels .red.label,.ui.ui.ui.inverted.red.label{background-color:#ff695e;border-color:#ff695e;color:#1b1c1d}.ui.inverted.labels a.red.label:hover,a.ui.ui.ui.inverted.red.label:hover{background-color:#ff392b;border-color:#ff392b;color:#1b1c1d}.ui.ui.ui.inverted.red.ribbon.label{border-color:#ff392b}.ui.inverted.basic.labels .red.label,.ui.ui.ui.inverted.basic.red.label{background-color:#1b1c1d;border-color:#ff695e;color:#ff695e}.ui.inverted.basic.labels a.red.label:hover,a.ui.ui.ui.inverted.basic.red.label:hover{border-color:#ff392b;background-color:#1b1c1d;color:#ff392b}.ui.inverted.basic.tag.labels .red.label,.ui.ui.ui.inverted.red.basic.tag.label{border:1px solid #ff695e}.ui.inverted.basic.tag.labels .red.label:before,.ui.ui.ui.inverted.red.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.orange.labels .label,.ui.ui.ui.orange.label{background-color:#f2711c;border-color:#f2711c;color:#fff}.ui.orange.labels a.label:hover,a.ui.ui.ui.orange.label:hover{background-color:#f26202;border-color:#f26202;color:#fff}.ui.ui.ui.orange.ribbon.label{border-color:#cf590c}.ui.basic.labels .orange.label,.ui.ui.ui.basic.orange.label{background:none #fff;border-color:#f2711c;color:#f2711c}.ui.basic.labels a.orange.label:hover,a.ui.ui.ui.basic.orange.label:hover{background:none #fff;border-color:#f26202;color:#f26202}.ui.inverted.labels .orange.label,.ui.ui.ui.inverted.orange.label{background-color:#ff851b;border-color:#ff851b;color:#1b1c1d}.ui.inverted.labels a.orange.label:hover,a.ui.ui.ui.inverted.orange.label:hover{background-color:#e76b00;border-color:#e76b00;color:#1b1c1d}.ui.ui.ui.inverted.orange.ribbon.label{border-color:#e76b00}.ui.inverted.basic.labels .orange.label,.ui.ui.ui.inverted.basic.orange.label{background-color:#1b1c1d;border-color:#ff851b;color:#ff851b}.ui.inverted.basic.labels a.orange.label:hover,a.ui.ui.ui.inverted.basic.orange.label:hover{border-color:#e76b00;background-color:#1b1c1d;color:#e76b00}.ui.inverted.basic.tag.labels .orange.label,.ui.ui.ui.inverted.orange.basic.tag.label{border:1px solid #ff851b}.ui.inverted.basic.tag.labels .orange.label:before,.ui.ui.ui.inverted.orange.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.ui.ui.yellow.label,.ui.yellow.labels .label{background-color:#fbbd08;border-color:#fbbd08;color:#fff}.ui.yellow.labels a.label:hover,a.ui.ui.ui.yellow.label:hover{background-color:#eaae00;border-color:#eaae00;color:#fff}.ui.ui.ui.yellow.ribbon.label{border-color:#cd9903}.ui.basic.labels .yellow.label,.ui.ui.ui.basic.yellow.label{background:none #fff;border-color:#fbbd08;color:#fbbd08}.ui.basic.labels a.yellow.label:hover,a.ui.ui.ui.basic.yellow.label:hover{background:none #fff;border-color:#eaae00;color:#eaae00}.ui.inverted.labels .yellow.label,.ui.ui.ui.inverted.yellow.label{background-color:#ffe21f;border-color:#ffe21f;color:#1b1c1d}.ui.inverted.labels a.yellow.label:hover,a.ui.ui.ui.inverted.yellow.label:hover{background-color:#ebcd00;border-color:#ebcd00;color:#1b1c1d}.ui.ui.ui.inverted.yellow.ribbon.label{border-color:#ebcd00}.ui.inverted.basic.labels .yellow.label,.ui.ui.ui.inverted.basic.yellow.label{background-color:#1b1c1d;border-color:#ffe21f;color:#ffe21f}.ui.inverted.basic.labels a.yellow.label:hover,a.ui.ui.ui.inverted.basic.yellow.label:hover{border-color:#ebcd00;background-color:#1b1c1d;color:#ebcd00}.ui.inverted.basic.tag.labels .yellow.label,.ui.ui.ui.inverted.yellow.basic.tag.label{border:1px solid #ffe21f}.ui.inverted.basic.tag.labels .yellow.label:before,.ui.ui.ui.inverted.yellow.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.olive.labels .label,.ui.ui.ui.olive.label{background-color:#b5cc18;border-color:#b5cc18;color:#fff}.ui.olive.labels a.label:hover,a.ui.ui.ui.olive.label:hover{background-color:#a7bd0d;border-color:#a7bd0d;color:#fff}.ui.ui.ui.olive.ribbon.label{border-color:#8d9e13}.ui.basic.labels .olive.label,.ui.ui.ui.basic.olive.label{background:none #fff;border-color:#b5cc18;color:#b5cc18}.ui.basic.labels a.olive.label:hover,a.ui.ui.ui.basic.olive.label:hover{background:none #fff;border-color:#a7bd0d;color:#a7bd0d}.ui.inverted.labels .olive.label,.ui.ui.ui.inverted.olive.label{background-color:#d9e778;border-color:#d9e778;color:#1b1c1d}.ui.inverted.labels a.olive.label:hover,a.ui.ui.ui.inverted.olive.label:hover{background-color:#d2e745;border-color:#d2e745;color:#1b1c1d}.ui.ui.ui.inverted.olive.ribbon.label{border-color:#cddf4d}.ui.inverted.basic.labels .olive.label,.ui.ui.ui.inverted.basic.olive.label{background-color:#1b1c1d;border-color:#d9e778;color:#d9e778}.ui.inverted.basic.labels a.olive.label:hover,a.ui.ui.ui.inverted.basic.olive.label:hover{border-color:#d2e745;background-color:#1b1c1d;color:#d2e745}.ui.inverted.basic.tag.labels .olive.label,.ui.ui.ui.inverted.olive.basic.tag.label{border:1px solid #d9e778}.ui.inverted.basic.tag.labels .olive.label:before,.ui.ui.ui.inverted.olive.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.green.labels .label,.ui.ui.ui.green.label{background-color:#21ba45;border-color:#21ba45;color:#fff}.ui.green.labels a.label:hover,a.ui.ui.ui.green.label:hover{background-color:#16ab39;border-color:#16ab39;color:#fff}.ui.ui.ui.green.ribbon.label{border-color:#198f35}.ui.basic.labels .green.label,.ui.ui.ui.basic.green.label{background:none #fff;border-color:#21ba45;color:#21ba45}.ui.basic.labels a.green.label:hover,a.ui.ui.ui.basic.green.label:hover{background:none #fff;border-color:#16ab39;color:#16ab39}.ui.inverted.labels .green.label,.ui.ui.ui.inverted.green.label{background-color:#2ecc40;border-color:#2ecc40;color:#1b1c1d}.ui.inverted.labels a.green.label:hover,a.ui.ui.ui.inverted.green.label:hover{background-color:#1ea92e;border-color:#1ea92e;color:#1b1c1d}.ui.ui.ui.inverted.green.ribbon.label{border-color:#25a233}.ui.inverted.basic.labels .green.label,.ui.ui.ui.inverted.basic.green.label{background-color:#1b1c1d;border-color:#2ecc40;color:#2ecc40}.ui.inverted.basic.labels a.green.label:hover,a.ui.ui.ui.inverted.basic.green.label:hover{border-color:#1ea92e;background-color:#1b1c1d;color:#1ea92e}.ui.inverted.basic.tag.labels .green.label,.ui.ui.ui.inverted.green.basic.tag.label{border:1px solid #2ecc40}.ui.inverted.basic.tag.labels .green.label:before,.ui.ui.ui.inverted.green.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.teal.labels .label,.ui.ui.ui.teal.label{background-color:#00b5ad;border-color:#00b5ad;color:#fff}.ui.teal.labels a.label:hover,a.ui.ui.ui.teal.label:hover{background-color:#009c95;border-color:#009c95;color:#fff}.ui.ui.ui.teal.ribbon.label{border-color:#00827c}.ui.basic.labels .teal.label,.ui.ui.ui.basic.teal.label{background:none #fff;border-color:#00b5ad;color:#00b5ad}.ui.basic.labels a.teal.label:hover,a.ui.ui.ui.basic.teal.label:hover{background:none #fff;border-color:#009c95;color:#009c95}.ui.inverted.labels .teal.label,.ui.ui.ui.inverted.teal.label{background-color:#6dffff;border-color:#6dffff;color:#1b1c1d}.ui.inverted.labels a.teal.label:hover,a.ui.ui.ui.inverted.teal.label:hover{background-color:#3affff;border-color:#3affff;color:#1b1c1d}.ui.ui.ui.inverted.teal.ribbon.label{border-color:#3affff}.ui.inverted.basic.labels .teal.label,.ui.ui.ui.inverted.basic.teal.label{background-color:#1b1c1d;border-color:#6dffff;color:#6dffff}.ui.inverted.basic.labels a.teal.label:hover,a.ui.ui.ui.inverted.basic.teal.label:hover{border-color:#3affff;background-color:#1b1c1d;color:#3affff}.ui.inverted.basic.tag.labels .teal.label,.ui.ui.ui.inverted.teal.basic.tag.label{border:1px solid #6dffff}.ui.inverted.basic.tag.labels .teal.label:before,.ui.ui.ui.inverted.teal.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.blue.labels .label,.ui.ui.ui.blue.label{background-color:#2185d0;border-color:#2185d0;color:#fff}.ui.blue.labels a.label:hover,a.ui.ui.ui.blue.label:hover{background-color:#1678c2;border-color:#1678c2;color:#fff}.ui.ui.ui.blue.ribbon.label{border-color:#1a69a4}.ui.basic.labels .blue.label,.ui.ui.ui.basic.blue.label{background:none #fff;border-color:#2185d0;color:#2185d0}.ui.basic.labels a.blue.label:hover,a.ui.ui.ui.basic.blue.label:hover{background:none #fff;border-color:#1678c2;color:#1678c2}.ui.inverted.labels .blue.label,.ui.ui.ui.inverted.blue.label{background-color:#54c8ff;border-color:#54c8ff;color:#1b1c1d}.ui.inverted.labels a.blue.label:hover,a.ui.ui.ui.inverted.blue.label:hover{background-color:#21b8ff;border-color:#21b8ff;color:#1b1c1d}.ui.ui.ui.inverted.blue.ribbon.label{border-color:#21b8ff}.ui.inverted.basic.labels .blue.label,.ui.ui.ui.inverted.basic.blue.label{background-color:#1b1c1d;border-color:#54c8ff;color:#54c8ff}.ui.inverted.basic.labels a.blue.label:hover,a.ui.ui.ui.inverted.basic.blue.label:hover{border-color:#21b8ff;background-color:#1b1c1d;color:#21b8ff}.ui.inverted.basic.tag.labels .blue.label,.ui.ui.ui.inverted.blue.basic.tag.label{border:1px solid #54c8ff}.ui.inverted.basic.tag.labels .blue.label:before,.ui.ui.ui.inverted.blue.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.ui.ui.violet.label,.ui.violet.labels .label{background-color:#6435c9;border-color:#6435c9;color:#fff}.ui.violet.labels a.label:hover,a.ui.ui.ui.violet.label:hover{background-color:#5829bb;border-color:#5829bb;color:#fff}.ui.ui.ui.violet.ribbon.label{border-color:#502aa1}.ui.basic.labels .violet.label,.ui.ui.ui.basic.violet.label{background:none #fff;border-color:#6435c9;color:#6435c9}.ui.basic.labels a.violet.label:hover,a.ui.ui.ui.basic.violet.label:hover{background:none #fff;border-color:#5829bb;color:#5829bb}.ui.inverted.labels .violet.label,.ui.ui.ui.inverted.violet.label{background-color:#a291fb;border-color:#a291fb;color:#1b1c1d}.ui.inverted.labels a.violet.label:hover,a.ui.ui.ui.inverted.violet.label:hover{background-color:#745aff;border-color:#745aff;color:#1b1c1d}.ui.ui.ui.inverted.violet.ribbon.label{border-color:#7860f9}.ui.inverted.basic.labels .violet.label,.ui.ui.ui.inverted.basic.violet.label{background-color:#1b1c1d;border-color:#a291fb;color:#a291fb}.ui.inverted.basic.labels a.violet.label:hover,a.ui.ui.ui.inverted.basic.violet.label:hover{border-color:#745aff;background-color:#1b1c1d;color:#745aff}.ui.inverted.basic.tag.labels .violet.label,.ui.ui.ui.inverted.violet.basic.tag.label{border:1px solid #a291fb}.ui.inverted.basic.tag.labels .violet.label:before,.ui.ui.ui.inverted.violet.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.purple.labels .label,.ui.ui.ui.purple.label{background-color:#a333c8;border-color:#a333c8;color:#fff}.ui.purple.labels a.label:hover,a.ui.ui.ui.purple.label:hover{background-color:#9627ba;border-color:#9627ba;color:#fff}.ui.ui.ui.purple.ribbon.label{border-color:#82299f}.ui.basic.labels .purple.label,.ui.ui.ui.basic.purple.label{background:none #fff;border-color:#a333c8;color:#a333c8}.ui.basic.labels a.purple.label:hover,a.ui.ui.ui.basic.purple.label:hover{background:none #fff;border-color:#9627ba;color:#9627ba}.ui.inverted.labels .purple.label,.ui.ui.ui.inverted.purple.label{background-color:#dc73ff;border-color:#dc73ff;color:#1b1c1d}.ui.inverted.labels a.purple.label:hover,a.ui.ui.ui.inverted.purple.label:hover{background-color:#cf40ff;border-color:#cf40ff;color:#1b1c1d}.ui.ui.ui.inverted.purple.ribbon.label{border-color:#cf40ff}.ui.inverted.basic.labels .purple.label,.ui.ui.ui.inverted.basic.purple.label{background-color:#1b1c1d;border-color:#dc73ff;color:#dc73ff}.ui.inverted.basic.labels a.purple.label:hover,a.ui.ui.ui.inverted.basic.purple.label:hover{border-color:#cf40ff;background-color:#1b1c1d;color:#cf40ff}.ui.inverted.basic.tag.labels .purple.label,.ui.ui.ui.inverted.purple.basic.tag.label{border:1px solid #dc73ff}.ui.inverted.basic.tag.labels .purple.label:before,.ui.ui.ui.inverted.purple.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.pink.labels .label,.ui.ui.ui.pink.label{background-color:#e03997;border-color:#e03997;color:#fff}.ui.pink.labels a.label:hover,a.ui.ui.ui.pink.label:hover{background-color:#e61a8d;border-color:#e61a8d;color:#fff}.ui.ui.ui.pink.ribbon.label{border-color:#c71f7e}.ui.basic.labels .pink.label,.ui.ui.ui.basic.pink.label{background:none #fff;border-color:#e03997;color:#e03997}.ui.basic.labels a.pink.label:hover,a.ui.ui.ui.basic.pink.label:hover{background:none #fff;border-color:#e61a8d;color:#e61a8d}.ui.inverted.labels .pink.label,.ui.ui.ui.inverted.pink.label{background-color:#ff8edf;border-color:#ff8edf;color:#1b1c1d}.ui.inverted.labels a.pink.label:hover,a.ui.ui.ui.inverted.pink.label:hover{background-color:#ff5bd1;border-color:#ff5bd1;color:#1b1c1d}.ui.ui.ui.inverted.pink.ribbon.label{border-color:#ff5bd1}.ui.inverted.basic.labels .pink.label,.ui.ui.ui.inverted.basic.pink.label{background-color:#1b1c1d;border-color:#ff8edf;color:#ff8edf}.ui.inverted.basic.labels a.pink.label:hover,a.ui.ui.ui.inverted.basic.pink.label:hover{border-color:#ff5bd1;background-color:#1b1c1d;color:#ff5bd1}.ui.inverted.basic.tag.labels .pink.label,.ui.ui.ui.inverted.pink.basic.tag.label{border:1px solid #ff8edf}.ui.inverted.basic.tag.labels .pink.label:before,.ui.ui.ui.inverted.pink.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.brown.labels .label,.ui.ui.ui.brown.label{background-color:#a5673f;border-color:#a5673f;color:#fff}.ui.brown.labels a.label:hover,a.ui.ui.ui.brown.label:hover{background-color:#975b33;border-color:#975b33;color:#fff}.ui.ui.ui.brown.ribbon.label{border-color:#805031}.ui.basic.labels .brown.label,.ui.ui.ui.basic.brown.label{background:none #fff;border-color:#a5673f;color:#a5673f}.ui.basic.labels a.brown.label:hover,a.ui.ui.ui.basic.brown.label:hover{background:none #fff;border-color:#975b33;color:#975b33}.ui.inverted.labels .brown.label,.ui.ui.ui.inverted.brown.label{background-color:#d67c1c;border-color:#d67c1c;color:#1b1c1d}.ui.inverted.labels a.brown.label:hover,a.ui.ui.ui.inverted.brown.label:hover{background-color:#b0620f;border-color:#b0620f;color:#1b1c1d}.ui.ui.ui.inverted.brown.ribbon.label{border-color:#a96216}.ui.inverted.basic.labels .brown.label,.ui.ui.ui.inverted.basic.brown.label{background-color:#1b1c1d;border-color:#d67c1c;color:#d67c1c}.ui.inverted.basic.labels a.brown.label:hover,a.ui.ui.ui.inverted.basic.brown.label:hover{border-color:#b0620f;background-color:#1b1c1d;color:#b0620f}.ui.inverted.basic.tag.labels .brown.label,.ui.ui.ui.inverted.brown.basic.tag.label{border:1px solid #d67c1c}.ui.inverted.basic.tag.labels .brown.label:before,.ui.ui.ui.inverted.brown.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.grey.labels .label,.ui.ui.ui.grey.label{background-color:#767676;border-color:#767676;color:#fff}.ui.grey.labels a.label:hover,a.ui.ui.ui.grey.label:hover{background-color:#838383;border-color:#838383;color:#fff}.ui.ui.ui.grey.ribbon.label{border-color:#5d5d5d}.ui.basic.labels .grey.label,.ui.ui.ui.basic.grey.label{background:none #fff;border-color:#767676;color:#767676}.ui.basic.labels a.grey.label:hover,a.ui.ui.ui.basic.grey.label:hover{background:none #fff;border-color:#838383;color:#838383}.ui.inverted.labels .grey.label,.ui.ui.ui.inverted.grey.label{background-color:#dcddde;border-color:#dcddde;color:#1b1c1d}.ui.inverted.labels a.grey.label:hover,a.ui.ui.ui.inverted.grey.label:hover{background-color:#c2c4c5;border-color:#c2c4c5;color:#fff}.ui.ui.ui.inverted.grey.ribbon.label{border-color:#e9eaea}.ui.inverted.basic.labels .grey.label,.ui.ui.ui.inverted.basic.grey.label{background-color:#1b1c1d;border-color:#dcddde;color:rgba(255,255,255,.9)}.ui.inverted.basic.labels a.grey.label:hover,a.ui.ui.ui.inverted.basic.grey.label:hover{border-color:#c2c4c5;background-color:#1b1c1d}.ui.inverted.basic.tag.labels .grey.label,.ui.ui.ui.inverted.grey.basic.tag.label{border:1px solid #dcddde}.ui.inverted.basic.tag.labels .grey.label:before,.ui.ui.ui.inverted.grey.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.black.labels .label,.ui.ui.ui.black.label{background-color:#1b1c1d;border-color:#1b1c1d;color:#fff}.ui.black.labels a.label:hover,a.ui.ui.ui.black.label:hover{background-color:#27292a;border-color:#27292a;color:#fff}.ui.ui.ui.black.ribbon.label{border-color:#020203}.ui.basic.labels .black.label,.ui.ui.ui.basic.black.label{background:none #fff;border-color:#1b1c1d;color:#1b1c1d}.ui.basic.labels a.black.label:hover,a.ui.ui.ui.basic.black.label:hover{background:none #fff;border-color:#27292a;color:#27292a}.ui.inverted.labels .black.label,.ui.ui.ui.inverted.black.label{background-color:#545454;border-color:#545454;color:#1b1c1d}.ui.inverted.labels a.black.label:hover,a.ui.ui.ui.inverted.black.label:hover{background-color:#000;border-color:#000;color:#fff}.ui.ui.ui.inverted.black.ribbon.label{border-color:#616161}.ui.inverted.basic.labels .black.label,.ui.ui.ui.inverted.basic.black.label{background-color:#1b1c1d;border-color:#545454;color:rgba(255,255,255,.9)}.ui.inverted.basic.labels a.black.label:hover,a.ui.ui.ui.inverted.basic.black.label:hover{border-color:#000;background-color:#1b1c1d}.ui.inverted.basic.tag.labels .black.label,.ui.ui.ui.inverted.black.basic.tag.label{border:1px solid #545454}.ui.inverted.basic.tag.labels .black.label:before,.ui.ui.ui.inverted.black.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.horizontal.label,.ui.horizontal.labels .label{margin:0 .5em 0 0;padding:.4em .833em;min-width:3em;text-align:center}.ui.circular.label,.ui.circular.labels .label{min-width:2em;min-height:2em;padding:.5em!important;line-height:1em;text-align:center;border-radius:500rem}.ui.empty.circular.label,.ui.empty.circular.labels .label{min-width:0;min-height:0;overflow:hidden;width:.5em;height:.5em;vertical-align:baseline}.ui.pointing.label{position:relative}.ui.attached.pointing.label{position:absolute}.ui.pointing.label:before{background-color:inherit;background-image:inherit;border-width:0;border-style:solid;border-color:inherit}.ui.pointing.label:before{position:absolute;content:'';-webkit-transform:rotate(45deg);transform:rotate(45deg);background-image:none;z-index:2;width:.6666em;height:.6666em;-webkit-transition:none;transition:none}.ui.pointing.label,.ui[class*="pointing above"].label{margin-top:1em}.ui.pointing.label:before,.ui[class*="pointing above"].label:before{border-width:1px 0 0 1px;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);top:0;left:50%}.ui[class*="bottom pointing"].label,.ui[class*="pointing below"].label{margin-top:0;margin-bottom:1em}.ui[class*="bottom pointing"].label:before,.ui[class*="pointing below"].label:before{border-width:0 1px 1px 0;top:auto;right:auto;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);top:100%;left:50%}.ui[class*="left pointing"].label{margin-top:0;margin-left:.6666em}.ui[class*="left pointing"].label:before{border-width:0 0 1px 1px;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);bottom:auto;right:auto;top:50%;left:0}.ui[class*="right pointing"].label{margin-top:0;margin-right:.6666em}.ui[class*="right pointing"].label:before{border-width:1px 1px 0 0;-webkit-transform:translateX(50%) translateY(-50%) rotate(45deg);transform:translateX(50%) translateY(-50%) rotate(45deg);top:50%;right:0;bottom:auto;left:auto}.ui.basic.pointing.label:before,.ui.basic[class*="pointing above"].label:before{margin-top:-1px}.ui.basic[class*="bottom pointing"].label:before,.ui.basic[class*="pointing below"].label:before{bottom:auto;top:100%;margin-top:1px}.ui.basic[class*="left pointing"].label:before{top:50%;left:-1px}.ui.basic[class*="right pointing"].label:before{top:50%;right:-1px}.ui.floating.label{position:absolute;z-index:100;top:-1em;right:0;white-space:nowrap;-webkit-transform:translateX(50%);transform:translateX(50%)}.ui.right.aligned.floating.label{-webkit-transform:translateX(1.2em);transform:translateX(1.2em)}.ui.left.floating.label{left:0;right:auto;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.ui.left.aligned.floating.label{-webkit-transform:translateX(-1.2em);transform:translateX(-1.2em)}.ui.bottom.floating.label{top:auto;bottom:-1em}.ui.label,.ui.labels .label{font-size:.85714286rem}.ui.mini.label,.ui.mini.labels .label{font-size:.64285714rem}.ui.tiny.label,.ui.tiny.labels .label{font-size:.71428571rem}.ui.small.label,.ui.small.labels .label{font-size:.78571429rem}.ui.large.label,.ui.large.labels .label{font-size:1rem}.ui.big.label,.ui.big.labels .label{font-size:1.28571429rem}.ui.huge.label,.ui.huge.labels .label{font-size:1.42857143rem}.ui.massive.label,.ui.massive.labels .label{font-size:1.71428571rem}/*! + * # Fomantic-UI - List + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.list,ol.ui.list,ul.ui.list{list-style-type:none;margin:1em 0;padding:0 0}.ui.list:first-child,ol.ui.list:first-child,ul.ui.list:first-child{margin-top:0;padding-top:0}.ui.list:last-child,ol.ui.list:last-child,ul.ui.list:last-child{margin-bottom:0;padding-bottom:0}.ui.list .list>.item,.ui.list>.item,ol.ui.list li,ul.ui.list li{display:list-item;table-layout:fixed;list-style-type:none;list-style-position:outside;padding:.21428571em 0;line-height:1.14285714em}.ui.list>.item:after,.ui.list>.list>.item:after,ol.ui.list>li:first-child:after,ul.ui.list>li:first-child:after{content:'';display:block;height:0;clear:both;visibility:hidden}.ui.list .list>.item:first-child,.ui.list>.item:first-child,ol.ui.list li:first-child,ul.ui.list li:first-child{padding-top:0}.ui.list .list>.item:last-child,.ui.list>.item:last-child,ol.ui.list li:last-child,ul.ui.list li:last-child{padding-bottom:0}.ui.list .list:not(.icon),ol.ui.list ol,ul.ui.list ul{clear:both;margin:0;padding:.75em 0 .25em .5em}.ui.list .list>.item,ol.ui.list ol li,ul.ui.list ul li{padding:.14285714em 0;line-height:inherit}.ui.list .list>.item>i.icon,.ui.list>.item>i.icon{display:table-cell;min-width:1.55em;margin:0;padding-top:0;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.list .list>.item>i.icon:not(.loading),.ui.list>.item>i.icon:not(.loading){padding-right:.28571429em;vertical-align:top}.ui.list .list>.item>i.icon:only-child,.ui.list>.item>i.icon:only-child{display:inline-block;min-width:auto;vertical-align:top}.ui.list .list>.item>.image,.ui.list>.item>.image{display:table-cell;background-color:transparent;margin:0;vertical-align:top}.ui.list .list>.item>.image:not(:only-child):not(img),.ui.list>.item>.image:not(:only-child):not(img){padding-right:.5em}.ui.list .list>.item>.image img,.ui.list>.item>.image img{vertical-align:top}.ui.list .list>.item>.image:only-child,.ui.list .list>.item>img.image,.ui.list>.item>.image:only-child,.ui.list>.item>img.image{display:inline-block}.ui.list .list>.item>.content,.ui.list>.item>.content{line-height:1.14285714em;color:rgba(0,0,0,.87)}.ui.list .list>.item>.icon+.content,.ui.list .list>.item>.image+.content,.ui.list>.item>.icon+.content,.ui.list>.item>.image+.content{display:table-cell;width:100%;padding:0 0 0 .5em;vertical-align:top}.ui.list .list>.item>.loading.icon+.content,.ui.list>.item>.loading.icon+.content{padding-left:calc(.2857142857142857em + .5em)}.ui.list .list>.item>img.image+.content,.ui.list>.item>img.image+.content{display:inline-block;width:auto}.ui.list .list>.item>.content>.list,.ui.list>.item>.content>.list{margin-left:0;padding-left:0}.ui.list .list>.item .header,.ui.list>.item .header{display:block;margin:0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;color:rgba(0,0,0,.87)}.ui.list .list>.item .description,.ui.list>.item .description{display:block;color:rgba(0,0,0,.7)}.ui.list .list>.item a,.ui.list>.item a{cursor:pointer}.ui.list .list>a.item,.ui.list>a.item{cursor:pointer;color:#4183c4}.ui.list .list>a.item:hover,.ui.list>a.item:hover{color:#1e70bf}.ui.list .list>a.item>i.icon,.ui.list .list>a.item>i.icons,.ui.list>a.item>i.icon,.ui.list>a.item>i.icons{color:rgba(0,0,0,.4)}.ui.list .list>.item a.header,.ui.list>.item a.header{cursor:pointer;color:#4183c4!important}.ui.list .list>.item>a.header:hover,.ui.list>.item>a.header:hover{color:#1e70bf!important}.ui[class*="left floated"].list{float:left}.ui[class*="right floated"].list{float:right}.ui.list .list>.item [class*="left floated"],.ui.list>.item [class*="left floated"]{float:left;margin:0 1em 0 0}.ui.list .list>.item [class*="right floated"],.ui.list>.item [class*="right floated"]{float:right;margin:0 0 0 1em}.ui.menu .ui.list .list>.item,.ui.menu .ui.list>.item{display:list-item;table-layout:fixed;background-color:transparent;list-style-type:none;list-style-position:outside;padding:.21428571em 0;line-height:1.14285714em}.ui.menu .ui.list .list>.item:before,.ui.menu .ui.list>.item:before{border:none;background:0 0}.ui.menu .ui.list .list>.item:first-child,.ui.menu .ui.list>.item:first-child{padding-top:0}.ui.menu .ui.list .list>.item:last-child,.ui.menu .ui.list>.item:last-child{padding-bottom:0}.ui.horizontal.list{display:inline-block;font-size:0}.ui.horizontal.list>.item{display:inline-block;margin-right:1em;font-size:1rem}.ui.horizontal.list:not(.celled)>.item:last-child{margin-right:0;padding-right:0}.ui.horizontal.list .list:not(.icon){padding-left:0;padding-bottom:0}.ui.horizontal.list .list>.item>.content,.ui.horizontal.list .list>.item>.icon,.ui.horizontal.list .list>.item>.image,.ui.horizontal.list>.item>.content,.ui.horizontal.list>.item>.icon,.ui.horizontal.list>.item>.image{vertical-align:middle}.ui.horizontal.list>.item:first-child,.ui.horizontal.list>.item:last-child{padding-top:.21428571em;padding-bottom:.21428571em}.ui.horizontal.list .item>i.icons>i.icon,.ui.horizontal.list>.item>i.icon{margin:0;padding:0 .25em 0 0}.ui.horizontal.list>.item>.icon,.ui.horizontal.list>.item>.icon+.content,.ui.horizontal.list>.item>.image+.content{float:none;display:inline-block;width:auto}.ui.horizontal.list>.item>.image{display:inline-block}.ui.list .list>.disabled.item,.ui.list>.disabled.item{pointer-events:none;color:rgba(40,40,40,.3)!important}.ui.inverted.list .list>.disabled.item,.ui.inverted.list>.disabled.item{color:rgba(225,225,225,.3)!important}.ui.list .list>a.item:hover>.icon,.ui.list .list>a.item:hover>.icons,.ui.list>a.item:hover>.icon,.ui.list>a.item:hover>.icons{color:rgba(0,0,0,.87)}.ui.inverted.list .list>a.item>.icon,.ui.inverted.list>a.item>.icon{color:rgba(255,255,255,.7)}.ui.inverted.list .list>.item .header,.ui.inverted.list>.item .header{color:rgba(255,255,255,.9)}.ui.inverted.list .list>.item .description,.ui.inverted.list>.item .description{color:rgba(255,255,255,.7)}.ui.inverted.list .list>.item>.content,.ui.inverted.list>.item>.content{color:rgba(255,255,255,.7)}.ui.inverted.list .list>a.item,.ui.inverted.list>a.item{cursor:pointer;color:rgba(255,255,255,.9)}.ui.inverted.list .list>a.item:hover,.ui.inverted.list>a.item:hover{color:#1e70bf}.ui.inverted.list .item a:not(.ui){color:rgba(255,255,255,.9)!important}.ui.inverted.list .item a:not(.ui):hover{color:#1e70bf!important}.ui.list [class*="top aligned"],.ui.list[class*="top aligned"] .content,.ui.list[class*="top aligned"] .image{vertical-align:top!important}.ui.list [class*="middle aligned"],.ui.list[class*="middle aligned"] .content,.ui.list[class*="middle aligned"] .image{vertical-align:middle!important}.ui.list [class*="bottom aligned"],.ui.list[class*="bottom aligned"] .content,.ui.list[class*="bottom aligned"] .image{vertical-align:bottom!important}.ui.link.list .item,.ui.link.list .item a:not(.ui),.ui.link.list a.item{color:rgba(0,0,0,.4);-webkit-transition:.1s color ease;transition:.1s color ease}.ui.link.list.list .item a:not(.ui):hover,.ui.link.list.list a.item:hover{color:rgba(0,0,0,.8)}.ui.link.list.list .item a:not(.ui):active,.ui.link.list.list a.item:active{color:rgba(0,0,0,.9)}.ui.link.list.list .active.item,.ui.link.list.list .active.item a:not(.ui){color:rgba(0,0,0,.95)}.ui.inverted.link.list .item,.ui.inverted.link.list .item a:not(.ui),.ui.inverted.link.list a.item{color:rgba(255,255,255,.5)}.ui.inverted.link.list.list .item a:not(.ui):hover,.ui.inverted.link.list.list a.item:hover{color:#fff}.ui.inverted.link.list.list .item a:not(.ui):active,.ui.inverted.link.list.list a.item:active{color:#fff}.ui.inverted.link.list.list .active.item a:not(.ui),.ui.inverted.link.list.list a.active.item{color:#fff}.ui.selection.list .list>.item,.ui.selection.list>.item{cursor:pointer;background:0 0;padding:.5em .5em;margin:0;color:rgba(0,0,0,.4);border-radius:.5em;-webkit-transition:.1s color ease,.1s padding-left ease,.1s background-color ease;transition:.1s color ease,.1s padding-left ease,.1s background-color ease}.ui.selection.list .list>.item:last-child,.ui.selection.list>.item:last-child{margin-bottom:0}.ui.selection.list .list>.item:hover,.ui.selection.list>.item:hover{background:rgba(0,0,0,.03);color:rgba(0,0,0,.8)}.ui.selection.list .list>.item:active,.ui.selection.list>.item:active{background:rgba(0,0,0,.05);color:rgba(0,0,0,.9)}.ui.selection.list .list>.item.active,.ui.selection.list>.item.active{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.inverted.selection.list>.item{background:0 0;color:rgba(255,255,255,.5)}.ui.inverted.selection.list>.item:hover{background:rgba(255,255,255,.02);color:#fff}.ui.inverted.selection.list>.item:active{background:rgba(255,255,255,.08);color:#fff}.ui.inverted.selection.list>.item.active{background:rgba(255,255,255,.08);color:#fff}.ui.celled.selection.list .list>.item,.ui.celled.selection.list>.item,.ui.divided.selection.list .list>.item,.ui.divided.selection.list>.item{border-radius:0}.ui.animated.list>.item{-webkit-transition:.25s color ease .1s,.25s padding-left ease .1s,.25s background-color ease .1s;transition:.25s color ease .1s,.25s padding-left ease .1s,.25s background-color ease .1s}.ui.animated.list:not(.horizontal)>.item:hover{padding-left:1em}.ui.fitted.list:not(.selection) .list>.item,.ui.fitted.list:not(.selection)>.item{padding-left:0;padding-right:0}.ui.fitted.selection.list .list>.item,.ui.fitted.selection.list>.item{margin-left:-.5em;margin-right:-.5em}.ui.bulleted.list,ul.ui.list{margin-left:1.25rem}.ui.bulleted.list .list>.item,.ui.bulleted.list>.item,ul.ui.list li{position:relative}.ui.bulleted.list .list>.item:before,.ui.bulleted.list>.item:before,ul.ui.list li:before{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;position:absolute;top:auto;left:auto;font-weight:400;margin-left:-1.25rem;content:'\2022';opacity:1;color:inherit;vertical-align:top}.ui.bulleted.list .list>a.item:before,.ui.bulleted.list>a.item:before,ul.ui.list li:before{color:rgba(0,0,0,.87)}.ui.bulleted.list .list:not(.icon),ul.ui.list ul{padding-left:1.25rem}.ui.horizontal.bulleted.list,ul.ui.horizontal.bulleted.list{margin-left:0}.ui.horizontal.bulleted.list>.item,ul.ui.horizontal.bulleted.list li{margin-left:1.75rem}.ui.horizontal.bulleted.list>.item:first-child,ul.ui.horizontal.bulleted.list li:first-child{margin-left:0}.ui.horizontal.bulleted.list>.item::before,ul.ui.horizontal.bulleted.list li::before{color:rgba(0,0,0,.87)}.ui.horizontal.bulleted.list>.item:first-child::before,ul.ui.horizontal.bulleted.list li:first-child::before{display:none}.ui.ordered.list,.ui.ordered.list .list:not(.icon),ol.ui.list,ol.ui.list ol{counter-reset:ordered;margin-left:1.25rem;list-style-type:none}.ui.ordered.list .list>.item,.ui.ordered.list>.item,ol.ui.list li{list-style-type:none;position:relative}.ui.ordered.list .list>.item:before,.ui.ordered.list>.item:before,ol.ui.list li:before{position:absolute;top:auto;left:auto;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;margin-left:-1.25rem;counter-increment:ordered;content:counters(ordered, ".") " ";text-align:right;color:rgba(0,0,0,.87);vertical-align:middle;opacity:.8}.ui.ordered.inverted.list .list>.item:before,.ui.ordered.inverted.list>.item:before,ol.ui.inverted.list li:before{color:rgba(255,255,255,.7)}.ui.ordered.list .list>.item[data-value]:before,.ui.ordered.list>.item[data-value]:before{content:attr(data-value)}ol.ui.list li[value]:before{content:attr(value)}.ui.ordered.list .list:not(.icon),ol.ui.list ol{margin-left:1em}.ui.ordered.list .list>.item:before,ol.ui.list ol li:before{margin-left:-2em}.ui.ordered.horizontal.list,ol.ui.horizontal.list{margin-left:0}.ui.ordered.horizontal.list .list>.item:before,.ui.ordered.horizontal.list>.item:before,ol.ui.horizontal.list li:before{position:static;margin:0 .5em 0 0}.ui.suffixed.ordered.list .list>.item:before,.ui.suffixed.ordered.list>.item:before,ol.ui.suffixed.list li:before{content:counters(ordered, ".") "."}.ui.divided.list>.item{border-top:1px solid rgba(34,36,38,.15)}.ui.divided.list .list>.item{border-top:none}.ui.divided.list .item .list>.item{border-top:none}.ui.divided.list .list>.item:first-child,.ui.divided.list>.item:first-child{border-top:none}.ui.divided.list:not(.horizontal) .list>.item:first-child{border-top-width:1px}.ui.divided.bulleted.list .list:not(.icon),.ui.divided.bulleted.list:not(.horizontal){margin-left:0;padding-left:0}.ui.divided.bulleted.list>.item:not(.horizontal){padding-left:1.25rem}.ui.divided.ordered.list{margin-left:0}.ui.divided.ordered.list .list>.item,.ui.divided.ordered.list>.item{padding-left:1.25rem}.ui.divided.ordered.list .item .list:not(.icon){margin-left:0;margin-right:0;padding-bottom:.21428571em}.ui.divided.ordered.list .item .list>.item{padding-left:1em}.ui.divided.selection.list .list>.item,.ui.divided.selection.list>.item{margin:0;border-radius:0}.ui.divided.horizontal.list{margin-left:0}.ui.divided.horizontal.list>.item{padding-left:.5em}.ui.divided.horizontal.list>.item:not(:last-child){padding-right:.5em}.ui.divided.horizontal.list>.item{border-top:none;border-right:1px solid rgba(34,36,38,.15);margin:0;line-height:.6}.ui.horizontal.divided.list>.item:last-child{border-right:none}.ui.divided.inverted.horizontal.list>.item,.ui.divided.inverted.list>.item,.ui.divided.inverted.list>.list{border-color:rgba(255,255,255,.1)}.ui.celled.list>.item,.ui.celled.list>.list{border-top:1px solid rgba(34,36,38,.15);padding-left:.5em;padding-right:.5em}.ui.celled.list>.item:last-child{border-bottom:1px solid rgba(34,36,38,.15)}.ui.celled.list>.item:first-child,.ui.celled.list>.item:last-child{padding-top:.21428571em;padding-bottom:.21428571em}.ui.celled.list .item .list>.item{border-width:0}.ui.celled.list .list>.item:first-child{border-top-width:0}.ui.celled.bulleted.list{margin-left:0}.ui.celled.bulleted.list .list>.item,.ui.celled.bulleted.list>.item{padding-left:1.25rem}.ui.celled.bulleted.list .item .list:not(.icon){margin-left:-1.25rem;margin-right:-1.25rem;padding-bottom:.21428571em}.ui.celled.ordered.list{margin-left:0}.ui.celled.ordered.list .list>.item,.ui.celled.ordered.list>.item{padding-left:1.25rem}.ui.celled.ordered.list .item .list:not(.icon){margin-left:0;margin-right:0;padding-bottom:.21428571em}.ui.celled.ordered.list .list>.item{padding-left:1em}.ui.horizontal.celled.list{margin-left:0}.ui.horizontal.celled.list .list>.item,.ui.horizontal.celled.list>.item{border-top:none;border-left:1px solid rgba(34,36,38,.15);margin:0;padding-left:.5em;padding-right:.5em;line-height:.6}.ui.horizontal.celled.list .list>.item:last-child,.ui.horizontal.celled.list>.item:last-child{border-bottom:none;border-right:1px solid rgba(34,36,38,.15)}.ui.celled.inverted.list>.item,.ui.celled.inverted.list>.list{border-color:rgba(255,255,255,.1)}.ui.celled.inverted.horizontal.list .list>.item,.ui.celled.inverted.horizontal.list>.item{border-color:rgba(255,255,255,.1)}.ui.relaxed.list:not(.horizontal)>.item:not(:first-child){padding-top:.42857143em}.ui.relaxed.list:not(.horizontal)>.item:not(:last-child){padding-bottom:.42857143em}.ui.horizontal.relaxed.list .list>.item:not(:first-child),.ui.horizontal.relaxed.list>.item:not(:first-child){padding-left:1rem}.ui.horizontal.relaxed.list .list>.item:not(:last-child),.ui.horizontal.relaxed.list>.item:not(:last-child){padding-right:1rem}.ui[class*="very relaxed"].list:not(.horizontal)>.item:not(:first-child){padding-top:.85714286em}.ui[class*="very relaxed"].list:not(.horizontal)>.item:not(:last-child){padding-bottom:.85714286em}.ui.horizontal[class*="very relaxed"].list .list>.item:not(:first-child),.ui.horizontal[class*="very relaxed"].list>.item:not(:first-child){padding-left:1.5rem}.ui.horizontal[class*="very relaxed"].list .list>.item:not(:last-child),.ui.horizontal[class*="very relaxed"].list>.item:not(:last-child){padding-right:1.5rem}.ui.list{font-size:1em}.ui.mini.list{font-size:.78571429em}.ui.mini.horizontal.list .list>.item,.ui.mini.horizontal.list>.item{font-size:.78571429rem}.ui.tiny.list{font-size:.85714286em}.ui.tiny.horizontal.list .list>.item,.ui.tiny.horizontal.list>.item{font-size:.85714286rem}.ui.small.list{font-size:.92857143em}.ui.small.horizontal.list .list>.item,.ui.small.horizontal.list>.item{font-size:.92857143rem}.ui.large.list{font-size:1.14285714em}.ui.large.horizontal.list .list>.item,.ui.large.horizontal.list>.item{font-size:1.14285714rem}.ui.big.list{font-size:1.28571429em}.ui.big.horizontal.list .list>.item,.ui.big.horizontal.list>.item{font-size:1.28571429rem}.ui.huge.list{font-size:1.42857143em}.ui.huge.horizontal.list .list>.item,.ui.huge.horizontal.list>.item{font-size:1.42857143rem}.ui.massive.list{font-size:1.71428571em}.ui.massive.horizontal.list .list>.item,.ui.massive.horizontal.list>.item{font-size:1.71428571rem}/*! + * # Fomantic-UI - Loader + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.loader{display:none;position:absolute;top:50%;left:50%;margin:0;text-align:center;z-index:1000;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.ui.loader:before{position:absolute;content:'';top:0;left:50%;width:100%;height:100%;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loader:after{position:absolute;content:'';top:0;left:50%;width:100%;height:100%;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent}.ui.fast.loader:after,.ui.fast.loading.loading .input>i.icon:after,.ui.fast.loading.loading:after,.ui.fast.loading.loading>i.icon:after{-webkit-animation-duration:.3s;animation-duration:.3s}.ui.slow.loader:after,.ui.slow.loading.loading .input>i.icon:after,.ui.slow.loading.loading:after,.ui.slow.loading.loading>i.icon:after{-webkit-animation-duration:.9s;animation-duration:.9s}@-webkit-keyframes loader{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loader{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.ui.dimmer>.loader{display:block}.ui.dimmer>.ui.loader{color:rgba(255,255,255,.9)}.ui.dimmer>.ui.loader:not(.elastic):before{border-color:rgba(255,255,255,.15)}.ui.inverted.dimmer>.ui.loader{color:rgba(0,0,0,.87)}.ui.inverted.dimmer>.ui.loader:not(.elastic):before{border-color:rgba(0,0,0,.1)}.ui.ui.ui.ui.text.loader{width:auto;height:auto;text-align:center;font-style:normal}.ui.indeterminate.loader:after{animation-direction:reverse;-webkit-animation-duration:1.2s;animation-duration:1.2s}.ui.loader.active,.ui.loader.visible{display:block}.ui.loader.disabled,.ui.loader.hidden{display:none}.ui.loader{width:2.28571429rem;height:2.28571429rem;font-size:1em}.ui.loader:after,.ui.loader:before{width:2.28571429rem;height:2.28571429rem;margin:0 0 0 -1.14285714rem}.ui.text.loader{min-width:2.28571429rem;padding-top:3.07142857rem}.ui.mini.loader{width:1rem;height:1rem;font-size:.78571429em}.ui.mini.loader:after,.ui.mini.loader:before{width:1rem;height:1rem;margin:0 0 0 -.5rem}.ui.mini.text.loader{min-width:1rem;padding-top:1.78571429rem}.ui.tiny.loader{width:1.14285714rem;height:1.14285714rem;font-size:.85714286em}.ui.tiny.loader:after,.ui.tiny.loader:before{width:1.14285714rem;height:1.14285714rem;margin:0 0 0 -.57142857rem}.ui.tiny.text.loader{min-width:1.14285714rem;padding-top:1.92857143rem}.ui.small.loader{width:1.71428571rem;height:1.71428571rem;font-size:.92857143em}.ui.small.loader:after,.ui.small.loader:before{width:1.71428571rem;height:1.71428571rem;margin:0 0 0 -.85714286rem}.ui.small.text.loader{min-width:1.71428571rem;padding-top:2.5rem}.ui.large.loader{width:3.42857143rem;height:3.42857143rem;font-size:1.14285714em}.ui.large.loader:after,.ui.large.loader:before{width:3.42857143rem;height:3.42857143rem;margin:0 0 0 -1.71428571rem}.ui.large.text.loader{min-width:3.42857143rem;padding-top:4.21428571rem}.ui.big.loader{width:3.71428571rem;height:3.71428571rem;font-size:1.28571429em}.ui.big.loader:after,.ui.big.loader:before{width:3.71428571rem;height:3.71428571rem;margin:0 0 0 -1.85714286rem}.ui.big.text.loader{min-width:3.71428571rem;padding-top:4.5rem}.ui.huge.loader{width:4.14285714rem;height:4.14285714rem;font-size:1.42857143em}.ui.huge.loader:after,.ui.huge.loader:before{width:4.14285714rem;height:4.14285714rem;margin:0 0 0 -2.07142857rem}.ui.huge.text.loader{min-width:4.14285714rem;padding-top:4.92857143rem}.ui.massive.loader{width:4.57142857rem;height:4.57142857rem;font-size:1.71428571em}.ui.massive.loader:after,.ui.massive.loader:before{width:4.57142857rem;height:4.57142857rem;margin:0 0 0 -2.28571429rem}.ui.massive.text.loader{min-width:4.57142857rem;padding-top:5.35714286rem}.ui.primary.basic.elastic.loading.button:after,.ui.primary.basic.elastic.loading.button:before,.ui.primary.elastic.loader.loader:before,.ui.primary.elastic.loading.loading.loading .input>i.icon:before,.ui.primary.elastic.loading.loading.loading.loading>i.icon:before,.ui.primary.elastic.loading.loading.loading:not(.segment):before,.ui.primary.loader.loader.loader:after,.ui.primary.loading.loading.loading.loading .input>i.icon:after,.ui.primary.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.primary.loading.loading.loading.loading>i.icon:after{color:#2185d0}.ui.inverted.primary.elastic.loader:before,.ui.inverted.primary.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.primary.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.primary.elastic.loading.loading.loading>i.icon:before,.ui.inverted.primary.loader.loader.loader:after,.ui.inverted.primary.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.primary.loading.loading.loading.loading:not(.usual):after,.ui.inverted.primary.loading.loading.loading.loading>i.icon:after{color:#54c8ff}.ui.secondary.basic.elastic.loading.button:after,.ui.secondary.basic.elastic.loading.button:before,.ui.secondary.elastic.loader.loader:before,.ui.secondary.elastic.loading.loading.loading .input>i.icon:before,.ui.secondary.elastic.loading.loading.loading.loading>i.icon:before,.ui.secondary.elastic.loading.loading.loading:not(.segment):before,.ui.secondary.loader.loader.loader:after,.ui.secondary.loading.loading.loading.loading .input>i.icon:after,.ui.secondary.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.secondary.loading.loading.loading.loading>i.icon:after{color:#1b1c1d}.ui.inverted.secondary.elastic.loader:before,.ui.inverted.secondary.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.secondary.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.secondary.elastic.loading.loading.loading>i.icon:before,.ui.inverted.secondary.loader.loader.loader:after,.ui.inverted.secondary.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.secondary.loading.loading.loading.loading:not(.usual):after,.ui.inverted.secondary.loading.loading.loading.loading>i.icon:after{color:#545454}.ui.red.basic.elastic.loading.button:after,.ui.red.basic.elastic.loading.button:before,.ui.red.elastic.loader.loader:before,.ui.red.elastic.loading.loading.loading .input>i.icon:before,.ui.red.elastic.loading.loading.loading.loading>i.icon:before,.ui.red.elastic.loading.loading.loading:not(.segment):before,.ui.red.loader.loader.loader:after,.ui.red.loading.loading.loading.loading .input>i.icon:after,.ui.red.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.red.loading.loading.loading.loading>i.icon:after{color:#db2828}.ui.inverted.red.elastic.loader:before,.ui.inverted.red.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.red.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.red.elastic.loading.loading.loading>i.icon:before,.ui.inverted.red.loader.loader.loader:after,.ui.inverted.red.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.red.loading.loading.loading.loading:not(.usual):after,.ui.inverted.red.loading.loading.loading.loading>i.icon:after{color:#ff695e}.ui.orange.basic.elastic.loading.button:after,.ui.orange.basic.elastic.loading.button:before,.ui.orange.elastic.loader.loader:before,.ui.orange.elastic.loading.loading.loading .input>i.icon:before,.ui.orange.elastic.loading.loading.loading.loading>i.icon:before,.ui.orange.elastic.loading.loading.loading:not(.segment):before,.ui.orange.loader.loader.loader:after,.ui.orange.loading.loading.loading.loading .input>i.icon:after,.ui.orange.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.orange.loading.loading.loading.loading>i.icon:after{color:#f2711c}.ui.inverted.orange.elastic.loader:before,.ui.inverted.orange.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.orange.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.orange.elastic.loading.loading.loading>i.icon:before,.ui.inverted.orange.loader.loader.loader:after,.ui.inverted.orange.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.orange.loading.loading.loading.loading:not(.usual):after,.ui.inverted.orange.loading.loading.loading.loading>i.icon:after{color:#ff851b}.ui.yellow.basic.elastic.loading.button:after,.ui.yellow.basic.elastic.loading.button:before,.ui.yellow.elastic.loader.loader:before,.ui.yellow.elastic.loading.loading.loading .input>i.icon:before,.ui.yellow.elastic.loading.loading.loading.loading>i.icon:before,.ui.yellow.elastic.loading.loading.loading:not(.segment):before,.ui.yellow.loader.loader.loader:after,.ui.yellow.loading.loading.loading.loading .input>i.icon:after,.ui.yellow.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.yellow.loading.loading.loading.loading>i.icon:after{color:#fbbd08}.ui.inverted.yellow.elastic.loader:before,.ui.inverted.yellow.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.yellow.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.yellow.elastic.loading.loading.loading>i.icon:before,.ui.inverted.yellow.loader.loader.loader:after,.ui.inverted.yellow.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.yellow.loading.loading.loading.loading:not(.usual):after,.ui.inverted.yellow.loading.loading.loading.loading>i.icon:after{color:#ffe21f}.ui.olive.basic.elastic.loading.button:after,.ui.olive.basic.elastic.loading.button:before,.ui.olive.elastic.loader.loader:before,.ui.olive.elastic.loading.loading.loading .input>i.icon:before,.ui.olive.elastic.loading.loading.loading.loading>i.icon:before,.ui.olive.elastic.loading.loading.loading:not(.segment):before,.ui.olive.loader.loader.loader:after,.ui.olive.loading.loading.loading.loading .input>i.icon:after,.ui.olive.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.olive.loading.loading.loading.loading>i.icon:after{color:#b5cc18}.ui.inverted.olive.elastic.loader:before,.ui.inverted.olive.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.olive.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.olive.elastic.loading.loading.loading>i.icon:before,.ui.inverted.olive.loader.loader.loader:after,.ui.inverted.olive.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.olive.loading.loading.loading.loading:not(.usual):after,.ui.inverted.olive.loading.loading.loading.loading>i.icon:after{color:#d9e778}.ui.green.basic.elastic.loading.button:after,.ui.green.basic.elastic.loading.button:before,.ui.green.elastic.loader.loader:before,.ui.green.elastic.loading.loading.loading .input>i.icon:before,.ui.green.elastic.loading.loading.loading.loading>i.icon:before,.ui.green.elastic.loading.loading.loading:not(.segment):before,.ui.green.loader.loader.loader:after,.ui.green.loading.loading.loading.loading .input>i.icon:after,.ui.green.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.green.loading.loading.loading.loading>i.icon:after{color:#21ba45}.ui.inverted.green.elastic.loader:before,.ui.inverted.green.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.green.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.green.elastic.loading.loading.loading>i.icon:before,.ui.inverted.green.loader.loader.loader:after,.ui.inverted.green.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.green.loading.loading.loading.loading:not(.usual):after,.ui.inverted.green.loading.loading.loading.loading>i.icon:after{color:#2ecc40}.ui.teal.basic.elastic.loading.button:after,.ui.teal.basic.elastic.loading.button:before,.ui.teal.elastic.loader.loader:before,.ui.teal.elastic.loading.loading.loading .input>i.icon:before,.ui.teal.elastic.loading.loading.loading.loading>i.icon:before,.ui.teal.elastic.loading.loading.loading:not(.segment):before,.ui.teal.loader.loader.loader:after,.ui.teal.loading.loading.loading.loading .input>i.icon:after,.ui.teal.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.teal.loading.loading.loading.loading>i.icon:after{color:#00b5ad}.ui.inverted.teal.elastic.loader:before,.ui.inverted.teal.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.teal.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.teal.elastic.loading.loading.loading>i.icon:before,.ui.inverted.teal.loader.loader.loader:after,.ui.inverted.teal.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.teal.loading.loading.loading.loading:not(.usual):after,.ui.inverted.teal.loading.loading.loading.loading>i.icon:after{color:#6dffff}.ui.blue.basic.elastic.loading.button:after,.ui.blue.basic.elastic.loading.button:before,.ui.blue.elastic.loader.loader:before,.ui.blue.elastic.loading.loading.loading .input>i.icon:before,.ui.blue.elastic.loading.loading.loading.loading>i.icon:before,.ui.blue.elastic.loading.loading.loading:not(.segment):before,.ui.blue.loader.loader.loader:after,.ui.blue.loading.loading.loading.loading .input>i.icon:after,.ui.blue.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.blue.loading.loading.loading.loading>i.icon:after{color:#2185d0}.ui.inverted.blue.elastic.loader:before,.ui.inverted.blue.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.blue.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.blue.elastic.loading.loading.loading>i.icon:before,.ui.inverted.blue.loader.loader.loader:after,.ui.inverted.blue.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.blue.loading.loading.loading.loading:not(.usual):after,.ui.inverted.blue.loading.loading.loading.loading>i.icon:after{color:#54c8ff}.ui.violet.basic.elastic.loading.button:after,.ui.violet.basic.elastic.loading.button:before,.ui.violet.elastic.loader.loader:before,.ui.violet.elastic.loading.loading.loading .input>i.icon:before,.ui.violet.elastic.loading.loading.loading.loading>i.icon:before,.ui.violet.elastic.loading.loading.loading:not(.segment):before,.ui.violet.loader.loader.loader:after,.ui.violet.loading.loading.loading.loading .input>i.icon:after,.ui.violet.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.violet.loading.loading.loading.loading>i.icon:after{color:#6435c9}.ui.inverted.violet.elastic.loader:before,.ui.inverted.violet.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.violet.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.violet.elastic.loading.loading.loading>i.icon:before,.ui.inverted.violet.loader.loader.loader:after,.ui.inverted.violet.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.violet.loading.loading.loading.loading:not(.usual):after,.ui.inverted.violet.loading.loading.loading.loading>i.icon:after{color:#a291fb}.ui.purple.basic.elastic.loading.button:after,.ui.purple.basic.elastic.loading.button:before,.ui.purple.elastic.loader.loader:before,.ui.purple.elastic.loading.loading.loading .input>i.icon:before,.ui.purple.elastic.loading.loading.loading.loading>i.icon:before,.ui.purple.elastic.loading.loading.loading:not(.segment):before,.ui.purple.loader.loader.loader:after,.ui.purple.loading.loading.loading.loading .input>i.icon:after,.ui.purple.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.purple.loading.loading.loading.loading>i.icon:after{color:#a333c8}.ui.inverted.purple.elastic.loader:before,.ui.inverted.purple.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.purple.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.purple.elastic.loading.loading.loading>i.icon:before,.ui.inverted.purple.loader.loader.loader:after,.ui.inverted.purple.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.purple.loading.loading.loading.loading:not(.usual):after,.ui.inverted.purple.loading.loading.loading.loading>i.icon:after{color:#dc73ff}.ui.pink.basic.elastic.loading.button:after,.ui.pink.basic.elastic.loading.button:before,.ui.pink.elastic.loader.loader:before,.ui.pink.elastic.loading.loading.loading .input>i.icon:before,.ui.pink.elastic.loading.loading.loading.loading>i.icon:before,.ui.pink.elastic.loading.loading.loading:not(.segment):before,.ui.pink.loader.loader.loader:after,.ui.pink.loading.loading.loading.loading .input>i.icon:after,.ui.pink.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.pink.loading.loading.loading.loading>i.icon:after{color:#e03997}.ui.inverted.pink.elastic.loader:before,.ui.inverted.pink.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.pink.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.pink.elastic.loading.loading.loading>i.icon:before,.ui.inverted.pink.loader.loader.loader:after,.ui.inverted.pink.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.pink.loading.loading.loading.loading:not(.usual):after,.ui.inverted.pink.loading.loading.loading.loading>i.icon:after{color:#ff8edf}.ui.brown.basic.elastic.loading.button:after,.ui.brown.basic.elastic.loading.button:before,.ui.brown.elastic.loader.loader:before,.ui.brown.elastic.loading.loading.loading .input>i.icon:before,.ui.brown.elastic.loading.loading.loading.loading>i.icon:before,.ui.brown.elastic.loading.loading.loading:not(.segment):before,.ui.brown.loader.loader.loader:after,.ui.brown.loading.loading.loading.loading .input>i.icon:after,.ui.brown.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.brown.loading.loading.loading.loading>i.icon:after{color:#a5673f}.ui.inverted.brown.elastic.loader:before,.ui.inverted.brown.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.brown.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.brown.elastic.loading.loading.loading>i.icon:before,.ui.inverted.brown.loader.loader.loader:after,.ui.inverted.brown.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.brown.loading.loading.loading.loading:not(.usual):after,.ui.inverted.brown.loading.loading.loading.loading>i.icon:after{color:#d67c1c}.ui.grey.basic.elastic.loading.button:after,.ui.grey.basic.elastic.loading.button:before,.ui.grey.elastic.loader.loader:before,.ui.grey.elastic.loading.loading.loading .input>i.icon:before,.ui.grey.elastic.loading.loading.loading.loading>i.icon:before,.ui.grey.elastic.loading.loading.loading:not(.segment):before,.ui.grey.loader.loader.loader:after,.ui.grey.loading.loading.loading.loading .input>i.icon:after,.ui.grey.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.grey.loading.loading.loading.loading>i.icon:after{color:#767676}.ui.inverted.grey.elastic.loader:before,.ui.inverted.grey.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.grey.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.grey.elastic.loading.loading.loading>i.icon:before,.ui.inverted.grey.loader.loader.loader:after,.ui.inverted.grey.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.grey.loading.loading.loading.loading:not(.usual):after,.ui.inverted.grey.loading.loading.loading.loading>i.icon:after{color:#dcddde}.ui.black.basic.elastic.loading.button:after,.ui.black.basic.elastic.loading.button:before,.ui.black.elastic.loader.loader:before,.ui.black.elastic.loading.loading.loading .input>i.icon:before,.ui.black.elastic.loading.loading.loading.loading>i.icon:before,.ui.black.elastic.loading.loading.loading:not(.segment):before,.ui.black.loader.loader.loader:after,.ui.black.loading.loading.loading.loading .input>i.icon:after,.ui.black.loading.loading.loading.loading:not(.usual):not(.button):after,.ui.black.loading.loading.loading.loading>i.icon:after{color:#1b1c1d}.ui.inverted.black.elastic.loader:before,.ui.inverted.black.elastic.loading.loading.loading .input>i.icon:before,.ui.inverted.black.elastic.loading.loading.loading:not(.segment):before,.ui.inverted.black.elastic.loading.loading.loading>i.icon:before,.ui.inverted.black.loader.loader.loader:after,.ui.inverted.black.loading.loading.loading.loading .input>i.icon:after,.ui.inverted.black.loading.loading.loading.loading:not(.usual):after,.ui.inverted.black.loading.loading.loading.loading>i.icon:after{color:#545454}.ui.elastic.loader.loader:before,.ui.elastic.loading.loading.loading .input>i.icon:before,.ui.elastic.loading.loading.loading:before,.ui.elastic.loading.loading.loading>i.icon:before,.ui.loader.loader.loader:after,.ui.loading.loading.loading.loading .input>i.icon:after,.ui.loading.loading.loading.loading:not(.usual):after,.ui.loading.loading.loading.loading>i.icon:after{border-color:currentColor}.ui.elastic.loading.loading.loading.loading.button:not(.inverted):not(.basic):before{color:#fff}.ui.elastic.basic.loading.button:after,.ui.elastic.basic.loading.button:before{color:#767676}.ui.double.loading.loading.loading.loading.button:after{border-bottom-color:currentColor}.ui.inline.loader{position:relative;vertical-align:middle;margin:0;left:0;top:0;-webkit-transform:none;transform:none}.ui.inline.loader.active,.ui.inline.loader.visible{display:inline-block}.ui.centered.inline.loader.active,.ui.centered.inline.loader.visible{display:block;margin-left:auto;margin-right:auto}.ui.loader.loader.loader.loader.loader:after,.ui.loading.loading.loading.loading.loading.loading .input>i.icon:after,.ui.loading.loading.loading.loading.loading.loading:after,.ui.loading.loading.loading.loading.loading.loading>i.icon:after{border-left-color:transparent;border-right-color:transparent}.ui.loader.loader.loader.loader.loader.loader:not(.double):after,.ui.loading.loading.loading.loading.loading.loading.loading:not(.double) .input>i.icon:after,.ui.loading.loading.loading.loading.loading.loading.loading:not(.double):after,.ui.loading.loading.loading.loading.loading.loading.loading:not(.double)>i.icon:after{border-bottom-color:transparent}.ui.loading.loading.loading.loading.loading.loading.form:after,.ui.loading.loading.loading.loading.loading.loading.segment:after{border-left-color:rgba(0,0,0,.1);border-right-color:rgba(0,0,0,.1)}.ui.loading.loading.loading.loading.loading.loading.form:not(.double):after,.ui.loading.loading.loading.loading.loading.loading.segment:not(.double):after{border-bottom-color:rgba(0,0,0,.1)}.ui.dimmer>.ui.elastic.loader{color:#fff}.ui.inverted.dimmer>.ui.elastic.loader{color:#767676}.ui.elastic.loader.loader:after,.ui.elastic.loading.loading .input>i.icon:after,.ui.elastic.loading.loading:not(.form):not(.segment):after,.ui.elastic.loading.loading>i.icon:after{-webkit-animation:loader 1s infinite cubic-bezier(.27,1.05,.92,.61);animation:loader 1s infinite cubic-bezier(.27,1.05,.92,.61);-webkit-animation-delay:.3s;animation-delay:.3s}.ui.elastic.loader.loader:before,.ui.elastic.loading.loading.loading .input>i.icon:before,.ui.elastic.loading.loading.loading:not(.form):not(.segment):before,.ui.elastic.loading.loading.loading>i.icon:before{-webkit-animation:elastic-loader 1s infinite cubic-bezier(.27,1.05,.92,.61);animation:elastic-loader 1s infinite cubic-bezier(.27,1.05,.92,.61);-moz-animation:currentcolor-elastic-loader 1s infinite cubic-bezier(.27,1.05,.92,.61);border-right-color:transparent}.ui.elastic.inline.loader:empty{-webkit-animation:loader 8s infinite linear;animation:loader 8s infinite linear}.ui.slow.elastic.loader.loader:after,.ui.slow.elastic.loading.loading .input>i.icon:after,.ui.slow.elastic.loading.loading:not(.form):not(.segment):after,.ui.slow.elastic.loading.loading>i.icon:after{-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-delay:.45s;animation-delay:.45s}.ui.slow.elastic.loader.loader:before,.ui.slow.elastic.loading.loading.loading .input>i.icon:before,.ui.slow.elastic.loading.loading.loading:not(.form):not(.segment):before,.ui.slow.elastic.loading.loading.loading>i.icon:before{-webkit-animation-duration:1.5s;animation-duration:1.5s}.ui.fast.elastic.loader.loader:after,.ui.fast.elastic.loading.loading .input>i.icon:after,.ui.fast.elastic.loading.loading:not(.form):not(.segment):after,.ui.fast.elastic.loading.loading>i.icon:after{-webkit-animation-duration:.66s;animation-duration:.66s;-webkit-animation-delay:.2s;animation-delay:.2s}.ui.fast.elastic.loader.loader:before,.ui.fast.elastic.loading.loading.loading .input>i.icon:before,.ui.fast.elastic.loading.loading.loading:not(.form):not(.segment):before,.ui.fast.elastic.loading.loading.loading>i.icon:before{-webkit-animation-duration:.66s;animation-duration:.66s}@-webkit-keyframes elastic-loader{0%,1%{border-left-color:transparent;border-bottom-color:transparent}1.1%,50%{border-left-color:inherit}10%,35.1%{border-bottom-color:transparent}10.1%,35%{border-bottom-color:inherit}50.1%{border-left-color:transparent}100%{border-left-color:transparent;border-bottom-color:transparent;-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes elastic-loader{0%,1%{border-left-color:transparent;border-bottom-color:transparent}1.1%,50%{border-left-color:inherit}10%,35.1%{border-bottom-color:transparent}10.1%,35%{border-bottom-color:inherit}50.1%{border-left-color:transparent}100%{border-left-color:transparent;border-bottom-color:transparent;-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes currentcolor-elastic-loader{0%,1%{border-left-color:transparent;border-bottom-color:transparent}1.1%,50%{border-left-color:currentColor}10%,35.1%{border-bottom-color:transparent}10.1%,35%{border-bottom-color:currentColor}50.1%{border-left-color:transparent}100%{border-left-color:transparent;border-bottom-color:transparent;-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes currentcolor-elastic-loader{0%,1%{border-left-color:transparent;border-bottom-color:transparent}1.1%,50%{border-left-color:currentColor}10%,35.1%{border-bottom-color:transparent}10.1%,35%{border-bottom-color:currentColor}50.1%{border-left-color:transparent}100%{border-left-color:transparent;border-bottom-color:transparent;-webkit-transform:rotate(360deg);transform:rotate(360deg)}}/*! + * # Fomantic-UI - Loader + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.placeholder{position:static;overflow:hidden;-webkit-animation:placeholderShimmer 2s linear;animation:placeholderShimmer 2s linear;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;background-color:#fff;background-image:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(0,0,0,.08)),color-stop(15%,rgba(0,0,0,.15)),color-stop(30%,rgba(0,0,0,.08)));background-image:-webkit-linear-gradient(left,rgba(0,0,0,.08) 0,rgba(0,0,0,.15) 15%,rgba(0,0,0,.08) 30%);background-image:linear-gradient(to right,rgba(0,0,0,.08) 0,rgba(0,0,0,.15) 15%,rgba(0,0,0,.08) 30%);background-size:1200px 100%;max-width:30rem}@-webkit-keyframes placeholderShimmer{0%{background-position:-1200px 0}100%{background-position:1200px 0}}@keyframes placeholderShimmer{0%{background-position:-1200px 0}100%{background-position:1200px 0}}.ui.placeholder+.ui.placeholder{margin-top:2rem}.ui.placeholder+.ui.placeholder{-webkit-animation-delay:.15s;animation-delay:.15s}.ui.placeholder+.ui.placeholder+.ui.placeholder{-webkit-animation-delay:.3s;animation-delay:.3s}.ui.placeholder+.ui.placeholder+.ui.placeholder+.ui.placeholder{-webkit-animation-delay:.45s;animation-delay:.45s}.ui.placeholder+.ui.placeholder+.ui.placeholder+.ui.placeholder+.ui.placeholder{-webkit-animation-delay:.6s;animation-delay:.6s}.ui.placeholder,.ui.placeholder .image.header:after,.ui.placeholder .line,.ui.placeholder .line:after,.ui.placeholder>:before{background-color:#fff}.ui.placeholder.hidden{display:none}.ui.placeholder .image:not(.header):not(.ui):not(.icon){height:100px}.ui.placeholder .square.image:not(.header){height:0;overflow:hidden;padding-top:100%}.ui.placeholder .rectangular.image:not(.header){height:0;overflow:hidden;padding-top:75%}.ui.placeholder .line{position:relative;height:.85714286em}.ui.placeholder .line:after,.ui.placeholder .line:before{top:100%;position:absolute;content:'';background-color:inherit}.ui.placeholder .line:before{left:0}.ui.placeholder .line:after{right:0}.ui.placeholder .line{margin-bottom:.5em}.ui.placeholder .line:after,.ui.placeholder .line:before{height:.5em}.ui.placeholder .line:not(:first-child){margin-top:.5em}.ui.placeholder .line:nth-child(1):after{width:0}.ui.placeholder .line:nth-child(2):after{width:50%}.ui.placeholder .line:nth-child(3):after{width:10%}.ui.placeholder .line:nth-child(4):after{width:35%}.ui.placeholder .line:nth-child(5):after{width:65%}.ui.placeholder .header{position:relative;overflow:hidden}.ui.placeholder .header .line{margin-bottom:.64285714em}.ui.placeholder .header .line:after,.ui.placeholder .header .line:before{height:.64285714em}.ui.placeholder .header .line:not(:first-child){margin-top:.64285714em}.ui.placeholder .header .line:after{width:20%}.ui.placeholder .header .line:nth-child(2):after{width:60%}.ui.placeholder .image.header .line{margin-left:3em}.ui.placeholder .image.header .line:before{width:.71428571rem}.ui.placeholder .image.header:after{display:block;height:.85714286em;content:'';margin-left:3em}.ui.placeholder .header .line:first-child,.ui.placeholder .image .line:first-child,.ui.placeholder .paragraph .line:first-child{height:.01px}.ui.placeholder .header:not(:first-child):before,.ui.placeholder .image:not(:first-child):before,.ui.placeholder .paragraph:not(:first-child):before{height:1.42857143em;content:'';display:block}.ui.inverted.placeholder{background-image:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,.08)),color-stop(15%,rgba(255,255,255,.14)),color-stop(30%,rgba(255,255,255,.08)));background-image:-webkit-linear-gradient(left,rgba(255,255,255,.08) 0,rgba(255,255,255,.14) 15%,rgba(255,255,255,.08) 30%);background-image:linear-gradient(to right,rgba(255,255,255,.08) 0,rgba(255,255,255,.14) 15%,rgba(255,255,255,.08) 30%)}.ui.inverted.placeholder,.ui.inverted.placeholder .image.header:after,.ui.inverted.placeholder .line,.ui.inverted.placeholder .line:after,.ui.inverted.placeholder>:before{background-color:#1b1c1d}.ui.placeholder .full.line.line.line:after{width:0}.ui.placeholder .very.long.line.line.line:after{width:10%}.ui.placeholder .long.line.line.line:after{width:35%}.ui.placeholder .medium.line.line.line:after{width:50%}.ui.placeholder .short.line.line.line:after{width:65%}.ui.placeholder .very.short.line.line.line:after{width:80%}.ui.fluid.placeholder{max-width:none}/*! + * # Fomantic-UI - Rail + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.rail{position:absolute;top:0;width:300px;height:100%}.ui.left.rail{left:auto;right:100%;padding:0 2rem 0 0;margin:0 2rem 0 0}.ui.right.rail{left:100%;right:auto;padding:0 0 0 2rem;margin:0 0 0 2rem}.ui.left.internal.rail{left:0;right:auto;padding:0 0 0 2rem;margin:0 0 0 2rem}.ui.right.internal.rail{left:auto;right:0;padding:0 2rem 0 0;margin:0 2rem 0 0}.ui.dividing.rail{width:302.5px}.ui.left.dividing.rail{padding:0 2.5rem 0 0;margin:0 2.5rem 0 0;border-right:1px solid rgba(34,36,38,.15)}.ui.right.dividing.rail{border-left:1px solid rgba(34,36,38,.15);padding:0 0 0 2.5rem;margin:0 0 0 2.5rem}.ui.close.rail{width:calc(300px + 1em)}.ui.close.left.rail{padding:0 1em 0 0;margin:0 1em 0 0}.ui.close.right.rail{padding:0 0 0 1em;margin:0 0 0 1em}.ui.very.close.rail{width:calc(300px + .5em)}.ui.very.close.left.rail{padding:0 .5em 0 0;margin:0 .5em 0 0}.ui.very.close.right.rail{padding:0 0 0 .5em;margin:0 0 0 .5em}.ui.attached.left.rail,.ui.attached.right.rail{padding:0;margin:0}.ui.rail{font-size:1rem}.ui.mini.rail{font-size:.78571429rem}.ui.tiny.rail{font-size:.85714286rem}.ui.small.rail{font-size:.92857143rem}.ui.large.rail{font-size:1.14285714rem}.ui.big.rail{font-size:1.28571429rem}.ui.huge.rail{font-size:1.42857143rem}.ui.massive.rail{font-size:1.71428571rem}/*! + * # Fomantic-UI - Reveal + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.reveal{display:inherit;position:relative!important;font-size:0}.ui.reveal>.visible.content{position:absolute!important;top:0!important;left:0!important;z-index:3!important;-webkit-transition:all .5s ease .1s;transition:all .5s ease .1s}.ui.reveal>.hidden.content{position:relative!important;z-index:2!important}.ui.active.reveal .visible.content,.ui.reveal:hover .visible.content{z-index:4!important}.ui.slide.reveal{position:relative!important;overflow:hidden!important;white-space:nowrap}.ui.slide.reveal>.content{display:block;width:100%;white-space:normal;float:left;margin:0;-webkit-transition:-webkit-transform .5s ease .1s;transition:-webkit-transform .5s ease .1s;transition:transform .5s ease .1s;transition:transform .5s ease .1s,-webkit-transform .5s ease .1s}.ui.slide.reveal>.visible.content{position:relative!important}.ui.slide.reveal>.hidden.content{position:absolute!important;left:0!important;width:100%!important;-webkit-transform:translateX(100%)!important;transform:translateX(100%)!important}.ui.slide.active.reveal>.visible.content,.ui.slide.reveal:hover>.visible.content{-webkit-transform:translateX(-100%)!important;transform:translateX(-100%)!important}.ui.slide.active.reveal>.hidden.content,.ui.slide.reveal:hover>.hidden.content{-webkit-transform:translateX(0)!important;transform:translateX(0)!important}.ui.slide.right.reveal>.visible.content{-webkit-transform:translateX(0)!important;transform:translateX(0)!important}.ui.slide.right.reveal>.hidden.content{-webkit-transform:translateX(-100%)!important;transform:translateX(-100%)!important}.ui.slide.right.active.reveal>.visible.content,.ui.slide.right.reveal:hover>.visible.content{-webkit-transform:translateX(100%)!important;transform:translateX(100%)!important}.ui.slide.right.active.reveal>.hidden.content,.ui.slide.right.reveal:hover>.hidden.content{-webkit-transform:translateX(0)!important;transform:translateX(0)!important}.ui.slide.up.reveal>.hidden.content{-webkit-transform:translateY(100%)!important;transform:translateY(100%)!important}.ui.slide.up.active.reveal>.visible.content,.ui.slide.up.reveal:hover>.visible.content{-webkit-transform:translateY(-100%)!important;transform:translateY(-100%)!important}.ui.slide.up.active.reveal>.hidden.content,.ui.slide.up.reveal:hover>.hidden.content{-webkit-transform:translateY(0)!important;transform:translateY(0)!important}.ui.slide.down.reveal>.hidden.content{-webkit-transform:translateY(-100%)!important;transform:translateY(-100%)!important}.ui.slide.down.active.reveal>.visible.content,.ui.slide.down.reveal:hover>.visible.content{-webkit-transform:translateY(100%)!important;transform:translateY(100%)!important}.ui.slide.down.active.reveal>.hidden.content,.ui.slide.down.reveal:hover>.hidden.content{-webkit-transform:translateY(0)!important;transform:translateY(0)!important}.ui.fade.reveal>.visible.content{opacity:1}.ui.fade.active.reveal>.visible.content,.ui.fade.reveal:hover>.visible.content{opacity:0}.ui.move.reveal{position:relative!important;overflow:hidden!important;white-space:nowrap}.ui.move.reveal>.content{display:block;float:left;white-space:normal;margin:0;-webkit-transition:-webkit-transform .5s cubic-bezier(.175,.885,.32,1) .1s;transition:-webkit-transform .5s cubic-bezier(.175,.885,.32,1) .1s;transition:transform .5s cubic-bezier(.175,.885,.32,1) .1s;transition:transform .5s cubic-bezier(.175,.885,.32,1) .1s,-webkit-transform .5s cubic-bezier(.175,.885,.32,1) .1s}.ui.move.reveal>.visible.content{position:relative!important}.ui.move.reveal>.hidden.content{position:absolute!important;left:0!important;width:100%!important}.ui.move.active.reveal>.visible.content,.ui.move.reveal:hover>.visible.content{-webkit-transform:translateX(-100%)!important;transform:translateX(-100%)!important}.ui.move.right.active.reveal>.visible.content,.ui.move.right.reveal:hover>.visible.content{-webkit-transform:translateX(100%)!important;transform:translateX(100%)!important}.ui.move.up.active.reveal>.visible.content,.ui.move.up.reveal:hover>.visible.content{-webkit-transform:translateY(-100%)!important;transform:translateY(-100%)!important}.ui.move.down.active.reveal>.visible.content,.ui.move.down.reveal:hover>.visible.content{-webkit-transform:translateY(100%)!important;transform:translateY(100%)!important}.ui.rotate.reveal>.visible.content{-webkit-transition-duration:.5s;transition-duration:.5s;-webkit-transform:rotate(0);transform:rotate(0)}.ui.rotate.reveal>.visible.content,.ui.rotate.right.reveal>.visible.content{-webkit-transform-origin:bottom right;transform-origin:bottom right}.ui.rotate.active.reveal>.visible.content,.ui.rotate.reveal:hover>.visible.content,.ui.rotate.right.active.reveal>.visible.content,.ui.rotate.right.reveal:hover>.visible.content{-webkit-transform:rotate(110deg);transform:rotate(110deg)}.ui.rotate.left.reveal>.visible.content{-webkit-transform-origin:bottom left;transform-origin:bottom left}.ui.rotate.left.active.reveal>.visible.content,.ui.rotate.left.reveal:hover>.visible.content{-webkit-transform:rotate(-110deg);transform:rotate(-110deg)}.ui.disabled.reveal:hover>.visible.visible.content{position:static!important;display:block!important;opacity:1!important;top:0!important;left:0!important;right:auto!important;bottom:auto!important;-webkit-transform:none!important;transform:none!important}.ui.disabled.reveal:hover>.hidden.hidden.content{display:none!important}.ui.reveal>.ui.ribbon.label{z-index:5}.ui.visible.reveal{overflow:visible}.ui.instant.reveal>.content{-webkit-transition-delay:0s!important;transition-delay:0s!important}.ui.reveal>.content{font-size:1rem}.ui.mini.reveal>.content{font-size:.78571429rem}.ui.tiny.reveal>.content{font-size:.85714286rem}.ui.small.reveal>.content{font-size:.92857143rem}.ui.large.reveal>.content{font-size:1.14285714rem}.ui.big.reveal>.content{font-size:1.28571429rem}.ui.huge.reveal>.content{font-size:1.42857143rem}.ui.massive.reveal>.content{font-size:1.71428571rem}/*! + * # Fomantic-UI - Segment + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.segment{position:relative;background:#fff;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);margin:1rem 0;padding:1em 1em;border-radius:.28571429rem;border:1px solid rgba(34,36,38,.15)}.ui.segment:first-child{margin-top:0}.ui.segment:last-child{margin-bottom:0}.ui.vertical.segment{margin:0;padding-left:0;padding-right:0;background:none transparent;border-radius:0;-webkit-box-shadow:none;box-shadow:none;border:none;border-bottom:1px solid rgba(34,36,38,.15)}.ui.vertical.segment:last-child{border-bottom:none}.ui.inverted.segment>.ui.header,.ui.inverted.segment>.ui.header>.sub.header{color:#fff}.ui[class*="bottom attached"].segment>[class*="top attached"].label{border-top-left-radius:0;border-top-right-radius:0}.ui[class*="top attached"].segment>[class*="bottom attached"].label{border-bottom-left-radius:0;border-bottom-right-radius:0}.ui.attached.segment:not(.top):not(.bottom)>[class*="top attached"].label{border-top-left-radius:0;border-top-right-radius:0}.ui.attached.segment:not(.top):not(.bottom)>[class*="bottom attached"].label{border-bottom-left-radius:0;border-bottom-right-radius:0}.ui.grid>.row>.ui.segment.column,.ui.grid>.ui.segment.column,.ui.page.grid.segment{padding-top:2em;padding-bottom:2em}.ui.grid.segment{margin:1rem 0;border-radius:.28571429rem}.ui.basic.table.segment{background:#fff;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.ui[class*="very basic"].table.segment{padding:1em 1em}.ui.segment.tab:last-child{margin-bottom:1rem}.ui.placeholder.segment{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;max-width:initial;-webkit-animation:none;animation:none;overflow:visible;padding:1em 1em;min-height:18rem;background:#f9fafb;border-color:rgba(34,36,38,.15);-webkit-box-shadow:0 2px 25px 0 rgba(34,36,38,.05) inset;box-shadow:0 2px 25px 0 rgba(34,36,38,.05) inset}.ui.placeholder.segment .button,.ui.placeholder.segment textarea{display:block}.ui.placeholder.segment .button,.ui.placeholder.segment .field,.ui.placeholder.segment textarea,.ui.placeholder.segment>.ui.input{max-width:15rem;margin-left:auto;margin-right:auto}.ui.placeholder.segment .column .button,.ui.placeholder.segment .column .field,.ui.placeholder.segment .column textarea,.ui.placeholder.segment .column>.ui.input{max-width:15rem;margin-left:auto;margin-right:auto}.ui.placeholder.segment>.inline{-ms-flex-item-align:center;align-self:center}.ui.placeholder.segment>.inline>.button{display:inline-block;width:auto;margin:0 .35714286rem 0 0}.ui.placeholder.segment>.inline>.button:last-child{margin-right:0}.ui.piled.segment,.ui.piled.segments{margin:3em 0;-webkit-box-shadow:'';box-shadow:'';z-index:auto}.ui.piled.segment:first-child{margin-top:0}.ui.piled.segment:last-child{margin-bottom:0}.ui.piled.segment:after,.ui.piled.segment:before,.ui.piled.segments:after,.ui.piled.segments:before{background-color:#fff;visibility:visible;content:'';display:block;height:100%;left:0;position:absolute;width:100%;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:'';box-shadow:''}.ui.piled.segment:before,.ui.piled.segments:before{-webkit-transform:rotate(-1.2deg);transform:rotate(-1.2deg);top:0;z-index:-2}.ui.piled.segment:after,.ui.piled.segments:after{-webkit-transform:rotate(1.2deg);transform:rotate(1.2deg);top:0;z-index:-1}.ui[class*="top attached"].piled.segment{margin-top:3em;margin-bottom:0}.ui.piled.segment[class*="top attached"]:first-child{margin-top:0}.ui.piled.segment[class*="bottom attached"]{margin-top:0;margin-bottom:3em}.ui.piled.segment[class*="bottom attached"]:last-child{margin-bottom:0}.ui.stacked.segment{padding-bottom:1.4em}.ui.stacked.segment:after,.ui.stacked.segment:before,.ui.stacked.segments:after,.ui.stacked.segments:before{content:'';position:absolute;bottom:-3px;left:0;border-top:1px solid rgba(34,36,38,.15);background:rgba(0,0,0,.03);width:100%;height:6px;visibility:visible}.ui.stacked.segment:before,.ui.stacked.segments:before{display:none}.ui.tall.stacked.segment:before,.ui.tall.stacked.segments:before{display:block;bottom:0}.ui.stacked.inverted.segment:after,.ui.stacked.inverted.segment:before,.ui.stacked.inverted.segments:after,.ui.stacked.inverted.segments:before{background-color:rgba(0,0,0,.03);border-top:1px solid rgba(34,36,38,.35)}.ui.padded.segment{padding:1.5em}.ui[class*="very padded"].segment{padding:3em}.ui.padded.segment.vertical.segment,.ui[class*="very padded"].vertical.segment{padding-left:0;padding-right:0}.ui.compact.segment{display:table}.ui.compact.segments{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}.ui.compact.segments .segment,.ui.segments .compact.segment{display:block;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto}.ui.circular.segment{display:table-cell;padding:2em;text-align:center;vertical-align:middle;border-radius:500em}.ui.raised.raised.segment,.ui.raised.raised.segments{-webkit-box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.segments{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;position:relative;margin:1rem 0;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);border-radius:.28571429rem}.ui.segments:first-child{margin-top:0}.ui.segments:last-child{margin-bottom:0}.ui.segments>.segment{top:0;bottom:0;border-radius:0;margin:0;width:auto;-webkit-box-shadow:none;box-shadow:none;border:none;border-top:1px solid rgba(34,36,38,.15)}.ui.segments:not(.horizontal)>.segment:first-child{top:0;bottom:0;border-top:none;margin-top:0;margin-bottom:0;border-radius:.28571429rem .28571429rem 0 0}.ui.segments:not(.horizontal)>.segment:last-child{top:0;bottom:0;margin-top:0;margin-bottom:0;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;border-radius:0 0 .28571429rem .28571429rem}.ui.segments:not(.horizontal)>.segment:only-child{border-radius:.28571429rem}.ui.segments>.ui.segments{border-top:1px solid rgba(34,36,38,.15);margin:1rem 1rem}.ui.segments>.segments:first-child{border-top:none}.ui.segments>.segment+.segments:not(.horizontal){margin-top:0}.ui.horizontal.segments{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;background-color:transparent;padding:0;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);margin:1rem 0;border-radius:.28571429rem;border:1px solid rgba(34,36,38,.15)}.ui.stackable.horizontal.segments{-ms-flex-wrap:wrap;flex-wrap:wrap}.ui.segments>.horizontal.segments{margin:0;background-color:transparent;border-radius:0;border:none;-webkit-box-shadow:none;box-shadow:none;border-top:1px solid rgba(34,36,38,.15)}.ui.horizontal.segments:not(.compact)>.segment:not(.compact){-webkit-box-flex:1;flex:1 1 auto;-ms-flex:1 1 0}.ui.horizontal.segments>.segment{margin:0;min-width:0;border-radius:0;border:none;-webkit-box-shadow:none;box-shadow:none;border-left:1px solid rgba(34,36,38,.15)}.ui.segments>.horizontal.segments:first-child{border-top:none}.ui.horizontal.segments:not(.stackable)>.segment:first-child{border-left:none}.ui.disabled.segment{opacity:.45;color:rgba(40,40,40,.3)}.ui.loading.segment{position:relative;cursor:default;pointer-events:none;text-shadow:none!important;-webkit-transition:all 0s linear;transition:all 0s linear}.ui.loading.segment:before{position:absolute;content:'';top:0;left:0;background:rgba(255,255,255,.8);width:100%;height:100%;border-radius:.28571429rem;z-index:100}.ui.loading.segment:after{position:absolute;content:'';top:50%;left:50%;margin:-1.5em 0 0 -1.5em;width:3em;height:3em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent;visibility:visible;z-index:101}.ui.basic.segment,.ui.basic.segments,.ui.segments .ui.basic.segment{background:none transparent;-webkit-box-shadow:none;box-shadow:none;border:none;border-radius:0}.ui.clearing.segment:after{content:"";display:block;clear:both}.ui.red.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #db2828}.ui.inverted.red.segment.segment.segment.segment.segment{background-color:#db2828;color:#fff}.ui.orange.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #f2711c}.ui.inverted.orange.segment.segment.segment.segment.segment{background-color:#f2711c;color:#fff}.ui.yellow.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #fbbd08}.ui.inverted.yellow.segment.segment.segment.segment.segment{background-color:#fbbd08;color:#fff}.ui.olive.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #b5cc18}.ui.inverted.olive.segment.segment.segment.segment.segment{background-color:#b5cc18;color:#fff}.ui.green.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #21ba45}.ui.inverted.green.segment.segment.segment.segment.segment{background-color:#21ba45;color:#fff}.ui.teal.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #00b5ad}.ui.inverted.teal.segment.segment.segment.segment.segment{background-color:#00b5ad;color:#fff}.ui.blue.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #2185d0}.ui.inverted.blue.segment.segment.segment.segment.segment{background-color:#2185d0;color:#fff}.ui.violet.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #6435c9}.ui.inverted.violet.segment.segment.segment.segment.segment{background-color:#6435c9;color:#fff}.ui.purple.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #a333c8}.ui.inverted.purple.segment.segment.segment.segment.segment{background-color:#a333c8;color:#fff}.ui.pink.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #e03997}.ui.inverted.pink.segment.segment.segment.segment.segment{background-color:#e03997;color:#fff}.ui.brown.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #a5673f}.ui.inverted.brown.segment.segment.segment.segment.segment{background-color:#a5673f;color:#fff}.ui.grey.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #767676}.ui.inverted.grey.segment.segment.segment.segment.segment{background-color:#767676;color:#fff}.ui.black.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #1b1c1d}.ui.inverted.black.segment.segment.segment.segment.segment{background-color:#1b1c1d;color:#fff}.ui[class*="left aligned"].segment{text-align:left}.ui[class*="right aligned"].segment{text-align:right}.ui[class*="center aligned"].segment{text-align:center}.ui.floated.segment,.ui[class*="left floated"].segment{float:left;margin-right:1em}.ui[class*="right floated"].segment{float:right;margin-left:1em}.ui.inverted.segment{border:none;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.segment,.ui.primary.inverted.segment{background:#1b1c1d;color:rgba(255,255,255,.9)}.ui.inverted.segment .segment{color:rgba(0,0,0,.87)}.ui.inverted.segment .inverted.segment{color:rgba(255,255,255,.9)}.ui.inverted.attached.segment{border-color:#555}.ui.inverted.loading.segment{color:#fff}.ui.inverted.loading.segment:before{background:rgba(0,0,0,.85)}.ui.secondary.segment{background:#f3f4f5;color:rgba(0,0,0,.6)}.ui.secondary.inverted.segment{background:#4c4f52 -webkit-gradient(linear,left top,left bottom,color-stop(0,rgba(255,255,255,.2)),to(rgba(255,255,255,.2)));background:#4c4f52 -webkit-linear-gradient(rgba(255,255,255,.2) 0,rgba(255,255,255,.2) 100%);background:#4c4f52 linear-gradient(rgba(255,255,255,.2) 0,rgba(255,255,255,.2) 100%);color:rgba(255,255,255,.8)}.ui.tertiary.segment{background:#dcddde;color:rgba(0,0,0,.6)}.ui.tertiary.inverted.segment{background:#717579 -webkit-gradient(linear,left top,left bottom,color-stop(0,rgba(255,255,255,.35)),to(rgba(255,255,255,.35)));background:#717579 -webkit-linear-gradient(rgba(255,255,255,.35) 0,rgba(255,255,255,.35) 100%);background:#717579 linear-gradient(rgba(255,255,255,.35) 0,rgba(255,255,255,.35) 100%);color:rgba(255,255,255,.8)}.ui.attached.segment{top:0;bottom:0;border-radius:0;margin:0 -1px;width:calc(100% + 2px);max-width:calc(100% + 2px);-webkit-box-shadow:none;box-shadow:none;border:1px solid #d4d4d5}.ui.attached:not(.message)+.ui.attached.segment:not(.top){border-top:none}.ui[class*="top attached"].segment{bottom:0;margin-bottom:0;top:0;margin-top:1rem;border-radius:.28571429rem .28571429rem 0 0}.ui.segment[class*="top attached"]:first-child{margin-top:0}.ui.segment[class*="bottom attached"]{bottom:0;margin-top:0;top:0;margin-bottom:1rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;border-radius:0 0 .28571429rem .28571429rem}.ui.segment[class*="bottom attached"]:last-child{margin-bottom:1rem}.ui.fitted.segment:not(.horizontally){padding-top:0;padding-bottom:0}.ui.fitted.segment:not(.vertically){padding-left:0;padding-right:0}.ui.segment,.ui.segments .segment{font-size:1rem}.ui.mini.segment,.ui.mini.segments .segment{font-size:.78571429rem}.ui.tiny.segment,.ui.tiny.segments .segment{font-size:.85714286rem}.ui.small.segment,.ui.small.segments .segment{font-size:.92857143rem}.ui.large.segment,.ui.large.segments .segment{font-size:1.14285714rem}.ui.big.segment,.ui.big.segments .segment{font-size:1.28571429rem}.ui.huge.segment,.ui.huge.segments .segment{font-size:1.42857143rem}.ui.massive.segment,.ui.massive.segments .segment{font-size:1.71428571rem}/*! + * # Fomantic-UI - Step + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.steps{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;margin:1em 0;background:'';-webkit-box-shadow:none;box-shadow:none;line-height:1.14285714em;border-radius:.28571429rem;border:1px solid rgba(34,36,38,.15)}.ui.steps:not(.unstackable){-ms-flex-wrap:wrap;flex-wrap:wrap}.ui.steps:first-child{margin-top:0}.ui.steps:last-child{margin-bottom:0}.ui.steps .step{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;vertical-align:middle;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin:0 0;padding:1.14285714em 2em;background:#fff;color:rgba(0,0,0,.87);-webkit-box-shadow:none;box-shadow:none;border-radius:0;border:none;border-right:1px solid rgba(34,36,38,.15);-webkit-transition:background-color .1s ease,opacity .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background-color .1s ease,opacity .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background-color .1s ease,opacity .1s ease,color .1s ease,box-shadow .1s ease;transition:background-color .1s ease,opacity .1s ease,color .1s ease,box-shadow .1s ease,-webkit-box-shadow .1s ease}.ui.steps .step:after{display:none;position:absolute;z-index:2;content:'';top:50%;right:0;background-color:#fff;width:1.14285714em;height:1.14285714em;border-style:solid;border-color:rgba(34,36,38,.15);border-width:0 1px 1px 0;-webkit-transition:background-color .1s ease,opacity .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background-color .1s ease,opacity .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background-color .1s ease,opacity .1s ease,color .1s ease,box-shadow .1s ease;transition:background-color .1s ease,opacity .1s ease,color .1s ease,box-shadow .1s ease,-webkit-box-shadow .1s ease;-webkit-transform:translateY(-50%) translateX(50%) rotate(-45deg);transform:translateY(-50%) translateX(50%) rotate(-45deg)}.ui.steps .step:first-child{padding-left:2em;border-radius:.28571429rem 0 0 .28571429rem}.ui.steps .step:last-child{border-radius:0 .28571429rem .28571429rem 0;border-right:none;margin-right:0}.ui.steps .step:only-child{border-radius:.28571429rem}.ui.steps .step .title{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1.14285714em;font-weight:700}.ui.steps .step>.title{width:100%}.ui.steps .step .description{font-weight:400;font-size:.92857143em;color:rgba(0,0,0,.87)}.ui.steps .step>.description{width:100%}.ui.steps .step .title~.description{margin-top:.25em}.ui.steps .step>.icon{line-height:1;font-size:2.5em;margin:0 1rem 0 0}.ui.steps .step>.icon,.ui.steps .step>.icon~.content{display:block;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;-ms-flex-item-align:middle;align-self:middle}.ui.steps:not(.vertical) .step>.icon{width:auto}.ui.steps .link.step,.ui.steps a.step{cursor:pointer}.ui.ordered.steps{counter-reset:ordered}.ui.ordered.steps .step:before{display:block;position:static;text-align:center;content:counter(ordered);-ms-flex-item-align:middle;align-self:middle;margin-right:1rem;font-size:2.5em;counter-increment:ordered;font-family:inherit;font-weight:700}.ui.ordered.steps .step>*{display:block;-ms-flex-item-align:middle;align-self:middle}.ui.vertical.steps{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;overflow:visible}.ui.vertical.steps .step{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;border-radius:0;padding:1.14285714em 2em;border-right:none;border-bottom:1px solid rgba(34,36,38,.15)}.ui.vertical.steps .step:first-child{padding:1.14285714em 2em;border-radius:.28571429rem .28571429rem 0 0}.ui.vertical.steps .step:last-child{border-bottom:none;border-radius:0 0 .28571429rem .28571429rem}.ui.vertical.steps .step:only-child{border-radius:.28571429rem}.ui.vertical.steps .step:after{top:50%;right:0;border-width:0 1px 1px 0;display:none}.ui.vertical.steps .active.step:after{display:block}.ui.vertical.steps .step:last-child:after{display:none}.ui.vertical.steps .active.step:last-child:after{display:block}@media only screen and (max-width:767.98px){.ui.steps:not(.unstackable){display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;overflow:visible;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.steps:not(.unstackable) .step{width:100%!important;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;border-radius:0;padding:1.14285714em 2em;border-right:none;border-bottom:1px solid rgba(34,36,38,.15)}.ui.steps:not(.unstackable) .step:first-child{padding:1.14285714em 2em;border-radius:.28571429rem .28571429rem 0 0}.ui.steps:not(.unstackable) .step:last-child{border-radius:0 0 .28571429rem .28571429rem;border-bottom:none}.ui.steps:not(.unstackable) .step:after{top:unset;bottom:-1.14285714em;right:50%;-webkit-transform:translateY(-50%) translateX(50%) rotate(45deg);transform:translateY(-50%) translateX(50%) rotate(45deg)}.ui.vertical.steps .active.step:last-child:after{display:none}.ui.steps:not(.unstackable) .step .content{text-align:center}.ui.ordered.steps:not(.unstackable) .step:before,.ui.steps:not(.unstackable) .step>.icon{margin:0 0 1rem 0}}.ui.steps .link.step:hover,.ui.steps .link.step:hover::after,.ui.steps a.step:hover,.ui.steps a.step:hover::after{background:#f9fafb;color:rgba(0,0,0,.8)}.ui.steps .link.step:active,.ui.steps .link.step:active::after,.ui.steps a.step:active,.ui.steps a.step:active::after{background:#f3f4f5;color:rgba(0,0,0,.9)}.ui.steps .step.active{cursor:auto;background:#f3f4f5}.ui.steps .step.active:after{background:#f3f4f5}.ui.steps .step.active .title{color:#4183c4}.ui.ordered.steps .step.active:before,.ui.steps .active.step .icon{color:rgba(0,0,0,.85)}.ui.steps .step:after{display:block}.ui.steps .active.step:after{display:block}.ui.steps .step:last-child:after{display:none}.ui.steps .active.step:last-child:after{display:none}.ui.steps .link.active.step:hover,.ui.steps .link.active.step:hover::after,.ui.steps a.active.step:hover,.ui.steps a.active.step:hover::after{cursor:pointer;background:#dcddde;color:rgba(0,0,0,.87)}.ui.ordered.steps .step.completed:before,.ui.steps .step.completed>.icon:before{color:#21ba45}.ui.steps .disabled.step{cursor:auto;background:#fff;pointer-events:none}.ui.steps .disabled.step,.ui.steps .disabled.step .description,.ui.steps .disabled.step .title{color:rgba(40,40,40,.3)}.ui.steps .disabled.step:after{background:#fff}@media only screen and (max-width:991.98px){.ui[class*="tablet stackable"].steps{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;overflow:visible;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui[class*="tablet stackable"].steps .step{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;border-radius:0;padding:1.14285714em 2em;border-right:none;border-bottom:1px solid rgba(34,36,38,.15)}.ui[class*="tablet stackable"].steps .step:first-child{padding:1.14285714em 2em;border-radius:.28571429rem .28571429rem 0 0}.ui[class*="tablet stackable"].steps .step:last-child{border-radius:0 0 .28571429rem .28571429rem;border-bottom:none}.ui[class*="tablet stackable"].steps .step:after{top:unset;bottom:-1.14285714em;right:50%;-webkit-transform:translateY(-50%) translateX(50%) rotate(45deg);transform:translateY(-50%) translateX(50%) rotate(45deg)}.ui[class*="tablet stackable"].steps .step .content{text-align:center}.ui[class*="tablet stackable"].ordered.steps .step:before,.ui[class*="tablet stackable"].steps .step>.icon{margin:0 0 1rem 0}}.ui.fluid.steps{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%}.ui.attached.steps{width:calc(100% + 2px)!important;margin:0 -1px 0;max-width:calc(100% + 2px);border-radius:.28571429rem .28571429rem 0 0}.ui.attached.steps .step:first-child{border-radius:.28571429rem 0 0 0}.ui.attached.steps .step:last-child{border-radius:0 .28571429rem 0 0}.ui.bottom.attached.steps{margin:0 -1px 0;border-radius:0 0 .28571429rem .28571429rem}.ui.bottom.attached.steps .step:first-child{border-radius:0 0 0 .28571429rem}.ui.bottom.attached.steps .step:last-child{border-radius:0 0 .28571429rem 0}.ui.eight.steps,.ui.five.steps,.ui.four.steps,.ui.one.steps,.ui.seven.steps,.ui.six.steps,.ui.three.steps,.ui.two.steps{width:100%}.ui.eight.steps>.step,.ui.five.steps>.step,.ui.four.steps>.step,.ui.one.steps>.step,.ui.seven.steps>.step,.ui.six.steps>.step,.ui.three.steps>.step,.ui.two.steps>.step{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.ui.one.steps>.step{width:100%}.ui.two.steps>.step{width:50%}.ui.three.steps>.step{width:33.333%}.ui.four.steps>.step{width:25%}.ui.five.steps>.step{width:20%}.ui.six.steps>.step{width:16.666%}.ui.seven.steps>.step{width:14.285%}.ui.eight.steps>.step{width:12.5%}.ui.step,.ui.steps .step{font-size:1rem}.ui.mini.step,.ui.mini.steps .step{font-size:.78571429rem}.ui.tiny.step,.ui.tiny.steps .step{font-size:.85714286rem}.ui.small.step,.ui.small.steps .step{font-size:.92857143rem}.ui.large.step,.ui.large.steps .step{font-size:1.14285714rem}.ui.big.step,.ui.big.steps .step{font-size:1.28571429rem}.ui.huge.step,.ui.huge.steps .step{font-size:1.42857143rem}.ui.massive.step,.ui.massive.steps .step{font-size:1.71428571rem}.ui.inverted.steps{border:1px solid #555}.ui.inverted.steps .step{color:rgba(255,255,255,.9);background:#1b1c1d;border-color:#555}.ui.inverted.steps .step:after{background-color:#1b1c1d;border-color:#555}.ui.inverted.steps .step .description{color:rgba(255,255,255,.9)}.ui.inverted.steps .step.active,.ui.inverted.steps .step.active:after{background:#333}.ui.inverted.ordered.steps .step.active:before,.ui.inverted.steps .active.step .icon{color:#fff}.ui.inverted.steps .disabled.step,.ui.inverted.steps .disabled.step:after{background:#222}.ui.inverted.steps .disabled.step,.ui.inverted.steps .disabled.step .description,.ui.inverted.steps .disabled.step .title{color:rgba(225,225,225,.3)}.ui.inverted.steps .link.step:hover,.ui.inverted.steps .link.step:hover::after,.ui.inverted.steps a.step:hover,.ui.inverted.steps a.step:hover::after{background:#3f3f3f;color:#fff}.ui.inverted.steps .link.step:active,.ui.inverted.steps .link.step:active::after,.ui.inverted.steps a.step:active,.ui.inverted.steps a.step:active::after{background:#444;color:#fff}@font-face{font-family:Step;src:url(data:application/x-font-ttf;charset=utf-8;;base64,AAEAAAAOAIAAAwBgT1MvMj3hSQEAAADsAAAAVmNtYXDQEhm3AAABRAAAAUpjdnQgBkn/lAAABuwAAAAcZnBnbYoKeDsAAAcIAAAJkWdhc3AAAAAQAAAG5AAAAAhnbHlm32cEdgAAApAAAAC2aGVhZAErPHsAAANIAAAANmhoZWEHUwNNAAADgAAAACRobXR4CykAAAAAA6QAAAAMbG9jYQA4AFsAAAOwAAAACG1heHAApgm8AAADuAAAACBuYW1lzJ0aHAAAA9gAAALNcG9zdK69QJgAAAaoAAAAO3ByZXCSoZr/AAAQnAAAAFYAAQO4AZAABQAIAnoCvAAAAIwCegK8AAAB4AAxAQIAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA6ADoAQNS/2oAWgMLAE8AAAABAAAAAAAAAAAAAwAAAAMAAAAcAAEAAAAAAEQAAwABAAAAHAAEACgAAAAGAAQAAQACAADoAf//AAAAAOgA//8AABgBAAEAAAAAAAAAAAEGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAADpAKYABUAHEAZDwEAAQFCAAIBAmoAAQABagAAAGEUFxQDEisBFAcBBiInASY0PwE2Mh8BATYyHwEWA6QP/iAQLBD+6g8PTBAsEKQBbhAsEEwPAhYWEP4gDw8BFhAsEEwQEKUBbxAQTBAAAAH//f+xA18DCwAMABJADwABAQpDAAAACwBEFRMCESsBFA4BIi4CPgEyHgEDWXLG6MhuBnq89Lp+AV51xHR0xOrEdHTEAAAAAAEAAAABAADDeRpdXw889QALA+gAAAAAzzWYjQAAAADPNWBN//3/sQOkAwsAAAAIAAIAAAAAAAAAAQAAA1L/agBaA+gAAP/3A6QAAQAAAAAAAAAAAAAAAAAAAAMD6AAAA+gAAANZAAAAAAAAADgAWwABAAAAAwAWAAEAAAAAAAIABgATAG4AAAAtCZEAAAAAAAAAEgDeAAEAAAAAAAAANQAAAAEAAAAAAAEACAA1AAEAAAAAAAIABwA9AAEAAAAAAAMACABEAAEAAAAAAAQACABMAAEAAAAAAAUACwBUAAEAAAAAAAYACABfAAEAAAAAAAoAKwBnAAEAAAAAAAsAEwCSAAMAAQQJAAAAagClAAMAAQQJAAEAEAEPAAMAAQQJAAIADgEfAAMAAQQJAAMAEAEtAAMAAQQJAAQAEAE9AAMAAQQJAAUAFgFNAAMAAQQJAAYAEAFjAAMAAQQJAAoAVgFzAAMAAQQJAAsAJgHJQ29weXJpZ2h0IChDKSAyMDE0IGJ5IG9yaWdpbmFsIGF1dGhvcnMgQCBmb250ZWxsby5jb21mb250ZWxsb1JlZ3VsYXJmb250ZWxsb2ZvbnRlbGxvVmVyc2lvbiAxLjBmb250ZWxsb0dlbmVyYXRlZCBieSBzdmcydHRmIGZyb20gRm9udGVsbG8gcHJvamVjdC5odHRwOi8vZm9udGVsbG8uY29tAEMAbwBwAHkAcgBpAGcAaAB0ACAAKABDACkAIAAyADAAMQA0ACAAYgB5ACAAbwByAGkAZwBpAG4AYQBsACAAYQB1AHQAaABvAHIAcwAgAEAAIABmAG8AbgB0AGUAbABsAG8ALgBjAG8AbQBmAG8AbgB0AGUAbABsAG8AUgBlAGcAdQBsAGEAcgBmAG8AbgB0AGUAbABsAG8AZgBvAG4AdABlAGwAbABvAFYAZQByAHMAaQBvAG4AIAAxAC4AMABmAG8AbgB0AGUAbABsAG8ARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAQIBAwljaGVja21hcmsGY2lyY2xlAAAAAAEAAf//AA8AAAAAAAAAAAAAAAAAAAAAADIAMgML/7EDC/+xsAAssCBgZi2wASwgZCCwwFCwBCZasARFW1ghIyEbilggsFBQWCGwQFkbILA4UFghsDhZWSCwCkVhZLAoUFghsApFILAwUFghsDBZGyCwwFBYIGYgiophILAKUFhgGyCwIFBYIbAKYBsgsDZQWCGwNmAbYFlZWRuwACtZWSOwAFBYZVlZLbACLCBFILAEJWFkILAFQ1BYsAUjQrAGI0IbISFZsAFgLbADLCMhIyEgZLEFYkIgsAYjQrIKAAIqISCwBkMgiiCKsAArsTAFJYpRWGBQG2FSWVgjWSEgsEBTWLAAKxshsEBZI7AAUFhlWS2wBCywB0MrsgACAENgQi2wBSywByNCIyCwACNCYbCAYrABYLAEKi2wBiwgIEUgsAJFY7ABRWJgRLABYC2wBywgIEUgsAArI7ECBCVgIEWKI2EgZCCwIFBYIbAAG7AwUFiwIBuwQFlZI7AAUFhlWbADJSNhRESwAWAtsAgssQUFRbABYUQtsAkssAFgICCwCUNKsABQWCCwCSNCWbAKQ0qwAFJYILAKI0JZLbAKLCC4BABiILgEAGOKI2GwC0NgIIpgILALI0IjLbALLEtUWLEHAURZJLANZSN4LbAMLEtRWEtTWLEHAURZGyFZJLATZSN4LbANLLEADENVWLEMDEOwAWFCsAorWbAAQ7ACJUKxCQIlQrEKAiVCsAEWIyCwAyVQWLEBAENgsAQlQoqKIIojYbAJKiEjsAFhIIojYbAJKiEbsQEAQ2CwAiVCsAIlYbAJKiFZsAlDR7AKQ0dgsIBiILACRWOwAUViYLEAABMjRLABQ7AAPrIBAQFDYEItsA4ssQAFRVRYALAMI0IgYLABYbUNDQEACwBCQopgsQ0FK7BtKxsiWS2wDyyxAA4rLbAQLLEBDistsBEssQIOKy2wEiyxAw4rLbATLLEEDistsBQssQUOKy2wFSyxBg4rLbAWLLEHDistsBcssQgOKy2wGCyxCQ4rLbAZLLAIK7EABUVUWACwDCNCIGCwAWG1DQ0BAAsAQkKKYLENBSuwbSsbIlktsBossQAZKy2wGyyxARkrLbAcLLECGSstsB0ssQMZKy2wHiyxBBkrLbAfLLEFGSstsCAssQYZKy2wISyxBxkrLbAiLLEIGSstsCMssQkZKy2wJCwgPLABYC2wJSwgYLANYCBDI7ABYEOwAiVhsAFgsCQqIS2wJiywJSuwJSotsCcsICBHICCwAkVjsAFFYmAjYTgjIIpVWCBHICCwAkVjsAFFYmAjYTgbIVktsCgssQAFRVRYALABFrAnKrABFTAbIlktsCkssAgrsQAFRVRYALABFrAnKrABFTAbIlktsCosIDWwAWAtsCssALADRWOwAUVisAArsAJFY7ABRWKwACuwABa0AAAAAABEPiM4sSoBFSotsCwsIDwgRyCwAkVjsAFFYmCwAENhOC2wLSwuFzwtsC4sIDwgRyCwAkVjsAFFYmCwAENhsAFDYzgtsC8ssQIAFiUgLiBHsAAjQrACJUmKikcjRyNhIFhiGyFZsAEjQrIuAQEVFCotsDAssAAWsAQlsAQlRyNHI2GwBkUrZYouIyAgPIo4LbAxLLAAFrAEJbAEJSAuRyNHI2EgsAQjQrAGRSsgsGBQWCCwQFFYswIgAyAbswImAxpZQkIjILAIQyCKI0cjRyNhI0ZgsARDsIBiYCCwACsgiophILACQ2BkI7ADQ2FkUFiwAkNhG7ADQ2BZsAMlsIBiYSMgILAEJiNGYTgbI7AIQ0awAiWwCENHI0cjYWAgsARDsIBiYCMgsAArI7AEQ2CwACuwBSVhsAUlsIBisAQmYSCwBCVgZCOwAyVgZFBYIRsjIVkjICCwBCYjRmE4WS2wMiywABYgICCwBSYgLkcjRyNhIzw4LbAzLLAAFiCwCCNCICAgRiNHsAArI2E4LbA0LLAAFrADJbACJUcjRyNhsABUWC4gPCMhG7ACJbACJUcjRyNhILAFJbAEJUcjRyNhsAYlsAUlSbACJWGwAUVjIyBYYhshWWOwAUViYCMuIyAgPIo4IyFZLbA1LLAAFiCwCEMgLkcjRyNhIGCwIGBmsIBiIyAgPIo4LbA2LCMgLkawAiVGUlggPFkusSYBFCstsDcsIyAuRrACJUZQWCA8WS6xJgEUKy2wOCwjIC5GsAIlRlJYIDxZIyAuRrACJUZQWCA8WS6xJgEUKy2wOSywMCsjIC5GsAIlRlJYIDxZLrEmARQrLbA6LLAxK4ogIDywBCNCijgjIC5GsAIlRlJYIDxZLrEmARQrsARDLrAmKy2wOyywABawBCWwBCYgLkcjRyNhsAZFKyMgPCAuIzixJgEUKy2wPCyxCAQlQrAAFrAEJbAEJSAuRyNHI2EgsAQjQrAGRSsgsGBQWCCwQFFYswIgAyAbswImAxpZQkIjIEewBEOwgGJgILAAKyCKimEgsAJDYGQjsANDYWRQWLACQ2EbsANDYFmwAyWwgGJhsAIlRmE4IyA8IzgbISAgRiNHsAArI2E4IVmxJgEUKy2wPSywMCsusSYBFCstsD4ssDErISMgIDywBCNCIzixJgEUK7AEQy6wJistsD8ssAAVIEewACNCsgABARUUEy6wLCotsEAssAAVIEewACNCsgABARUUEy6wLCotsEEssQABFBOwLSotsEIssC8qLbBDLLAAFkUjIC4gRoojYTixJgEUKy2wRCywCCNCsEMrLbBFLLIAADwrLbBGLLIAATwrLbBHLLIBADwrLbBILLIBATwrLbBJLLIAAD0rLbBKLLIAAT0rLbBLLLIBAD0rLbBMLLIBAT0rLbBNLLIAADkrLbBOLLIAATkrLbBPLLIBADkrLbBQLLIBATkrLbBRLLIAADsrLbBSLLIAATsrLbBTLLIBADsrLbBULLIBATsrLbBVLLIAAD4rLbBWLLIAAT4rLbBXLLIBAD4rLbBYLLIBAT4rLbBZLLIAADorLbBaLLIAATorLbBbLLIBADorLbBcLLIBATorLbBdLLAyKy6xJgEUKy2wXiywMiuwNistsF8ssDIrsDcrLbBgLLAAFrAyK7A4Ky2wYSywMysusSYBFCstsGIssDMrsDYrLbBjLLAzK7A3Ky2wZCywMyuwOCstsGUssDQrLrEmARQrLbBmLLA0K7A2Ky2wZyywNCuwNystsGgssDQrsDgrLbBpLLA1Ky6xJgEUKy2waiywNSuwNistsGsssDUrsDcrLbBsLLA1K7A4Ky2wbSwrsAhlsAMkUHiwARUwLQAAAEu4AMhSWLEBAY5ZuQgACABjILABI0SwAyNwsgQoCUVSRLIKAgcqsQYBRLEkAYhRWLBAiFixBgNEsSYBiFFYuAQAiFixBgFEWVlZWbgB/4WwBI2xBQBEAAA=) format('truetype'),url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAoUAA4AAAAAEPQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAEQAAABWPeFJAWNtYXAAAAGIAAAAOgAAAUrQEhm3Y3Z0IAAAAcQAAAAUAAAAHAZJ/5RmcGdtAAAB2AAABPkAAAmRigp4O2dhc3AAAAbUAAAACAAAAAgAAAAQZ2x5ZgAABtwAAACuAAAAtt9nBHZoZWFkAAAHjAAAADUAAAA2ASs8e2hoZWEAAAfEAAAAIAAAACQHUwNNaG10eAAAB+QAAAAMAAAADAspAABsb2NhAAAH8AAAAAgAAAAIADgAW21heHAAAAf4AAAAIAAAACAApgm8bmFtZQAACBgAAAF3AAACzcydGhxwb3N0AAAJkAAAACoAAAA7rr1AmHByZXAAAAm8AAAAVgAAAFaSoZr/eJxjYGTewTiBgZWBg6mKaQ8DA0MPhGZ8wGDIyMTAwMTAysyAFQSkuaYwOLxgeMHIHPQ/iyGKmZvBHyjMCJIDAPe9C2B4nGNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZgYGF4w/v8PUvCCAURLMELVAwEjG8OIBwBk5AavAAB4nGNgQANGDEbM3P83gjAAELQD4XicnVXZdtNWFJU8ZHASOmSgoA7X3DhQ68qEKRgwaSrFdiEdHAitBB2kDHTkncc+62uOQrtWH/m07n09JLR0rbYsls++R1tn2DrnRhwjKn0aiGvUoZKXA6msPZZK90lc13Uvj5UMBnFdthJPSZuonSRKat3sUC7xWOsqWSdYJ+PlIFZPVZ5noAziFB5lSUQbRBuplyZJ4onjJ4kWZxAfJUkgJaMQp9LIUEI1GsRS1aFM6dCr1xNx00DKRqMedVhU90PFJ8c1p9SsA0YqVznCFevVRr4bpwMve5DEOsGzrYcxHnisfpQqkIqR6cg/dkpOlIaBVHHUoVbi6DCTX/eRTCrNQKaMYkWl7oG43f102xYxPXQ6vi5KlUaqurnOKJrt0fGogygP2cbppNzQ2fbw5RlTVKtdcbPtQGYNXErJbHSfRAAdJlLj6QFONZwCqRn1R8XZ588BEslclKo8VTKHegOZMzt7cTHtbiersnCknwcyb3Z2452HQ6dXh3/R+hdM4cxHj+Jifj5C+lBqfiJOJKVGWMzyp4YfcVcgQrkxiAsXyuBThDl0RdrZZl3jtTH2hs/5SqlhPQna6KP4fgr9TiQrHGdRo/VInM1j13Wt3GdQS7W7Fzsyr0OVIu7vCwuuM+eEYZ4WC1VfnvneBTT/Bohn/EDeNIVL+5YpSrRvm6JMu2iKCu0SVKVdNsUU7YoppmnPmmKG9h1TzNKeMzLj/8vc55H7HN7xkJv2XeSmfQ+5ad9HbtoPkJtWITdtHblpLyA3rUZu2lWjOnYEGgZpF1IVQdA0svph3Fab9UDWjDR8aWDyLmLI+upER521tcofxX914gsHcmmip7siF5viLq/bFj483e6rj5pG3bDV+MaR8jAeRnocmtBZ+c3hv+1N3S6a7jKqMugBFUwKwABl7UAC0zrbCaT1mqf48gdgXIZ4zkpDtVSfO4am7+V5X/exOfG+x+3GLrdcd3kJWdYNcmP28N9SZKrrH+UtrVQnR6wrJ49VaxhDKrwour6SlHu0tRu/KKmy8l6U1srnk5CbPYMbQlu27mGwI0xpyiUeXlOlKD3UUo6yQyxvKco84JSLC1qGxLgOdQ9qa8TpoXoYGwshhqG0vRBwSCldFd+0ynfxHqtr2Oj4xRXh6XpyEhGf4ir7UfBU10b96A7avGbdMoMpVaqn+4xPsa/b9lFZaaSOsxe3VAfXNOsaORXTT+Rr4HRvOGjdAz1UfDRBI1U1x+jGKGM0ljXl3wR0MVZ+w2jVYvs93E+dpFWsuUuY7JsT9+C0u/0q+7WcW0bW/dcGvW3kip8jMb8tCvw7B2K3ZA3UO5OBGAvIWdAYxhYmdxiug23EbfY/Jqf/34aFRXJXOxq7eerD1ZNRJXfZ8rjLTXZZ16M2R9VOGvsIjS0PN+bY4XIstsRgQbb+wf8x7gF3aVEC4NDIZZiI2nShnurh6h6rsW04VxIBds2x43QAegAuQd8cu9bzCYD13CPnLsB9cgh2yCH4lByCz8i5BfA5OQRfkEMwIIdgl5w7AA/IIXhIDsEeOQSPyNkE+JIcgq/IIYjJIUjIuQ3wmByCJ+QQfE0OwTdGrk5k/pYH2QD6zqKbQKmdGhzaOGRGrk3Y+zxY9oFFZB9aROqRkesT6lMeLPV7i0j9wSJSfzRyY0L9iQdL/dkiUn+xiNRnxpeZIymvDp7zjg7+BJfqrV4AAAAAAQAB//8AD3icY2BkAALmJUwzGEQZZBwk+RkZGBmdGJgYmbIYgMwsoGSiiLgIs5A2owg7I5uSOqOaiT2jmZE8I5gQY17C/09BQEfg3yt+fh8gvYQxD0j68DOJiQn8U+DnZxQDcQUEljLmCwBpBgbG/3//b2SOZ+Zm4GEQcuAH2sblDLSEm8FFVJhJEGgLH6OSHpMdo5EcI3Nk0bEXJ/LYqvZ82VXHGFd6pKTkyCsQwQAAq+QkqAAAeJxjYGRgYADiw5VSsfH8Nl8ZuJlfAEUYzpvO6IXQCb7///7fyLyEmRvI5WBgAokCAFb/DJAAAAB4nGNgZGBgDvqfxRDF/IKB4f935iUMQBEUwAwAi5YFpgPoAAAD6AAAA1kAAAAAAAAAOABbAAEAAAADABYAAQAAAAAAAgAGABMAbgAAAC0JkQAAAAB4nHWQy2rCQBSG//HSi0JbWui2sypKabxgN4IgWHTTbqS4LTHGJBIzMhkFX6Pv0IfpS/RZ+puMpShNmMx3vjlz5mQAXOMbAvnzxJGzwBmjnAs4Rc9ykf7Zcon8YrmMKt4sn9C/W67gAYHlKm7wwQqidM5ogU/LAlfi0nIBF+LOcpH+0XKJ3LNcxq14tXxC71muYCJSy1Xci6+BWm11FIRG1gZ12W62OnK6lYoqStxYumsTKp3KvpyrxPhxrBxPLfc89oN17Op9uJ8nvk4jlciW09yrkZ/42jX+bFc93QRtY+ZyrtVSDm2GXGm18D3jhMasuo3G3/MwgMIKW2hEvKoQBhI12jrnNppooUOaMkMyM8+KkMBFTONizR1htpIy7nPMGSW0PjNisgOP3+WRH5MC7o9ZRR+tHsYT0u6MKPOSfTns7jBrREqyTDezs9/eU2x4WpvWcNeuS511JTE8qCF5H7u1BY1H72S3Ymi7aPD95/9+AN1fhEsAeJxjYGKAAC4G7ICZgYGRiZGZMzkjNTk7N7Eomy05syg5J5WBAQBE1QZBAABLuADIUlixAQGOWbkIAAgAYyCwASNEsAMjcLIEKAlFUkSyCgIHKrEGAUSxJAGIUViwQIhYsQYDRLEmAYhRWLgEAIhYsQYBRFlZWVm4Af+FsASNsQUARAAA) format('woff')}.ui.ordered.steps .step.completed:before,.ui.steps .step.completed>.icon:before{font-family:Step;content:'\e800'}/*! + * # Fomantic-UI - Text + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * https://github.com/fomantic/Fomantic-UI/blob/master/LICENSE.md + * + */span.ui.primary.text{color:#2185d0}span.ui.inverted.primary.text{color:#54c8ff}span.ui.secondary.text{color:#1b1c1d}span.ui.inverted.secondary.text{color:#545454}span.ui.red.text{color:#db2828}span.ui.inverted.red.text{color:#ff695e}span.ui.orange.text{color:#f2711c}span.ui.inverted.orange.text{color:#ff851b}span.ui.yellow.text{color:#fbbd08}span.ui.inverted.yellow.text{color:#ffe21f}span.ui.olive.text{color:#b5cc18}span.ui.inverted.olive.text{color:#d9e778}span.ui.green.text{color:#21ba45}span.ui.inverted.green.text{color:#2ecc40}span.ui.teal.text{color:#00b5ad}span.ui.inverted.teal.text{color:#6dffff}span.ui.blue.text{color:#2185d0}span.ui.inverted.blue.text{color:#54c8ff}span.ui.violet.text{color:#6435c9}span.ui.inverted.violet.text{color:#a291fb}span.ui.purple.text{color:#a333c8}span.ui.inverted.purple.text{color:#dc73ff}span.ui.pink.text{color:#e03997}span.ui.inverted.pink.text{color:#ff8edf}span.ui.brown.text{color:#a5673f}span.ui.inverted.brown.text{color:#d67c1c}span.ui.grey.text{color:#767676}span.ui.inverted.grey.text{color:#dcddde}span.ui.black.text{color:#1b1c1d}span.ui.inverted.black.text{color:#545454}span.ui.disabled.text{opacity:.45}span.ui.medium.text{font-size:1em}span.ui.mini.text{font-size:.4em}span.ui.tiny.text{font-size:.5em}span.ui.small.text{font-size:.75em}span.ui.large.text{font-size:1.5em}span.ui.big.text{font-size:2em}span.ui.huge.text{font-size:4em}span.ui.massive.text{font-size:8em}/*! + * # Fomantic-UI - Breadcrumb + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.breadcrumb{line-height:1.4285em;display:inline-block;margin:0 0;vertical-align:middle}.ui.breadcrumb:first-child{margin-top:0}.ui.breadcrumb:last-child{margin-bottom:0}.ui.breadcrumb .divider{display:inline-block;opacity:.7;margin:0 .21428571rem 0;font-size:.92857143em;color:rgba(0,0,0,.4);vertical-align:baseline}.ui.breadcrumb a{color:#4183c4}.ui.breadcrumb a:hover{color:#1e70bf}.ui.breadcrumb .icon.divider{font-size:.85714286em;vertical-align:baseline}.ui.breadcrumb a.section{cursor:pointer}.ui.breadcrumb .section{display:inline-block;margin:0;padding:0}.ui.breadcrumb.segment{display:inline-block;padding:.78571429em 1em}.ui.inverted.breadcrumb{color:#dcddde}.ui.inverted.breadcrumb>.active.section{color:#fff}.ui.inverted.breadcrumb>.divider{color:rgba(255,255,255,.7)}.ui.breadcrumb .active.section{font-weight:700}.ui.breadcrumb{font-size:1rem}.ui.mini.breadcrumb{font-size:.78571429rem}.ui.tiny.breadcrumb{font-size:.85714286rem}.ui.small.breadcrumb{font-size:.92857143rem}.ui.large.breadcrumb{font-size:1.14285714rem}.ui.big.breadcrumb{font-size:1.28571429rem}.ui.huge.breadcrumb{font-size:1.42857143rem}.ui.massive.breadcrumb{font-size:1.71428571rem}/*! + * # Fomantic-UI - Form + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.form{position:relative;max-width:100%}.ui.form>p{margin:1em 0}.ui.form .field{clear:both;margin:0 0 1em}.ui.form .field:last-child,.ui.form .fields .fields,.ui.form .fields:last-child .field{margin-bottom:0}.ui.form .fields .field{clear:both;margin:0}.ui.form .field>label{display:block;margin:0 0 .28571429rem 0;color:rgba(0,0,0,.87);font-size:.92857143em;font-weight:700;text-transform:none}.ui.form input:not([type]),.ui.form input[type=date],.ui.form input[type=datetime-local],.ui.form input[type=email],.ui.form input[type=file],.ui.form input[type=number],.ui.form input[type=password],.ui.form input[type=search],.ui.form input[type=tel],.ui.form input[type=text],.ui.form input[type=time],.ui.form input[type=url],.ui.form textarea{width:100%;vertical-align:top}.ui.form ::-webkit-datetime-edit,.ui.form ::-webkit-inner-spin-button{height:1.21428571em}.ui.form input:not([type]),.ui.form input[type=date],.ui.form input[type=datetime-local],.ui.form input[type=email],.ui.form input[type=file],.ui.form input[type=number],.ui.form input[type=password],.ui.form input[type=search],.ui.form input[type=tel],.ui.form input[type=text],.ui.form input[type=time],.ui.form input[type=url]{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;margin:0;outline:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);line-height:1.21428571em;padding:.67857143em 1em;font-size:1em;background:#fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);border-radius:.28571429rem;-webkit-box-shadow:0 0 0 0 transparent inset;box-shadow:0 0 0 0 transparent inset;-webkit-transition:color .1s ease,border-color .1s ease;transition:color .1s ease,border-color .1s ease}.ui.form textarea,.ui.input textarea{margin:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);padding:.78571429em 1em;background:#fff;border:1px solid rgba(34,36,38,.15);outline:0;color:rgba(0,0,0,.87);border-radius:.28571429rem;-webkit-box-shadow:0 0 0 0 transparent inset;box-shadow:0 0 0 0 transparent inset;-webkit-transition:color .1s ease,border-color .1s ease;transition:color .1s ease,border-color .1s ease;font-size:1em;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;line-height:1.2857;resize:vertical}.ui.form textarea:not([rows]){height:12em;min-height:8em;max-height:24em}.ui.form input[type=checkbox],.ui.form textarea{vertical-align:top}.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) label+.ui.ui.checkbox{margin-top:.7em}.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.checkbox{margin-top:2.41428571em}.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.toggle.checkbox{margin-top:2.21428571em}.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.slider.checkbox{margin-top:2.61428571em}.ui.ui.form .field .fields .field:not(:only-child) .ui.checkbox{margin-top:.6em}.ui.ui.form .field .fields .field:not(:only-child) .ui.toggle.checkbox{margin-top:.5em}.ui.ui.form .field .fields .field:not(:only-child) .ui.slider.checkbox{margin-top:.7em}.ui.form .field .transparent.input:not(.icon) input,.ui.form .field input.transparent,.ui.form .field textarea.transparent{padding:.67857143em 1em}.ui.form .field input.transparent,.ui.form .field textarea.transparent{border-color:transparent!important;background-color:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.form input.attached{width:auto}.ui.form select{display:block;height:auto;width:100%;background:#fff;border:1px solid rgba(34,36,38,.15);border-radius:.28571429rem;-webkit-box-shadow:0 0 0 0 transparent inset;box-shadow:0 0 0 0 transparent inset;padding:.62em 1em;color:rgba(0,0,0,.87);-webkit-transition:color .1s ease,border-color .1s ease;transition:color .1s ease,border-color .1s ease}.ui.form .field>.selection.dropdown{min-width:auto;width:100%}.ui.form .field>.selection.dropdown>.dropdown.icon{float:right}.ui.form .inline.field>.selection.dropdown,.ui.form .inline.fields .field>.selection.dropdown{width:auto}.ui.form .inline.field>.selection.dropdown>.dropdown.icon,.ui.form .inline.fields .field>.selection.dropdown>.dropdown.icon{float:none}.ui.form .field .ui.input,.ui.form .fields .field .ui.input,.ui.form .wide.field .ui.input{width:100%}.ui.form .inline.field:not(.wide) .ui.input,.ui.form .inline.fields .field:not(.wide) .ui.input{width:auto;vertical-align:middle}.ui.form .field .ui.input input,.ui.form .fields .field .ui.input input{width:auto}.ui.form .eight.fields .ui.input input,.ui.form .five.fields .ui.input input,.ui.form .four.fields .ui.input input,.ui.form .nine.fields .ui.input input,.ui.form .seven.fields .ui.input input,.ui.form .six.fields .ui.input input,.ui.form .ten.fields .ui.input input,.ui.form .three.fields .ui.input input,.ui.form .two.fields .ui.input input,.ui.form .wide.field .ui.input input{-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;width:0}.ui.form .error.message,.ui.form .error.message:empty{display:none}.ui.form .info.message,.ui.form .info.message:empty{display:none}.ui.form .success.message,.ui.form .success.message:empty{display:none}.ui.form .warning.message,.ui.form .warning.message:empty{display:none}.ui.form .message:first-child{margin-top:0}.ui.form .field .prompt.label{white-space:normal;background:#fff!important;border:1px solid #e0b4b4!important;color:#9f3a38!important}.ui.form .inline.field .prompt,.ui.form .inline.fields .field .prompt{vertical-align:top;margin:-.25em 0 -.5em .5em}.ui.form .inline.field .prompt:before,.ui.form .inline.fields .field .prompt:before{border-width:0 0 1px 1px;bottom:auto;right:auto;top:50%;left:0}.ui.form .field.field input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px ivory inset!important;box-shadow:0 0 0 100px ivory inset!important;border-color:#e5dfa1!important}.ui.form .field.field input:-webkit-autofill:focus{-webkit-box-shadow:0 0 0 100px ivory inset!important;box-shadow:0 0 0 100px ivory inset!important;border-color:#d5c315!important}.ui.form ::-webkit-input-placeholder{color:rgba(191,191,191,.87)}.ui.form :-ms-input-placeholder{color:rgba(191,191,191,.87)!important}.ui.form ::-moz-placeholder{color:rgba(191,191,191,.87)}.ui.form :focus::-webkit-input-placeholder{color:rgba(115,115,115,.87)}.ui.form :focus:-ms-input-placeholder{color:rgba(115,115,115,.87)!important}.ui.form :focus::-moz-placeholder{color:rgba(115,115,115,.87)}.ui.form input:not([type]):focus,.ui.form input[type=date]:focus,.ui.form input[type=datetime-local]:focus,.ui.form input[type=email]:focus,.ui.form input[type=file]:focus,.ui.form input[type=number]:focus,.ui.form input[type=password]:focus,.ui.form input[type=search]:focus,.ui.form input[type=tel]:focus,.ui.form input[type=text]:focus,.ui.form input[type=time]:focus,.ui.form input[type=url]:focus{color:rgba(0,0,0,.95);border-color:#85b7d9;border-radius:.28571429rem;background:#fff;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.35) inset;box-shadow:0 0 0 0 rgba(34,36,38,.35) inset}.ui.form .ui.action.input:not(.left) input:not([type]):focus,.ui.form .ui.action.input:not(.left) input[type=date]:focus,.ui.form .ui.action.input:not(.left) input[type=datetime-local]:focus,.ui.form .ui.action.input:not(.left) input[type=email]:focus,.ui.form .ui.action.input:not(.left) input[type=file]:focus,.ui.form .ui.action.input:not(.left) input[type=number]:focus,.ui.form .ui.action.input:not(.left) input[type=password]:focus,.ui.form .ui.action.input:not(.left) input[type=search]:focus,.ui.form .ui.action.input:not(.left) input[type=tel]:focus,.ui.form .ui.action.input:not(.left) input[type=text]:focus,.ui.form .ui.action.input:not(.left) input[type=time]:focus,.ui.form .ui.action.input:not(.left) input[type=url]:focus{border-top-right-radius:0;border-bottom-right-radius:0}.ui.form .ui.action.input.left input:not([type]),.ui.form .ui.action.input.left input[type=date],.ui.form .ui.action.input.left input[type=datetime-local],.ui.form .ui.action.input.left input[type=email],.ui.form .ui.action.input.left input[type=file],.ui.form .ui.action.input.left input[type=number],.ui.form .ui.action.input.left input[type=password],.ui.form .ui.action.input.left input[type=search],.ui.form .ui.action.input.left input[type=tel],.ui.form .ui.action.input.left input[type=text],.ui.form .ui.action.input.left input[type=time],.ui.form .ui.action.input.left input[type=url]{border-bottom-left-radius:0;border-top-left-radius:0}.ui.form textarea:focus{color:rgba(0,0,0,.95);border-color:#85b7d9;border-radius:.28571429rem;background:#fff;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.35) inset;box-shadow:0 0 0 0 rgba(34,36,38,.35) inset;-webkit-appearance:none}.ui.form.error .error.message:not(:empty){display:block}.ui.form.error .compact.error.message:not(:empty){display:inline-block}.ui.form.error .icon.error.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.form .field.error .error.message:not(:empty),.ui.form .fields.error .error.message:not(:empty){display:block}.ui.form .field.error .compact.error.message:not(:empty),.ui.form .fields.error .compact.error.message:not(:empty){display:inline-block}.ui.form .field.error .icon.error.message:not(:empty),.ui.form .fields.error .icon.error.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.form .field.error .input,.ui.ui.form .field.error label,.ui.ui.form .fields.error .field .input,.ui.ui.form .fields.error .field label{color:#9f3a38}.ui.form .field.error .corner.label,.ui.form .fields.error .field .corner.label{border-color:#9f3a38;color:#fff}.ui.form .field.error input:not([type]),.ui.form .field.error input[type=date],.ui.form .field.error input[type=datetime-local],.ui.form .field.error input[type=email],.ui.form .field.error input[type=file],.ui.form .field.error input[type=number],.ui.form .field.error input[type=password],.ui.form .field.error input[type=search],.ui.form .field.error input[type=tel],.ui.form .field.error input[type=text],.ui.form .field.error input[type=time],.ui.form .field.error input[type=url],.ui.form .field.error select,.ui.form .field.error textarea,.ui.form .fields.error .field input:not([type]),.ui.form .fields.error .field input[type=date],.ui.form .fields.error .field input[type=datetime-local],.ui.form .fields.error .field input[type=email],.ui.form .fields.error .field input[type=file],.ui.form .fields.error .field input[type=number],.ui.form .fields.error .field input[type=password],.ui.form .fields.error .field input[type=search],.ui.form .fields.error .field input[type=tel],.ui.form .fields.error .field input[type=text],.ui.form .fields.error .field input[type=time],.ui.form .fields.error .field input[type=url],.ui.form .fields.error .field select,.ui.form .fields.error .field textarea{color:#9f3a38;background:#fff6f6;border-color:#e0b4b4;border-radius:'';-webkit-box-shadow:none;box-shadow:none}.ui.form .field.error input:not([type]):focus,.ui.form .field.error input[type=date]:focus,.ui.form .field.error input[type=datetime-local]:focus,.ui.form .field.error input[type=email]:focus,.ui.form .field.error input[type=file]:focus,.ui.form .field.error input[type=number]:focus,.ui.form .field.error input[type=password]:focus,.ui.form .field.error input[type=search]:focus,.ui.form .field.error input[type=tel]:focus,.ui.form .field.error input[type=text]:focus,.ui.form .field.error input[type=time]:focus,.ui.form .field.error input[type=url]:focus,.ui.form .field.error select:focus,.ui.form .field.error textarea:focus{background:#fff6f6;border-color:#e0b4b4;color:#9f3a38;-webkit-box-shadow:none;box-shadow:none}.ui.form .field.error select{-webkit-appearance:menulist-button}.ui.form .field.error .transparent.input input,.ui.form .field.error .transparent.input textarea,.ui.form .field.error input.transparent,.ui.form .field.error textarea.transparent{background-color:#fff6f6;color:#9f3a38}.ui.form .error.error input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #fffaf0 inset!important;box-shadow:0 0 0 100px #fffaf0 inset!important;border-color:#e0b4b4}.ui.form .error ::-webkit-input-placeholder{color:#e7bdbc}.ui.form .error :-ms-input-placeholder{color:#e7bdbc}.ui.form .error ::-moz-placeholder{color:#e7bdbc}.ui.form .error :focus::-webkit-input-placeholder{color:#da9796}.ui.form .error :focus:-ms-input-placeholder{color:#da9796}.ui.form .error :focus::-moz-placeholder{color:#da9796}.ui.form .field.error .ui.dropdown,.ui.form .field.error .ui.dropdown .item,.ui.form .field.error .ui.dropdown .text,.ui.form .fields.error .field .ui.dropdown,.ui.form .fields.error .field .ui.dropdown .item{background:#fff6f6;color:#9f3a38}.ui.form .field.error .ui.dropdown,.ui.form .fields.error .field .ui.dropdown{border-color:#e0b4b4!important}.ui.form .field.error .ui.dropdown:hover,.ui.form .fields.error .field .ui.dropdown:hover{border-color:#e0b4b4!important}.ui.form .field.error .ui.dropdown:hover .menu,.ui.form .fields.error .field .ui.dropdown:hover .menu{border-color:#e0b4b4}.ui.form .field.error .ui.multiple.selection.dropdown>.label,.ui.form .fields.error .field .ui.multiple.selection.dropdown>.label{background-color:#eacbcb;color:#9f3a38}.ui.form .field.error .ui.dropdown .menu .item:hover,.ui.form .fields.error .field .ui.dropdown .menu .item:hover{background-color:#fbe7e7}.ui.form .field.error .ui.dropdown .menu .selected.item,.ui.form .fields.error .field .ui.dropdown .menu .selected.item{background-color:#fbe7e7}.ui.form .field.error .ui.dropdown .menu .active.item,.ui.form .fields.error .field .ui.dropdown .menu .active.item{background-color:#fdcfcf}.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box,.ui.form .field.error .checkbox:not(.toggle):not(.slider) label,.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box,.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label{color:#9f3a38}.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .field.error .checkbox:not(.toggle):not(.slider) label:before,.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label:before{background:#fff6f6;border-color:#e0b4b4}.ui.form .field.error .checkbox .box:after,.ui.form .field.error .checkbox label:after,.ui.form .fields.error .field .checkbox .box:after,.ui.form .fields.error .field .checkbox label:after{color:#9f3a38}.ui.form.info .info.message:not(:empty){display:block}.ui.form.info .compact.info.message:not(:empty){display:inline-block}.ui.form.info .icon.info.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.form .field.info .info.message:not(:empty),.ui.form .fields.info .info.message:not(:empty){display:block}.ui.form .field.info .compact.info.message:not(:empty),.ui.form .fields.info .compact.info.message:not(:empty){display:inline-block}.ui.form .field.info .icon.info.message:not(:empty),.ui.form .fields.info .icon.info.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.form .field.info .input,.ui.ui.form .field.info label,.ui.ui.form .fields.info .field .input,.ui.ui.form .fields.info .field label{color:#276f86}.ui.form .field.info .corner.label,.ui.form .fields.info .field .corner.label{border-color:#276f86;color:#fff}.ui.form .field.info input:not([type]),.ui.form .field.info input[type=date],.ui.form .field.info input[type=datetime-local],.ui.form .field.info input[type=email],.ui.form .field.info input[type=file],.ui.form .field.info input[type=number],.ui.form .field.info input[type=password],.ui.form .field.info input[type=search],.ui.form .field.info input[type=tel],.ui.form .field.info input[type=text],.ui.form .field.info input[type=time],.ui.form .field.info input[type=url],.ui.form .field.info select,.ui.form .field.info textarea,.ui.form .fields.info .field input:not([type]),.ui.form .fields.info .field input[type=date],.ui.form .fields.info .field input[type=datetime-local],.ui.form .fields.info .field input[type=email],.ui.form .fields.info .field input[type=file],.ui.form .fields.info .field input[type=number],.ui.form .fields.info .field input[type=password],.ui.form .fields.info .field input[type=search],.ui.form .fields.info .field input[type=tel],.ui.form .fields.info .field input[type=text],.ui.form .fields.info .field input[type=time],.ui.form .fields.info .field input[type=url],.ui.form .fields.info .field select,.ui.form .fields.info .field textarea{color:#276f86;background:#f8ffff;border-color:#a9d5de;border-radius:'';-webkit-box-shadow:none;box-shadow:none}.ui.form .field.info input:not([type]):focus,.ui.form .field.info input[type=date]:focus,.ui.form .field.info input[type=datetime-local]:focus,.ui.form .field.info input[type=email]:focus,.ui.form .field.info input[type=file]:focus,.ui.form .field.info input[type=number]:focus,.ui.form .field.info input[type=password]:focus,.ui.form .field.info input[type=search]:focus,.ui.form .field.info input[type=tel]:focus,.ui.form .field.info input[type=text]:focus,.ui.form .field.info input[type=time]:focus,.ui.form .field.info input[type=url]:focus,.ui.form .field.info select:focus,.ui.form .field.info textarea:focus{background:#f8ffff;border-color:#a9d5de;color:#276f86;-webkit-box-shadow:none;box-shadow:none}.ui.form .field.info select{-webkit-appearance:menulist-button}.ui.form .field.info .transparent.input input,.ui.form .field.info .transparent.input textarea,.ui.form .field.info input.transparent,.ui.form .field.info textarea.transparent{background-color:#f8ffff;color:#276f86}.ui.form .info.info input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #f0faff inset!important;box-shadow:0 0 0 100px #f0faff inset!important;border-color:#b3e0e0}.ui.form .info ::-webkit-input-placeholder{color:#98cfe1}.ui.form .info :-ms-input-placeholder{color:#98cfe1}.ui.form .info ::-moz-placeholder{color:#98cfe1}.ui.form .info :focus::-webkit-input-placeholder{color:#70bdd6}.ui.form .info :focus:-ms-input-placeholder{color:#70bdd6}.ui.form .info :focus::-moz-placeholder{color:#70bdd6}.ui.form .field.info .ui.dropdown,.ui.form .field.info .ui.dropdown .item,.ui.form .field.info .ui.dropdown .text,.ui.form .fields.info .field .ui.dropdown,.ui.form .fields.info .field .ui.dropdown .item{background:#f8ffff;color:#276f86}.ui.form .field.info .ui.dropdown,.ui.form .fields.info .field .ui.dropdown{border-color:#a9d5de!important}.ui.form .field.info .ui.dropdown:hover,.ui.form .fields.info .field .ui.dropdown:hover{border-color:#a9d5de!important}.ui.form .field.info .ui.dropdown:hover .menu,.ui.form .fields.info .field .ui.dropdown:hover .menu{border-color:#a9d5de}.ui.form .field.info .ui.multiple.selection.dropdown>.label,.ui.form .fields.info .field .ui.multiple.selection.dropdown>.label{background-color:#cce3ea;color:#276f86}.ui.form .field.info .ui.dropdown .menu .item:hover,.ui.form .fields.info .field .ui.dropdown .menu .item:hover{background-color:#e9f2fb}.ui.form .field.info .ui.dropdown .menu .selected.item,.ui.form .fields.info .field .ui.dropdown .menu .selected.item{background-color:#e9f2fb}.ui.form .field.info .ui.dropdown .menu .active.item,.ui.form .fields.info .field .ui.dropdown .menu .active.item{background-color:#cef1fd}.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box,.ui.form .field.info .checkbox:not(.toggle):not(.slider) label,.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box,.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label{color:#276f86}.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .field.info .checkbox:not(.toggle):not(.slider) label:before,.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label:before{background:#f8ffff;border-color:#a9d5de}.ui.form .field.info .checkbox .box:after,.ui.form .field.info .checkbox label:after,.ui.form .fields.info .field .checkbox .box:after,.ui.form .fields.info .field .checkbox label:after{color:#276f86}.ui.form.success .success.message:not(:empty){display:block}.ui.form.success .compact.success.message:not(:empty){display:inline-block}.ui.form.success .icon.success.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.form .field.success .success.message:not(:empty),.ui.form .fields.success .success.message:not(:empty){display:block}.ui.form .field.success .compact.success.message:not(:empty),.ui.form .fields.success .compact.success.message:not(:empty){display:inline-block}.ui.form .field.success .icon.success.message:not(:empty),.ui.form .fields.success .icon.success.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.form .field.success .input,.ui.ui.form .field.success label,.ui.ui.form .fields.success .field .input,.ui.ui.form .fields.success .field label{color:#2c662d}.ui.form .field.success .corner.label,.ui.form .fields.success .field .corner.label{border-color:#2c662d;color:#fff}.ui.form .field.success input:not([type]),.ui.form .field.success input[type=date],.ui.form .field.success input[type=datetime-local],.ui.form .field.success input[type=email],.ui.form .field.success input[type=file],.ui.form .field.success input[type=number],.ui.form .field.success input[type=password],.ui.form .field.success input[type=search],.ui.form .field.success input[type=tel],.ui.form .field.success input[type=text],.ui.form .field.success input[type=time],.ui.form .field.success input[type=url],.ui.form .field.success select,.ui.form .field.success textarea,.ui.form .fields.success .field input:not([type]),.ui.form .fields.success .field input[type=date],.ui.form .fields.success .field input[type=datetime-local],.ui.form .fields.success .field input[type=email],.ui.form .fields.success .field input[type=file],.ui.form .fields.success .field input[type=number],.ui.form .fields.success .field input[type=password],.ui.form .fields.success .field input[type=search],.ui.form .fields.success .field input[type=tel],.ui.form .fields.success .field input[type=text],.ui.form .fields.success .field input[type=time],.ui.form .fields.success .field input[type=url],.ui.form .fields.success .field select,.ui.form .fields.success .field textarea{color:#2c662d;background:#fcfff5;border-color:#a3c293;border-radius:'';-webkit-box-shadow:none;box-shadow:none}.ui.form .field.success input:not([type]):focus,.ui.form .field.success input[type=date]:focus,.ui.form .field.success input[type=datetime-local]:focus,.ui.form .field.success input[type=email]:focus,.ui.form .field.success input[type=file]:focus,.ui.form .field.success input[type=number]:focus,.ui.form .field.success input[type=password]:focus,.ui.form .field.success input[type=search]:focus,.ui.form .field.success input[type=tel]:focus,.ui.form .field.success input[type=text]:focus,.ui.form .field.success input[type=time]:focus,.ui.form .field.success input[type=url]:focus,.ui.form .field.success select:focus,.ui.form .field.success textarea:focus{background:#fcfff5;border-color:#a3c293;color:#2c662d;-webkit-box-shadow:none;box-shadow:none}.ui.form .field.success select{-webkit-appearance:menulist-button}.ui.form .field.success .transparent.input input,.ui.form .field.success .transparent.input textarea,.ui.form .field.success input.transparent,.ui.form .field.success textarea.transparent{background-color:#fcfff5;color:#2c662d}.ui.form .success.success input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #f0fff0 inset!important;box-shadow:0 0 0 100px #f0fff0 inset!important;border-color:#bee0b3}.ui.form .success ::-webkit-input-placeholder{color:#8fcf90}.ui.form .success :-ms-input-placeholder{color:#8fcf90}.ui.form .success ::-moz-placeholder{color:#8fcf90}.ui.form .success :focus::-webkit-input-placeholder{color:#6cbf6d}.ui.form .success :focus:-ms-input-placeholder{color:#6cbf6d}.ui.form .success :focus::-moz-placeholder{color:#6cbf6d}.ui.form .field.success .ui.dropdown,.ui.form .field.success .ui.dropdown .item,.ui.form .field.success .ui.dropdown .text,.ui.form .fields.success .field .ui.dropdown,.ui.form .fields.success .field .ui.dropdown .item{background:#fcfff5;color:#2c662d}.ui.form .field.success .ui.dropdown,.ui.form .fields.success .field .ui.dropdown{border-color:#a3c293!important}.ui.form .field.success .ui.dropdown:hover,.ui.form .fields.success .field .ui.dropdown:hover{border-color:#a3c293!important}.ui.form .field.success .ui.dropdown:hover .menu,.ui.form .fields.success .field .ui.dropdown:hover .menu{border-color:#a3c293}.ui.form .field.success .ui.multiple.selection.dropdown>.label,.ui.form .fields.success .field .ui.multiple.selection.dropdown>.label{background-color:#cceacc;color:#2c662d}.ui.form .field.success .ui.dropdown .menu .item:hover,.ui.form .fields.success .field .ui.dropdown .menu .item:hover{background-color:#e9fbe9}.ui.form .field.success .ui.dropdown .menu .selected.item,.ui.form .fields.success .field .ui.dropdown .menu .selected.item{background-color:#e9fbe9}.ui.form .field.success .ui.dropdown .menu .active.item,.ui.form .fields.success .field .ui.dropdown .menu .active.item{background-color:#dafdce}.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box,.ui.form .field.success .checkbox:not(.toggle):not(.slider) label,.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box,.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label{color:#2c662d}.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .field.success .checkbox:not(.toggle):not(.slider) label:before,.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label:before{background:#fcfff5;border-color:#a3c293}.ui.form .field.success .checkbox .box:after,.ui.form .field.success .checkbox label:after,.ui.form .fields.success .field .checkbox .box:after,.ui.form .fields.success .field .checkbox label:after{color:#2c662d}.ui.form.warning .warning.message:not(:empty){display:block}.ui.form.warning .compact.warning.message:not(:empty){display:inline-block}.ui.form.warning .icon.warning.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.form .field.warning .warning.message:not(:empty),.ui.form .fields.warning .warning.message:not(:empty){display:block}.ui.form .field.warning .compact.warning.message:not(:empty),.ui.form .fields.warning .compact.warning.message:not(:empty){display:inline-block}.ui.form .field.warning .icon.warning.message:not(:empty),.ui.form .fields.warning .icon.warning.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.form .field.warning .input,.ui.ui.form .field.warning label,.ui.ui.form .fields.warning .field .input,.ui.ui.form .fields.warning .field label{color:#573a08}.ui.form .field.warning .corner.label,.ui.form .fields.warning .field .corner.label{border-color:#573a08;color:#fff}.ui.form .field.warning input:not([type]),.ui.form .field.warning input[type=date],.ui.form .field.warning input[type=datetime-local],.ui.form .field.warning input[type=email],.ui.form .field.warning input[type=file],.ui.form .field.warning input[type=number],.ui.form .field.warning input[type=password],.ui.form .field.warning input[type=search],.ui.form .field.warning input[type=tel],.ui.form .field.warning input[type=text],.ui.form .field.warning input[type=time],.ui.form .field.warning input[type=url],.ui.form .field.warning select,.ui.form .field.warning textarea,.ui.form .fields.warning .field input:not([type]),.ui.form .fields.warning .field input[type=date],.ui.form .fields.warning .field input[type=datetime-local],.ui.form .fields.warning .field input[type=email],.ui.form .fields.warning .field input[type=file],.ui.form .fields.warning .field input[type=number],.ui.form .fields.warning .field input[type=password],.ui.form .fields.warning .field input[type=search],.ui.form .fields.warning .field input[type=tel],.ui.form .fields.warning .field input[type=text],.ui.form .fields.warning .field input[type=time],.ui.form .fields.warning .field input[type=url],.ui.form .fields.warning .field select,.ui.form .fields.warning .field textarea{color:#573a08;background:#fffaf3;border-color:#c9ba9b;border-radius:'';-webkit-box-shadow:none;box-shadow:none}.ui.form .field.warning input:not([type]):focus,.ui.form .field.warning input[type=date]:focus,.ui.form .field.warning input[type=datetime-local]:focus,.ui.form .field.warning input[type=email]:focus,.ui.form .field.warning input[type=file]:focus,.ui.form .field.warning input[type=number]:focus,.ui.form .field.warning input[type=password]:focus,.ui.form .field.warning input[type=search]:focus,.ui.form .field.warning input[type=tel]:focus,.ui.form .field.warning input[type=text]:focus,.ui.form .field.warning input[type=time]:focus,.ui.form .field.warning input[type=url]:focus,.ui.form .field.warning select:focus,.ui.form .field.warning textarea:focus{background:#fffaf3;border-color:#c9ba9b;color:#573a08;-webkit-box-shadow:none;box-shadow:none}.ui.form .field.warning select{-webkit-appearance:menulist-button}.ui.form .field.warning .transparent.input input,.ui.form .field.warning .transparent.input textarea,.ui.form .field.warning input.transparent,.ui.form .field.warning textarea.transparent{background-color:#fffaf3;color:#573a08}.ui.form .warning.warning input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #ffffe0 inset!important;box-shadow:0 0 0 100px #ffffe0 inset!important;border-color:#e0e0b3}.ui.form .warning ::-webkit-input-placeholder{color:#edad3e}.ui.form .warning :-ms-input-placeholder{color:#edad3e}.ui.form .warning ::-moz-placeholder{color:#edad3e}.ui.form .warning :focus::-webkit-input-placeholder{color:#e39715}.ui.form .warning :focus:-ms-input-placeholder{color:#e39715}.ui.form .warning :focus::-moz-placeholder{color:#e39715}.ui.form .field.warning .ui.dropdown,.ui.form .field.warning .ui.dropdown .item,.ui.form .field.warning .ui.dropdown .text,.ui.form .fields.warning .field .ui.dropdown,.ui.form .fields.warning .field .ui.dropdown .item{background:#fffaf3;color:#573a08}.ui.form .field.warning .ui.dropdown,.ui.form .fields.warning .field .ui.dropdown{border-color:#c9ba9b!important}.ui.form .field.warning .ui.dropdown:hover,.ui.form .fields.warning .field .ui.dropdown:hover{border-color:#c9ba9b!important}.ui.form .field.warning .ui.dropdown:hover .menu,.ui.form .fields.warning .field .ui.dropdown:hover .menu{border-color:#c9ba9b}.ui.form .field.warning .ui.multiple.selection.dropdown>.label,.ui.form .fields.warning .field .ui.multiple.selection.dropdown>.label{background-color:#eaeacc;color:#573a08}.ui.form .field.warning .ui.dropdown .menu .item:hover,.ui.form .fields.warning .field .ui.dropdown .menu .item:hover{background-color:#fbfbe9}.ui.form .field.warning .ui.dropdown .menu .selected.item,.ui.form .fields.warning .field .ui.dropdown .menu .selected.item{background-color:#fbfbe9}.ui.form .field.warning .ui.dropdown .menu .active.item,.ui.form .fields.warning .field .ui.dropdown .menu .active.item{background-color:#fdfdce}.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box,.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label,.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box,.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label{color:#573a08}.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label:before,.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label:before{background:#fffaf3;border-color:#c9ba9b}.ui.form .field.warning .checkbox .box:after,.ui.form .field.warning .checkbox label:after,.ui.form .fields.warning .field .checkbox .box:after,.ui.form .fields.warning .field .checkbox label:after{color:#573a08}.ui.form .disabled.field,.ui.form .disabled.fields .field,.ui.form .field :disabled{pointer-events:none;opacity:.45}.ui.form .field.disabled>label,.ui.form .fields.disabled>label{opacity:.45}.ui.form .field.disabled :disabled{opacity:1}.ui.loading.form{position:relative;cursor:default;pointer-events:none}.ui.loading.form:before{position:absolute;content:'';top:0;left:0;background:rgba(255,255,255,.8);width:100%;height:100%;z-index:100}.ui.loading.form.segments:before{border-radius:.28571429rem}.ui.loading.form:after{position:absolute;content:'';top:50%;left:50%;margin:-1.5em 0 0 -1.5em;width:3em;height:3em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent;visibility:visible;z-index:101}.ui.form .required.field>.checkbox:after,.ui.form .required.field>label:after,.ui.form .required.fields.grouped>label:after,.ui.form .required.fields:not(.grouped)>.field>.checkbox:after,.ui.form .required.fields:not(.grouped)>.field>label:after,.ui.form label.required:after{margin:-.2em 0 0 .2em;content:'*';color:#db2828}.ui.form .required.field>label:after,.ui.form .required.fields.grouped>label:after,.ui.form .required.fields:not(.grouped)>.field>label:after,.ui.form label.required:after{display:inline-block;vertical-align:top}.ui.form .required.field>.checkbox:after,.ui.form .required.fields:not(.grouped)>.field>.checkbox:after{position:absolute;top:0;left:100%}.ui.form .inverted.segment .ui.checkbox .box,.ui.form .inverted.segment .ui.checkbox label,.ui.form .inverted.segment label,.ui.inverted.form .inline.field>label,.ui.inverted.form .inline.field>p,.ui.inverted.form .inline.fields .field>label,.ui.inverted.form .inline.fields .field>p,.ui.inverted.form .inline.fields>label,.ui.inverted.form .ui.checkbox .box,.ui.inverted.form .ui.checkbox label,.ui.inverted.form label{color:rgba(255,255,255,.9)}.ui.inverted.loading.form{color:#fff}.ui.inverted.loading.form:before{background:rgba(0,0,0,.85)}.ui.inverted.form input:not([type]),.ui.inverted.form input[type=date],.ui.inverted.form input[type=datetime-local],.ui.inverted.form input[type=email],.ui.inverted.form input[type=file],.ui.inverted.form input[type=number],.ui.inverted.form input[type=password],.ui.inverted.form input[type=search],.ui.inverted.form input[type=tel],.ui.inverted.form input[type=text],.ui.inverted.form input[type=time],.ui.inverted.form input[type=url]{background:#fff;border-color:rgba(255,255,255,.1);color:rgba(0,0,0,.87);-webkit-box-shadow:none;box-shadow:none}.ui.form .grouped.fields{display:block;margin:0 0 1em}.ui.form .grouped.fields:last-child{margin-bottom:0}.ui.form .grouped.fields>label{margin:0 0 .28571429rem 0;color:rgba(0,0,0,.87);font-size:.92857143em;font-weight:700;text-transform:none}.ui.form .grouped.fields .field,.ui.form .grouped.inline.fields .field{display:block;margin:.5em 0;padding:0}.ui.form .fields{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin:0 -.5em 1em}.ui.form .fields>.field{-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;padding-left:.5em;padding-right:.5em}.ui.form .fields>.field:first-child{border-left:none;-webkit-box-shadow:none;box-shadow:none}.ui.form .two.fields>.field,.ui.form .two.fields>.fields{width:50%}.ui.form .three.fields>.field,.ui.form .three.fields>.fields{width:33.33333333%}.ui.form .four.fields>.field,.ui.form .four.fields>.fields{width:25%}.ui.form .five.fields>.field,.ui.form .five.fields>.fields{width:20%}.ui.form .six.fields>.field,.ui.form .six.fields>.fields{width:16.66666667%}.ui.form .seven.fields>.field,.ui.form .seven.fields>.fields{width:14.28571429%}.ui.form .eight.fields>.field,.ui.form .eight.fields>.fields{width:12.5%}.ui.form .nine.fields>.field,.ui.form .nine.fields>.fields{width:11.11111111%}.ui.form .ten.fields>.field,.ui.form .ten.fields>.fields{width:10%}@media only screen and (max-width:767.98px){.ui.form .fields{-ms-flex-wrap:wrap;flex-wrap:wrap}.ui.form:not(.unstackable) .eight.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .eight.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .five.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .five.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .four.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .four.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .nine.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .nine.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .seven.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .seven.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .six.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .six.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .ten.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .ten.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .three.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .three.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .two.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .two.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) [class*="equal width"].fields:not(.unstackable)>.field,.ui[class*="equal width"].form:not(.unstackable) .fields>.field{width:100%!important;margin:0 0 1em}}.ui.form .fields .wide.field{width:6.25%;padding-left:.5em;padding-right:.5em}.ui.form .one.wide.field{width:6.25%!important}.ui.form .two.wide.field{width:12.5%!important}.ui.form .three.wide.field{width:18.75%!important}.ui.form .four.wide.field{width:25%!important}.ui.form .five.wide.field{width:31.25%!important}.ui.form .six.wide.field{width:37.5%!important}.ui.form .seven.wide.field{width:43.75%!important}.ui.form .eight.wide.field{width:50%!important}.ui.form .nine.wide.field{width:56.25%!important}.ui.form .ten.wide.field{width:62.5%!important}.ui.form .eleven.wide.field{width:68.75%!important}.ui.form .twelve.wide.field{width:75%!important}.ui.form .thirteen.wide.field{width:81.25%!important}.ui.form .fourteen.wide.field{width:87.5%!important}.ui.form .fifteen.wide.field{width:93.75%!important}.ui.form .sixteen.wide.field{width:100%!important}@media only screen and (max-width:767.98px){.ui.form:not(.unstackable) .fields:not(.unstackable)>.eight.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.eleven.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.fifteen.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.five.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.four.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.fourteen.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.nine.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.seven.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.six.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.sixteen.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.ten.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.thirteen.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.three.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.twelve.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.two.wide.field,.ui.form:not(.unstackable) .five.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .five.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .four.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .four.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .three.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .three.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .two.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .two.fields:not(.unstackable)>.fields{width:100%!important}.ui.form .fields{margin-bottom:0}}.ui.form [class*="equal width"].fields>.field,.ui[class*="equal width"].form .fields>.field{width:100%;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.ui.form .inline.fields{margin:0 0 1em;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.form .inline.fields .field{margin:0;padding:0 1em 0 0}.ui.form .inline.field>label,.ui.form .inline.field>p,.ui.form .inline.fields .field>label,.ui.form .inline.fields .field>p,.ui.form .inline.fields>label{display:inline-block;width:auto;margin-top:0;margin-bottom:0;vertical-align:baseline;font-size:.92857143em;font-weight:700;color:rgba(0,0,0,.87);text-transform:none}.ui.form .inline.fields>label{margin:.035714em 1em 0 0}.ui.form .inline.field>input,.ui.form .inline.field>select,.ui.form .inline.fields .field>input,.ui.form .inline.fields .field>select{display:inline-block;width:auto;margin-top:0;margin-bottom:0;vertical-align:middle;font-size:1em}.ui.form .inline.field>:first-child,.ui.form .inline.fields .field>:first-child{margin:0 .85714286em 0 0}.ui.form .inline.field>:only-child,.ui.form .inline.fields .field>:only-child{margin:0}.ui.form .inline.fields .wide.field{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.form .inline.fields .wide.field>input,.ui.form .inline.fields .wide.field>select{width:100%}.ui.form,.ui.form .field .dropdown,.ui.form .field .dropdown .menu>.item{font-size:1rem}.ui.mini.form,.ui.mini.form .field .dropdown,.ui.mini.form .field .dropdown .menu>.item{font-size:.78571429rem}.ui.tiny.form,.ui.tiny.form .field .dropdown,.ui.tiny.form .field .dropdown .menu>.item{font-size:.85714286rem}.ui.small.form,.ui.small.form .field .dropdown,.ui.small.form .field .dropdown .menu>.item{font-size:.92857143rem}.ui.large.form,.ui.large.form .field .dropdown,.ui.large.form .field .dropdown .menu>.item{font-size:1.14285714rem}.ui.big.form,.ui.big.form .field .dropdown,.ui.big.form .field .dropdown .menu>.item{font-size:1.28571429rem}.ui.huge.form,.ui.huge.form .field .dropdown,.ui.huge.form .field .dropdown .menu>.item{font-size:1.42857143rem}.ui.massive.form,.ui.massive.form .field .dropdown,.ui.massive.form .field .dropdown .menu>.item{font-size:1.71428571rem}/*! + * # Fomantic-UI - Grid + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.grid{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;padding:0}.ui.grid{margin-top:-1rem;margin-bottom:-1rem;margin-left:-1rem;margin-right:-1rem}.ui.relaxed.grid{margin-left:-1.5rem;margin-right:-1.5rem}.ui[class*="very relaxed"].grid{margin-left:-2.5rem;margin-right:-2.5rem}.ui.grid+.grid{margin-top:1rem}.ui.grid>.column:not(.row),.ui.grid>.row>.column{position:relative;display:inline-block;width:6.25%;padding-left:1rem;padding-right:1rem;vertical-align:top}.ui.grid>*{padding-left:1rem;padding-right:1rem}.ui.grid>.row{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:inherit;-ms-flex-pack:inherit;justify-content:inherit;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%!important;padding:0;padding-top:1rem;padding-bottom:1rem}.ui.grid>.column:not(.row){padding-top:1rem;padding-bottom:1rem}.ui.grid>.row>.column{margin-top:0;margin-bottom:0}.ui.grid>.row>.column>img,.ui.grid>.row>img{max-width:100%}.ui.grid>.ui.grid:first-child{margin-top:0}.ui.grid>.ui.grid:last-child{margin-bottom:0}.ui.aligned.grid .column>.segment:not(.compact):not(.attached),.ui.grid .aligned.row>.column>.segment:not(.compact):not(.attached){width:100%}.ui.grid .row+.ui.divider{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;margin:1rem 1rem}.ui.grid .column+.ui.vertical.divider{height:calc(50% - 1rem)}.ui.grid>.column:last-child>.horizontal.segment,.ui.grid>.row>.column:last-child>.horizontal.segment{-webkit-box-shadow:none;box-shadow:none}@media only screen and (max-width:767.98px){.ui.page.grid{width:auto;padding-left:0;padding-right:0;margin-left:0;margin-right:0}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.page.grid{width:auto;margin-left:0;margin-right:0;padding-left:2em;padding-right:2em}}@media only screen and (min-width:992px) and (max-width:1199.98px){.ui.page.grid{width:auto;margin-left:0;margin-right:0;padding-left:3%;padding-right:3%}}@media only screen and (min-width:1200px) and (max-width:1919.98px){.ui.page.grid{width:auto;margin-left:0;margin-right:0;padding-left:15%;padding-right:15%}}@media only screen and (min-width:1920px){.ui.page.grid{width:auto;margin-left:0;margin-right:0;padding-left:23%;padding-right:23%}}.ui.grid>.column:only-child,.ui.grid>.row>.column:only-child{width:100%}.ui[class*="one column"].grid>.column:not(.row),.ui[class*="one column"].grid>.row>.column{width:100%}.ui[class*="two column"].grid>.column:not(.row),.ui[class*="two column"].grid>.row>.column{width:50%}.ui[class*="three column"].grid>.column:not(.row),.ui[class*="three column"].grid>.row>.column{width:33.33333333%}.ui[class*="four column"].grid>.column:not(.row),.ui[class*="four column"].grid>.row>.column{width:25%}.ui[class*="five column"].grid>.column:not(.row),.ui[class*="five column"].grid>.row>.column{width:20%}.ui[class*="six column"].grid>.column:not(.row),.ui[class*="six column"].grid>.row>.column{width:16.66666667%}.ui[class*="seven column"].grid>.column:not(.row),.ui[class*="seven column"].grid>.row>.column{width:14.28571429%}.ui[class*="eight column"].grid>.column:not(.row),.ui[class*="eight column"].grid>.row>.column{width:12.5%}.ui[class*="nine column"].grid>.column:not(.row),.ui[class*="nine column"].grid>.row>.column{width:11.11111111%}.ui[class*="ten column"].grid>.column:not(.row),.ui[class*="ten column"].grid>.row>.column{width:10%}.ui[class*="eleven column"].grid>.column:not(.row),.ui[class*="eleven column"].grid>.row>.column{width:9.09090909%}.ui[class*="twelve column"].grid>.column:not(.row),.ui[class*="twelve column"].grid>.row>.column{width:8.33333333%}.ui[class*="thirteen column"].grid>.column:not(.row),.ui[class*="thirteen column"].grid>.row>.column{width:7.69230769%}.ui[class*="fourteen column"].grid>.column:not(.row),.ui[class*="fourteen column"].grid>.row>.column{width:7.14285714%}.ui[class*="fifteen column"].grid>.column:not(.row),.ui[class*="fifteen column"].grid>.row>.column{width:6.66666667%}.ui[class*="sixteen column"].grid>.column:not(.row),.ui[class*="sixteen column"].grid>.row>.column{width:6.25%}.ui.grid>[class*="one column"].row>.column{width:100%!important}.ui.grid>[class*="two column"].row>.column{width:50%!important}.ui.grid>[class*="three column"].row>.column{width:33.33333333%!important}.ui.grid>[class*="four column"].row>.column{width:25%!important}.ui.grid>[class*="five column"].row>.column{width:20%!important}.ui.grid>[class*="six column"].row>.column{width:16.66666667%!important}.ui.grid>[class*="seven column"].row>.column{width:14.28571429%!important}.ui.grid>[class*="eight column"].row>.column{width:12.5%!important}.ui.grid>[class*="nine column"].row>.column{width:11.11111111%!important}.ui.grid>[class*="ten column"].row>.column{width:10%!important}.ui.grid>[class*="eleven column"].row>.column{width:9.09090909%!important}.ui.grid>[class*="twelve column"].row>.column{width:8.33333333%!important}.ui.grid>[class*="thirteen column"].row>.column{width:7.69230769%!important}.ui.grid>[class*="fourteen column"].row>.column{width:7.14285714%!important}.ui.grid>[class*="fifteen column"].row>.column{width:6.66666667%!important}.ui.grid>[class*="sixteen column"].row>.column{width:6.25%!important}.ui.celled.page.grid{-webkit-box-shadow:none;box-shadow:none}.ui.column.grid>[class*="one wide"].column,.ui.grid>.column.row>[class*="one wide"].column,.ui.grid>.row>[class*="one wide"].column,.ui.grid>[class*="one wide"].column{width:6.25%!important}.ui.column.grid>[class*="two wide"].column,.ui.grid>.column.row>[class*="two wide"].column,.ui.grid>.row>[class*="two wide"].column,.ui.grid>[class*="two wide"].column{width:12.5%!important}.ui.column.grid>[class*="three wide"].column,.ui.grid>.column.row>[class*="three wide"].column,.ui.grid>.row>[class*="three wide"].column,.ui.grid>[class*="three wide"].column{width:18.75%!important}.ui.column.grid>[class*="four wide"].column,.ui.grid>.column.row>[class*="four wide"].column,.ui.grid>.row>[class*="four wide"].column,.ui.grid>[class*="four wide"].column{width:25%!important}.ui.column.grid>[class*="five wide"].column,.ui.grid>.column.row>[class*="five wide"].column,.ui.grid>.row>[class*="five wide"].column,.ui.grid>[class*="five wide"].column{width:31.25%!important}.ui.column.grid>[class*="six wide"].column,.ui.grid>.column.row>[class*="six wide"].column,.ui.grid>.row>[class*="six wide"].column,.ui.grid>[class*="six wide"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide"].column,.ui.grid>.column.row>[class*="seven wide"].column,.ui.grid>.row>[class*="seven wide"].column,.ui.grid>[class*="seven wide"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide"].column,.ui.grid>.column.row>[class*="eight wide"].column,.ui.grid>.row>[class*="eight wide"].column,.ui.grid>[class*="eight wide"].column{width:50%!important}.ui.column.grid>[class*="nine wide"].column,.ui.grid>.column.row>[class*="nine wide"].column,.ui.grid>.row>[class*="nine wide"].column,.ui.grid>[class*="nine wide"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide"].column,.ui.grid>.column.row>[class*="ten wide"].column,.ui.grid>.row>[class*="ten wide"].column,.ui.grid>[class*="ten wide"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide"].column,.ui.grid>.column.row>[class*="eleven wide"].column,.ui.grid>.row>[class*="eleven wide"].column,.ui.grid>[class*="eleven wide"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide"].column,.ui.grid>.column.row>[class*="twelve wide"].column,.ui.grid>.row>[class*="twelve wide"].column,.ui.grid>[class*="twelve wide"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide"].column,.ui.grid>.column.row>[class*="thirteen wide"].column,.ui.grid>.row>[class*="thirteen wide"].column,.ui.grid>[class*="thirteen wide"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide"].column,.ui.grid>.column.row>[class*="fourteen wide"].column,.ui.grid>.row>[class*="fourteen wide"].column,.ui.grid>[class*="fourteen wide"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide"].column,.ui.grid>.column.row>[class*="fifteen wide"].column,.ui.grid>.row>[class*="fifteen wide"].column,.ui.grid>[class*="fifteen wide"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide"].column,.ui.grid>.column.row>[class*="sixteen wide"].column,.ui.grid>.row>[class*="sixteen wide"].column,.ui.grid>[class*="sixteen wide"].column{width:100%!important}@media only screen and (min-width:320px) and (max-width:767.98px){.ui.column.grid>[class*="one wide mobile"].column,.ui.grid>.column.row>[class*="one wide mobile"].column,.ui.grid>.row>[class*="one wide mobile"].column,.ui.grid>[class*="one wide mobile"].column{width:6.25%!important}.ui.column.grid>[class*="two wide mobile"].column,.ui.grid>.column.row>[class*="two wide mobile"].column,.ui.grid>.row>[class*="two wide mobile"].column,.ui.grid>[class*="two wide mobile"].column{width:12.5%!important}.ui.column.grid>[class*="three wide mobile"].column,.ui.grid>.column.row>[class*="three wide mobile"].column,.ui.grid>.row>[class*="three wide mobile"].column,.ui.grid>[class*="three wide mobile"].column{width:18.75%!important}.ui.column.grid>[class*="four wide mobile"].column,.ui.grid>.column.row>[class*="four wide mobile"].column,.ui.grid>.row>[class*="four wide mobile"].column,.ui.grid>[class*="four wide mobile"].column{width:25%!important}.ui.column.grid>[class*="five wide mobile"].column,.ui.grid>.column.row>[class*="five wide mobile"].column,.ui.grid>.row>[class*="five wide mobile"].column,.ui.grid>[class*="five wide mobile"].column{width:31.25%!important}.ui.column.grid>[class*="six wide mobile"].column,.ui.grid>.column.row>[class*="six wide mobile"].column,.ui.grid>.row>[class*="six wide mobile"].column,.ui.grid>[class*="six wide mobile"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide mobile"].column,.ui.grid>.column.row>[class*="seven wide mobile"].column,.ui.grid>.row>[class*="seven wide mobile"].column,.ui.grid>[class*="seven wide mobile"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide mobile"].column,.ui.grid>.column.row>[class*="eight wide mobile"].column,.ui.grid>.row>[class*="eight wide mobile"].column,.ui.grid>[class*="eight wide mobile"].column{width:50%!important}.ui.column.grid>[class*="nine wide mobile"].column,.ui.grid>.column.row>[class*="nine wide mobile"].column,.ui.grid>.row>[class*="nine wide mobile"].column,.ui.grid>[class*="nine wide mobile"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide mobile"].column,.ui.grid>.column.row>[class*="ten wide mobile"].column,.ui.grid>.row>[class*="ten wide mobile"].column,.ui.grid>[class*="ten wide mobile"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide mobile"].column,.ui.grid>.column.row>[class*="eleven wide mobile"].column,.ui.grid>.row>[class*="eleven wide mobile"].column,.ui.grid>[class*="eleven wide mobile"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide mobile"].column,.ui.grid>.column.row>[class*="twelve wide mobile"].column,.ui.grid>.row>[class*="twelve wide mobile"].column,.ui.grid>[class*="twelve wide mobile"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide mobile"].column,.ui.grid>.column.row>[class*="thirteen wide mobile"].column,.ui.grid>.row>[class*="thirteen wide mobile"].column,.ui.grid>[class*="thirteen wide mobile"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide mobile"].column,.ui.grid>.column.row>[class*="fourteen wide mobile"].column,.ui.grid>.row>[class*="fourteen wide mobile"].column,.ui.grid>[class*="fourteen wide mobile"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide mobile"].column,.ui.grid>.column.row>[class*="fifteen wide mobile"].column,.ui.grid>.row>[class*="fifteen wide mobile"].column,.ui.grid>[class*="fifteen wide mobile"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide mobile"].column,.ui.grid>.column.row>[class*="sixteen wide mobile"].column,.ui.grid>.row>[class*="sixteen wide mobile"].column,.ui.grid>[class*="sixteen wide mobile"].column{width:100%!important}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.column.grid>[class*="one wide tablet"].column,.ui.grid>.column.row>[class*="one wide tablet"].column,.ui.grid>.row>[class*="one wide tablet"].column,.ui.grid>[class*="one wide tablet"].column{width:6.25%!important}.ui.column.grid>[class*="two wide tablet"].column,.ui.grid>.column.row>[class*="two wide tablet"].column,.ui.grid>.row>[class*="two wide tablet"].column,.ui.grid>[class*="two wide tablet"].column{width:12.5%!important}.ui.column.grid>[class*="three wide tablet"].column,.ui.grid>.column.row>[class*="three wide tablet"].column,.ui.grid>.row>[class*="three wide tablet"].column,.ui.grid>[class*="three wide tablet"].column{width:18.75%!important}.ui.column.grid>[class*="four wide tablet"].column,.ui.grid>.column.row>[class*="four wide tablet"].column,.ui.grid>.row>[class*="four wide tablet"].column,.ui.grid>[class*="four wide tablet"].column{width:25%!important}.ui.column.grid>[class*="five wide tablet"].column,.ui.grid>.column.row>[class*="five wide tablet"].column,.ui.grid>.row>[class*="five wide tablet"].column,.ui.grid>[class*="five wide tablet"].column{width:31.25%!important}.ui.column.grid>[class*="six wide tablet"].column,.ui.grid>.column.row>[class*="six wide tablet"].column,.ui.grid>.row>[class*="six wide tablet"].column,.ui.grid>[class*="six wide tablet"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide tablet"].column,.ui.grid>.column.row>[class*="seven wide tablet"].column,.ui.grid>.row>[class*="seven wide tablet"].column,.ui.grid>[class*="seven wide tablet"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide tablet"].column,.ui.grid>.column.row>[class*="eight wide tablet"].column,.ui.grid>.row>[class*="eight wide tablet"].column,.ui.grid>[class*="eight wide tablet"].column{width:50%!important}.ui.column.grid>[class*="nine wide tablet"].column,.ui.grid>.column.row>[class*="nine wide tablet"].column,.ui.grid>.row>[class*="nine wide tablet"].column,.ui.grid>[class*="nine wide tablet"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide tablet"].column,.ui.grid>.column.row>[class*="ten wide tablet"].column,.ui.grid>.row>[class*="ten wide tablet"].column,.ui.grid>[class*="ten wide tablet"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide tablet"].column,.ui.grid>.column.row>[class*="eleven wide tablet"].column,.ui.grid>.row>[class*="eleven wide tablet"].column,.ui.grid>[class*="eleven wide tablet"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide tablet"].column,.ui.grid>.column.row>[class*="twelve wide tablet"].column,.ui.grid>.row>[class*="twelve wide tablet"].column,.ui.grid>[class*="twelve wide tablet"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide tablet"].column,.ui.grid>.column.row>[class*="thirteen wide tablet"].column,.ui.grid>.row>[class*="thirteen wide tablet"].column,.ui.grid>[class*="thirteen wide tablet"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide tablet"].column,.ui.grid>.column.row>[class*="fourteen wide tablet"].column,.ui.grid>.row>[class*="fourteen wide tablet"].column,.ui.grid>[class*="fourteen wide tablet"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide tablet"].column,.ui.grid>.column.row>[class*="fifteen wide tablet"].column,.ui.grid>.row>[class*="fifteen wide tablet"].column,.ui.grid>[class*="fifteen wide tablet"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide tablet"].column,.ui.grid>.column.row>[class*="sixteen wide tablet"].column,.ui.grid>.row>[class*="sixteen wide tablet"].column,.ui.grid>[class*="sixteen wide tablet"].column{width:100%!important}}@media only screen and (min-width:992px){.ui.column.grid>[class*="one wide computer"].column,.ui.grid>.column.row>[class*="one wide computer"].column,.ui.grid>.row>[class*="one wide computer"].column,.ui.grid>[class*="one wide computer"].column{width:6.25%!important}.ui.column.grid>[class*="two wide computer"].column,.ui.grid>.column.row>[class*="two wide computer"].column,.ui.grid>.row>[class*="two wide computer"].column,.ui.grid>[class*="two wide computer"].column{width:12.5%!important}.ui.column.grid>[class*="three wide computer"].column,.ui.grid>.column.row>[class*="three wide computer"].column,.ui.grid>.row>[class*="three wide computer"].column,.ui.grid>[class*="three wide computer"].column{width:18.75%!important}.ui.column.grid>[class*="four wide computer"].column,.ui.grid>.column.row>[class*="four wide computer"].column,.ui.grid>.row>[class*="four wide computer"].column,.ui.grid>[class*="four wide computer"].column{width:25%!important}.ui.column.grid>[class*="five wide computer"].column,.ui.grid>.column.row>[class*="five wide computer"].column,.ui.grid>.row>[class*="five wide computer"].column,.ui.grid>[class*="five wide computer"].column{width:31.25%!important}.ui.column.grid>[class*="six wide computer"].column,.ui.grid>.column.row>[class*="six wide computer"].column,.ui.grid>.row>[class*="six wide computer"].column,.ui.grid>[class*="six wide computer"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide computer"].column,.ui.grid>.column.row>[class*="seven wide computer"].column,.ui.grid>.row>[class*="seven wide computer"].column,.ui.grid>[class*="seven wide computer"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide computer"].column,.ui.grid>.column.row>[class*="eight wide computer"].column,.ui.grid>.row>[class*="eight wide computer"].column,.ui.grid>[class*="eight wide computer"].column{width:50%!important}.ui.column.grid>[class*="nine wide computer"].column,.ui.grid>.column.row>[class*="nine wide computer"].column,.ui.grid>.row>[class*="nine wide computer"].column,.ui.grid>[class*="nine wide computer"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide computer"].column,.ui.grid>.column.row>[class*="ten wide computer"].column,.ui.grid>.row>[class*="ten wide computer"].column,.ui.grid>[class*="ten wide computer"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide computer"].column,.ui.grid>.column.row>[class*="eleven wide computer"].column,.ui.grid>.row>[class*="eleven wide computer"].column,.ui.grid>[class*="eleven wide computer"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide computer"].column,.ui.grid>.column.row>[class*="twelve wide computer"].column,.ui.grid>.row>[class*="twelve wide computer"].column,.ui.grid>[class*="twelve wide computer"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide computer"].column,.ui.grid>.column.row>[class*="thirteen wide computer"].column,.ui.grid>.row>[class*="thirteen wide computer"].column,.ui.grid>[class*="thirteen wide computer"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide computer"].column,.ui.grid>.column.row>[class*="fourteen wide computer"].column,.ui.grid>.row>[class*="fourteen wide computer"].column,.ui.grid>[class*="fourteen wide computer"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide computer"].column,.ui.grid>.column.row>[class*="fifteen wide computer"].column,.ui.grid>.row>[class*="fifteen wide computer"].column,.ui.grid>[class*="fifteen wide computer"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide computer"].column,.ui.grid>.column.row>[class*="sixteen wide computer"].column,.ui.grid>.row>[class*="sixteen wide computer"].column,.ui.grid>[class*="sixteen wide computer"].column{width:100%!important}}@media only screen and (min-width:1200px) and (max-width:1919.98px){.ui.column.grid>[class*="one wide large screen"].column,.ui.grid>.column.row>[class*="one wide large screen"].column,.ui.grid>.row>[class*="one wide large screen"].column,.ui.grid>[class*="one wide large screen"].column{width:6.25%!important}.ui.column.grid>[class*="two wide large screen"].column,.ui.grid>.column.row>[class*="two wide large screen"].column,.ui.grid>.row>[class*="two wide large screen"].column,.ui.grid>[class*="two wide large screen"].column{width:12.5%!important}.ui.column.grid>[class*="three wide large screen"].column,.ui.grid>.column.row>[class*="three wide large screen"].column,.ui.grid>.row>[class*="three wide large screen"].column,.ui.grid>[class*="three wide large screen"].column{width:18.75%!important}.ui.column.grid>[class*="four wide large screen"].column,.ui.grid>.column.row>[class*="four wide large screen"].column,.ui.grid>.row>[class*="four wide large screen"].column,.ui.grid>[class*="four wide large screen"].column{width:25%!important}.ui.column.grid>[class*="five wide large screen"].column,.ui.grid>.column.row>[class*="five wide large screen"].column,.ui.grid>.row>[class*="five wide large screen"].column,.ui.grid>[class*="five wide large screen"].column{width:31.25%!important}.ui.column.grid>[class*="six wide large screen"].column,.ui.grid>.column.row>[class*="six wide large screen"].column,.ui.grid>.row>[class*="six wide large screen"].column,.ui.grid>[class*="six wide large screen"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide large screen"].column,.ui.grid>.column.row>[class*="seven wide large screen"].column,.ui.grid>.row>[class*="seven wide large screen"].column,.ui.grid>[class*="seven wide large screen"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide large screen"].column,.ui.grid>.column.row>[class*="eight wide large screen"].column,.ui.grid>.row>[class*="eight wide large screen"].column,.ui.grid>[class*="eight wide large screen"].column{width:50%!important}.ui.column.grid>[class*="nine wide large screen"].column,.ui.grid>.column.row>[class*="nine wide large screen"].column,.ui.grid>.row>[class*="nine wide large screen"].column,.ui.grid>[class*="nine wide large screen"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide large screen"].column,.ui.grid>.column.row>[class*="ten wide large screen"].column,.ui.grid>.row>[class*="ten wide large screen"].column,.ui.grid>[class*="ten wide large screen"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide large screen"].column,.ui.grid>.column.row>[class*="eleven wide large screen"].column,.ui.grid>.row>[class*="eleven wide large screen"].column,.ui.grid>[class*="eleven wide large screen"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide large screen"].column,.ui.grid>.column.row>[class*="twelve wide large screen"].column,.ui.grid>.row>[class*="twelve wide large screen"].column,.ui.grid>[class*="twelve wide large screen"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide large screen"].column,.ui.grid>.column.row>[class*="thirteen wide large screen"].column,.ui.grid>.row>[class*="thirteen wide large screen"].column,.ui.grid>[class*="thirteen wide large screen"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide large screen"].column,.ui.grid>.column.row>[class*="fourteen wide large screen"].column,.ui.grid>.row>[class*="fourteen wide large screen"].column,.ui.grid>[class*="fourteen wide large screen"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide large screen"].column,.ui.grid>.column.row>[class*="fifteen wide large screen"].column,.ui.grid>.row>[class*="fifteen wide large screen"].column,.ui.grid>[class*="fifteen wide large screen"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide large screen"].column,.ui.grid>.column.row>[class*="sixteen wide large screen"].column,.ui.grid>.row>[class*="sixteen wide large screen"].column,.ui.grid>[class*="sixteen wide large screen"].column{width:100%!important}}@media only screen and (min-width:1920px){.ui.column.grid>[class*="one wide widescreen"].column,.ui.grid>.column.row>[class*="one wide widescreen"].column,.ui.grid>.row>[class*="one wide widescreen"].column,.ui.grid>[class*="one wide widescreen"].column{width:6.25%!important}.ui.column.grid>[class*="two wide widescreen"].column,.ui.grid>.column.row>[class*="two wide widescreen"].column,.ui.grid>.row>[class*="two wide widescreen"].column,.ui.grid>[class*="two wide widescreen"].column{width:12.5%!important}.ui.column.grid>[class*="three wide widescreen"].column,.ui.grid>.column.row>[class*="three wide widescreen"].column,.ui.grid>.row>[class*="three wide widescreen"].column,.ui.grid>[class*="three wide widescreen"].column{width:18.75%!important}.ui.column.grid>[class*="four wide widescreen"].column,.ui.grid>.column.row>[class*="four wide widescreen"].column,.ui.grid>.row>[class*="four wide widescreen"].column,.ui.grid>[class*="four wide widescreen"].column{width:25%!important}.ui.column.grid>[class*="five wide widescreen"].column,.ui.grid>.column.row>[class*="five wide widescreen"].column,.ui.grid>.row>[class*="five wide widescreen"].column,.ui.grid>[class*="five wide widescreen"].column{width:31.25%!important}.ui.column.grid>[class*="six wide widescreen"].column,.ui.grid>.column.row>[class*="six wide widescreen"].column,.ui.grid>.row>[class*="six wide widescreen"].column,.ui.grid>[class*="six wide widescreen"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide widescreen"].column,.ui.grid>.column.row>[class*="seven wide widescreen"].column,.ui.grid>.row>[class*="seven wide widescreen"].column,.ui.grid>[class*="seven wide widescreen"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide widescreen"].column,.ui.grid>.column.row>[class*="eight wide widescreen"].column,.ui.grid>.row>[class*="eight wide widescreen"].column,.ui.grid>[class*="eight wide widescreen"].column{width:50%!important}.ui.column.grid>[class*="nine wide widescreen"].column,.ui.grid>.column.row>[class*="nine wide widescreen"].column,.ui.grid>.row>[class*="nine wide widescreen"].column,.ui.grid>[class*="nine wide widescreen"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide widescreen"].column,.ui.grid>.column.row>[class*="ten wide widescreen"].column,.ui.grid>.row>[class*="ten wide widescreen"].column,.ui.grid>[class*="ten wide widescreen"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide widescreen"].column,.ui.grid>.column.row>[class*="eleven wide widescreen"].column,.ui.grid>.row>[class*="eleven wide widescreen"].column,.ui.grid>[class*="eleven wide widescreen"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide widescreen"].column,.ui.grid>.column.row>[class*="twelve wide widescreen"].column,.ui.grid>.row>[class*="twelve wide widescreen"].column,.ui.grid>[class*="twelve wide widescreen"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide widescreen"].column,.ui.grid>.column.row>[class*="thirteen wide widescreen"].column,.ui.grid>.row>[class*="thirteen wide widescreen"].column,.ui.grid>[class*="thirteen wide widescreen"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide widescreen"].column,.ui.grid>.column.row>[class*="fourteen wide widescreen"].column,.ui.grid>.row>[class*="fourteen wide widescreen"].column,.ui.grid>[class*="fourteen wide widescreen"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide widescreen"].column,.ui.grid>.column.row>[class*="fifteen wide widescreen"].column,.ui.grid>.row>[class*="fifteen wide widescreen"].column,.ui.grid>[class*="fifteen wide widescreen"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide widescreen"].column,.ui.grid>.column.row>[class*="sixteen wide widescreen"].column,.ui.grid>.row>[class*="sixteen wide widescreen"].column,.ui.grid>[class*="sixteen wide widescreen"].column{width:100%!important}}.ui.centered.grid,.ui.centered.grid>.row,.ui.grid>.centered.row{text-align:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.centered.grid>.column:not(.aligned):not(.justified):not(.row),.ui.centered.grid>.row>.column:not(.aligned):not(.justified),.ui.grid .centered.row>.column:not(.aligned):not(.justified){text-align:left}.ui.grid>.centered.column,.ui.grid>.row>.centered.column{display:block;margin-left:auto;margin-right:auto}.ui.grid>.relaxed.row>.column,.ui.relaxed.grid>.column:not(.row),.ui.relaxed.grid>.row>.column{padding-left:1.5rem;padding-right:1.5rem}.ui.grid>[class*="very relaxed"].row>.column,.ui[class*="very relaxed"].grid>.column:not(.row),.ui[class*="very relaxed"].grid>.row>.column{padding-left:2.5rem;padding-right:2.5rem}.ui.grid .relaxed.row+.ui.divider,.ui.relaxed.grid .row+.ui.divider{margin-left:1.5rem;margin-right:1.5rem}.ui.grid [class*="very relaxed"].row+.ui.divider,.ui[class*="very relaxed"].grid .row+.ui.divider{margin-left:2.5rem;margin-right:2.5rem}.ui.padded.grid:not(.vertically):not(.horizontally){margin:0!important}[class*="horizontally padded"].ui.grid{margin-left:0!important;margin-right:0!important}[class*="vertically padded"].ui.grid{margin-top:0!important;margin-bottom:0!important}.ui.grid [class*="left floated"].column{margin-right:auto}.ui.grid [class*="right floated"].column{margin-left:auto}.ui.divided.grid:not([class*="vertically divided"])>.column:not(.row),.ui.divided.grid:not([class*="vertically divided"])>.row>.column{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui[class*="vertically divided"].grid>.column:not(.row),.ui[class*="vertically divided"].grid>.row>.column{margin-top:1rem;margin-bottom:1rem;padding-top:0;padding-bottom:0}.ui[class*="vertically divided"].grid>.row{margin-top:0;margin-bottom:0}.ui.divided.grid:not([class*="vertically divided"])>.column:first-child,.ui.divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui[class*="vertically divided"].grid>.row:first-child>.column{margin-top:0}.ui.grid>.divided.row>.column{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui.grid>.divided.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui[class*="vertically divided"].grid>.row{position:relative}.ui[class*="vertically divided"].grid>.row:before{position:absolute;content:"";top:0;left:0;width:calc(100% - 2rem);height:1px;margin:0 1rem;-webkit-box-shadow:0 -1px 0 0 rgba(34,36,38,.15);box-shadow:0 -1px 0 0 rgba(34,36,38,.15)}.ui.padded.divided.grid:not(.vertically):not(.horizontally),[class*="horizontally padded"].ui.divided.grid{width:100%}.ui[class*="vertically divided"].grid>.row:first-child:before{-webkit-box-shadow:none;box-shadow:none}.ui.inverted.divided.grid:not([class*="vertically divided"])>.column:not(.row),.ui.inverted.divided.grid:not([class*="vertically divided"])>.row>.column{-webkit-box-shadow:-1px 0 0 0 rgba(255,255,255,.1);box-shadow:-1px 0 0 0 rgba(255,255,255,.1)}.ui.inverted.divided.grid:not([class*="vertically divided"])>.column:not(.row):first-child,.ui.inverted.divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui.inverted[class*="vertically divided"].grid>.row:before{-webkit-box-shadow:0 -1px 0 0 rgba(255,255,255,.1);box-shadow:0 -1px 0 0 rgba(255,255,255,.1)}.ui.relaxed[class*="vertically divided"].grid>.row:before{margin-left:1.5rem;margin-right:1.5rem;width:calc(100% - 3rem)}.ui[class*="very relaxed"][class*="vertically divided"].grid>.row:before{margin-left:2.5rem;margin-right:2.5rem;width:calc(100% - 5rem)}.ui.celled.grid{width:100%;margin:1em 0;-webkit-box-shadow:0 0 0 1px #d4d4d5;box-shadow:0 0 0 1px #d4d4d5}.ui.celled.grid>.row{width:100%!important;margin:0;padding:0;-webkit-box-shadow:0 -1px 0 0 #d4d4d5;box-shadow:0 -1px 0 0 #d4d4d5}.ui.celled.grid>.column:not(.row),.ui.celled.grid>.row>.column{-webkit-box-shadow:-1px 0 0 0 #d4d4d5;box-shadow:-1px 0 0 0 #d4d4d5}.ui.celled.grid>.column:first-child,.ui.celled.grid>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui.celled.grid>.column:not(.row),.ui.celled.grid>.row>.column{padding:1em}.ui.relaxed.celled.grid>.column:not(.row),.ui.relaxed.celled.grid>.row>.column{padding:1.5em}.ui[class*="very relaxed"].celled.grid>.column:not(.row),.ui[class*="very relaxed"].celled.grid>.row>.column{padding:2em}.ui[class*="internally celled"].grid{-webkit-box-shadow:none;box-shadow:none;margin:0}.ui[class*="internally celled"].grid>.row:first-child{-webkit-box-shadow:none;box-shadow:none}.ui[class*="internally celled"].grid>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui.grid>.row>[class*="top aligned"].column,.ui.grid>[class*="top aligned"].column:not(.row),.ui.grid>[class*="top aligned"].row>.column,.ui[class*="top aligned"].grid>.column:not(.row),.ui[class*="top aligned"].grid>.row>.column{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;vertical-align:top;-ms-flex-item-align:start!important;align-self:flex-start!important}.ui.grid>.row>[class*="middle aligned"].column,.ui.grid>[class*="middle aligned"].column:not(.row),.ui.grid>[class*="middle aligned"].row>.column,.ui[class*="middle aligned"].grid>.column:not(.row),.ui[class*="middle aligned"].grid>.row>.column{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;vertical-align:middle;-ms-flex-item-align:center!important;align-self:center!important}.ui.grid>.row>[class*="bottom aligned"].column,.ui.grid>[class*="bottom aligned"].column:not(.row),.ui.grid>[class*="bottom aligned"].row>.column,.ui[class*="bottom aligned"].grid>.column:not(.row),.ui[class*="bottom aligned"].grid>.row>.column{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;vertical-align:bottom;-ms-flex-item-align:end!important;align-self:flex-end!important}.ui.grid>.row>.stretched.column,.ui.grid>.stretched.column:not(.row),.ui.grid>.stretched.row>.column,.ui.stretched.grid>.column,.ui.stretched.grid>.row>.column{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important;-ms-flex-item-align:stretch;align-self:stretch;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.grid>.row>.stretched.column>*,.ui.grid>.stretched.column:not(.row)>*,.ui.grid>.stretched.row>.column>*,.ui.stretched.grid>.column>*,.ui.stretched.grid>.row>.column>*{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.ui.grid>.row>[class*="left aligned"].column.column,.ui.grid>[class*="left aligned"].column.column,.ui.grid>[class*="left aligned"].row>.column,.ui[class*="left aligned"].grid>.column,.ui[class*="left aligned"].grid>.row>.column{text-align:left;-ms-flex-item-align:inherit;align-self:inherit}.ui.grid>.row>[class*="center aligned"].column.column,.ui.grid>[class*="center aligned"].column.column,.ui.grid>[class*="center aligned"].row>.column,.ui[class*="center aligned"].grid>.column,.ui[class*="center aligned"].grid>.row>.column{text-align:center;-ms-flex-item-align:inherit;align-self:inherit}.ui[class*="center aligned"].grid{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.grid>.row>[class*="right aligned"].column.column,.ui.grid>[class*="right aligned"].column.column,.ui.grid>[class*="right aligned"].row>.column,.ui[class*="right aligned"].grid>.column,.ui[class*="right aligned"].grid>.row>.column{text-align:right;-ms-flex-item-align:inherit;align-self:inherit}.ui.grid>.justified.column.column,.ui.grid>.justified.row>.column,.ui.grid>.row>.justified.column.column,.ui.justified.grid>.column,.ui.justified.grid>.row>.column{text-align:justify;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.ui.grid>.primary.column,.ui.grid>.primary.row,.ui.grid>.row>.primary.column{background-color:#2185d0;color:#fff}.ui.grid>.row>.secondary.column,.ui.grid>.secondary.column,.ui.grid>.secondary.row{background-color:#1b1c1d;color:#fff}.ui.grid>.red.column,.ui.grid>.red.row,.ui.grid>.row>.red.column{background-color:#db2828;color:#fff}.ui.grid>.orange.column,.ui.grid>.orange.row,.ui.grid>.row>.orange.column{background-color:#f2711c;color:#fff}.ui.grid>.row>.yellow.column,.ui.grid>.yellow.column,.ui.grid>.yellow.row{background-color:#fbbd08;color:#fff}.ui.grid>.olive.column,.ui.grid>.olive.row,.ui.grid>.row>.olive.column{background-color:#b5cc18;color:#fff}.ui.grid>.green.column,.ui.grid>.green.row,.ui.grid>.row>.green.column{background-color:#21ba45;color:#fff}.ui.grid>.row>.teal.column,.ui.grid>.teal.column,.ui.grid>.teal.row{background-color:#00b5ad;color:#fff}.ui.grid>.blue.column,.ui.grid>.blue.row,.ui.grid>.row>.blue.column{background-color:#2185d0;color:#fff}.ui.grid>.row>.violet.column,.ui.grid>.violet.column,.ui.grid>.violet.row{background-color:#6435c9;color:#fff}.ui.grid>.purple.column,.ui.grid>.purple.row,.ui.grid>.row>.purple.column{background-color:#a333c8;color:#fff}.ui.grid>.pink.column,.ui.grid>.pink.row,.ui.grid>.row>.pink.column{background-color:#e03997;color:#fff}.ui.grid>.brown.column,.ui.grid>.brown.row,.ui.grid>.row>.brown.column{background-color:#a5673f;color:#fff}.ui.grid>.grey.column,.ui.grid>.grey.row,.ui.grid>.row>.grey.column{background-color:#767676;color:#fff}.ui.grid>.black.column,.ui.grid>.black.row,.ui.grid>.row>.black.column{background-color:#1b1c1d;color:#fff}.ui.grid>[class*="equal width"].row>.column,.ui[class*="equal width"].grid>.column:not(.row),.ui[class*="equal width"].grid>.row>.column{display:inline-block;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.ui.grid>[class*="equal width"].row>.wide.column,.ui[class*="equal width"].grid>.row>.wide.column,.ui[class*="equal width"].grid>.wide.column{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}@media only screen and (max-width:767.98px){.ui.grid>[class*="mobile reversed"].row,.ui[class*="mobile reversed"].grid,.ui[class*="mobile reversed"].grid>.row{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.ui.stackable[class*="mobile reversed"],.ui[class*="mobile vertically reversed"].grid{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"])>.column:first-child,.ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"])>.column:last-child,.ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}.ui.grid[class*="vertically divided"][class*="mobile vertically reversed"]>.row:first-child:before{-webkit-box-shadow:0 -1px 0 0 rgba(34,36,38,.15);box-shadow:0 -1px 0 0 rgba(34,36,38,.15)}.ui.grid[class*="vertically divided"][class*="mobile vertically reversed"]>.row:last-child:before{-webkit-box-shadow:none;box-shadow:none}.ui[class*="mobile reversed"].celled.grid>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 #d4d4d5;box-shadow:-1px 0 0 0 #d4d4d5}.ui[class*="mobile reversed"].celled.grid>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.grid>[class*="tablet reversed"].row,.ui[class*="tablet reversed"].grid,.ui[class*="tablet reversed"].grid>.row{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.ui[class*="tablet vertically reversed"].grid{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"])>.column:first-child,.ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"])>.column:last-child,.ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}.ui.grid[class*="vertically divided"][class*="tablet vertically reversed"]>.row:first-child:before{-webkit-box-shadow:0 -1px 0 0 rgba(34,36,38,.15);box-shadow:0 -1px 0 0 rgba(34,36,38,.15)}.ui.grid[class*="vertically divided"][class*="tablet vertically reversed"]>.row:last-child:before{-webkit-box-shadow:none;box-shadow:none}.ui[class*="tablet reversed"].celled.grid>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 #d4d4d5;box-shadow:-1px 0 0 0 #d4d4d5}.ui[class*="tablet reversed"].celled.grid>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}}@media only screen and (min-width:992px){.ui.grid>[class*="computer reversed"].row,.ui[class*="computer reversed"].grid,.ui[class*="computer reversed"].grid>.row{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.ui[class*="computer vertically reversed"].grid{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"])>.column:first-child,.ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"])>.column:last-child,.ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}.ui.grid[class*="vertically divided"][class*="computer vertically reversed"]>.row:first-child:before{-webkit-box-shadow:0 -1px 0 0 rgba(34,36,38,.15);box-shadow:0 -1px 0 0 rgba(34,36,38,.15)}.ui.grid[class*="vertically divided"][class*="computer vertically reversed"]>.row:last-child:before{-webkit-box-shadow:none;box-shadow:none}.ui[class*="computer reversed"].celled.grid>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 #d4d4d5;box-shadow:-1px 0 0 0 #d4d4d5}.ui[class*="computer reversed"].celled.grid>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.doubling.grid{width:auto}.ui.doubling.grid>.row,.ui.grid>.doubling.row{margin:0!important;padding:0!important}.ui.doubling.grid>.row>.column,.ui.grid>.doubling.row>.column{display:inline-block!important;padding-top:1rem!important;padding-bottom:1rem!important;-webkit-box-shadow:none!important;box-shadow:none!important;margin:0}.ui.grid>[class*="two column"].doubling.row.row>.column,.ui[class*="two column"].doubling.grid>.column:not(.row),.ui[class*="two column"].doubling.grid>.row>.column{width:100%!important}.ui.grid>[class*="three column"].doubling.row.row>.column,.ui[class*="three column"].doubling.grid>.column:not(.row),.ui[class*="three column"].doubling.grid>.row>.column{width:50%!important}.ui.grid>[class*="four column"].doubling.row.row>.column,.ui[class*="four column"].doubling.grid>.column:not(.row),.ui[class*="four column"].doubling.grid>.row>.column{width:50%!important}.ui.grid>[class*="five column"].doubling.row.row>.column,.ui[class*="five column"].doubling.grid>.column:not(.row),.ui[class*="five column"].doubling.grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="six column"].doubling.row.row>.column,.ui[class*="six column"].doubling.grid>.column:not(.row),.ui[class*="six column"].doubling.grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="seven column"].doubling.row.row>.column,.ui[class*="seven column"].doubling.grid>.column:not(.row),.ui[class*="seven column"].doubling.grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="eight column"].doubling.row.row>.column,.ui[class*="eight column"].doubling.grid>.column:not(.row),.ui[class*="eight column"].doubling.grid>.row>.column{width:25%!important}.ui.grid>[class*="nine column"].doubling.row.row>.column,.ui[class*="nine column"].doubling.grid>.column:not(.row),.ui[class*="nine column"].doubling.grid>.row>.column{width:25%!important}.ui.grid>[class*="ten column"].doubling.row.row>.column,.ui[class*="ten column"].doubling.grid>.column:not(.row),.ui[class*="ten column"].doubling.grid>.row>.column{width:20%!important}.ui.grid>[class*="eleven column"].doubling.row.row>.column,.ui[class*="eleven column"].doubling.grid>.column:not(.row),.ui[class*="eleven column"].doubling.grid>.row>.column{width:20%!important}.ui.grid>[class*="twelve column"].doubling.row.row>.column,.ui[class*="twelve column"].doubling.grid>.column:not(.row),.ui[class*="twelve column"].doubling.grid>.row>.column{width:16.66666667%!important}.ui.grid>[class*="thirteen column"].doubling.row.row>.column,.ui[class*="thirteen column"].doubling.grid>.column:not(.row),.ui[class*="thirteen column"].doubling.grid>.row>.column{width:16.66666667%!important}.ui.grid>[class*="fourteen column"].doubling.row.row>.column,.ui[class*="fourteen column"].doubling.grid>.column:not(.row),.ui[class*="fourteen column"].doubling.grid>.row>.column{width:14.28571429%!important}.ui.grid>[class*="fifteen column"].doubling.row.row>.column,.ui[class*="fifteen column"].doubling.grid>.column:not(.row),.ui[class*="fifteen column"].doubling.grid>.row>.column{width:14.28571429%!important}.ui.grid>[class*="sixteen column"].doubling.row.row>.column,.ui[class*="sixteen column"].doubling.grid>.column:not(.row),.ui[class*="sixteen column"].doubling.grid>.row>.column{width:12.5%!important}}@media only screen and (max-width:767.98px){.ui.doubling.grid>.row,.ui.grid>.doubling.row{margin:0!important;padding:0!important}.ui.doubling.grid>.row>.column,.ui.grid>.doubling.row>.column{padding-top:1rem!important;padding-bottom:1rem!important;margin:0!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.grid>[class*="two column"].doubling:not(.stackable).row.row>.column,.ui[class*="two column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="two column"].doubling:not(.stackable).grid>.row>.column{width:100%!important}.ui.grid>[class*="three column"].doubling:not(.stackable).row.row>.column,.ui[class*="three column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="three column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="four column"].doubling:not(.stackable).row.row>.column,.ui[class*="four column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="four column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="five column"].doubling:not(.stackable).row.row>.column,.ui[class*="five column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="five column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="six column"].doubling:not(.stackable).row.row>.column,.ui[class*="six column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="six column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="seven column"].doubling:not(.stackable).row.row>.column,.ui[class*="seven column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="seven column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="eight column"].doubling:not(.stackable).row.row>.column,.ui[class*="eight column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="eight column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="nine column"].doubling:not(.stackable).row.row>.column,.ui[class*="nine column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="nine column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="ten column"].doubling:not(.stackable).row.row>.column,.ui[class*="ten column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="ten column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="eleven column"].doubling:not(.stackable).row.row>.column,.ui[class*="eleven column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="eleven column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="twelve column"].doubling:not(.stackable).row.row>.column,.ui[class*="twelve column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="twelve column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="thirteen column"].doubling:not(.stackable).row.row>.column,.ui[class*="thirteen column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="thirteen column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="fourteen column"].doubling:not(.stackable).row.row>.column,.ui[class*="fourteen column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="fourteen column"].doubling:not(.stackable).grid>.row>.column{width:25%!important}.ui.grid>[class*="fifteen column"].doubling:not(.stackable).row.row>.column,.ui[class*="fifteen column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="fifteen column"].doubling:not(.stackable).grid>.row>.column{width:25%!important}.ui.grid>[class*="sixteen column"].doubling:not(.stackable).row.row>.column,.ui[class*="sixteen column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="sixteen column"].doubling:not(.stackable).grid>.row>.column{width:25%!important}}@media only screen and (max-width:767.98px){.ui.stackable.grid{width:auto;margin-left:0!important;margin-right:0!important}.ui.grid>.stackable.stackable.stackable.row>.column,.ui.stackable.grid>.column.grid>.column,.ui.stackable.grid>.column.row>.column,.ui.stackable.grid>.column:not(.row),.ui.stackable.grid>.row>.column,.ui.stackable.grid>.row>.wide.column,.ui.stackable.grid>.wide.column{width:100%!important;margin:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important;padding:1rem 1rem}.ui.stackable.grid:not(.vertically)>.row{margin:0;padding:0}.ui.container>.ui.stackable.grid>.column,.ui.container>.ui.stackable.grid>.row>.column{padding-left:0!important;padding-right:0!important}.ui.grid .ui.stackable.grid,.ui.segment:not(.vertical) .ui.stackable.page.grid{margin-left:-1rem!important;margin-right:-1rem!important}.ui.stackable.celled.grid>.column:not(.row):first-child,.ui.stackable.celled.grid>.row:first-child>.column:first-child,.ui.stackable.divided.grid>.column:not(.row):first-child,.ui.stackable.divided.grid>.row:first-child>.column:first-child{border-top:none!important}.ui.inverted.stackable.celled.grid>.column:not(.row),.ui.inverted.stackable.celled.grid>.row>.column,.ui.inverted.stackable.divided.grid>.column:not(.row),.ui.inverted.stackable.divided.grid>.row>.column{border-top:1px solid rgba(255,255,255,.1)}.ui.stackable.celled.grid>.column:not(.row),.ui.stackable.celled.grid>.row>.column,.ui.stackable.divided:not(.vertically).grid>.column:not(.row),.ui.stackable.divided:not(.vertically).grid>.row>.column{border-top:1px solid rgba(34,36,38,.15);-webkit-box-shadow:none!important;box-shadow:none!important;padding-top:2rem!important;padding-bottom:2rem!important}.ui.stackable.celled.grid>.row{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.stackable.divided:not(.vertically).grid>.column:not(.row),.ui.stackable.divided:not(.vertically).grid>.row>.column{padding-left:0!important;padding-right:0!important}}@media only screen and (max-width:767.98px){.ui.grid.grid.grid>.row>[class*="tablet only"].column:not(.mobile),.ui.grid.grid.grid>[class*="tablet only"].column:not(.mobile),.ui.grid.grid.grid>[class*="tablet only"].row:not(.mobile),.ui[class*="tablet only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="computer only"].column:not(.mobile),.ui.grid.grid.grid>[class*="computer only"].column:not(.mobile),.ui.grid.grid.grid>[class*="computer only"].row:not(.mobile),.ui[class*="computer only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].row:not(.mobile),.ui[class*="large screen only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].row:not(.mobile),.ui[class*="widescreen only"].grid.grid.grid:not(.mobile){display:none!important}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.grid.grid.grid>.row>[class*="mobile only"].column:not(.tablet),.ui.grid.grid.grid>[class*="mobile only"].column:not(.tablet),.ui.grid.grid.grid>[class*="mobile only"].row:not(.tablet),.ui[class*="mobile only"].grid.grid.grid:not(.tablet){display:none!important}.ui.grid.grid.grid>.row>[class*="computer only"].column:not(.tablet),.ui.grid.grid.grid>[class*="computer only"].column:not(.tablet),.ui.grid.grid.grid>[class*="computer only"].row:not(.tablet),.ui[class*="computer only"].grid.grid.grid:not(.tablet){display:none!important}.ui.grid.grid.grid>.row>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].row:not(.mobile),.ui[class*="large screen only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].row:not(.mobile),.ui[class*="widescreen only"].grid.grid.grid:not(.mobile){display:none!important}}@media only screen and (min-width:992px) and (max-width:1199.98px){.ui.grid.grid.grid>.row>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].row:not(.computer),.ui[class*="mobile only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].row:not(.computer),.ui[class*="tablet only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].row:not(.mobile),.ui[class*="large screen only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].row:not(.mobile),.ui[class*="widescreen only"].grid.grid.grid:not(.mobile){display:none!important}}@media only screen and (min-width:1200px) and (max-width:1919.98px){.ui.grid.grid.grid>.row>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].row:not(.computer),.ui[class*="mobile only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].row:not(.computer),.ui[class*="tablet only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].row:not(.mobile),.ui[class*="widescreen only"].grid.grid.grid:not(.mobile){display:none!important}}@media only screen and (min-width:1920px){.ui.grid.grid.grid>.row>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].row:not(.computer),.ui[class*="mobile only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].row:not(.computer),.ui[class*="tablet only"].grid.grid.grid:not(.computer){display:none!important}}.ui.ui.ui.compact.grid>.column:not(.row),.ui.ui.ui.compact.grid>.row>.column{padding-left:.5rem;padding-right:.5rem}.ui.ui.ui.compact.grid>*{padding-left:.5rem;padding-right:.5rem}.ui.ui.ui.compact.grid>.row{padding-top:.5rem;padding-bottom:.5rem}.ui.ui.ui.compact.grid>.column:not(.row){padding-top:.5rem;padding-bottom:.5rem}.ui.compact.relaxed.celled.grid>.column:not(.row),.ui.compact.relaxed.celled.grid>.row>.column{padding:.75em}.ui.compact[class*="very relaxed"].celled.grid>.column:not(.row),.ui.compact[class*="very relaxed"].celled.grid>.row>.column{padding:1em}.ui[class*="very compact"].grid>.column:not(.row),.ui[class*="very compact"].grid>.row>.column{padding-left:.25rem;padding-right:.5rem}.ui[class*="very compact"].grid>*{padding-left:.25rem;padding-right:.25rem}.ui[class*="very compact"].grid>.row{padding-top:.25rem;padding-bottom:.25rem}.ui[class*="very compact"].grid>.column:not(.row){padding-top:.25rem;padding-bottom:.25rem}.ui[class*="very compact"].relaxed.celled.grid>.column:not(.row),.ui[class*="very compact"].relaxed.celled.grid>.row>.column{padding:.375em}.ui[class*="very compact"][class*="very relaxed"].celled.grid>.column:not(.row),.ui[class*="very compact"][class*="very relaxed"].celled.grid>.row>.column{padding:.5em}.ui.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin:1rem 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;background:#fff;font-weight:400;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);border-radius:.28571429rem;min-height:2.85714286em}.ui.menu:after{content:'';display:block;height:0;clear:both;visibility:hidden}.ui.menu:first-child{margin-top:0}.ui.menu:last-child{margin-bottom:0}.ui.menu .menu{margin:0}.ui.menu:not(.vertical)>.menu{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.menu:not(.vertical) .item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.menu .item{position:relative;vertical-align:middle;line-height:1;text-decoration:none;-webkit-tap-highlight-color:transparent;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:0 0;padding:.92857143em 1.14285714em;text-transform:none;color:rgba(0,0,0,.87);font-weight:400;-webkit-transition:background .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background .1s ease,box-shadow .1s ease,color .1s ease;transition:background .1s ease,box-shadow .1s ease,color .1s ease,-webkit-box-shadow .1s ease}.ui.menu>.item:first-child{border-radius:.28571429rem 0 0 .28571429rem}.ui.menu .item:before{position:absolute;content:'';top:0;right:0;height:100%;width:1px;background:rgba(34,36,38,.1)}.ui.menu .item>a:not(.ui),.ui.menu .item>p:only-child,.ui.menu .text.item>*{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;line-height:1.3}.ui.menu .item>p:first-child{margin-top:0}.ui.menu .item>p:last-child{margin-bottom:0}.ui.menu .item>i.icon{opacity:.9;float:none;margin:0 .35714286em 0 0}.ui.menu:not(.vertical) .item>.button{position:relative;top:0;margin:-.5em 0;padding-bottom:.78571429em;padding-top:.78571429em;font-size:1em}.ui.menu>.container,.ui.menu>.grid{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:inherit;-ms-flex-align:inherit;align-items:inherit;-webkit-box-orient:inherit;-webkit-box-direction:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.menu .item>.input{width:100%}.ui.menu:not(.vertical) .item>.input{position:relative;top:0;margin:-.5em 0}.ui.menu .item>.input input{font-size:1em;padding-top:.57142857em;padding-bottom:.57142857em}.ui.menu .header.item,.ui.vertical.menu .header.item{margin:0;background:'';text-transform:normal;font-weight:700}.ui.vertical.menu .item>.header:not(.ui){margin:0 0 .5em;font-size:1em;font-weight:700}.ui.menu .item>i.dropdown.icon{padding:0;float:right;margin:0 0 0 1em}.ui.menu .dropdown.item .menu{min-width:calc(100% - 1px);border-radius:0 0 .28571429rem .28571429rem;background:#fff;margin:0 0 0;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.08);box-shadow:0 1px 3px 0 rgba(0,0,0,.08);-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.ui.menu .ui.dropdown .menu>.item{margin:0;text-align:left;font-size:1em!important;padding:.78571429em 1.14285714em!important;background:0 0!important;color:rgba(0,0,0,.87)!important;text-transform:none!important;font-weight:400!important;-webkit-box-shadow:none!important;box-shadow:none!important;-webkit-transition:none!important;transition:none!important}.ui.menu .ui.dropdown .menu>.item:hover{background:rgba(0,0,0,.05)!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown .menu>.selected.item{background:rgba(0,0,0,.05)!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown .menu>.active.item{background:rgba(0,0,0,.03)!important;font-weight:700!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown.item .menu .item:not(.filtered){display:block}.ui.menu .ui.dropdown .menu>.item .icon:not(.dropdown){display:inline-block;font-size:1em!important;float:none;margin:0 .75em 0 0!important}.ui.secondary.menu .dropdown.item>.menu,.ui.text.menu .dropdown.item>.menu{border-radius:.28571429rem;margin-top:.35714286em}.ui.menu .pointing.dropdown.item .menu{margin-top:.75em}.ui.inverted.menu .search.dropdown.item>.search,.ui.inverted.menu .search.dropdown.item>.text{color:rgba(255,255,255,.9)}.ui.vertical.menu .dropdown.item>.icon{float:right;content:"\f0da";margin-left:1em}.ui.vertical.menu .dropdown.item .menu{left:100%;min-width:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;margin:0;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.08);box-shadow:0 1px 3px 0 rgba(0,0,0,.08);border-radius:0 .28571429rem .28571429rem .28571429rem}.ui.vertical.menu .dropdown.item.upward .menu{bottom:0}.ui.vertical.menu .dropdown.item:not(.upward) .menu{top:0}.ui.vertical.menu .active.dropdown.item{border-top-right-radius:0;border-bottom-right-radius:0}.ui.vertical.menu .dropdown.active.item{-webkit-box-shadow:none;box-shadow:none}.ui.item.menu .dropdown .menu .item{width:100%}.ui.menu .item>.label:not(.floating){margin-left:1em;padding:.3em .78571429em}.ui.vertical.menu .item>.label{margin-top:-.15em;margin-bottom:-.15em;padding:.3em .78571429em}.ui.menu .item>.floating.label{padding:.3em .78571429em}.ui.menu .item>.label{background:#999;color:#fff}.ui.menu .item>.image.label img{margin:-.2833em .8em -.2833em -.8em;height:1.5666em}.ui.menu .item>img:not(.ui){display:inline-block;vertical-align:middle;margin:-.3em 0;width:2.5em}.ui.vertical.menu .item>img:not(.ui):only-child{display:block;max-width:100%;width:auto}.ui.menu .list .item:before{background:0 0!important}.ui.vertical.sidebar.menu>.item:first-child:before{display:block!important}.ui.vertical.sidebar.menu>.item::before{top:auto;bottom:0}@media only screen and (max-width:767.98px){.ui.menu>.ui.container{width:100%!important;margin-left:0!important;margin-right:0!important}}@media only screen and (min-width:768px){.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.item:not(.right):not(.borderless):first-child{border-left:1px solid rgba(34,36,38,.1)}.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.right.item:not(.borderless):last-child,.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.right.menu>.item:not(.borderless):last-child{border-right:1px solid rgba(34,36,38,.1)}}.ui.link.menu .item:hover,.ui.menu .dropdown.item:hover,.ui.menu .link.item:hover,.ui.menu a.item:hover{cursor:pointer;background:rgba(0,0,0,.03);color:rgba(0,0,0,.95)}.ui.link.menu .item:active,.ui.menu .link.item:active,.ui.menu a.item:active{background:rgba(0,0,0,.03);color:rgba(0,0,0,.95)}.ui.menu .active.item{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);font-weight:400;-webkit-box-shadow:none;box-shadow:none}.ui.menu .active.item>i.icon{opacity:1}.ui.menu .active.item:hover,.ui.vertical.menu .active.item:hover{background-color:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.ui.menu .item.disabled{cursor:default;background-color:transparent;color:rgba(40,40,40,.3);pointer-events:none}.ui.menu:not(.vertical) .left.item,.ui.menu:not(.vertical) .left.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-right:auto!important}.ui.menu:not(.vertical) .right.item,.ui.menu:not(.vertical) .right.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-left:auto!important}.ui.menu:not(.vertical) :not(.dropdown)>.left.menu,.ui.menu:not(.vertical) :not(.dropdown)>.right.menu{display:inherit}.ui.menu:not(.vertical) .center.item,.ui.menu:not(.vertical) .center.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-left:auto!important;margin-right:auto!important}.ui.menu .right.item::before,.ui.menu .right.menu>.item::before{right:auto;left:0}.ui.menu .center.item:last-child::before,.ui.menu .center.menu>.item:last-child::before{display:none}.ui.vertical.menu{display:block;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;background:#fff;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.ui.vertical.menu .item{display:block;background:0 0;border-top:none;border-right:none}.ui.vertical.menu>.item:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.vertical.menu>.item:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.vertical.menu .item>.label{float:right;text-align:center}.ui.vertical.menu .item>i.icon,.ui.vertical.menu .item>i.icons{width:1.18em;float:right;margin:0 0 0 .5em}.ui.vertical.menu .item>.label+i.icon{float:none;margin:0 .5em 0 0}.ui.vertical.menu .item:before{position:absolute;content:'';top:0;left:0;width:100%;height:1px;background:rgba(34,36,38,.1)}.ui.vertical.menu .item:first-child:before{display:none!important}.ui.vertical.menu .item>.menu{margin:.5em -1.14285714em 0}.ui.vertical.menu .menu .item{background:0 0;padding:.5em 1.33333333em;font-size:.85714286em;color:rgba(0,0,0,.5)}.ui.vertical.menu .item .menu .link.item:hover,.ui.vertical.menu .item .menu a.item:hover{color:rgba(0,0,0,.85)}.ui.vertical.menu .menu .item:before{display:none}.ui.vertical.menu .active.item{background:rgba(0,0,0,.05);border-radius:0;-webkit-box-shadow:none;box-shadow:none}.ui.vertical.menu>.active.item:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.vertical.menu>.active.item:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.vertical.menu>.active.item:only-child{border-radius:.28571429rem}.ui.vertical.menu .active.item .menu .active.item{border-left:none}.ui.vertical.menu .item .menu .active.item{background-color:transparent;font-weight:700;color:rgba(0,0,0,.95)}.ui.tabular.menu{border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border:none;background:none transparent;border-bottom:1px solid #d4d4d5}.ui.tabular.fluid.menu{width:calc(100% + 2px)!important}.ui.tabular.menu .item{background:0 0;border-bottom:none;border-left:1px solid transparent;border-right:1px solid transparent;border-top:2px solid transparent;padding:.92857143em 1.42857143em;color:rgba(0,0,0,.87)}.ui.tabular.menu .item:before{display:none}.ui.tabular.menu .item:hover{background-color:transparent;color:rgba(0,0,0,.8)}.ui.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-top-width:1px;border-color:#d4d4d5;font-weight:700;margin-bottom:-1px;-webkit-box-shadow:none;box-shadow:none;border-radius:.28571429rem .28571429rem 0 0!important}.ui.tabular.menu+.attached:not(.top).segment,.ui.tabular.menu+.attached:not(.top).segment+.attached:not(.top).segment{border-top:none;margin-left:0;margin-top:0;margin-right:0;width:100%}.top.attached.segment+.ui.bottom.tabular.menu{position:relative;width:calc(100% + 2px);left:-1px}.ui.bottom.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-top:1px solid #d4d4d5}.ui.bottom.tabular.menu .item{background:0 0;border-left:1px solid transparent;border-right:1px solid transparent;border-bottom:1px solid transparent;border-top:none}.ui.bottom.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:-1px 0 0 0;border-radius:0 0 .28571429rem .28571429rem!important}.ui.vertical.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-right:1px solid #d4d4d5}.ui.vertical.tabular.menu .item{background:0 0;border-left:1px solid transparent;border-bottom:1px solid transparent;border-top:1px solid transparent;border-right:none}.ui.vertical.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:0 -1px 0 0;border-radius:.28571429rem 0 0 .28571429rem!important}.ui.vertical.right.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-right:none;border-left:1px solid #d4d4d5}.ui.vertical.right.tabular.menu .item{background:0 0;border-right:1px solid transparent;border-bottom:1px solid transparent;border-top:1px solid transparent;border-left:none}.ui.vertical.right.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:0 0 0 -1px;border-radius:0 .28571429rem .28571429rem 0!important}.ui.tabular.menu .active.dropdown.item{margin-bottom:0;border-left:1px solid transparent;border-right:1px solid transparent;border-top:2px solid transparent;border-bottom:none}.ui.pagination.menu{margin:0;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.ui.pagination.menu .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.compact.menu .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.pagination.menu .item:last-child:before{display:none}.ui.pagination.menu .item{min-width:3em;text-align:center}.ui.pagination.menu .icon.item i.icon{vertical-align:top}.ui.pagination.menu .active.item{border-top:none;padding-top:.92857143em;background-color:rgba(0,0,0,.05);color:rgba(0,0,0,.95);-webkit-box-shadow:none;box-shadow:none}.ui.secondary.menu{background:0 0;margin-left:-.35714286em;margin-right:-.35714286em;border-radius:0;border:none;-webkit-box-shadow:none;box-shadow:none}.ui.secondary.menu .item{-ms-flex-item-align:center;align-self:center;-webkit-box-shadow:none;box-shadow:none;border:none;padding:.78571429em .92857143em;margin:0 .35714286em;background:0 0;-webkit-transition:color .1s ease;transition:color .1s ease;border-radius:.28571429rem}.ui.secondary.menu .item:before{display:none!important}.ui.secondary.menu .header.item{border-radius:0;border-right:none;background:none transparent}.ui.secondary.menu .item>img:not(.ui){margin:0}.ui.secondary.menu .dropdown.item:hover,.ui.secondary.menu .link.item:hover,.ui.secondary.menu a.item:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.secondary.menu .active.item{-webkit-box-shadow:none;box-shadow:none;background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);border-radius:.28571429rem}.ui.secondary.menu .active.item:hover{-webkit-box-shadow:none;box-shadow:none;background:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.secondary.inverted.menu .link.item:not(.disabled),.ui.secondary.inverted.menu a.item:not(.disabled){color:rgba(255,255,255,.7)}.ui.secondary.inverted.menu .dropdown.item:hover,.ui.secondary.inverted.menu .link.item:hover,.ui.secondary.inverted.menu a.item:hover{background:rgba(255,255,255,.08);color:#fff}.ui.secondary.inverted.menu .active.item{background:rgba(255,255,255,.15);color:#fff}.ui.secondary.item.menu{margin-left:0;margin-right:0}.ui.secondary.item.menu .item:last-child{margin-right:0}.ui.secondary.attached.menu{-webkit-box-shadow:none;box-shadow:none}.ui.vertical.secondary.menu .item:not(.dropdown)>.menu{margin:0 -.92857143em}.ui.vertical.secondary.menu .item:not(.dropdown)>.menu>.item{margin:0;padding:.5em 1.33333333em}.ui.secondary.vertical.menu>.item{border:none;margin:0 0 .35714286em;border-radius:.28571429rem!important}.ui.secondary.vertical.menu>.header.item{border-radius:0}.ui.vertical.secondary.menu .item>.menu .item{background-color:transparent}.ui.secondary.inverted.menu{background-color:transparent}.ui.secondary.pointing.menu{margin-left:0;margin-right:0;border-bottom:2px solid rgba(34,36,38,.15)}.ui.secondary.pointing.menu .item{border-bottom-color:transparent;border-bottom-style:solid;border-radius:0;-ms-flex-item-align:end;align-self:flex-end;margin:0 0 -2px;padding:.85714286em 1.14285714em;border-bottom-width:2px;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.secondary.pointing.menu .ui.dropdown .menu .item{border-bottom-width:0}.ui.secondary.pointing.menu .item>.label:not(.floating){margin-top:-.3em;margin-bottom:-.3em}.ui.secondary.pointing.menu .item>.circular.label{margin-top:-.5em;margin-bottom:-.5em}.ui.secondary.pointing.menu .header.item{color:rgba(0,0,0,.85)!important}.ui.secondary.pointing.menu .text.item{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.secondary.pointing.menu .item:after{display:none}.ui.secondary.pointing.menu .dropdown.item:hover,.ui.secondary.pointing.menu .link.item:hover,.ui.secondary.pointing.menu a.item:hover{background-color:transparent;color:rgba(0,0,0,.87)}.ui.secondary.pointing.menu .dropdown.item:active,.ui.secondary.pointing.menu .link.item:active,.ui.secondary.pointing.menu a.item:active{background-color:transparent;border-color:rgba(34,36,38,.15)}.ui.secondary.pointing.menu .active.item{background-color:transparent;-webkit-box-shadow:none;box-shadow:none;border-color:currentColor;font-weight:700;color:rgba(0,0,0,.95)}.ui.secondary.pointing.menu .active.item:hover{border-color:currentColor;color:rgba(0,0,0,.95)}.ui.secondary.pointing.menu .active.dropdown.item{border-color:transparent}.ui.secondary.vertical.pointing.menu{border-bottom-width:0;border-right-width:2px;border-right-style:solid;border-right-color:rgba(34,36,38,.15)}.ui.secondary.vertical.pointing.menu .item{border-bottom:none;border-right-style:solid;border-right-color:transparent;border-radius:0!important;margin:0 -2px 0 0;border-right-width:2px}.ui.secondary.vertical.pointing.menu .active.item{border-color:currentColor}.ui.secondary.inverted.pointing.menu{border-color:rgba(255,255,255,.1)}.ui.secondary.inverted.pointing.menu .item:not(.disabled){color:rgba(255,255,255,.9)}.ui.secondary.inverted.pointing.menu .header.item{color:#fff!important}.ui.secondary.inverted.pointing.menu .link.item:hover,.ui.secondary.inverted.pointing.menu a.item:hover{color:#fff}.ui.ui.secondary.inverted.pointing.menu .active.item{border-color:#fff;color:#fff;background-color:transparent}.ui.text.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none;box-shadow:none;border:none;margin:1em -.5em}.ui.text.menu .item{border-radius:0;-webkit-box-shadow:none;box-shadow:none;-ms-flex-item-align:center;align-self:center;margin:0 0;padding:.35714286em .5em;font-weight:400;color:rgba(0,0,0,.6);-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.text.menu .item:before,.ui.text.menu .menu .item:before{display:none!important}.ui.text.menu .header.item{background-color:transparent;opacity:1;color:rgba(0,0,0,.85);font-size:.92857143em;text-transform:uppercase;font-weight:700}.ui.text.menu .item>img:not(.ui){margin:0}.ui.text.item.menu .item{margin:0}.ui.vertical.text.menu{margin:1em 0}.ui.vertical.text.menu:first-child{margin-top:0}.ui.vertical.text.menu:last-child{margin-bottom:0}.ui.vertical.text.menu .item{margin:.57142857em 0;padding-left:0;padding-right:0}.ui.vertical.text.menu .item>i.icon{float:none;margin:0 .35714286em 0 0}.ui.vertical.text.menu .header.item{margin:.57142857em 0 .71428571em}.ui.vertical.text.menu .item:not(.dropdown)>.menu{margin:0}.ui.vertical.text.menu .item:not(.dropdown)>.menu>.item{margin:0;padding:.5em 0}.ui.text.menu .item:hover{opacity:1;background-color:transparent}.ui.text.menu .active.item{background-color:transparent;border:none;-webkit-box-shadow:none;box-shadow:none;font-weight:400;color:rgba(0,0,0,.95)}.ui.text.menu .active.item:hover{background-color:transparent}.ui.text.pointing.menu .active.item:after{-webkit-box-shadow:none;box-shadow:none}.ui.text.attached.menu{-webkit-box-shadow:none;box-shadow:none}.ui.inverted.text.menu,.ui.inverted.text.menu .active.item,.ui.inverted.text.menu .item,.ui.inverted.text.menu .item:hover{background-color:transparent}.ui.fluid.text.menu{margin-left:0;margin-right:0}.ui.vertical.icon.menu{display:inline-block;width:auto}.ui.icon.menu .item{height:auto;text-align:center;color:#1b1c1d}.ui.icon.menu .item>.icon:not(.dropdown){margin:0;opacity:1}.ui.icon.menu .icon:before{opacity:1}.ui.menu .icon.item>.icon{width:auto;margin:0 auto}.ui.vertical.icon.menu .item>.icon:not(.dropdown){display:block;opacity:1;margin:0 auto;float:none}.ui.inverted.icon.menu .item{color:#fff}.ui.labeled.icon.menu{text-align:center}.ui.labeled.icon.menu .item{min-width:6em;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.labeled.icon.menu>.item>.icon:not(.dropdown){height:1em;display:block;font-size:1.71428571em!important;margin:0 auto .5rem!important}.ui.fluid.labeled.icon.menu>.item{min-width:0}@media only screen and (max-width:767.98px){.ui.stackable.menu{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.stackable.menu .item{width:100%!important}.ui.stackable.menu .item:before{position:absolute;content:'';top:auto;bottom:0;left:0;width:100%;height:1px;background:rgba(34,36,38,.1)}.ui.stackable.menu .left.item,.ui.stackable.menu .left.menu{margin-right:0!important}.ui.stackable.menu .right.item,.ui.stackable.menu .right.menu{margin-left:0!important}.ui.stackable.menu .center.item,.ui.stackable.menu .center.menu{margin-left:0!important;margin-right:0!important}.ui.stackable.menu .center.menu,.ui.stackable.menu .left.menu,.ui.stackable.menu .right.menu{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.ui.ui.menu .primary.active.item,.ui.ui.primary.menu .active.item,.ui.ui.primary.menu .active.item:hover{color:#2185d0}.ui.ui.menu .red.active.item,.ui.ui.red.menu .active.item,.ui.ui.red.menu .active.item:hover{color:#db2828}.ui.ui.menu .orange.active.item,.ui.ui.orange.menu .active.item,.ui.ui.orange.menu .active.item:hover{color:#f2711c}.ui.ui.menu .yellow.active.item,.ui.ui.yellow.menu .active.item,.ui.ui.yellow.menu .active.item:hover{color:#fbbd08}.ui.ui.menu .olive.active.item,.ui.ui.olive.menu .active.item,.ui.ui.olive.menu .active.item:hover{color:#b5cc18}.ui.ui.green.menu .active.item,.ui.ui.green.menu .active.item:hover,.ui.ui.menu .green.active.item{color:#21ba45}.ui.ui.menu .teal.active.item,.ui.ui.teal.menu .active.item,.ui.ui.teal.menu .active.item:hover{color:#00b5ad}.ui.ui.blue.menu .active.item,.ui.ui.blue.menu .active.item:hover,.ui.ui.menu .blue.active.item{color:#2185d0}.ui.ui.menu .violet.active.item,.ui.ui.violet.menu .active.item,.ui.ui.violet.menu .active.item:hover{color:#6435c9}.ui.ui.menu .purple.active.item,.ui.ui.purple.menu .active.item,.ui.ui.purple.menu .active.item:hover{color:#a333c8}.ui.ui.menu .pink.active.item,.ui.ui.pink.menu .active.item,.ui.ui.pink.menu .active.item:hover{color:#e03997}.ui.ui.brown.menu .active.item,.ui.ui.brown.menu .active.item:hover,.ui.ui.menu .brown.active.item{color:#a5673f}.ui.ui.grey.menu .active.item,.ui.ui.grey.menu .active.item:hover,.ui.ui.menu .grey.active.item{color:#767676}.ui.ui.black.menu .active.item,.ui.ui.black.menu .active.item:hover,.ui.ui.menu .black.active.item{color:#1b1c1d}.ui.inverted.menu{border:0 solid transparent;background:#1b1c1d;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.menu .item,.ui.inverted.menu .item>a:not(.ui){background:0 0;color:rgba(255,255,255,.9)}.ui.inverted.menu .item.menu{background:0 0}.ui.inverted.menu .item:before{background:rgba(255,255,255,.08)}.ui.vertical.inverted.menu .item:before{background:rgba(255,255,255,.08)}.ui.vertical.inverted.menu .menu .item,.ui.vertical.inverted.menu .menu .item a:not(.ui){color:rgba(255,255,255,.5)}.ui.inverted.menu .header.item{margin:0;background:0 0;-webkit-box-shadow:none;box-shadow:none}.ui.ui.inverted.menu .item.disabled{color:rgba(225,225,225,.3)}.ui.inverted.menu .dropdown.item:hover,.ui.inverted.menu .link.item:hover,.ui.inverted.menu a.item:hover,.ui.link.inverted.menu .item:hover{background:rgba(255,255,255,.08);color:#fff}.ui.vertical.inverted.menu .item .menu .link.item:hover,.ui.vertical.inverted.menu .item .menu a.item:hover{background:0 0;color:#fff}.ui.inverted.menu .link.item:active,.ui.inverted.menu a.item:active{background:rgba(255,255,255,.08);color:#fff}.ui.inverted.menu .active.item{background:#3d3e3f;color:#fff!important}.ui.inverted.vertical.menu .item .menu .active.item{background:0 0;color:#fff}.ui.inverted.pointing.menu .active.item:after{background:#3d3e3f;margin:0!important;-webkit-box-shadow:none!important;box-shadow:none!important;border:none!important}.ui.inverted.menu .active.item:hover{background:#3d3e3f;color:#fff!important}.ui.inverted.pointing.menu .active.item:hover:after{background:#3d3e3f}.ui.floated.menu{float:left;margin:0 .5rem 0 0}.ui.floated.menu .item:last-child:before{display:none}.ui.right.floated.menu{float:right;margin:0 0 0 .5rem}.ui.ui.inverted.menu .primary.active.item,.ui.ui.inverted.primary.menu{background-color:#2185d0}.ui.inverted.primary.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.primary.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.primary.menu .active.item{background-color:#1678c2}.ui.ui.inverted.menu .red.active.item,.ui.ui.inverted.red.menu{background-color:#db2828}.ui.inverted.red.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.red.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.red.menu .active.item{background-color:#d01919}.ui.ui.inverted.menu .orange.active.item,.ui.ui.inverted.orange.menu{background-color:#f2711c}.ui.inverted.orange.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.orange.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.orange.menu .active.item{background-color:#f26202}.ui.ui.inverted.menu .yellow.active.item,.ui.ui.inverted.yellow.menu{background-color:#fbbd08}.ui.inverted.yellow.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.yellow.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.yellow.menu .active.item{background-color:#eaae00}.ui.ui.inverted.menu .olive.active.item,.ui.ui.inverted.olive.menu{background-color:#b5cc18}.ui.inverted.olive.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.olive.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.olive.menu .active.item{background-color:#a7bd0d}.ui.ui.inverted.green.menu,.ui.ui.inverted.menu .green.active.item{background-color:#21ba45}.ui.inverted.green.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.green.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.green.menu .active.item{background-color:#16ab39}.ui.ui.inverted.menu .teal.active.item,.ui.ui.inverted.teal.menu{background-color:#00b5ad}.ui.inverted.teal.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.teal.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.teal.menu .active.item{background-color:#009c95}.ui.ui.inverted.blue.menu,.ui.ui.inverted.menu .blue.active.item{background-color:#2185d0}.ui.inverted.blue.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.blue.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.blue.menu .active.item{background-color:#1678c2}.ui.ui.inverted.menu .violet.active.item,.ui.ui.inverted.violet.menu{background-color:#6435c9}.ui.inverted.violet.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.violet.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.violet.menu .active.item{background-color:#5829bb}.ui.ui.inverted.menu .purple.active.item,.ui.ui.inverted.purple.menu{background-color:#a333c8}.ui.inverted.purple.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.purple.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.purple.menu .active.item{background-color:#9627ba}.ui.ui.inverted.menu .pink.active.item,.ui.ui.inverted.pink.menu{background-color:#e03997}.ui.inverted.pink.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.pink.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.pink.menu .active.item{background-color:#e61a8d}.ui.ui.inverted.brown.menu,.ui.ui.inverted.menu .brown.active.item{background-color:#a5673f}.ui.inverted.brown.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.brown.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.brown.menu .active.item{background-color:#975b33}.ui.ui.inverted.grey.menu,.ui.ui.inverted.menu .grey.active.item{background-color:#767676}.ui.inverted.grey.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.grey.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.grey.menu .active.item{background-color:#838383}.ui.ui.inverted.black.menu,.ui.ui.inverted.menu .black.active.item{background-color:#1b1c1d}.ui.inverted.black.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.black.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.black.menu .active.item{background-color:#27292a}.ui.ui.ui.inverted.pointing.menu .active.item:after{background-color:inherit}.ui.fitted.menu .item,.ui.fitted.menu .item .menu .item,.ui.menu .fitted.item{padding:0}.ui.horizontally.fitted.menu .item,.ui.horizontally.fitted.menu .item .menu .item,.ui.menu .horizontally.fitted.item{padding-top:.92857143em;padding-bottom:.92857143em}.ui.menu .vertically.fitted.item,.ui.vertically.fitted.menu .item,.ui.vertically.fitted.menu .item .menu .item{padding-left:1.14285714em;padding-right:1.14285714em}.ui.borderless.menu .item .menu .item:before,.ui.borderless.menu .item:before,.ui.menu .borderless.item:before{background:0 0!important}.ui.compact.menu{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin:0;vertical-align:middle}.ui.compact.vertical.menu{display:-ms-inline-flexbox!important;display:inline-block}.ui.compact.menu:not(.secondary) .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.compact.menu .item:last-child:before{display:none}.ui.compact.vertical.menu{width:auto!important}.ui.compact.vertical.menu .item:last-child::before{display:block}.ui.menu.fluid,.ui.vertical.menu.fluid{width:100%!important}.ui.item.menu,.ui.item.menu .item{width:100%;padding-left:0!important;padding-right:0!important;margin-left:0!important;margin-right:0!important;text-align:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.attached.item.menu:not(.tabular){margin:0 -1px!important}.ui.item.menu .item:last-child:before{display:none}.ui.menu.two.item .item{width:50%}.ui.menu.three.item .item{width:33.333%}.ui.menu.four.item .item{width:25%}.ui.menu.five.item .item{width:20%}.ui.menu.six.item .item{width:16.666%}.ui.menu.seven.item .item{width:14.285%}.ui.menu.eight.item .item{width:12.5%}.ui.menu.nine.item .item{width:11.11%}.ui.menu.ten.item .item{width:10%}.ui.menu.eleven.item .item{width:9.09%}.ui.menu.twelve.item .item{width:8.333%}.ui.menu.fixed{position:fixed;z-index:101;margin:0;width:100%}.ui.menu.fixed,.ui.menu.fixed .item:first-child,.ui.menu.fixed .item:last-child{border-radius:0!important}.ui.fixed.menu,.ui[class*="top fixed"].menu{top:0;left:0;right:auto;bottom:auto}.ui[class*="top fixed"].menu{border-top:none;border-left:none;border-right:none}.ui[class*="right fixed"].menu{border-top:none;border-bottom:none;border-right:none;top:0;right:0;left:auto;bottom:auto;width:auto;height:100%}.ui[class*="bottom fixed"].menu{border-bottom:none;border-left:none;border-right:none;bottom:0;left:0;top:auto;right:auto}.ui[class*="left fixed"].menu{border-top:none;border-bottom:none;border-left:none;top:0;left:0;right:auto;bottom:auto;width:auto;height:100%}.ui.fixed.menu+.ui.grid{padding-top:2.75rem}.ui.pointing.menu .item:after{visibility:hidden;position:absolute;content:'';top:100%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);background:0 0;margin:.5px 0 0;width:.57142857em;height:.57142857em;border:none;border-bottom:1px solid #d4d4d5;border-right:1px solid #d4d4d5;z-index:2;-webkit-transition:background .1s ease;transition:background .1s ease}.ui.vertical.pointing.menu .item:after{position:absolute;top:50%;right:0;bottom:auto;left:auto;-webkit-transform:translateX(50%) translateY(-50%) rotate(45deg);transform:translateX(50%) translateY(-50%) rotate(45deg);margin:0 -.5px 0 0;border:none;border-top:1px solid #d4d4d5;border-right:1px solid #d4d4d5}.ui.pointing.menu .ui.dropdown .menu .item:after,.ui.vertical.pointing.menu .ui.dropdown .menu .item:after{display:none}.ui.pointing.menu .active.item:after{visibility:visible}.ui.pointing.menu .active.dropdown.item:after{visibility:hidden}.ui.pointing.menu .active.item .menu .active.item:after,.ui.pointing.menu .dropdown.active.item:after{display:none}.ui.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.pointing.menu .active.item:after{background-color:#f2f2f2}.ui.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .active.item:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .menu .active.item:after{background-color:#fff}.ui.inverted.pointing.menu .primary.active.item:after{background-color:#2185d0}.ui.inverted.pointing.menu .secondary.active.item:after{background-color:#1b1c1d}.ui.inverted.pointing.menu .red.active.item:after{background-color:#db2828}.ui.inverted.pointing.menu .orange.active.item:after{background-color:#f2711c}.ui.inverted.pointing.menu .yellow.active.item:after{background-color:#fbbd08}.ui.inverted.pointing.menu .olive.active.item:after{background-color:#b5cc18}.ui.inverted.pointing.menu .green.active.item:after{background-color:#21ba45}.ui.inverted.pointing.menu .teal.active.item:after{background-color:#00b5ad}.ui.inverted.pointing.menu .blue.active.item:after{background-color:#2185d0}.ui.inverted.pointing.menu .violet.active.item:after{background-color:#6435c9}.ui.inverted.pointing.menu .purple.active.item:after{background-color:#a333c8}.ui.inverted.pointing.menu .pink.active.item:after{background-color:#e03997}.ui.inverted.pointing.menu .brown.active.item:after{background-color:#a5673f}.ui.inverted.pointing.menu .grey.active.item:after{background-color:#767676}.ui.inverted.pointing.menu .black.active.item:after{background-color:#1b1c1d}.ui.attached.menu{top:0;bottom:0;border-radius:0;margin:0 -1px;width:calc(100% + 2px);max-width:calc(100% + 2px);-webkit-box-shadow:none;box-shadow:none}.ui.attached+.ui.attached.menu:not(.top){border-top:none}.ui[class*="top attached"].menu{bottom:0;margin-bottom:0;top:0;margin-top:1rem;border-radius:.28571429rem .28571429rem 0 0}.ui.menu[class*="top attached"]:first-child{margin-top:0}.ui[class*="bottom attached"].menu{bottom:0;margin-top:0;top:0;margin-bottom:1rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;border-radius:0 0 .28571429rem .28571429rem}.ui[class*="bottom attached"].menu:last-child{margin-bottom:0}.ui.top.attached.menu>.item:first-child{border-radius:.28571429rem 0 0 0}.ui.bottom.attached.menu>.item:first-child{border-radius:0 0 0 .28571429rem}.ui.attached.menu:not(.tabular){border:1px solid #d4d4d5}.ui.attached.inverted.menu{border:none}.ui.attached.tabular.menu{margin-left:0;margin-right:0;width:100%}.ui.menu{font-size:1rem}.ui.vertical.menu{width:15rem}.ui.mini.menu,.ui.mini.menu .dropdown,.ui.mini.menu .dropdown .menu>.item{font-size:.78571429rem}.ui.mini.vertical.menu:not(.icon){width:9rem}.ui.tiny.menu,.ui.tiny.menu .dropdown,.ui.tiny.menu .dropdown .menu>.item{font-size:.85714286rem}.ui.tiny.vertical.menu:not(.icon){width:11rem}.ui.small.menu,.ui.small.menu .dropdown,.ui.small.menu .dropdown .menu>.item{font-size:.92857143rem}.ui.small.vertical.menu:not(.icon){width:13rem}.ui.large.menu,.ui.large.menu .dropdown,.ui.large.menu .dropdown .menu>.item{font-size:1.07142857rem}.ui.large.vertical.menu:not(.icon){width:18rem}.ui.big.menu,.ui.big.menu .dropdown,.ui.big.menu .dropdown .menu>.item{font-size:1.14285714rem}.ui.big.vertical.menu:not(.icon){width:20rem}.ui.huge.menu,.ui.huge.menu .dropdown,.ui.huge.menu .dropdown .menu>.item{font-size:1.21428571rem}.ui.huge.vertical.menu:not(.icon){width:22rem}.ui.massive.menu,.ui.massive.menu .dropdown,.ui.massive.menu .dropdown .menu>.item{font-size:1.28571429rem}.ui.massive.vertical.menu:not(.icon){width:25rem}.ui.menu .ui.inverted.inverted.dropdown.item .menu{background:#1b1c1d;-webkit-box-shadow:none;box-shadow:none}.ui.menu .ui.inverted.dropdown .menu>.item{color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.active.item{background:0 0!important;color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.item:hover{background:rgba(255,255,255,.08)!important;color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.selected.item{background:rgba(255,255,255,.15)!important;color:rgba(255,255,255,.8)!important}.ui.vertical.menu .inverted.dropdown.item .menu{-webkit-box-shadow:none;box-shadow:none}/*! + * # Fomantic-UI - Message + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.message{position:relative;min-height:1em;margin:1em 0;background:#f8f8f9;padding:1em 1.5em;line-height:1.4285em;color:rgba(0,0,0,.87);-webkit-transition:opacity .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,color .1s ease,background .1s ease,box-shadow .1s ease;transition:opacity .1s ease,color .1s ease,background .1s ease,box-shadow .1s ease,-webkit-box-shadow .1s ease;border-radius:.28571429rem;-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.22) inset,0 0 0 0 transparent;box-shadow:0 0 0 1px rgba(34,36,38,.22) inset,0 0 0 0 transparent}.ui.message:first-child{margin-top:0}.ui.message:last-child{margin-bottom:0}.ui.message .header{display:block;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;margin:-.14285714em 0 0 0}.ui.message .header:not(.ui){font-size:1.14285714em}.ui.message p{opacity:.85;margin:.75em 0}.ui.message p:first-child{margin-top:0}.ui.message p:last-child{margin-bottom:0}.ui.message .header+p{margin-top:.25em}.ui.message .list:not(.ui){text-align:left;padding:0;opacity:.85;list-style-position:inside;margin:.5em 0 0}.ui.message .list:not(.ui):first-child{margin-top:0}.ui.message .list:not(.ui):last-child{margin-bottom:0}.ui.message .list:not(.ui) li{position:relative;list-style-type:none;margin:0 0 .3em 1em;padding:0}.ui.message .list:not(.ui) li:before{position:absolute;content:'•';left:-1em;height:100%;vertical-align:baseline}.ui.message .list:not(.ui) li:last-child{margin-bottom:0}.ui.message>.icon{margin-right:.6em}.ui.message>.close.icon{cursor:pointer;position:absolute;margin:0;top:.78575em;right:.5em;opacity:.7;-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.message>.close.icon:hover{opacity:1}.ui.message>:first-child{margin-top:0}.ui.message>:last-child{margin-bottom:0}.ui.dropdown .menu>.message{margin:0 -1px}.ui.visible.visible.visible.visible.message{display:block}.ui.icon.visible.visible.visible.visible.message{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.hidden.hidden.hidden.hidden.message{display:none}.ui.compact.message{display:inline-block}.ui.compact.icon.message{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;width:auto}.ui.attached.message{margin-bottom:-1px;border-radius:.28571429rem .28571429rem 0 0;-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset;margin-left:-1px;margin-right:-1px}.ui.attached+.ui.attached.message:not(.top):not(.bottom){margin-top:-1px;border-radius:0}.ui.bottom.attached.message{margin-top:-1px;border-radius:0 0 .28571429rem .28571429rem;-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.15) inset,0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px rgba(34,36,38,.15) inset,0 1px 2px 0 rgba(34,36,38,.15)}.ui.bottom.attached.message:not(:last-child){margin-bottom:1em}.ui.attached.icon.message{width:auto}.ui.icon.message{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.icon.message>.icon:not(.close){display:block;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;line-height:1;vertical-align:middle;font-size:3em;opacity:.8}.ui.icon.message>.content{display:block;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;vertical-align:middle}.ui.icon.message .icon:not(.close)+.content{padding-left:0}.ui.icon.message .circular.icon{width:1em}.ui.floating.message{-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.22) inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px rgba(34,36,38,.22) inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.positive.message{background-color:#fcfff5;color:#2c662d}.ui.attached.positive.message,.ui.positive.message{-webkit-box-shadow:0 0 0 1px #a3c293 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #a3c293 inset,0 0 0 0 transparent}.ui.floating.positive.message{-webkit-box-shadow:0 0 0 1px #a3c293 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #a3c293 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.positive.message .header{color:#1a531b}.ui.negative.message{background-color:#fff6f6;color:#9f3a38}.ui.attached.negative.message,.ui.negative.message{-webkit-box-shadow:0 0 0 1px #e0b4b4 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #e0b4b4 inset,0 0 0 0 transparent}.ui.floating.negative.message{-webkit-box-shadow:0 0 0 1px #e0b4b4 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #e0b4b4 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.negative.message .header{color:#912d2b}.ui.info.message{background-color:#f8ffff;color:#276f86}.ui.attached.info.message,.ui.info.message{-webkit-box-shadow:0 0 0 1px #a9d5de inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #a9d5de inset,0 0 0 0 transparent}.ui.floating.info.message{-webkit-box-shadow:0 0 0 1px #a9d5de inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #a9d5de inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.info.message .header{color:#0e566c}.ui.warning.message{background-color:#fffaf3;color:#573a08}.ui.attached.warning.message,.ui.warning.message{-webkit-box-shadow:0 0 0 1px #c9ba9b inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #c9ba9b inset,0 0 0 0 transparent}.ui.floating.warning.message{-webkit-box-shadow:0 0 0 1px #c9ba9b inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #c9ba9b inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.warning.message .header{color:#794b02}.ui.error.message{background-color:#fff6f6;color:#9f3a38}.ui.attached.error.message,.ui.error.message{-webkit-box-shadow:0 0 0 1px #e0b4b4 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #e0b4b4 inset,0 0 0 0 transparent}.ui.floating.error.message{-webkit-box-shadow:0 0 0 1px #e0b4b4 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #e0b4b4 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.error.message .header{color:#912d2b}.ui.success.message{background-color:#fcfff5;color:#2c662d}.ui.attached.success.message,.ui.success.message{-webkit-box-shadow:0 0 0 1px #a3c293 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #a3c293 inset,0 0 0 0 transparent}.ui.floating.success.message{-webkit-box-shadow:0 0 0 1px #a3c293 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #a3c293 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.success.message .header{color:#1a531b}.ui.primary.message{background-color:#dff0ff;color:rgba(255,255,255,.9)}.ui.attached.primary.message,.ui.primary.message{-webkit-box-shadow:0 0 0 1px #2185d0 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #2185d0 inset,0 0 0 0 transparent}.ui.floating.primary.message{-webkit-box-shadow:0 0 0 1px #2185d0 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #2185d0 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.primary.message .header{color:rgba(242,242,242,.9)}.ui.secondary.message{background-color:#f4f4f4;color:rgba(255,255,255,.9)}.ui.attached.secondary.message,.ui.secondary.message{-webkit-box-shadow:0 0 0 1px #1b1c1d inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #1b1c1d inset,0 0 0 0 transparent}.ui.floating.secondary.message{-webkit-box-shadow:0 0 0 1px #1b1c1d inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #1b1c1d inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.secondary.message .header{color:rgba(242,242,242,.9)}.ui.red.message{background-color:#ffe8e6;color:#db2828}.ui.attached.red.message,.ui.red.message{-webkit-box-shadow:0 0 0 1px #db2828 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #db2828 inset,0 0 0 0 transparent}.ui.floating.red.message{-webkit-box-shadow:0 0 0 1px #db2828 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #db2828 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.red.message .header{color:#c82121}.ui.orange.message{background-color:#ffedde;color:#f2711c}.ui.attached.orange.message,.ui.orange.message{-webkit-box-shadow:0 0 0 1px #f2711c inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #f2711c inset,0 0 0 0 transparent}.ui.floating.orange.message{-webkit-box-shadow:0 0 0 1px #f2711c inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #f2711c inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.orange.message .header{color:#e7640d}.ui.yellow.message{background-color:#fff8db;color:#b58105}.ui.attached.yellow.message,.ui.yellow.message{-webkit-box-shadow:0 0 0 1px #b58105 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #b58105 inset,0 0 0 0 transparent}.ui.floating.yellow.message{-webkit-box-shadow:0 0 0 1px #b58105 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #b58105 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.yellow.message .header{color:#9c6f04}.ui.olive.message{background-color:#fbfdef;color:#8abc1e}.ui.attached.olive.message,.ui.olive.message{-webkit-box-shadow:0 0 0 1px #8abc1e inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #8abc1e inset,0 0 0 0 transparent}.ui.floating.olive.message{-webkit-box-shadow:0 0 0 1px #8abc1e inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #8abc1e inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.olive.message .header{color:#7aa61a}.ui.green.message{background-color:#e5f9e7;color:#1ebc30}.ui.attached.green.message,.ui.green.message{-webkit-box-shadow:0 0 0 1px #1ebc30 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #1ebc30 inset,0 0 0 0 transparent}.ui.floating.green.message{-webkit-box-shadow:0 0 0 1px #1ebc30 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #1ebc30 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.green.message .header{color:#1aa62a}.ui.teal.message{background-color:#e1f7f7;color:#10a3a3}.ui.attached.teal.message,.ui.teal.message{-webkit-box-shadow:0 0 0 1px #10a3a3 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #10a3a3 inset,0 0 0 0 transparent}.ui.floating.teal.message{-webkit-box-shadow:0 0 0 1px #10a3a3 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #10a3a3 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.teal.message .header{color:#0e8c8c}.ui.blue.message{background-color:#dff0ff;color:#2185d0}.ui.attached.blue.message,.ui.blue.message{-webkit-box-shadow:0 0 0 1px #2185d0 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #2185d0 inset,0 0 0 0 transparent}.ui.floating.blue.message{-webkit-box-shadow:0 0 0 1px #2185d0 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #2185d0 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.blue.message .header{color:#1e77ba}.ui.violet.message{background-color:#eae7ff;color:#6435c9}.ui.attached.violet.message,.ui.violet.message{-webkit-box-shadow:0 0 0 1px #6435c9 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #6435c9 inset,0 0 0 0 transparent}.ui.floating.violet.message{-webkit-box-shadow:0 0 0 1px #6435c9 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #6435c9 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.violet.message .header{color:#5a30b5}.ui.purple.message{background-color:#f6e7ff;color:#a333c8}.ui.attached.purple.message,.ui.purple.message{-webkit-box-shadow:0 0 0 1px #a333c8 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #a333c8 inset,0 0 0 0 transparent}.ui.floating.purple.message{-webkit-box-shadow:0 0 0 1px #a333c8 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #a333c8 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.purple.message .header{color:#922eb4}.ui.pink.message{background-color:#ffe3fb;color:#e03997}.ui.attached.pink.message,.ui.pink.message{-webkit-box-shadow:0 0 0 1px #e03997 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #e03997 inset,0 0 0 0 transparent}.ui.floating.pink.message{-webkit-box-shadow:0 0 0 1px #e03997 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #e03997 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.pink.message .header{color:#dd238b}.ui.brown.message{background-color:#f1e2d3;color:#a5673f}.ui.attached.brown.message,.ui.brown.message{-webkit-box-shadow:0 0 0 1px #a5673f inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #a5673f inset,0 0 0 0 transparent}.ui.floating.brown.message{-webkit-box-shadow:0 0 0 1px #a5673f inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #a5673f inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.brown.message .header{color:#935b38}.ui.grey.message{background-color:#f4f4f4;color:#767676}.ui.attached.grey.message,.ui.grey.message{-webkit-box-shadow:0 0 0 1px #767676 inset,0 0 0 0 transparent;box-shadow:0 0 0 1px #767676 inset,0 0 0 0 transparent}.ui.floating.grey.message{-webkit-box-shadow:0 0 0 1px #767676 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #767676 inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.grey.message .header{color:#696969}.ui.black.message{background-color:#1b1c1d;color:rgba(255,255,255,.9)}.ui.black.message .header{color:rgba(255,255,255,.9)}.ui.inverted.message{background-color:#1b1c1d;color:rgba(255,255,255,.9)}.ui.message{font-size:1em}.ui.mini.message{font-size:.78571429em}.ui.tiny.message{font-size:.85714286em}.ui.small.message{font-size:.92857143em}.ui.large.message{font-size:1.14285714em}.ui.big.message{font-size:1.28571429em}.ui.huge.message{font-size:1.42857143em}.ui.massive.message{font-size:1.71428571em}/*! + * # Fomantic-UI - Table + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.table{width:100%;background:#fff;margin:1em 0;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:none;box-shadow:none;border-radius:.28571429rem;text-align:left;vertical-align:middle;color:rgba(0,0,0,.87);border-collapse:separate;border-spacing:0}.ui.table:first-child{margin-top:0}.ui.table:last-child{margin-bottom:0}.ui.table>tbody,.ui.table>thead{text-align:inherit;vertical-align:inherit}.ui.table td,.ui.table th{-webkit-transition:background .1s ease,color .1s ease;transition:background .1s ease,color .1s ease}.ui.table>thead{-webkit-box-shadow:none;box-shadow:none}.ui.table>thead>tr>th{cursor:auto;background:#f9fafb;text-align:inherit;color:rgba(0,0,0,.87);padding:.92857143em .78571429em;vertical-align:inherit;font-style:none;font-weight:700;text-transform:none;border-bottom:1px solid rgba(34,36,38,.1);border-left:none}.ui.table>thead>tr>th:first-child{border-left:none}.ui.table>thead>tr:first-child>th:first-child{border-radius:.28571429rem 0 0 0}.ui.table>thead>tr:first-child>th:last-child{border-radius:0 .28571429rem 0 0}.ui.table>thead>tr:first-child>th:only-child{border-radius:.28571429rem .28571429rem 0 0}.ui.table>tfoot{-webkit-box-shadow:none;box-shadow:none}.ui.table>tfoot>tr>td,.ui.table>tfoot>tr>th{cursor:auto;border-top:1px solid rgba(34,36,38,.15);background:#f9fafb;text-align:inherit;color:rgba(0,0,0,.87);padding:.78571429em .78571429em;vertical-align:middle;font-style:normal;font-weight:400;text-transform:none}.ui.table>tfoot>tr>td:first-child,.ui.table>tfoot>tr>th:first-child{border-left:none}.ui.table>tfoot>tr:first-child>td:first-child,.ui.table>tfoot>tr:first-child>th:first-child{border-radius:0 0 0 .28571429rem}.ui.table>tfoot>tr:first-child>td:last-child,.ui.table>tfoot>tr:first-child>th:last-child{border-radius:0 0 .28571429rem 0}.ui.table>tfoot>tr:first-child>td:only-child,.ui.table>tfoot>tr:first-child>th:only-child{border-radius:0 0 .28571429rem .28571429rem}.ui.table>tbody>tr>td,.ui.table>tr>td{border-top:1px solid rgba(34,36,38,.1)}.ui.table>tbody>tr:first-child>td,.ui.table>tr:first-child>td{border-top:none}.ui.table>tbody+tbody tr:first-child>td{border-top:1px solid rgba(34,36,38,.1)}.ui.table>tbody>tr>td,.ui.table>tr>td{padding:.78571429em .78571429em;text-align:inherit}.ui.table>.icon{vertical-align:baseline}.ui.table>.icon:only-child{margin:0}.ui.table.segment{padding:0}.ui.table.segment:after{display:none}.ui.table.segment.stacked:after{display:block}@media only screen and (max-width:767.98px){.ui.table:not(.unstackable){width:100%;padding:0}.ui.table:not(.unstackable)>tbody,.ui.table:not(.unstackable)>tbody>tr,.ui.table:not(.unstackable)>tbody>tr>td,.ui.table:not(.unstackable)>tbody>tr>th,.ui.table:not(.unstackable)>tfoot,.ui.table:not(.unstackable)>tfoot>tr,.ui.table:not(.unstackable)>tfoot>tr>td,.ui.table:not(.unstackable)>tfoot>tr>th,.ui.table:not(.unstackable)>thead,.ui.table:not(.unstackable)>thead>tr,.ui.table:not(.unstackable)>thead>tr>th,.ui.table:not(.unstackable)>tr,.ui.table:not(.unstackable)>tr>td,.ui.table:not(.unstackable)>tr>th{display:block!important;width:auto!important}.ui.table:not(.unstackable)>thead{display:block}.ui.table:not(.unstackable)>tfoot{display:block}.ui.ui.ui.ui.table:not(.unstackable)>tbody>tr,.ui.ui.ui.ui.table:not(.unstackable)>tfoot>tr,.ui.ui.ui.ui.table:not(.unstackable)>thead>tr,.ui.ui.ui.ui.table:not(.unstackable)>tr{padding-top:1em;padding-bottom:1em;-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset}.ui.ui.ui.ui.table:not(.unstackable)>tbody>tr>td,.ui.ui.ui.ui.table:not(.unstackable)>tbody>tr>th,.ui.ui.ui.ui.table:not(.unstackable)>tfoot>tr>td,.ui.ui.ui.ui.table:not(.unstackable)>tfoot>tr>th,.ui.ui.ui.ui.table:not(.unstackable)>thead>tr>th,.ui.ui.ui.ui.table:not(.unstackable)>tr>td,.ui.ui.ui.ui.table:not(.unstackable)>tr>th{background:0 0;border:none;padding:.25em .75em;-webkit-box-shadow:none;box-shadow:none}.ui.table:not(.unstackable)>tbody>tr>td:first-child,.ui.table:not(.unstackable)>tbody>tr>th:first-child,.ui.table:not(.unstackable)>tfoot>tr>td:first-child,.ui.table:not(.unstackable)>tfoot>tr>th:first-child,.ui.table:not(.unstackable)>thead>tr>th:first-child,.ui.table:not(.unstackable)>tr>td:first-child,.ui.table:not(.unstackable)>tr>th:first-child{font-weight:700}.ui.definition.table:not(.unstackable)>thead>tr>th:first-child{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.primary.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2185d0 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2185d0 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.primary.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2185d0 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2185d0 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.primary.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #54c8ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #54c8ff inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.primary.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #54c8ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #54c8ff inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.secondary.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #1b1c1d inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #1b1c1d inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.secondary.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #1b1c1d inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #1b1c1d inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.secondary.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #545454 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #545454 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.secondary.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #545454 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #545454 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.red.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #db2828 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #db2828 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.red.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #db2828 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #db2828 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.red.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff695e inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff695e inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.red.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff695e inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff695e inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.orange.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #f2711c inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #f2711c inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.orange.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #f2711c inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #f2711c inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.orange.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff851b inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff851b inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.orange.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff851b inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff851b inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.yellow.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #fbbd08 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #fbbd08 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.yellow.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #fbbd08 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #fbbd08 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.yellow.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ffe21f inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ffe21f inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.yellow.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ffe21f inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ffe21f inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.olive.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #b5cc18 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #b5cc18 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.olive.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #b5cc18 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #b5cc18 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.olive.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #d9e778 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #d9e778 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.olive.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #d9e778 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #d9e778 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.green.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #21ba45 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #21ba45 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.green.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #21ba45 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #21ba45 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.green.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2ecc40 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2ecc40 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.green.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2ecc40 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2ecc40 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.teal.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #00b5ad inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #00b5ad inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.teal.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #00b5ad inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #00b5ad inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.teal.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #6dffff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #6dffff inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.teal.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #6dffff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #6dffff inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.blue.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2185d0 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2185d0 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.blue.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2185d0 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2185d0 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.blue.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #54c8ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #54c8ff inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.blue.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #54c8ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #54c8ff inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.violet.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #6435c9 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #6435c9 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.violet.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #6435c9 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #6435c9 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.violet.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a291fb inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a291fb inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.violet.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a291fb inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a291fb inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.purple.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a333c8 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a333c8 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.purple.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a333c8 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a333c8 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.purple.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #dc73ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #dc73ff inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.purple.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #dc73ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #dc73ff inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.pink.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #e03997 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #e03997 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.pink.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #e03997 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #e03997 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.pink.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff8edf inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff8edf inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.pink.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff8edf inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff8edf inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.brown.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a5673f inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a5673f inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.brown.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a5673f inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a5673f inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.brown.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #d67c1c inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #d67c1c inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.brown.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #d67c1c inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #d67c1c inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.grey.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #767676 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #767676 inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.grey.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #767676 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #767676 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.grey.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #dcddde inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #dcddde inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.grey.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #dcddde inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #dcddde inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.black.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #1b1c1d inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #1b1c1d inset}.ui.ui.ui.ui.table:not(.unstackable) tr.marked.black.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #1b1c1d inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #1b1c1d inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.black.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #545454 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #545454 inset}.ui.ui.ui.ui.inverted.table:not(.unstackable) tr.marked.black.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #545454 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #545454 inset}}.ui.table .collapsing .image,.ui.table .collapsing .image img{max-width:none}.ui.structured.table{border-collapse:collapse}.ui.structured.table>thead>tr>th{border-left:none;border-right:none}.ui.structured.sortable.table>thead>tr>th{border-left:1px solid rgba(34,36,38,.15);border-right:1px solid rgba(34,36,38,.15)}.ui.structured.basic.table>tbody>tr>th,.ui.structured.basic.table>tfoot>tr>th,.ui.structured.basic.table>thead>tr>th,.ui.structured.basic.table>tr>th{border-left:none;border-right:none}.ui.structured.celled.table>tbody>tr>td,.ui.structured.celled.table>tbody>tr>th,.ui.structured.celled.table>tfoot>tr>td,.ui.structured.celled.table>tfoot>tr>th,.ui.structured.celled.table>thead>tr>th,.ui.structured.celled.table>tr>td,.ui.structured.celled.table>tr>th{border-left:1px solid rgba(34,36,38,.1);border-right:1px solid rgba(34,36,38,.1)}.ui.definition.table>thead:not(.full-width)>tr>th:first-child{pointer-events:none;background:#fff;font-weight:400;color:rgba(0,0,0,.4);-webkit-box-shadow:-.1em -.2em 0 .1em #fff;box-shadow:-.1em -.2em 0 .1em #fff;-moz-transform:scale(1)}.ui.definition.table>tfoot:not(.full-width)>tr>th:first-child{pointer-events:none;background:#fff;font-weight:400;color:rgba(0,0,0,.4);-webkit-box-shadow:-.1em .2em 0 .1em #fff;box-shadow:-.1em .2em 0 .1em #fff;-moz-transform:scale(1)}.ui.definition.table tr td.definition,.ui.definition.table>tbody>tr>td:first-child:not(.ignored),.ui.definition.table>tfoot>tr>td:first-child:not(.ignored),.ui.definition.table>tr>td:first-child:not(.ignored){background:rgba(0,0,0,.03);font-weight:700;color:rgba(0,0,0,.95);text-transform:'';-webkit-box-shadow:'';box-shadow:'';text-align:'';font-size:1em;padding-left:'';padding-right:''}.ui.definition.table>thead:not(.full-width)>tr>th:nth-child(2){border-left:1px solid rgba(34,36,38,.15)}.ui.definition.table>tfoot:not(.full-width)>tr>td:nth-child(2),.ui.definition.table>tfoot:not(.full-width)>tr>th:nth-child(2){border-left:1px solid rgba(34,36,38,.15)}.ui.definition.table>tbody>tr>td:nth-child(2),.ui.definition.table>tr>td:nth-child(2){border-left:1px solid rgba(34,36,38,.15)}.ui.ui.table td.positive,.ui.ui.ui.ui.table tr.positive{-webkit-box-shadow:0 0 0 #a3c293 inset;box-shadow:0 0 0 #a3c293 inset;background:#fcfff5;color:#2c662d}.ui.ui.table td.negative,.ui.ui.ui.ui.table tr.negative{-webkit-box-shadow:0 0 0 #e0b4b4 inset;box-shadow:0 0 0 #e0b4b4 inset;background:#fff6f6;color:#9f3a38}.ui.ui.table td.error,.ui.ui.ui.ui.table tr.error{-webkit-box-shadow:0 0 0 #e0b4b4 inset;box-shadow:0 0 0 #e0b4b4 inset;background:#fff6f6;color:#9f3a38}.ui.ui.table td.warning,.ui.ui.ui.ui.table tr.warning{-webkit-box-shadow:0 0 0 #c9ba9b inset;box-shadow:0 0 0 #c9ba9b inset;background:#fffaf3;color:#573a08}.ui.ui.table td.active,.ui.ui.ui.ui.table tr.active{-webkit-box-shadow:0 0 0 rgba(0,0,0,.87) inset;box-shadow:0 0 0 rgba(0,0,0,.87) inset;background:#e0e0e0;color:rgba(0,0,0,.87)}.ui.table tr td.disabled,.ui.table tr.disabled td,.ui.table tr.disabled:hover,.ui.table tr:hover td.disabled{pointer-events:none;color:rgba(40,40,40,.3)}@media only screen and (max-width:991.98px){.ui[class*="tablet stackable"].table,.ui[class*="tablet stackable"].table>tbody,.ui[class*="tablet stackable"].table>tbody>tr,.ui[class*="tablet stackable"].table>tbody>tr>td,.ui[class*="tablet stackable"].table>tbody>tr>th,.ui[class*="tablet stackable"].table>tfoot,.ui[class*="tablet stackable"].table>tfoot>tr,.ui[class*="tablet stackable"].table>tfoot>tr>td,.ui[class*="tablet stackable"].table>tfoot>tr>th,.ui[class*="tablet stackable"].table>thead,.ui[class*="tablet stackable"].table>thead>tr,.ui[class*="tablet stackable"].table>thead>tr>th,.ui[class*="tablet stackable"].table>tr,.ui[class*="tablet stackable"].table>tr>td,.ui[class*="tablet stackable"].table>tr>th{display:block!important;width:100%!important}.ui[class*="tablet stackable"].table{padding:0}.ui[class*="tablet stackable"].table>thead{display:block}.ui[class*="tablet stackable"].table>tfoot{display:block}.ui.ui.ui.ui[class*="tablet stackable"].table>tbody>tr,.ui.ui.ui.ui[class*="tablet stackable"].table>tfoot>tr,.ui.ui.ui.ui[class*="tablet stackable"].table>thead>tr,.ui.ui.ui.ui[class*="tablet stackable"].table>tr{padding-top:1em;padding-bottom:1em;-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset}.ui[class*="tablet stackable"].table>tbody>tr>td,.ui[class*="tablet stackable"].table>tbody>tr>th,.ui[class*="tablet stackable"].table>tfoot>tr>td,.ui[class*="tablet stackable"].table>tfoot>tr>th,.ui[class*="tablet stackable"].table>thead>tr>th,.ui[class*="tablet stackable"].table>tr>td,.ui[class*="tablet stackable"].table>tr>th{background:0 0;border:none!important;padding:.25em .75em;-webkit-box-shadow:none;box-shadow:none}.ui.definition[class*="tablet stackable"].table>thead>tr>th:first-child{-webkit-box-shadow:none!important;box-shadow:none!important}}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.primary.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2185d0 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2185d0 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.primary.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2185d0 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2185d0 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.primary.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #54c8ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #54c8ff inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.primary.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #54c8ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #54c8ff inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.secondary.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #1b1c1d inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #1b1c1d inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.secondary.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #1b1c1d inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #1b1c1d inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.secondary.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #545454 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #545454 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.secondary.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #545454 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #545454 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.red.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #db2828 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #db2828 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.red.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #db2828 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #db2828 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.red.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff695e inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff695e inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.red.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff695e inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff695e inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.orange.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #f2711c inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #f2711c inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.orange.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #f2711c inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #f2711c inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.orange.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff851b inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff851b inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.orange.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff851b inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff851b inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.yellow.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #fbbd08 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #fbbd08 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.yellow.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #fbbd08 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #fbbd08 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.yellow.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ffe21f inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ffe21f inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.yellow.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ffe21f inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ffe21f inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.olive.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #b5cc18 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #b5cc18 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.olive.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #b5cc18 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #b5cc18 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.olive.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #d9e778 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #d9e778 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.olive.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #d9e778 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #d9e778 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.green.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #21ba45 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #21ba45 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.green.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #21ba45 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #21ba45 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.green.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2ecc40 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2ecc40 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.green.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2ecc40 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2ecc40 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.teal.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #00b5ad inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #00b5ad inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.teal.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #00b5ad inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #00b5ad inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.teal.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #6dffff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #6dffff inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.teal.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #6dffff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #6dffff inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.blue.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2185d0 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #2185d0 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.blue.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2185d0 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #2185d0 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.blue.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #54c8ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #54c8ff inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.blue.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #54c8ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #54c8ff inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.violet.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #6435c9 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #6435c9 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.violet.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #6435c9 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #6435c9 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.violet.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a291fb inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a291fb inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.violet.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a291fb inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a291fb inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.purple.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a333c8 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a333c8 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.purple.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a333c8 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a333c8 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.purple.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #dc73ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #dc73ff inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.purple.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #dc73ff inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #dc73ff inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.pink.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #e03997 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #e03997 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.pink.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #e03997 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #e03997 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.pink.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff8edf inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #ff8edf inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.pink.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff8edf inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #ff8edf inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.brown.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a5673f inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #a5673f inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.brown.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a5673f inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #a5673f inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.brown.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #d67c1c inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #d67c1c inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.brown.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #d67c1c inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #d67c1c inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.grey.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #767676 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #767676 inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.grey.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #767676 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #767676 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.grey.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #dcddde inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #dcddde inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.grey.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #dcddde inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #dcddde inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.black.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #1b1c1d inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #1b1c1d inset}.ui.ui.ui.ui[class*="tablet stackable"].table tr.marked.black.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #1b1c1d inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #1b1c1d inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.black.left{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #545454 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,.2em 0 0 0 #545454 inset}.ui.ui.ui.ui[class*="tablet stackable"].inverted.table tr.marked.black.right{-webkit-box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #545454 inset;box-shadow:0 -1px 0 0 rgba(0,0,0,.1) inset,-.2em 0 0 0 #545454 inset}.ui.table [class*="left aligned"],.ui.table[class*="left aligned"]{text-align:left}.ui.table [class*="center aligned"],.ui.table[class*="center aligned"]{text-align:center}.ui.table [class*="right aligned"],.ui.table[class*="right aligned"]{text-align:right}.ui.table [class*="top aligned"],.ui.table[class*="top aligned"]{vertical-align:top}.ui.table [class*="middle aligned"],.ui.table[class*="middle aligned"]{vertical-align:middle}.ui.table [class*="bottom aligned"],.ui.table[class*="bottom aligned"]{vertical-align:bottom}.ui.table td.collapsing,.ui.table th.collapsing{width:1px;white-space:nowrap}.ui.fixed.table{table-layout:fixed}.ui.fixed.table td,.ui.fixed.table th{overflow:hidden;text-overflow:ellipsis}.ui.table tbody tr td.selectable:hover,.ui.ui.selectable.table>tbody>tr:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.inverted.table tbody tr td.selectable:hover,.ui.ui.selectable.inverted.table>tbody>tr:hover{background:rgba(255,255,255,.08);color:#fff}.ui.table tbody tr td.selectable{padding:0}.ui.table tbody tr td.selectable>a:not(.ui){display:block;color:inherit;padding:.78571429em .78571429em}.ui.selectable.table>tbody>tr,.ui.selectable.table>tr,.ui.table>tbody>tr>td.selectable,.ui.table>tr>td.selectable{cursor:pointer}.ui.selectable.table tr:hover td.error,.ui.table tr td.selectable.error:hover,.ui.ui.selectable.table tr.error:hover{background:#ffe7e7;color:#943634}.ui.selectable.table tr:hover td.warning,.ui.table tr td.selectable.warning:hover,.ui.ui.selectable.table tr.warning:hover{background:#fff4e4;color:#493107}.ui.selectable.table tr:hover td.active,.ui.table tr td.selectable.active:hover,.ui.ui.selectable.table tr.active:hover{background:#e0e0e0;color:rgba(0,0,0,.87)}.ui.selectable.table tr:hover td.positive,.ui.table tr td.selectable.positive:hover,.ui.ui.selectable.table tr.positive:hover{background:#f7ffe6;color:#275b28}.ui.selectable.table tr:hover td.negative,.ui.table tr td.selectable.negative:hover,.ui.ui.selectable.table tr.negative:hover{background:#ffe7e7;color:#943634}.ui.attached.table{top:0;bottom:0;border-radius:0;margin:0 -1px;width:calc(100% + 2px);max-width:calc(100% + 2px);-webkit-box-shadow:none;box-shadow:none;border:1px solid #d4d4d5}.ui.attached+.ui.attached.table:not(.top){border-top:none}.ui[class*="top attached"].table{bottom:0;margin-bottom:0;top:0;margin-top:1em;border-radius:.28571429rem .28571429rem 0 0}.ui.table[class*="top attached"]:first-child{margin-top:0}.ui[class*="bottom attached"].table{bottom:0;margin-top:0;top:0;margin-bottom:1em;-webkit-box-shadow:none,none;box-shadow:none,none;border-radius:0 0 .28571429rem .28571429rem}.ui[class*="bottom attached"].table:last-child{margin-bottom:0}.ui.striped.table>tbody>tr:nth-child(2n),.ui.striped.table>tr:nth-child(2n){background-color:rgba(0,0,50,.02)}.ui.inverted.striped.table>tbody>tr:nth-child(2n),.ui.inverted.striped.table>tr:nth-child(2n){background-color:rgba(255,255,255,.05)}.ui.striped.selectable.selectable.selectable.table tbody tr.active:hover{background:#efefef;color:rgba(0,0,0,.95)}.ui.table [class*="single line"],.ui.table[class*="single line"]{white-space:nowrap}.ui.primary.table{border-top:.2em solid #2185d0}.ui.inverted.primary.table{background-color:#2185d0;color:#fff}.ui.ui.table td.primary:not(.marked),.ui.ui.ui.ui.table tr.primary:not(.marked){background:#ddf4ff;color:rgba(255,255,255,.9)}.ui.selectable.table tr:hover td.primary:not(.marked),.ui.table tr td.selectable.primary:not(.marked):hover,.ui.ui.selectable.table tr.primary:not(.marked):hover{background:#d3f1ff;color:rgba(255,255,255,.9)}.ui.table td.marked.primary.left,.ui.table tr.marked.primary.left{-webkit-box-shadow:.2em 0 0 0 #2185d0 inset;box-shadow:.2em 0 0 0 #2185d0 inset}.ui.table td.marked.primary.right,.ui.table tr.marked.primary.right{-webkit-box-shadow:-.2em 0 0 0 #2185d0 inset;box-shadow:-.2em 0 0 0 #2185d0 inset}.ui.inverted.table td.marked.primary.left,.ui.inverted.table tr.marked.primary.left{-webkit-box-shadow:.2em 0 0 0 #54c8ff inset;box-shadow:.2em 0 0 0 #54c8ff inset}.ui.inverted.table td.marked.primary.right,.ui.inverted.table tr.marked.primary.right{-webkit-box-shadow:-.2em 0 0 0 #54c8ff inset;box-shadow:-.2em 0 0 0 #54c8ff inset}.ui.secondary.table{border-top:.2em solid #1b1c1d}.ui.inverted.secondary.table{background-color:#1b1c1d;color:#fff}.ui.ui.table td.secondary:not(.marked),.ui.ui.ui.ui.table tr.secondary:not(.marked){background:#ddd;color:rgba(255,255,255,.9)}.ui.selectable.table tr:hover td.secondary:not(.marked),.ui.table tr td.selectable.secondary:not(.marked):hover,.ui.ui.selectable.table tr.secondary:not(.marked):hover{background:#e2e2e2;color:rgba(255,255,255,.9)}.ui.table td.marked.secondary.left,.ui.table tr.marked.secondary.left{-webkit-box-shadow:.2em 0 0 0 #1b1c1d inset;box-shadow:.2em 0 0 0 #1b1c1d inset}.ui.table td.marked.secondary.right,.ui.table tr.marked.secondary.right{-webkit-box-shadow:-.2em 0 0 0 #1b1c1d inset;box-shadow:-.2em 0 0 0 #1b1c1d inset}.ui.inverted.table td.marked.secondary.left,.ui.inverted.table tr.marked.secondary.left{-webkit-box-shadow:.2em 0 0 0 #545454 inset;box-shadow:.2em 0 0 0 #545454 inset}.ui.inverted.table td.marked.secondary.right,.ui.inverted.table tr.marked.secondary.right{-webkit-box-shadow:-.2em 0 0 0 #545454 inset;box-shadow:-.2em 0 0 0 #545454 inset}.ui.red.table{border-top:.2em solid #db2828}.ui.inverted.red.table{background-color:#db2828;color:#fff}.ui.ui.table td.red:not(.marked),.ui.ui.ui.ui.table tr.red:not(.marked){background:#ffe1df;color:#db2828}.ui.selectable.table tr:hover td.red:not(.marked),.ui.table tr td.selectable.red:not(.marked):hover,.ui.ui.selectable.table tr.red:not(.marked):hover{background:#ffd7d5;color:#db2828}.ui.table td.marked.red.left,.ui.table tr.marked.red.left{-webkit-box-shadow:.2em 0 0 0 #db2828 inset;box-shadow:.2em 0 0 0 #db2828 inset}.ui.table td.marked.red.right,.ui.table tr.marked.red.right{-webkit-box-shadow:-.2em 0 0 0 #db2828 inset;box-shadow:-.2em 0 0 0 #db2828 inset}.ui.inverted.table td.marked.red.left,.ui.inverted.table tr.marked.red.left{-webkit-box-shadow:.2em 0 0 0 #ff695e inset;box-shadow:.2em 0 0 0 #ff695e inset}.ui.inverted.table td.marked.red.right,.ui.inverted.table tr.marked.red.right{-webkit-box-shadow:-.2em 0 0 0 #ff695e inset;box-shadow:-.2em 0 0 0 #ff695e inset}.ui.orange.table{border-top:.2em solid #f2711c}.ui.inverted.orange.table{background-color:#f2711c;color:#fff}.ui.ui.table td.orange:not(.marked),.ui.ui.ui.ui.table tr.orange:not(.marked){background:#ffe7d1;color:#f2711c}.ui.selectable.table tr:hover td.orange:not(.marked),.ui.table tr td.selectable.orange:not(.marked):hover,.ui.ui.selectable.table tr.orange:not(.marked):hover{background:#fae1cc;color:#f2711c}.ui.table td.marked.orange.left,.ui.table tr.marked.orange.left{-webkit-box-shadow:.2em 0 0 0 #f2711c inset;box-shadow:.2em 0 0 0 #f2711c inset}.ui.table td.marked.orange.right,.ui.table tr.marked.orange.right{-webkit-box-shadow:-.2em 0 0 0 #f2711c inset;box-shadow:-.2em 0 0 0 #f2711c inset}.ui.inverted.table td.marked.orange.left,.ui.inverted.table tr.marked.orange.left{-webkit-box-shadow:.2em 0 0 0 #ff851b inset;box-shadow:.2em 0 0 0 #ff851b inset}.ui.inverted.table td.marked.orange.right,.ui.inverted.table tr.marked.orange.right{-webkit-box-shadow:-.2em 0 0 0 #ff851b inset;box-shadow:-.2em 0 0 0 #ff851b inset}.ui.yellow.table{border-top:.2em solid #fbbd08}.ui.inverted.yellow.table{background-color:#fbbd08;color:#fff}.ui.ui.table td.yellow:not(.marked),.ui.ui.ui.ui.table tr.yellow:not(.marked){background:#fff9d2;color:#b58105}.ui.selectable.table tr:hover td.yellow:not(.marked),.ui.table tr td.selectable.yellow:not(.marked):hover,.ui.ui.selectable.table tr.yellow:not(.marked):hover{background:#fbf5cc;color:#b58105}.ui.table td.marked.yellow.left,.ui.table tr.marked.yellow.left{-webkit-box-shadow:.2em 0 0 0 #fbbd08 inset;box-shadow:.2em 0 0 0 #fbbd08 inset}.ui.table td.marked.yellow.right,.ui.table tr.marked.yellow.right{-webkit-box-shadow:-.2em 0 0 0 #fbbd08 inset;box-shadow:-.2em 0 0 0 #fbbd08 inset}.ui.inverted.table td.marked.yellow.left,.ui.inverted.table tr.marked.yellow.left{-webkit-box-shadow:.2em 0 0 0 #ffe21f inset;box-shadow:.2em 0 0 0 #ffe21f inset}.ui.inverted.table td.marked.yellow.right,.ui.inverted.table tr.marked.yellow.right{-webkit-box-shadow:-.2em 0 0 0 #ffe21f inset;box-shadow:-.2em 0 0 0 #ffe21f inset}.ui.olive.table{border-top:.2em solid #b5cc18}.ui.inverted.olive.table{background-color:#b5cc18;color:#fff}.ui.ui.table td.olive:not(.marked),.ui.ui.ui.ui.table tr.olive:not(.marked){background:#f7fae4;color:#8abc1e}.ui.selectable.table tr:hover td.olive:not(.marked),.ui.table tr td.selectable.olive:not(.marked):hover,.ui.ui.selectable.table tr.olive:not(.marked):hover{background:#f6fada;color:#8abc1e}.ui.table td.marked.olive.left,.ui.table tr.marked.olive.left{-webkit-box-shadow:.2em 0 0 0 #b5cc18 inset;box-shadow:.2em 0 0 0 #b5cc18 inset}.ui.table td.marked.olive.right,.ui.table tr.marked.olive.right{-webkit-box-shadow:-.2em 0 0 0 #b5cc18 inset;box-shadow:-.2em 0 0 0 #b5cc18 inset}.ui.inverted.table td.marked.olive.left,.ui.inverted.table tr.marked.olive.left{-webkit-box-shadow:.2em 0 0 0 #d9e778 inset;box-shadow:.2em 0 0 0 #d9e778 inset}.ui.inverted.table td.marked.olive.right,.ui.inverted.table tr.marked.olive.right{-webkit-box-shadow:-.2em 0 0 0 #d9e778 inset;box-shadow:-.2em 0 0 0 #d9e778 inset}.ui.green.table{border-top:.2em solid #21ba45}.ui.inverted.green.table{background-color:#21ba45;color:#fff}.ui.ui.table td.green:not(.marked),.ui.ui.ui.ui.table tr.green:not(.marked){background:#d5f5d9;color:#1ebc30}.ui.selectable.table tr:hover td.green:not(.marked),.ui.table tr td.selectable.green:not(.marked):hover,.ui.ui.selectable.table tr.green:not(.marked):hover{background:#d2eed5;color:#1ebc30}.ui.table td.marked.green.left,.ui.table tr.marked.green.left{-webkit-box-shadow:.2em 0 0 0 #21ba45 inset;box-shadow:.2em 0 0 0 #21ba45 inset}.ui.table td.marked.green.right,.ui.table tr.marked.green.right{-webkit-box-shadow:-.2em 0 0 0 #21ba45 inset;box-shadow:-.2em 0 0 0 #21ba45 inset}.ui.inverted.table td.marked.green.left,.ui.inverted.table tr.marked.green.left{-webkit-box-shadow:.2em 0 0 0 #2ecc40 inset;box-shadow:.2em 0 0 0 #2ecc40 inset}.ui.inverted.table td.marked.green.right,.ui.inverted.table tr.marked.green.right{-webkit-box-shadow:-.2em 0 0 0 #2ecc40 inset;box-shadow:-.2em 0 0 0 #2ecc40 inset}.ui.teal.table{border-top:.2em solid #00b5ad}.ui.inverted.teal.table{background-color:#00b5ad;color:#fff}.ui.ui.table td.teal:not(.marked),.ui.ui.ui.ui.table tr.teal:not(.marked){background:#e2ffff;color:#10a3a3}.ui.selectable.table tr:hover td.teal:not(.marked),.ui.table tr td.selectable.teal:not(.marked):hover,.ui.ui.selectable.table tr.teal:not(.marked):hover{background:#d8ffff;color:#10a3a3}.ui.table td.marked.teal.left,.ui.table tr.marked.teal.left{-webkit-box-shadow:.2em 0 0 0 #00b5ad inset;box-shadow:.2em 0 0 0 #00b5ad inset}.ui.table td.marked.teal.right,.ui.table tr.marked.teal.right{-webkit-box-shadow:-.2em 0 0 0 #00b5ad inset;box-shadow:-.2em 0 0 0 #00b5ad inset}.ui.inverted.table td.marked.teal.left,.ui.inverted.table tr.marked.teal.left{-webkit-box-shadow:.2em 0 0 0 #6dffff inset;box-shadow:.2em 0 0 0 #6dffff inset}.ui.inverted.table td.marked.teal.right,.ui.inverted.table tr.marked.teal.right{-webkit-box-shadow:-.2em 0 0 0 #6dffff inset;box-shadow:-.2em 0 0 0 #6dffff inset}.ui.blue.table{border-top:.2em solid #2185d0}.ui.inverted.blue.table{background-color:#2185d0;color:#fff}.ui.ui.table td.blue:not(.marked),.ui.ui.ui.ui.table tr.blue:not(.marked){background:#ddf4ff;color:#2185d0}.ui.selectable.table tr:hover td.blue:not(.marked),.ui.table tr td.selectable.blue:not(.marked):hover,.ui.ui.selectable.table tr.blue:not(.marked):hover{background:#d3f1ff;color:#2185d0}.ui.table td.marked.blue.left,.ui.table tr.marked.blue.left{-webkit-box-shadow:.2em 0 0 0 #2185d0 inset;box-shadow:.2em 0 0 0 #2185d0 inset}.ui.table td.marked.blue.right,.ui.table tr.marked.blue.right{-webkit-box-shadow:-.2em 0 0 0 #2185d0 inset;box-shadow:-.2em 0 0 0 #2185d0 inset}.ui.inverted.table td.marked.blue.left,.ui.inverted.table tr.marked.blue.left{-webkit-box-shadow:.2em 0 0 0 #54c8ff inset;box-shadow:.2em 0 0 0 #54c8ff inset}.ui.inverted.table td.marked.blue.right,.ui.inverted.table tr.marked.blue.right{-webkit-box-shadow:-.2em 0 0 0 #54c8ff inset;box-shadow:-.2em 0 0 0 #54c8ff inset}.ui.violet.table{border-top:.2em solid #6435c9}.ui.inverted.violet.table{background-color:#6435c9;color:#fff}.ui.ui.table td.violet:not(.marked),.ui.ui.ui.ui.table tr.violet:not(.marked){background:#ece9fe;color:#6435c9}.ui.selectable.table tr:hover td.violet:not(.marked),.ui.table tr td.selectable.violet:not(.marked):hover,.ui.ui.selectable.table tr.violet:not(.marked):hover{background:#e3deff;color:#6435c9}.ui.table td.marked.violet.left,.ui.table tr.marked.violet.left{-webkit-box-shadow:.2em 0 0 0 #6435c9 inset;box-shadow:.2em 0 0 0 #6435c9 inset}.ui.table td.marked.violet.right,.ui.table tr.marked.violet.right{-webkit-box-shadow:-.2em 0 0 0 #6435c9 inset;box-shadow:-.2em 0 0 0 #6435c9 inset}.ui.inverted.table td.marked.violet.left,.ui.inverted.table tr.marked.violet.left{-webkit-box-shadow:.2em 0 0 0 #a291fb inset;box-shadow:.2em 0 0 0 #a291fb inset}.ui.inverted.table td.marked.violet.right,.ui.inverted.table tr.marked.violet.right{-webkit-box-shadow:-.2em 0 0 0 #a291fb inset;box-shadow:-.2em 0 0 0 #a291fb inset}.ui.purple.table{border-top:.2em solid #a333c8}.ui.inverted.purple.table{background-color:#a333c8;color:#fff}.ui.ui.table td.purple:not(.marked),.ui.ui.ui.ui.table tr.purple:not(.marked){background:#f8e3ff;color:#a333c8}.ui.selectable.table tr:hover td.purple:not(.marked),.ui.table tr td.selectable.purple:not(.marked):hover,.ui.ui.selectable.table tr.purple:not(.marked):hover{background:#f5d9ff;color:#a333c8}.ui.table td.marked.purple.left,.ui.table tr.marked.purple.left{-webkit-box-shadow:.2em 0 0 0 #a333c8 inset;box-shadow:.2em 0 0 0 #a333c8 inset}.ui.table td.marked.purple.right,.ui.table tr.marked.purple.right{-webkit-box-shadow:-.2em 0 0 0 #a333c8 inset;box-shadow:-.2em 0 0 0 #a333c8 inset}.ui.inverted.table td.marked.purple.left,.ui.inverted.table tr.marked.purple.left{-webkit-box-shadow:.2em 0 0 0 #dc73ff inset;box-shadow:.2em 0 0 0 #dc73ff inset}.ui.inverted.table td.marked.purple.right,.ui.inverted.table tr.marked.purple.right{-webkit-box-shadow:-.2em 0 0 0 #dc73ff inset;box-shadow:-.2em 0 0 0 #dc73ff inset}.ui.pink.table{border-top:.2em solid #e03997}.ui.inverted.pink.table{background-color:#e03997;color:#fff}.ui.ui.table td.pink:not(.marked),.ui.ui.ui.ui.table tr.pink:not(.marked){background:#ffe8f9;color:#e03997}.ui.selectable.table tr:hover td.pink:not(.marked),.ui.table tr td.selectable.pink:not(.marked):hover,.ui.ui.selectable.table tr.pink:not(.marked):hover{background:#ffdef6;color:#e03997}.ui.table td.marked.pink.left,.ui.table tr.marked.pink.left{-webkit-box-shadow:.2em 0 0 0 #e03997 inset;box-shadow:.2em 0 0 0 #e03997 inset}.ui.table td.marked.pink.right,.ui.table tr.marked.pink.right{-webkit-box-shadow:-.2em 0 0 0 #e03997 inset;box-shadow:-.2em 0 0 0 #e03997 inset}.ui.inverted.table td.marked.pink.left,.ui.inverted.table tr.marked.pink.left{-webkit-box-shadow:.2em 0 0 0 #ff8edf inset;box-shadow:.2em 0 0 0 #ff8edf inset}.ui.inverted.table td.marked.pink.right,.ui.inverted.table tr.marked.pink.right{-webkit-box-shadow:-.2em 0 0 0 #ff8edf inset;box-shadow:-.2em 0 0 0 #ff8edf inset}.ui.brown.table{border-top:.2em solid #a5673f}.ui.inverted.brown.table{background-color:#a5673f;color:#fff}.ui.ui.table td.brown:not(.marked),.ui.ui.ui.ui.table tr.brown:not(.marked){background:#f7e5d2;color:#a5673f}.ui.selectable.table tr:hover td.brown:not(.marked),.ui.table tr td.selectable.brown:not(.marked):hover,.ui.ui.selectable.table tr.brown:not(.marked):hover{background:#efe0cf;color:#a5673f}.ui.table td.marked.brown.left,.ui.table tr.marked.brown.left{-webkit-box-shadow:.2em 0 0 0 #a5673f inset;box-shadow:.2em 0 0 0 #a5673f inset}.ui.table td.marked.brown.right,.ui.table tr.marked.brown.right{-webkit-box-shadow:-.2em 0 0 0 #a5673f inset;box-shadow:-.2em 0 0 0 #a5673f inset}.ui.inverted.table td.marked.brown.left,.ui.inverted.table tr.marked.brown.left{-webkit-box-shadow:.2em 0 0 0 #d67c1c inset;box-shadow:.2em 0 0 0 #d67c1c inset}.ui.inverted.table td.marked.brown.right,.ui.inverted.table tr.marked.brown.right{-webkit-box-shadow:-.2em 0 0 0 #d67c1c inset;box-shadow:-.2em 0 0 0 #d67c1c inset}.ui.grey.table{border-top:.2em solid #767676}.ui.inverted.grey.table{background-color:#767676;color:#fff}.ui.ui.table td.grey:not(.marked),.ui.ui.ui.ui.table tr.grey:not(.marked){background:#dcddde;color:#767676}.ui.selectable.table tr:hover td.grey:not(.marked),.ui.table tr td.selectable.grey:not(.marked):hover,.ui.ui.selectable.table tr.grey:not(.marked):hover{background:#c2c4c5;color:#767676}.ui.table td.marked.grey.left,.ui.table tr.marked.grey.left{-webkit-box-shadow:.2em 0 0 0 #767676 inset;box-shadow:.2em 0 0 0 #767676 inset}.ui.table td.marked.grey.right,.ui.table tr.marked.grey.right{-webkit-box-shadow:-.2em 0 0 0 #767676 inset;box-shadow:-.2em 0 0 0 #767676 inset}.ui.inverted.table td.marked.grey.left,.ui.inverted.table tr.marked.grey.left{-webkit-box-shadow:.2em 0 0 0 #dcddde inset;box-shadow:.2em 0 0 0 #dcddde inset}.ui.inverted.table td.marked.grey.right,.ui.inverted.table tr.marked.grey.right{-webkit-box-shadow:-.2em 0 0 0 #dcddde inset;box-shadow:-.2em 0 0 0 #dcddde inset}.ui.black.table{border-top:.2em solid #1b1c1d}.ui.inverted.black.table{background-color:#1b1c1d;color:#fff}.ui.ui.table td.black:not(.marked),.ui.ui.ui.ui.table tr.black:not(.marked){background:#545454;color:#fff}.ui.selectable.table tr:hover td.black:not(.marked),.ui.table tr td.selectable.black:not(.marked):hover,.ui.ui.selectable.table tr.black:not(.marked):hover{background:#000;color:#fff}.ui.table td.marked.black.left,.ui.table tr.marked.black.left{-webkit-box-shadow:.2em 0 0 0 #1b1c1d inset;box-shadow:.2em 0 0 0 #1b1c1d inset}.ui.table td.marked.black.right,.ui.table tr.marked.black.right{-webkit-box-shadow:-.2em 0 0 0 #1b1c1d inset;box-shadow:-.2em 0 0 0 #1b1c1d inset}.ui.inverted.table td.marked.black.left,.ui.inverted.table tr.marked.black.left{-webkit-box-shadow:.2em 0 0 0 #545454 inset;box-shadow:.2em 0 0 0 #545454 inset}.ui.inverted.table td.marked.black.right,.ui.inverted.table tr.marked.black.right{-webkit-box-shadow:-.2em 0 0 0 #545454 inset;box-shadow:-.2em 0 0 0 #545454 inset}.ui.one.column.table td{width:100%}.ui.two.column.table td{width:50%}.ui.three.column.table td{width:33.33333333%}.ui.four.column.table td{width:25%}.ui.five.column.table td{width:20%}.ui.six.column.table td{width:16.66666667%}.ui.seven.column.table td{width:14.28571429%}.ui.eight.column.table td{width:12.5%}.ui.nine.column.table td{width:11.11111111%}.ui.ten.column.table td{width:10%}.ui.eleven.column.table td{width:9.09090909%}.ui.twelve.column.table td{width:8.33333333%}.ui.thirteen.column.table td{width:7.69230769%}.ui.fourteen.column.table td{width:7.14285714%}.ui.fifteen.column.table td{width:6.66666667%}.ui.sixteen.column.table td{width:6.25%}.ui.table td.one.wide,.ui.table th.one.wide{width:6.25%}.ui.table td.two.wide,.ui.table th.two.wide{width:12.5%}.ui.table td.three.wide,.ui.table th.three.wide{width:18.75%}.ui.table td.four.wide,.ui.table th.four.wide{width:25%}.ui.table td.five.wide,.ui.table th.five.wide{width:31.25%}.ui.table td.six.wide,.ui.table th.six.wide{width:37.5%}.ui.table td.seven.wide,.ui.table th.seven.wide{width:43.75%}.ui.table td.eight.wide,.ui.table th.eight.wide{width:50%}.ui.table td.nine.wide,.ui.table th.nine.wide{width:56.25%}.ui.table td.ten.wide,.ui.table th.ten.wide{width:62.5%}.ui.table td.eleven.wide,.ui.table th.eleven.wide{width:68.75%}.ui.table td.twelve.wide,.ui.table th.twelve.wide{width:75%}.ui.table td.thirteen.wide,.ui.table th.thirteen.wide{width:81.25%}.ui.table td.fourteen.wide,.ui.table th.fourteen.wide{width:87.5%}.ui.table td.fifteen.wide,.ui.table th.fifteen.wide{width:93.75%}.ui.table td.sixteen.wide,.ui.table th.sixteen.wide{width:100%}.ui.sortable.table>thead>tr>th{cursor:pointer;white-space:nowrap;border-left:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87)}.ui.sortable.table>thead>tr>th:first-child{border-left:none}.ui.sortable.table thead th.sorted,.ui.sortable.table thead th.sorted:hover{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ui.sortable.table>thead>tr>th:after{display:none;font-style:normal;font-weight:400;text-decoration:inherit;content:'';height:1em;width:auto;opacity:.8;margin:0 0 0 .5em;font-family:Icons}.ui.sortable.table thead th.ascending:after{content:'\f0d8'}.ui.sortable.table thead th.descending:after{content:'\f0d7'}.ui.sortable.table th.disabled:hover{cursor:auto;color:rgba(40,40,40,.3)}.ui.sortable.table>thead>tr>th:hover{color:rgba(0,0,0,.8)}.ui.sortable.table:not(.basic)>thead>tr>th:hover{background:rgba(0,0,0,.05)}.ui.sortable.table thead th.sorted{color:rgba(0,0,0,.95)}.ui.sortable.table:not(.basic) thead th.sorted{background:rgba(0,0,0,.05)}.ui.sortable.table thead th.sorted:after{display:inline-block}.ui.sortable.table thead th.sorted:hover{color:rgba(0,0,0,.95)}.ui.sortable.table:not(.basic) thead th.sorted:hover{background:rgba(0,0,0,.05)}.ui.inverted.sortable.table thead th.sorted{color:#fff}.ui.inverted.sortable.table:not(.basic) thead th.sorted{background:rgba(255,255,255,.15) -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:rgba(255,255,255,.15) -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:rgba(255,255,255,.15) linear-gradient(transparent,rgba(0,0,0,.05))}.ui.inverted.sortable.table>thead>tr>th:hover{color:#fff}.ui.inverted.sortable.table:not(.basic)>thead>tr>th:hover{background:rgba(255,255,255,.08) -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:rgba(255,255,255,.08) -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:rgba(255,255,255,.08) linear-gradient(transparent,rgba(0,0,0,.05))}.ui.inverted.sortable.table:not(.basic)>thead>tr>th{border-left-color:transparent;border-right-color:transparent}.ui.inverted.table{background:#333;color:rgba(255,255,255,.9);border:none}.ui.ui.inverted.table>tbody>tr>th,.ui.ui.inverted.table>tfoot>tr>td,.ui.ui.inverted.table>tfoot>tr>th,.ui.ui.inverted.table>thead>tr>th,.ui.ui.inverted.table>tr>th{background-color:rgba(0,0,0,.15);border-color:rgba(255,255,255,.1);color:rgba(255,255,255,.9)}.ui.inverted.table>tbody>tr>td,.ui.inverted.table>tfoot>tr>td,.ui.inverted.table>tr>td{border-color:rgba(255,255,255,.1)}.ui.inverted.table tr td.disabled,.ui.inverted.table tr.disabled td,.ui.inverted.table tr.disabled:hover td,.ui.inverted.table tr:hover td.disabled{pointer-events:none;color:rgba(225,225,225,.3)}.ui.inverted.table tr td.disabled:not([class=disabled]),.ui.inverted.table tr.disabled td[class]:not(.disabled),.ui.inverted.table tr.disabled:not([class=disabled]) td,.ui.inverted.table tr:hover td.disabled:not([class=disabled]){color:rgba(40,40,40,.3)}.ui.inverted.definition.table>tfoot:not(.full-width)>tr>th:first-child,.ui.inverted.definition.table>thead:not(.full-width)>tr>th:first-child{background:#fff}.ui.inverted.definition.table>tbody>tr>td:first-child .ui.inverted.definition.table>tfoot>tr>td:first-child,.ui.inverted.definition.table>tr>td:first-child{background:rgba(255,255,255,.02);color:#fff}.ui.collapsing.table{width:auto}.ui.basic.table{background:0 0;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:none;box-shadow:none}.ui.basic.table>tfoot,.ui.basic.table>thead{-webkit-box-shadow:none;box-shadow:none}.ui.basic.table>tbody>tr>th,.ui.basic.table>tfoot>tr>th,.ui.basic.table>thead>tr>th,.ui.basic.table>tr>th{background:0 0;border-left:none}.ui.basic.table>tbody>tr{border-bottom:1px solid rgba(0,0,0,.1)}.ui.basic.table>tbody>tr>td,.ui.basic.table>tfoot>tr>td,.ui.basic.table>tr>td{background:0 0}.ui.basic.striped.table>tbody>tr:nth-child(2n){background-color:rgba(0,0,0,.05)}.ui[class*="very basic"].table{border:none}.ui[class*="very basic"].table:not(.sortable):not(.striped)>tbody>tr>td,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tbody>tr>th,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tfoot>tr>th,.ui[class*="very basic"].table:not(.sortable):not(.striped)>thead>tr>th,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tr>td,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tr>th{padding:''}.ui[class*="very basic"].table:not(.sortable):not(.striped)>tbody>tr>td:first-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tbody>tr>th:first-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tfoot>tr>td:first-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tfoot>tr>th:first-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>thead>tr>th:first-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tr>td:first-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tr>th:first-child{padding-left:0}.ui[class*="very basic"].table:not(.sortable):not(.striped)>tbody>tr>td:last-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tbody>tr>th:last-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tfoot>tr>td:last-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tfoot>tr>th:last-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>thead>tr>th:last-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tr>td:last-child,.ui[class*="very basic"].table:not(.sortable):not(.striped)>tr>th:last-child{padding-right:0}.ui[class*="very basic"].table:not(.sortable):not(.striped)>thead>tr:first-child>th{padding-top:0}.ui.celled.table>tbody>tr>td,.ui.celled.table>tbody>tr>th,.ui.celled.table>tfoot>tr>td,.ui.celled.table>tfoot>tr>th,.ui.celled.table>thead>tr>th,.ui.celled.table>tr>td,.ui.celled.table>tr>th{border-left:1px solid rgba(34,36,38,.1)}.ui.inverted.celled.table>tbody>tr>td,.ui.inverted.celled.table>tr>td{border-left:1px solid rgba(255,255,255,.1)}.ui.celled.table>tbody>tr>td:first-child,.ui.celled.table>tbody>tr>th:first-child,.ui.celled.table>tfoot>tr>td:first-child,.ui.celled.table>tfoot>tr>th:first-child,.ui.celled.table>thead>tr>th:first-child,.ui.celled.table>tr>td:first-child,.ui.celled.table>tr>th:first-child{border-left:none}.ui.padded.table>tbody>tr>th,.ui.padded.table>tfoot>tr>th,.ui.padded.table>thead>tr>th,.ui.padded.table>tr>th{padding-left:1em;padding-right:1em}.ui.padded.table>tbody>tr>td,.ui.padded.table>tbody>tr>th,.ui.padded.table>tfoot>tr>td,.ui.padded.table>tfoot>tr>th,.ui.padded.table>thead>tr>th,.ui.padded.table>tr>td,.ui.padded.table>tr>th{padding:1em 1em}.ui[class*="very padded"].table>tbody>tr>th,.ui[class*="very padded"].table>tfoot>tr>th,.ui[class*="very padded"].table>thead>tr>th,.ui[class*="very padded"].table>tr>th{padding-left:1.5em;padding-right:1.5em}.ui[class*="very padded"].table>tbody>tr>td,.ui[class*="very padded"].table>tfoot>tr>td,.ui[class*="very padded"].table>tr>td{padding:1.5em 1.5em}.ui.compact.table>tbody>tr>th,.ui.compact.table>tfoot>tr>th,.ui.compact.table>thead>tr>th,.ui.compact.table>tr>th{padding-left:.7em;padding-right:.7em}.ui.compact.table>tbody>tr>td,.ui.compact.table>tfoot>tr>td,.ui.compact.table>tr>td{padding:.5em .7em}.ui[class*="very compact"].table>tbody>tr>th,.ui[class*="very compact"].table>tfoot>tr>th,.ui[class*="very compact"].table>thead>tr>th,.ui[class*="very compact"].table>tr>th{padding-left:.6em;padding-right:.6em}.ui[class*="very compact"].table>tbody>tr>td,.ui[class*="very compact"].table>tfoot>tr>td,.ui[class*="very compact"].table>tr>td{padding:.4em .6em}.ui.table{font-size:1em}.ui.mini.table{font-size:.78571429rem}.ui.tiny.table{font-size:.85714286rem}.ui.small.table{font-size:.9em}.ui.large.table{font-size:1.1em}.ui.big.table{font-size:1.28571429rem}.ui.huge.table{font-size:1.42857143rem}.ui.massive.table{font-size:1.71428571rem}/*! + * # Fomantic-UI - Ad + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Copyright 2013 Contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.ad{display:block;overflow:hidden;margin:1em 0}.ui.ad:first-child{margin:0}.ui.ad:last-child{margin:0}.ui.ad iframe{margin:0;padding:0;border:none;overflow:hidden}.ui.leaderboard.ad{width:728px;height:90px}.ui[class*="medium rectangle"].ad{width:300px;height:250px}.ui[class*="large rectangle"].ad{width:336px;height:280px}.ui[class*="half page"].ad{width:300px;height:600px}.ui.square.ad{width:250px;height:250px}.ui[class*="small square"].ad{width:200px;height:200px}.ui[class*="small rectangle"].ad{width:180px;height:150px}.ui[class*="vertical rectangle"].ad{width:240px;height:400px}.ui.button.ad{width:120px;height:90px}.ui[class*="square button"].ad{width:125px;height:125px}.ui[class*="small button"].ad{width:120px;height:60px}.ui.skyscraper.ad{width:120px;height:600px}.ui[class*="wide skyscraper"].ad{width:160px}.ui.banner.ad{width:468px;height:60px}.ui[class*="vertical banner"].ad{width:120px;height:240px}.ui[class*="top banner"].ad{width:930px;height:180px}.ui[class*="half banner"].ad{width:234px;height:60px}.ui[class*="large leaderboard"].ad{width:970px;height:90px}.ui.billboard.ad{width:970px;height:250px}.ui.panorama.ad{width:980px;height:120px}.ui.netboard.ad{width:580px;height:400px}.ui[class*="large mobile banner"].ad{width:320px;height:100px}.ui[class*="mobile leaderboard"].ad{width:320px;height:50px}.ui.mobile.ad{display:none}@media only screen and (max-width:767.98px){.ui.mobile.ad{display:block}}.ui.centered.ad{margin-left:auto;margin-right:auto}.ui.test.ad{position:relative;background:#545454}.ui.test.ad:after{position:absolute;top:50%;left:50%;width:100%;text-align:center;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);content:'Ad';color:#fff;font-size:1em;font-weight:700}.ui.mobile.test.ad:after{font-size:.85714286em}.ui.test.ad[data-text]:after{content:attr(data-text)}/*! + * # Fomantic-UI - Card + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.card,.ui.cards>.card{max-width:100%;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:290px;min-height:0;background:#fff;padding:0;border:none;border-radius:.28571429rem;-webkit-box-shadow:0 1px 3px 0 #d4d4d5,0 0 0 1px #d4d4d5;box-shadow:0 1px 3px 0 #d4d4d5,0 0 0 1px #d4d4d5;-webkit-transition:-webkit-box-shadow .1s ease,-webkit-transform .1s ease;transition:-webkit-box-shadow .1s ease,-webkit-transform .1s ease;transition:box-shadow .1s ease,transform .1s ease;transition:box-shadow .1s ease,transform .1s ease,-webkit-box-shadow .1s ease,-webkit-transform .1s ease;z-index:'';word-wrap:break-word}.ui.card{margin:1em 0}.ui.card a,.ui.cards>.card a{cursor:pointer}.ui.card:first-child{margin-top:0}.ui.card:last-child{margin-bottom:0}.ui.cards{display:-webkit-box;display:-ms-flexbox;display:flex;margin:-.875em -.5em;-ms-flex-wrap:wrap;flex-wrap:wrap}.ui.cards>.card{display:-webkit-box;display:-ms-flexbox;display:flex;margin:.875em .5em;float:none}.ui.card:after,.ui.cards:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.cards~.ui.cards{margin-top:.875em}.ui.card>:first-child,.ui.cards>.card>:first-child{border-radius:.28571429rem .28571429rem 0 0!important;border-top:none!important}.ui.card>:last-child,.ui.cards>.card>:last-child{border-radius:0 0 .28571429rem .28571429rem!important}.ui.card>:only-child,.ui.cards>.card>:only-child{border-radius:.28571429rem!important}.ui.card>.image,.ui.cards>.card>.image{position:relative;display:block;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;padding:0;background:rgba(0,0,0,.05)}.ui.card>.image>img,.ui.cards>.card>.image>img{display:block;width:100%;height:auto;border-radius:inherit}.ui.card>.image:not(.ui)>img,.ui.cards>.card>.image:not(.ui)>img{border:none}.ui.card>.content,.ui.cards>.card>.content{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;border:none;border-top:1px solid rgba(34,36,38,.1);background:0 0;margin:0;padding:1em 1em;-webkit-box-shadow:none;box-shadow:none;font-size:1em;border-radius:0}.ui.card>.content:after,.ui.cards>.card>.content:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.card>.content>.header,.ui.cards>.card>.content>.header{display:block;margin:'';font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;color:rgba(0,0,0,.85)}.ui.card>.content>.header:not(.ui),.ui.cards>.card>.content>.header:not(.ui){font-weight:700;font-size:1.28571429em;margin-top:-.21425em;line-height:1.28571429em}.ui.card>.content>.header+.description,.ui.card>.content>.meta+.description,.ui.cards>.card>.content>.header+.description,.ui.cards>.card>.content>.meta+.description{margin-top:.5em}.ui.card [class*="left floated"],.ui.cards>.card [class*="left floated"]{float:left}.ui.card [class*="right floated"],.ui.cards>.card [class*="right floated"]{float:right}.ui.card [class*="left aligned"],.ui.cards>.card [class*="left aligned"]{text-align:left}.ui.card [class*="center aligned"],.ui.cards>.card [class*="center aligned"]{text-align:center}.ui.card [class*="right aligned"],.ui.cards>.card [class*="right aligned"]{text-align:right}.ui.card .content img,.ui.cards>.card .content img{display:inline-block;vertical-align:middle;width:''}.ui.card .avatar img,.ui.card img.avatar,.ui.cards>.card .avatar img,.ui.cards>.card img.avatar{width:2em;height:2em;border-radius:500rem}.ui.card>.content>.description,.ui.cards>.card>.content>.description{clear:both;color:rgba(0,0,0,.68)}.ui.card>.content p,.ui.cards>.card>.content p{margin:0 0 .5em}.ui.card>.content p:last-child,.ui.cards>.card>.content p:last-child{margin-bottom:0}.ui.card .meta,.ui.cards>.card .meta{font-size:1em;color:rgba(0,0,0,.4)}.ui.card .meta *,.ui.cards>.card .meta *{margin-right:.3em}.ui.card .meta :last-child,.ui.cards>.card .meta :last-child{margin-right:0}.ui.card .meta [class*="right floated"],.ui.cards>.card .meta [class*="right floated"]{margin-right:0;margin-left:.3em}.ui.card>.content a:not(.ui),.ui.cards>.card>.content a:not(.ui){color:'';-webkit-transition:color .1s ease;transition:color .1s ease}.ui.card>.content a:not(.ui):hover,.ui.cards>.card>.content a:not(.ui):hover{color:''}.ui.card>.content>a.header,.ui.cards>.card>.content>a.header{color:rgba(0,0,0,.85)}.ui.card>.content>a.header:hover,.ui.cards>.card>.content>a.header:hover{color:#1e70bf}.ui.card .meta>a:not(.ui),.ui.cards>.card .meta>a:not(.ui){color:rgba(0,0,0,.4)}.ui.card .meta>a:not(.ui):hover,.ui.cards>.card .meta>a:not(.ui):hover{color:rgba(0,0,0,.87)}.ui.card>.button,.ui.card>.buttons,.ui.cards>.card>.button,.ui.cards>.card>.buttons{margin:0 -1px;width:calc(100% + 2px)}.ui.card>.button:last-child,.ui.card>.buttons:last-child,.ui.cards>.card>.button:last-child,.ui.cards>.card>.buttons:last-child{margin-bottom:-1px}.ui.card .dimmer,.ui.cards>.card .dimmer{background-color:'';z-index:10}.ui.card>.content .star.icon,.ui.cards>.card>.content .star.icon{cursor:pointer;opacity:.75;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.card>.content .star.icon:hover,.ui.cards>.card>.content .star.icon:hover{opacity:1;color:#ffb70a}.ui.card>.content .active.star.icon,.ui.cards>.card>.content .active.star.icon{color:#ffe623}.ui.card>.content .like.icon,.ui.cards>.card>.content .like.icon{cursor:pointer;opacity:.75;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.card>.content .like.icon:hover,.ui.cards>.card>.content .like.icon:hover{opacity:1;color:#ff2733}.ui.card>.content .active.like.icon,.ui.cards>.card>.content .active.like.icon{color:#ff2733}.ui.card>.extra,.ui.cards>.card>.extra{max-width:100%;min-height:0!important;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;border-top:1px solid rgba(0,0,0,.05)!important;position:static;background:0 0;width:auto;margin:0 0;padding:.75em 1em;top:0;left:0;color:rgba(0,0,0,.4);-webkit-box-shadow:none;box-shadow:none;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.card>.extra a:not(.ui),.ui.cards>.card>.extra a:not(.ui){color:rgba(0,0,0,.4)}.ui.card>.extra a:not(.ui):hover,.ui.cards>.card>.extra a:not(.ui):hover{color:#1e70bf}.ui.card.horizontal,.ui.horizontal.cards>.card{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;min-width:270px;width:400px;max-width:100%}.ui.card.horizontal>.image,.ui.horizontal.cards>.card>.image{border-radius:.28571429rem 0 0 .28571429rem;width:150px}.ui.card.horizontal>.image>img,.ui.horizontal.cards>.card>.image>img{background-size:cover;background-repeat:no-repeat;background-position:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;height:100%;border-radius:.28571429rem 0 0 .28571429rem}.ui.card.horizontal>.image:last-child>img,.ui.horizontal.cards>.card>.image:last-child>img{border-radius:0 .28571429rem .28571429rem 0}.ui.horizontal.card>.content,.ui.horizontal.cards>.card>.content{-ms-flex-preferred-size:1px;flex-basis:1px}.ui.horizontal.card>.extra,.ui.horizontal.cards>.card>.extra{-ms-flex-preferred-size:100%;flex-basis:100%}.ui.raised.card,.ui.raised.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px #d4d4d5,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.link.cards .raised.card:hover,.ui.link.raised.card:hover,.ui.raised.cards a.card:hover,a.ui.raised.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 4px 0 rgba(34,36,38,.15),0 2px 10px 0 rgba(34,36,38,.25);box-shadow:0 0 0 1px #d4d4d5,0 2px 4px 0 rgba(34,36,38,.15),0 2px 10px 0 rgba(34,36,38,.25)}.ui.centered.cards{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.centered.card{margin-left:auto;margin-right:auto}.ui.fluid.card{width:100%;max-width:9999px}.ui.cards a.card,.ui.link.card,.ui.link.cards .card,a.ui.card{-webkit-transform:none;transform:none}.ui.cards a.card:hover,.ui.link.card:hover,.ui.link.cards .card:not(.icon):hover,a.ui.card:hover{cursor:pointer;z-index:5;background:#fff;border:none;-webkit-box-shadow:0 1px 3px 0 #bcbdbd,0 0 0 1px #d4d4d5;box-shadow:0 1px 3px 0 #bcbdbd,0 0 0 1px #d4d4d5;-webkit-transform:translateY(-3px);transform:translateY(-3px)}.ui.cards>.primary.card,.ui.primary.card,.ui.primary.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #2185d0,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #2185d0,0 1px 3px 0 #d4d4d5}.ui.cards>.primary.card:hover,.ui.primary.card:hover,.ui.primary.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #1678c2,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #1678c2,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.primary.card,.ui.inverted.primary.card,.ui.inverted.primary.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #54c8ff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #54c8ff,0 0 0 1px #555}.ui.inverted.cards>.primary.card:hover,.ui.inverted.primary.card:hover,.ui.inverted.primary.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #21b8ff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #21b8ff,0 0 0 1px #555}.ui.cards>.secondary.card,.ui.secondary.card,.ui.secondary.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #1b1c1d,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #1b1c1d,0 1px 3px 0 #d4d4d5}.ui.cards>.secondary.card:hover,.ui.secondary.card:hover,.ui.secondary.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #27292a,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #27292a,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.secondary.card,.ui.inverted.secondary.card,.ui.inverted.secondary.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #545454,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #545454,0 0 0 1px #555}.ui.inverted.cards>.secondary.card:hover,.ui.inverted.secondary.card:hover,.ui.inverted.secondary.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #6e6e6e,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #6e6e6e,0 0 0 1px #555}.ui.cards>.red.card,.ui.red.card,.ui.red.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #db2828,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #db2828,0 1px 3px 0 #d4d4d5}.ui.cards>.red.card:hover,.ui.red.card:hover,.ui.red.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #d01919,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #d01919,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.red.card,.ui.inverted.red.card,.ui.inverted.red.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff695e,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff695e,0 0 0 1px #555}.ui.inverted.cards>.red.card:hover,.ui.inverted.red.card:hover,.ui.inverted.red.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff392b,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff392b,0 0 0 1px #555}.ui.cards>.orange.card,.ui.orange.card,.ui.orange.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #f2711c,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #f2711c,0 1px 3px 0 #d4d4d5}.ui.cards>.orange.card:hover,.ui.orange.card:hover,.ui.orange.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #f26202,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #f26202,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.orange.card,.ui.inverted.orange.card,.ui.inverted.orange.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff851b,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff851b,0 0 0 1px #555}.ui.inverted.cards>.orange.card:hover,.ui.inverted.orange.card:hover,.ui.inverted.orange.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #e76b00,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #e76b00,0 0 0 1px #555}.ui.cards>.yellow.card,.ui.yellow.card,.ui.yellow.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #fbbd08,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #fbbd08,0 1px 3px 0 #d4d4d5}.ui.cards>.yellow.card:hover,.ui.yellow.card:hover,.ui.yellow.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #eaae00,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #eaae00,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.yellow.card,.ui.inverted.yellow.card,.ui.inverted.yellow.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ffe21f,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ffe21f,0 0 0 1px #555}.ui.inverted.cards>.yellow.card:hover,.ui.inverted.yellow.card:hover,.ui.inverted.yellow.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ebcd00,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ebcd00,0 0 0 1px #555}.ui.cards>.olive.card,.ui.olive.card,.ui.olive.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #b5cc18,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #b5cc18,0 1px 3px 0 #d4d4d5}.ui.cards>.olive.card:hover,.ui.olive.card:hover,.ui.olive.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #a7bd0d,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #a7bd0d,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.olive.card,.ui.inverted.olive.card,.ui.inverted.olive.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #d9e778,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #d9e778,0 0 0 1px #555}.ui.inverted.cards>.olive.card:hover,.ui.inverted.olive.card:hover,.ui.inverted.olive.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #d2e745,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #d2e745,0 0 0 1px #555}.ui.cards>.green.card,.ui.green.card,.ui.green.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #21ba45,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #21ba45,0 1px 3px 0 #d4d4d5}.ui.cards>.green.card:hover,.ui.green.card:hover,.ui.green.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #16ab39,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #16ab39,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.green.card,.ui.inverted.green.card,.ui.inverted.green.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #2ecc40,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #2ecc40,0 0 0 1px #555}.ui.inverted.cards>.green.card:hover,.ui.inverted.green.card:hover,.ui.inverted.green.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #1ea92e,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #1ea92e,0 0 0 1px #555}.ui.cards>.teal.card,.ui.teal.card,.ui.teal.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #00b5ad,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #00b5ad,0 1px 3px 0 #d4d4d5}.ui.cards>.teal.card:hover,.ui.teal.card:hover,.ui.teal.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #009c95,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #009c95,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.teal.card,.ui.inverted.teal.card,.ui.inverted.teal.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #6dffff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #6dffff,0 0 0 1px #555}.ui.inverted.cards>.teal.card:hover,.ui.inverted.teal.card:hover,.ui.inverted.teal.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #3affff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #3affff,0 0 0 1px #555}.ui.blue.card,.ui.blue.cards>.card,.ui.cards>.blue.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #2185d0,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #2185d0,0 1px 3px 0 #d4d4d5}.ui.blue.card:hover,.ui.blue.cards>.card:hover,.ui.cards>.blue.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #1678c2,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #1678c2,0 1px 3px 0 #bcbdbd}.ui.inverted.blue.card,.ui.inverted.blue.cards>.card,.ui.inverted.cards>.blue.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #54c8ff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #54c8ff,0 0 0 1px #555}.ui.inverted.blue.card:hover,.ui.inverted.blue.cards>.card:hover,.ui.inverted.cards>.blue.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #21b8ff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #21b8ff,0 0 0 1px #555}.ui.cards>.violet.card,.ui.violet.card,.ui.violet.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #6435c9,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #6435c9,0 1px 3px 0 #d4d4d5}.ui.cards>.violet.card:hover,.ui.violet.card:hover,.ui.violet.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #5829bb,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #5829bb,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.violet.card,.ui.inverted.violet.card,.ui.inverted.violet.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #a291fb,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #a291fb,0 0 0 1px #555}.ui.inverted.cards>.violet.card:hover,.ui.inverted.violet.card:hover,.ui.inverted.violet.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #745aff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #745aff,0 0 0 1px #555}.ui.cards>.purple.card,.ui.purple.card,.ui.purple.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #a333c8,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #a333c8,0 1px 3px 0 #d4d4d5}.ui.cards>.purple.card:hover,.ui.purple.card:hover,.ui.purple.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #9627ba,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #9627ba,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.purple.card,.ui.inverted.purple.card,.ui.inverted.purple.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #dc73ff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #dc73ff,0 0 0 1px #555}.ui.inverted.cards>.purple.card:hover,.ui.inverted.purple.card:hover,.ui.inverted.purple.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #cf40ff,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #cf40ff,0 0 0 1px #555}.ui.cards>.pink.card,.ui.pink.card,.ui.pink.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #e03997,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #e03997,0 1px 3px 0 #d4d4d5}.ui.cards>.pink.card:hover,.ui.pink.card:hover,.ui.pink.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #e61a8d,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #e61a8d,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.pink.card,.ui.inverted.pink.card,.ui.inverted.pink.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff8edf,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff8edf,0 0 0 1px #555}.ui.inverted.cards>.pink.card:hover,.ui.inverted.pink.card:hover,.ui.inverted.pink.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff5bd1,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #ff5bd1,0 0 0 1px #555}.ui.brown.card,.ui.brown.cards>.card,.ui.cards>.brown.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #a5673f,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #a5673f,0 1px 3px 0 #d4d4d5}.ui.brown.card:hover,.ui.brown.cards>.card:hover,.ui.cards>.brown.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #975b33,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #975b33,0 1px 3px 0 #bcbdbd}.ui.inverted.brown.card,.ui.inverted.brown.cards>.card,.ui.inverted.cards>.brown.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #d67c1c,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #d67c1c,0 0 0 1px #555}.ui.inverted.brown.card:hover,.ui.inverted.brown.cards>.card:hover,.ui.inverted.cards>.brown.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #b0620f,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #b0620f,0 0 0 1px #555}.ui.cards>.grey.card,.ui.grey.card,.ui.grey.cards>.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #767676,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #767676,0 1px 3px 0 #d4d4d5}.ui.cards>.grey.card:hover,.ui.grey.card:hover,.ui.grey.cards>.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #838383,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #838383,0 1px 3px 0 #bcbdbd}.ui.inverted.cards>.grey.card,.ui.inverted.grey.card,.ui.inverted.grey.cards>.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #dcddde,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #dcddde,0 0 0 1px #555}.ui.inverted.cards>.grey.card:hover,.ui.inverted.grey.card:hover,.ui.inverted.grey.cards>.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #c2c4c5,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #c2c4c5,0 0 0 1px #555}.ui.black.card,.ui.black.cards>.card,.ui.cards>.black.card{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #1b1c1d,0 1px 3px 0 #d4d4d5;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #1b1c1d,0 1px 3px 0 #d4d4d5}.ui.black.card:hover,.ui.black.cards>.card:hover,.ui.cards>.black.card:hover{-webkit-box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #27292a,0 1px 3px 0 #bcbdbd;box-shadow:0 0 0 1px #d4d4d5,0 2px 0 0 #27292a,0 1px 3px 0 #bcbdbd}.ui.inverted.black.card,.ui.inverted.black.cards>.card,.ui.inverted.cards>.black.card{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #545454,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #545454,0 0 0 1px #555}.ui.inverted.black.card:hover,.ui.inverted.black.cards>.card:hover,.ui.inverted.cards>.black.card:hover{-webkit-box-shadow:0 1px 3px 0 #555,0 2px 0 0 #000,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 2px 0 0 #000,0 0 0 1px #555}.ui.one.cards{margin-left:0;margin-right:0}.ui.one.cards>.card{width:100%}.ui.two.cards{margin-left:-1em;margin-right:-1em}.ui.two.cards>.card{width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.three.cards{margin-left:-1em;margin-right:-1em}.ui.three.cards>.card{width:calc(33.33333333333333% - 2em);margin-left:1em;margin-right:1em}.ui.four.cards{margin-left:-.75em;margin-right:-.75em}.ui.four.cards>.card{width:calc(25% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.five.cards{margin-left:-.75em;margin-right:-.75em}.ui.five.cards>.card{width:calc(20% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.six.cards{margin-left:-.75em;margin-right:-.75em}.ui.six.cards>.card{width:calc(16.666666666666664% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.seven.cards{margin-left:-.5em;margin-right:-.5em}.ui.seven.cards>.card{width:calc(14.285714285714285% - 1em);margin-left:.5em;margin-right:.5em}.ui.eight.cards{margin-left:-.5em;margin-right:-.5em}.ui.eight.cards>.card{width:calc(12.5% - 1em);margin-left:.5em;margin-right:.5em;font-size:11px}.ui.nine.cards{margin-left:-.5em;margin-right:-.5em}.ui.nine.cards>.card{width:calc(11.11111111111111% - 1em);margin-left:.5em;margin-right:.5em;font-size:10px}.ui.ten.cards{margin-left:-.5em;margin-right:-.5em}.ui.ten.cards>.card{width:calc(10% - 1em);margin-left:.5em;margin-right:.5em}@media only screen and (max-width:767.98px){.ui.two.doubling.cards{margin-left:0;margin-right:0}.ui.two.doubling.cards>.card{width:100%;margin-left:0;margin-right:0}.ui.three.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.three.doubling.cards>.card{width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.four.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.four.doubling.cards>.card{width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.five.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.five.doubling.cards>.card{width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.six.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.six.doubling.cards>.card{width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.seven.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.seven.doubling.cards>.card{width:calc(33.33333333333333% - 2em);margin-left:1em;margin-right:1em}.ui.eight.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.eight.doubling.cards>.card{width:calc(33.33333333333333% - 2em);margin-left:1em;margin-right:1em}.ui.nine.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.nine.doubling.cards>.card{width:calc(33.33333333333333% - 2em);margin-left:1em;margin-right:1em}.ui.ten.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.ten.doubling.cards>.card{width:calc(33.33333333333333% - 2em);margin-left:1em;margin-right:1em}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.two.doubling.cards{margin-left:0;margin-right:0}.ui.two.doubling.cards>.card{width:100%;margin-left:0;margin-right:0}.ui.three.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.three.doubling.cards>.card{width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.four.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.four.doubling.cards>.card{width:calc(50% - 2em);margin-left:1em;margin-right:1em}.ui.five.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.five.doubling.cards>.card{width:calc(33.33333333333333% - 2em);margin-left:1em;margin-right:1em}.ui.six.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.six.doubling.cards>.card{width:calc(33.33333333333333% - 2em);margin-left:1em;margin-right:1em}.ui.eight.doubling.cards{margin-left:-1em;margin-right:-1em}.ui.eight.doubling.cards>.card{width:calc(33.33333333333333% - 2em);margin-left:1em;margin-right:1em}.ui.eight.doubling.cards{margin-left:-.75em;margin-right:-.75em}.ui.eight.doubling.cards>.card{width:calc(25% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.nine.doubling.cards{margin-left:-.75em;margin-right:-.75em}.ui.nine.doubling.cards>.card{width:calc(25% - 1.5em);margin-left:.75em;margin-right:.75em}.ui.ten.doubling.cards{margin-left:-.75em;margin-right:-.75em}.ui.ten.doubling.cards>.card{width:calc(20% - 1.5em);margin-left:.75em;margin-right:.75em}}@media only screen and (max-width:767.98px){.ui.stackable.cards{display:block!important}.ui.stackable.cards .card:first-child{margin-top:0!important}.ui.stackable.cards>.card{display:block!important;height:auto!important;margin:1em 1em;padding:0!important;width:calc(100% - 2em)!important}}.ui.cards>.card{font-size:1em}.ui.mini.cards .card{font-size:.78571429rem}.ui.tiny.cards .card{font-size:.85714286rem}.ui.small.cards .card{font-size:.92857143rem}.ui.large.cards .card{font-size:1.14285714rem}.ui.big.cards .card{font-size:1.28571429rem}.ui.huge.cards .card{font-size:1.42857143rem}.ui.massive.cards .card{font-size:1.71428571rem}.ui.inverted.card,.ui.inverted.cards>.card{background:#1b1c1d;-webkit-box-shadow:0 1px 3px 0 #555,0 0 0 1px #555;box-shadow:0 1px 3px 0 #555,0 0 0 1px #555}.ui.inverted.card>.content,.ui.inverted.cards>.card>.content{border-top:1px solid rgba(255,255,255,.15)}.ui.inverted.card>.content>.header,.ui.inverted.cards>.card>.content>.header{color:rgba(255,255,255,.9)}.ui.inverted.card>.content>.description,.ui.inverted.cards>.card>.content>.description{color:rgba(255,255,255,.8)}.ui.inverted.card .meta,.ui.inverted.cards>.card .meta{color:rgba(255,255,255,.7)}.ui.inverted.card .meta>a:not(.ui),.ui.inverted.cards>.card .meta>a:not(.ui){color:rgba(255,255,255,.7)}.ui.inverted.card .meta>a:not(.ui):hover,.ui.inverted.cards>.card .meta>a:not(.ui):hover{color:#fff}.ui.inverted.card>.extra,.ui.inverted.cards>.card>.extra{border-top:1px solid rgba(255,255,255,.15)!important;color:rgba(255,255,255,.7)}.ui.inverted.card>.extra a:not(.ui),.ui.inverted.cards>.card>.extra a:not(.ui){color:rgba(255,255,255,.5)}.ui.inverted.card>.extra a:not(.ui):hover,.ui.inverted.cards>.card>.extra a:not(.ui):hover{color:#1e70bf}.ui.inverted.cards a.card:hover,.ui.inverted.link.card:hover,.ui.inverted.link.cards .card:not(.icon):hover,a.inverted.ui.card:hover{background:#1b1c1d}/*! + * # Fomantic-UI - Comment + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.comments{margin:1.5em 0;max-width:650px}.ui.comments:first-child{margin-top:0}.ui.comments:last-child{margin-bottom:0}.ui.comments .comment{position:relative;background:0 0;margin:.5em 0 0;padding:.5em 0 0;border:none;border-top:none;line-height:1.2}.ui.comments .comment:first-child{margin-top:0;padding-top:0}.ui.comments .comment>.comments{margin:0 0 .5em .5em;padding:1em 0 1em 1em}.ui.comments .comment>.comments:before{position:absolute;top:0;left:0}.ui.comments .comment>.comments .comment{border:none;border-top:none;background:0 0}.ui.comments .comment .avatar{display:block;width:2.5em;height:auto;float:left;margin:.2em 0 0}.ui.comments .comment .avatar img,.ui.comments .comment img.avatar{display:block;margin:0 auto;width:100%;height:100%;border-radius:.25rem}.ui.comments .comment>.content{display:block}.ui.comments .comment>.avatar~.content{margin-left:3.5em}.ui.comments .comment .author{font-size:1em;color:rgba(0,0,0,.87);font-weight:700}.ui.comments .comment a.author{cursor:pointer}.ui.comments .comment a.author:hover{color:#1e70bf}.ui.comments .comment .metadata{display:inline-block;margin-left:.5em;color:rgba(0,0,0,.4);font-size:.875em}.ui.comments .comment .metadata>*{display:inline-block;margin:0 .5em 0 0}.ui.comments .comment .metadata>:last-child{margin-right:0}.ui.comments .comment .text{margin:.25em 0 .5em;font-size:1em;word-wrap:break-word;color:rgba(0,0,0,.87);line-height:1.3}.ui.comments .comment .actions{font-size:.875em}.ui.comments .comment .actions a{cursor:pointer;display:inline-block;margin:0 .75em 0 0;color:rgba(0,0,0,.4)}.ui.comments .comment .actions a:last-child{margin-right:0}.ui.comments .comment .actions a.active,.ui.comments .comment .actions a:hover{color:rgba(0,0,0,.8)}.ui.comments>.reply.form{margin-top:1em}.ui.comments .comment .reply.form{width:100%;margin-top:1em}.ui.comments .reply.form textarea{font-size:1em;height:12em}.ui.collapsed.comments,.ui.comments .collapsed.comment,.ui.comments .collapsed.comments{display:none}.ui.threaded.comments .comment>.comments{margin:-1.5em 0 -1em 1.25em;padding:3em 0 2em 2.25em;-webkit-box-shadow:-1px 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 rgba(34,36,38,.15)}.ui.minimal.comments .comment .actions{opacity:0;position:absolute;top:0;right:0;left:auto;-webkit-transition:opacity .2s ease;transition:opacity .2s ease;-webkit-transition-delay:.1s;transition-delay:.1s}.ui.minimal.comments .comment>.content:hover>.actions{opacity:1}.ui.comments{font-size:1rem}.ui.mini.comments{font-size:.78571429rem}.ui.tiny.comments{font-size:.85714286rem}.ui.small.comments{font-size:.92857143rem}.ui.large.comments{font-size:1.14285714rem}.ui.big.comments{font-size:1.28571429rem}.ui.huge.comments{font-size:1.42857143rem}.ui.massive.comments{font-size:1.71428571rem}.ui.inverted.comments .comment{background-color:#1b1c1d}.ui.inverted.comments .comment .author,.ui.inverted.comments .comment .text{color:rgba(255,255,255,.9)}.ui.inverted.comments .comment .actions a,.ui.inverted.comments .comment .metadata{color:rgba(255,255,255,.7)}.ui.inverted.comments .comment .actions a.active,.ui.inverted.comments .comment .actions a:hover,.ui.inverted.comments .comment a.author:hover{color:#fff}.ui.inverted.threaded.comments .comment>.comments{-webkit-box-shadow:-1px 0 0 #555;box-shadow:-1px 0 0 #555}/*! + * # Fomantic-UI - Feed + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.feed{margin:1em 0}.ui.feed:first-child{margin-top:0}.ui.feed:last-child{margin-bottom:0}.ui.feed>.event{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;width:100%;padding:.21428571rem 0;margin:0;background:0 0;border-top:none}.ui.feed>.event:first-child{border-top:0;padding-top:0}.ui.feed>.event:last-child{padding-bottom:0}.ui.feed>.event>.label{display:block;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:2.5em;height:auto;-ms-flex-item-align:stretch;align-self:stretch;text-align:left}.ui.feed>.event>.label .icon{opacity:1;font-size:1.5em;width:100%;padding:.25em;background:0 0;border:none;border-radius:none;color:rgba(0,0,0,.6)}.ui.feed>.event>.label img{width:100%;height:auto;border-radius:500rem}.ui.feed>.event>.label+.content{margin:.5em 0 .35714286em 1.14285714em}.ui.feed>.event>.content{display:block;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;-ms-flex-item-align:stretch;align-self:stretch;text-align:left;word-wrap:break-word}.ui.feed>.event:last-child>.content{padding-bottom:0}.ui.feed>.event>.content a{cursor:pointer}.ui.feed>.event>.content .date{margin:-.5rem 0 0;padding:0;color:rgba(0,0,0,.4);font-weight:400;font-size:1em;font-style:normal}.ui.feed>.event>.content .summary{margin:0;font-size:1em;font-weight:700;color:rgba(0,0,0,.87)}.ui.feed>.event>.content .summary img{display:inline-block;width:auto;height:10em;margin:-.25em .25em 0 0;border-radius:.25em;vertical-align:middle}.ui.feed>.event>.content .user{display:inline-block;font-weight:700;margin-right:0;vertical-align:baseline}.ui.feed>.event>.content .user img{margin:-.25em .25em 0 0;width:auto;height:10em;vertical-align:middle}.ui.feed>.event>.content .summary>.date{display:inline-block;float:none;font-weight:400;font-size:.85714286em;font-style:normal;margin:0 0 0 .5em;padding:0;color:rgba(0,0,0,.4)}.ui.feed>.event>.content .extra{margin:.5em 0 0;background:0 0;padding:0;color:rgba(0,0,0,.87)}.ui.feed>.event>.content .extra.images img{display:inline-block;margin:0 .25em 0 0;width:6em}.ui.feed>.event>.content .extra.text{padding:0;border-left:none;font-size:1em;max-width:500px;line-height:1.4285em}.ui.feed>.event>.content .meta{display:inline-block;font-size:.85714286em;margin:.5em 0 0;background:0 0;border:none;border-radius:0;-webkit-box-shadow:none;box-shadow:none;padding:0;color:rgba(0,0,0,.6)}.ui.feed>.event>.content .meta>*{position:relative;margin-left:.75em}.ui.feed>.event>.content .meta>:after{content:'';color:rgba(0,0,0,.2);top:0;left:-1em;opacity:1;position:absolute;vertical-align:top}.ui.feed>.event>.content .meta .like{color:'';-webkit-transition:.2s color ease;transition:.2s color ease}.ui.feed>.event>.content .meta .like:hover .icon{color:#ff2733}.ui.feed>.event>.content .meta .active.like .icon{color:#ef404a}.ui.feed>.event>.content .meta>:first-child{margin-left:0}.ui.feed>.event>.content .meta>:first-child::after{display:none}.ui.feed>.event>.content .meta a,.ui.feed>.event>.content .meta>.icon{cursor:pointer;opacity:1;color:rgba(0,0,0,.5);-webkit-transition:color .1s ease;transition:color .1s ease}.ui.feed>.event>.content .meta a:hover,.ui.feed>.event>.content .meta a:hover .icon,.ui.feed>.event>.content .meta>.icon:hover{color:rgba(0,0,0,.95)}.ui.feed{font-size:1rem}.ui.mini.feed{font-size:.78571429rem}.ui.tiny.feed{font-size:.85714286rem}.ui.small.feed{font-size:.92857143rem}.ui.large.feed{font-size:1.14285714rem}.ui.big.feed{font-size:1.28571429rem}.ui.huge.feed{font-size:1.42857143rem}.ui.massive.feed{font-size:1.71428571rem}.ui.inverted.feed>.event{background:#1b1c1d}.ui.inverted.feed>.event>.content .date,.ui.inverted.feed>.event>.content .meta .like{color:rgba(255,255,255,.7)}.ui.inverted.feed>.event>.content .extra.text,.ui.inverted.feed>.event>.content .summary{color:rgba(255,255,255,.9)}.ui.inverted.feed>.event>.content .meta .like:hover{color:#fff}/*! + * # Fomantic-UI - Item + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.items>.item{display:-webkit-box;display:-ms-flexbox;display:flex;margin:1em 0;width:100%;min-height:0;background:0 0;padding:0;border:none;border-radius:0;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:-webkit-box-shadow .1s ease;transition:-webkit-box-shadow .1s ease;transition:box-shadow .1s ease;transition:box-shadow .1s ease,-webkit-box-shadow .1s ease;z-index:''}.ui.items>.item a{cursor:pointer}.ui.items{margin:1.5em 0}.ui.items:first-child{margin-top:0!important}.ui.items:last-child{margin-bottom:0!important}.ui.items>.item:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item:first-child{margin-top:0}.ui.items>.item:last-child{margin-bottom:0}.ui.items>.item>.image{position:relative;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;display:block;float:none;margin:0;padding:0;max-height:'';-ms-flex-item-align:start;align-self:start}.ui.items>.item>.image>img{display:block;width:100%;height:auto;border-radius:.125rem;border:none}.ui.items>.item>.image:only-child>img{border-radius:0}.ui.items>.item>.content{display:block;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;background:0 0;color:rgba(0,0,0,.87);margin:0;padding:0;-webkit-box-shadow:none;box-shadow:none;font-size:1em;border:none;border-radius:0}.ui.items>.item>.content:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item>.image+.content{min-width:0;width:auto;display:block;margin-left:0;-ms-flex-item-align:start;align-self:start;padding-left:1.5em}.ui.items>.item>.content>.header{display:inline-block;margin:-.21425em 0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;color:rgba(0,0,0,.85)}.ui.items>.item>.content>.header:not(.ui){font-size:1.28571429em}.ui.items>.item [class*="left floated"]{float:left}.ui.items>.item [class*="right floated"]{float:right}.ui.items>.item .content img{-ms-flex-item-align:center;align-self:center;width:''}.ui.items>.item .avatar img,.ui.items>.item img.avatar{width:'';height:'';border-radius:500rem}.ui.items>.item>.content>.description{margin-top:.6em;max-width:auto;font-size:1em;line-height:1.4285em;color:rgba(0,0,0,.87)}.ui.items>.item>.content p{margin:0 0 .5em}.ui.items>.item>.content p:last-child{margin-bottom:0}.ui.items>.item .meta{margin:.5em 0 .5em;font-size:1em;line-height:1em;color:rgba(0,0,0,.6)}.ui.items>.item .meta *{margin-right:.3em}.ui.items>.item .meta :last-child{margin-right:0}.ui.items>.item .meta [class*="right floated"]{margin-right:0;margin-left:.3em}.ui.items>.item>.content a:not(.ui){color:'';-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content a:not(.ui):hover{color:''}.ui.items>.item>.content>a.header{color:rgba(0,0,0,.85)}.ui.items>.item>.content>a.header:hover{color:#1e70bf}.ui.items>.item .meta>a:not(.ui){color:rgba(0,0,0,.4)}.ui.items>.item .meta>a:not(.ui):hover{color:rgba(0,0,0,.87)}.ui.items>.item>.content .favorite.icon{cursor:pointer;opacity:.75;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content .favorite.icon:hover{opacity:1;color:#ffb70a}.ui.items>.item>.content .active.favorite.icon{color:#ffe623}.ui.items>.item>.content .like.icon{cursor:pointer;opacity:.75;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content .like.icon:hover{opacity:1;color:#ff2733}.ui.items>.item>.content .active.like.icon{color:#ff2733}.ui.items>.item .extra{display:block;position:relative;background:0 0;margin:.5rem 0 0;width:100%;padding:0 0 0;top:0;left:0;color:rgba(0,0,0,.4);-webkit-box-shadow:none;box-shadow:none;-webkit-transition:color .1s ease;transition:color .1s ease;border-top:none}.ui.items>.item .extra>*{margin:.25rem .5rem .25rem 0}.ui.items>.item .extra>[class*="right floated"]{margin:.25rem 0 .25rem .5rem}.ui.items>.item .extra:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item>.image:not(.ui){width:175px}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.items>.item{margin:1em 0}.ui.items>.item>.image:not(.ui){width:150px}.ui.items>.item>.image+.content{display:block;padding:0 0 0 1em}}@media only screen and (max-width:767.98px){.ui.items:not(.unstackable)>.item{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:2em 0}.ui.items:not(.unstackable)>.item>.image{display:block;margin-left:auto;margin-right:auto}.ui.items:not(.unstackable)>.item>.image,.ui.items:not(.unstackable)>.item>.image>img{max-width:100%!important;width:auto!important;max-height:250px!important}.ui.items:not(.unstackable)>.item>.image+.content{display:block;padding:1.5em 0 0}}.ui.items>.item>.image+[class*="top aligned"].content{-ms-flex-item-align:start;align-self:flex-start}.ui.items>.item>.image+[class*="middle aligned"].content{-ms-flex-item-align:center;align-self:center}.ui.items>.item>.image+[class*="bottom aligned"].content{-ms-flex-item-align:end;align-self:flex-end}.ui.relaxed.items>.item{margin:1.5em 0}.ui[class*="very relaxed"].items>.item{margin:2em 0}.ui.divided.items>.item{border-top:1px solid rgba(34,36,38,.15);margin:0;padding:1em 0}.ui.divided.items>.item:first-child{border-top:none;margin-top:0!important;padding-top:0!important}.ui.divided.items>.item:last-child{margin-bottom:0!important;padding-bottom:0!important}.ui.relaxed.divided.items>.item{margin:0;padding:1.5em 0}.ui[class*="very relaxed"].divided.items>.item{margin:0;padding:2em 0}.ui.items a.item:hover,.ui.link.items>.item:hover{cursor:pointer}.ui.items a.item:hover .content .header,.ui.link.items>.item:hover .content .header{color:#1e70bf}.ui.items>.item{font-size:1em}.ui.mini.items>.item{font-size:.78571429em}.ui.tiny.items>.item{font-size:.85714286em}.ui.small.items>.item{font-size:.92857143em}.ui.large.items>.item{font-size:1.14285714em}.ui.big.items>.item{font-size:1.28571429em}.ui.huge.items>.item{font-size:1.42857143em}.ui.massive.items>.item{font-size:1.71428571em}@media only screen and (max-width:767.98px){.ui.unstackable.items>.item>.image,.ui.unstackable.items>.item>.image>img{width:125px!important}}.ui.inverted.items>.item{background:0 0}.ui.inverted.items>.item>.content{background:0 0;color:rgba(255,255,255,.9)}.ui.inverted.items>.item .extra{background:0 0}.ui.inverted.items>.item>.content>.header{color:rgba(255,255,255,.9)}.ui.inverted.items>.item>.content>.description{color:rgba(255,255,255,.9)}.ui.inverted.items>.item .meta{color:rgba(255,255,255,.8)}.ui.inverted.items>.item>.content a:not(.ui){color:#57a4ef}.ui.inverted.items>.item>.content a:not(.ui):hover{color:#4183c4}.ui.inverted.items>.item>.content>a.header{color:rgba(255,255,255,.9)}.ui.inverted.items>.item>.content>a.header:hover{color:#fff}.ui.inverted.items>.item .meta>a:not(.ui){color:rgba(255,255,255,.7)}.ui.inverted.items>.item .meta>a:not(.ui):hover{color:rgba(255,255,255,.9)}.ui.inverted.items>.item>.content .favorite.icon:hover{color:#ffc63d}.ui.inverted.items>.item>.content .active.favorite.icon{color:#ffec56}.ui.inverted.items>.item>.content .like.icon:hover{color:#ff5a63}.ui.inverted.items>.item>.content .active.like.icon{color:#ff5a63}.ui.inverted.items>.item .extra{color:rgba(255,255,255,.7)}.ui.inverted.items a.item:hover .content .header,.ui.inverted.link.items>.item:hover .content .header{color:#fff}.ui.inverted.divided.items>.item{border-top:1px solid rgba(255,255,255,.1)}.ui.inverted.divided.items>.item:first-child{border-top:none}/*! + * # Fomantic-UI - Statistic + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.statistic{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:1em 0;max-width:none}.ui.statistic+.ui.statistic{margin:0 0 0 1.5em}.ui.statistic:first-child{margin-top:0}.ui.statistic:last-child{margin-bottom:0}.ui.statistics{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-ms-flex-wrap:wrap;flex-wrap:wrap}.ui.statistics>.statistic{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0 1.5em 1em;max-width:none}.ui.statistics{display:-webkit-box;display:-ms-flexbox;display:flex;margin:1em -1.5em -1em}.ui.statistics:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.statistics:first-child{margin-top:0}.ui.statistic>.value,.ui.statistics .statistic>.value{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:4rem;font-weight:400;line-height:1em;color:#1b1c1d;text-transform:uppercase;text-align:center}.ui.statistic>.label,.ui.statistics .statistic>.label{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1em;font-weight:700;color:rgba(0,0,0,.87);text-transform:uppercase;text-align:center}.ui.statistic>.label~.value,.ui.statistics .statistic>.label~.value{margin-top:0}.ui.statistic>.value~.label,.ui.statistics .statistic>.value~.label{margin-top:0}.ui.statistic>.value .icon,.ui.statistics .statistic>.value .icon{opacity:1;width:auto;margin:0}.ui.statistic>.text.value,.ui.statistics .statistic>.text.value{line-height:1em;min-height:2em;font-weight:700;text-align:center}.ui.statistic>.text.value+.label,.ui.statistics .statistic>.text.value+.label{text-align:center}.ui.statistic>.value img,.ui.statistics .statistic>.value img{max-height:3rem;vertical-align:baseline}.ui.ten.statistics{margin:0 0 -1em}.ui.ten.statistics .statistic{min-width:10%;margin:0 0 1em}.ui.nine.statistics{margin:0 0 -1em}.ui.nine.statistics .statistic{min-width:11.11111111%;margin:0 0 1em}.ui.eight.statistics{margin:0 0 -1em}.ui.eight.statistics .statistic{min-width:12.5%;margin:0 0 1em}.ui.seven.statistics{margin:0 0 -1em}.ui.seven.statistics .statistic{min-width:14.28571429%;margin:0 0 1em}.ui.six.statistics{margin:0 0 -1em}.ui.six.statistics .statistic{min-width:16.66666667%;margin:0 0 1em}.ui.five.statistics{margin:0 0 -1em}.ui.five.statistics .statistic{min-width:20%;margin:0 0 1em}.ui.four.statistics{margin:0 0 -1em}.ui.four.statistics .statistic{min-width:25%;margin:0 0 1em}.ui.three.statistics{margin:0 0 -1em}.ui.three.statistics .statistic{min-width:33.33333333%;margin:0 0 1em}.ui.two.statistics{margin:0 0 -1em}.ui.two.statistics .statistic{min-width:50%;margin:0 0 1em}.ui.one.statistics{margin:0 0 -1em}.ui.one.statistics .statistic{min-width:100%;margin:0 0 1em}.ui.horizontal.statistic{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.horizontal.statistics{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:0;max-width:none}.ui.horizontal.statistics .statistic{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;max-width:none;margin:1em 0}.ui.horizontal.statistic>.text.value,.ui.horizontal.statistics>.statistic>.text.value{min-height:0!important}.ui.horizontal.statistic>.value .icon,.ui.horizontal.statistics .statistic>.value .icon{width:1.18em}.ui.horizontal.statistic>.value,.ui.horizontal.statistics .statistic>.value{display:inline-block;vertical-align:middle}.ui.horizontal.statistic>.label,.ui.horizontal.statistics .statistic>.label{display:inline-block;vertical-align:middle;margin:0 0 0 .75em}.ui.inverted.statistic .value,.ui.inverted.statistics .statistic>.value{color:#fff}.ui.inverted.statistic .label,.ui.inverted.statistics .statistic>.label{color:rgba(255,255,255,.9)}.ui.primary.statistic>.value,.ui.primary.statistics .statistic>.value,.ui.statistics .primary.statistic>.value{color:#2185d0}.ui.inverted.primary.statistic>.value,.ui.inverted.primary.statistics .statistic>.value,.ui.statistics .inverted.primary.statistic>.value{color:#54c8ff}.ui.secondary.statistic>.value,.ui.secondary.statistics .statistic>.value,.ui.statistics .secondary.statistic>.value{color:#1b1c1d}.ui.inverted.secondary.statistic>.value,.ui.inverted.secondary.statistics .statistic>.value,.ui.statistics .inverted.secondary.statistic>.value{color:#545454}.ui.red.statistic>.value,.ui.red.statistics .statistic>.value,.ui.statistics .red.statistic>.value{color:#db2828}.ui.inverted.red.statistic>.value,.ui.inverted.red.statistics .statistic>.value,.ui.statistics .inverted.red.statistic>.value{color:#ff695e}.ui.orange.statistic>.value,.ui.orange.statistics .statistic>.value,.ui.statistics .orange.statistic>.value{color:#f2711c}.ui.inverted.orange.statistic>.value,.ui.inverted.orange.statistics .statistic>.value,.ui.statistics .inverted.orange.statistic>.value{color:#ff851b}.ui.statistics .yellow.statistic>.value,.ui.yellow.statistic>.value,.ui.yellow.statistics .statistic>.value{color:#fbbd08}.ui.inverted.yellow.statistic>.value,.ui.inverted.yellow.statistics .statistic>.value,.ui.statistics .inverted.yellow.statistic>.value{color:#ffe21f}.ui.olive.statistic>.value,.ui.olive.statistics .statistic>.value,.ui.statistics .olive.statistic>.value{color:#b5cc18}.ui.inverted.olive.statistic>.value,.ui.inverted.olive.statistics .statistic>.value,.ui.statistics .inverted.olive.statistic>.value{color:#d9e778}.ui.green.statistic>.value,.ui.green.statistics .statistic>.value,.ui.statistics .green.statistic>.value{color:#21ba45}.ui.inverted.green.statistic>.value,.ui.inverted.green.statistics .statistic>.value,.ui.statistics .inverted.green.statistic>.value{color:#2ecc40}.ui.statistics .teal.statistic>.value,.ui.teal.statistic>.value,.ui.teal.statistics .statistic>.value{color:#00b5ad}.ui.inverted.teal.statistic>.value,.ui.inverted.teal.statistics .statistic>.value,.ui.statistics .inverted.teal.statistic>.value{color:#6dffff}.ui.blue.statistic>.value,.ui.blue.statistics .statistic>.value,.ui.statistics .blue.statistic>.value{color:#2185d0}.ui.inverted.blue.statistic>.value,.ui.inverted.blue.statistics .statistic>.value,.ui.statistics .inverted.blue.statistic>.value{color:#54c8ff}.ui.statistics .violet.statistic>.value,.ui.violet.statistic>.value,.ui.violet.statistics .statistic>.value{color:#6435c9}.ui.inverted.violet.statistic>.value,.ui.inverted.violet.statistics .statistic>.value,.ui.statistics .inverted.violet.statistic>.value{color:#a291fb}.ui.purple.statistic>.value,.ui.purple.statistics .statistic>.value,.ui.statistics .purple.statistic>.value{color:#a333c8}.ui.inverted.purple.statistic>.value,.ui.inverted.purple.statistics .statistic>.value,.ui.statistics .inverted.purple.statistic>.value{color:#dc73ff}.ui.pink.statistic>.value,.ui.pink.statistics .statistic>.value,.ui.statistics .pink.statistic>.value{color:#e03997}.ui.inverted.pink.statistic>.value,.ui.inverted.pink.statistics .statistic>.value,.ui.statistics .inverted.pink.statistic>.value{color:#ff8edf}.ui.brown.statistic>.value,.ui.brown.statistics .statistic>.value,.ui.statistics .brown.statistic>.value{color:#a5673f}.ui.inverted.brown.statistic>.value,.ui.inverted.brown.statistics .statistic>.value,.ui.statistics .inverted.brown.statistic>.value{color:#d67c1c}.ui.grey.statistic>.value,.ui.grey.statistics .statistic>.value,.ui.statistics .grey.statistic>.value{color:#767676}.ui.inverted.grey.statistic>.value,.ui.inverted.grey.statistics .statistic>.value,.ui.statistics .inverted.grey.statistic>.value{color:#dcddde}.ui.black.statistic>.value,.ui.black.statistics .statistic>.value,.ui.statistics .black.statistic>.value{color:#1b1c1d}.ui.inverted.black.statistic>.value,.ui.inverted.black.statistics .statistic>.value,.ui.statistics .inverted.black.statistic>.value{color:#545454}.ui[class*="left floated"].statistic{float:left;margin:0 2em 1em 0}.ui[class*="right floated"].statistic{float:right;margin:0 0 1em 2em}.ui.floated.statistic:last-child{margin-bottom:0}@media only screen and (max-width:767.98px){.ui.stackable.statistics{width:auto;margin-left:0!important;margin-right:0!important}.ui.stackable.statistics>.statistic{width:100%!important;margin:0 0!important;padding:1rem 1rem!important}}.ui.statistic>.value,.ui.statistics .statistic>.value{font-size:4rem}.ui.horizontal.statistic>.value,.ui.horizontal.statistics .statistic>.value{font-size:3rem}.ui.statistic>.text.value,.ui.statistics .statistic>.text.value{font-size:2rem}.ui.mini.statistic>.value,.ui.mini.statistics .statistic>.value{font-size:1.5rem}.ui.mini.horizontal.statistic>.value,.ui.mini.horizontal.statistics .statistic>.value{font-size:1.5rem}.ui.mini.statistic>.text.value,.ui.mini.statistics .statistic>.text.value{font-size:1rem}.ui.tiny.statistic>.value,.ui.tiny.statistics .statistic>.value{font-size:2rem}.ui.tiny.horizontal.statistic>.value,.ui.tiny.horizontal.statistics .statistic>.value{font-size:2rem}.ui.tiny.statistic>.text.value,.ui.tiny.statistics .statistic>.text.value{font-size:1rem}.ui.small.statistic>.value,.ui.small.statistics .statistic>.value{font-size:3rem}.ui.small.horizontal.statistic>.value,.ui.small.horizontal.statistics .statistic>.value{font-size:2rem}.ui.small.statistic>.text.value,.ui.small.statistics .statistic>.text.value{font-size:1rem}.ui.large.statistic>.value,.ui.large.statistics .statistic>.value{font-size:5rem}.ui.large.horizontal.statistic>.value,.ui.large.horizontal.statistics .statistic>.value{font-size:4rem}.ui.large.statistic>.text.value,.ui.large.statistics .statistic>.text.value{font-size:2.5rem}.ui.big.statistic>.value,.ui.big.statistics .statistic>.value{font-size:5.5rem}.ui.big.horizontal.statistic>.value,.ui.big.horizontal.statistics .statistic>.value{font-size:4.5rem}.ui.big.statistic>.text.value,.ui.big.statistics .statistic>.text.value{font-size:2.5rem}.ui.huge.statistic>.value,.ui.huge.statistics .statistic>.value{font-size:6rem}.ui.huge.horizontal.statistic>.value,.ui.huge.horizontal.statistics .statistic>.value{font-size:5rem}.ui.huge.statistic>.text.value,.ui.huge.statistics .statistic>.text.value{font-size:2.5rem}.ui.massive.statistic>.value,.ui.massive.statistics .statistic>.value{font-size:7rem}.ui.massive.horizontal.statistic>.value,.ui.massive.horizontal.statistics .statistic>.value{font-size:6rem}.ui.massive.statistic>.text.value,.ui.massive.statistics .statistic>.text.value{font-size:3rem}/*! + * # Fomantic-UI - Accordion + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.accordion,.ui.accordion .accordion{max-width:100%}.ui.accordion .accordion{margin:1em 0 0;padding:0}.ui.accordion .accordion .title,.ui.accordion .title{cursor:pointer}.ui.accordion .title:not(.ui){padding:.5em 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1em;color:rgba(0,0,0,.87)}.ui.accordion:not(.styled) .accordion .title~.content:not(.ui),.ui.accordion:not(.styled) .title~.content:not(.ui){margin:'';padding:.5em 0 1em}.ui.accordion:not(.styled) .title~.content:not(.ui):last-child{padding-bottom:0}.ui.accordion .accordion .title .dropdown.icon,.ui.accordion .title .dropdown.icon{display:inline-block;float:none;opacity:1;width:1.25em;height:1em;margin:0 .25rem 0 0;padding:0;font-size:1em;-webkit-transition:opacity .1s ease,-webkit-transform .1s ease;transition:opacity .1s ease,-webkit-transform .1s ease;transition:transform .1s ease,opacity .1s ease;transition:transform .1s ease,opacity .1s ease,-webkit-transform .1s ease;vertical-align:baseline;-webkit-transform:none;transform:none}.ui.accordion.menu .item .title{display:block;padding:0}.ui.accordion.menu .item .title>.dropdown.icon{float:right;margin:.21425em 0 0 1em;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.ui.accordion .ui.header .dropdown.icon{font-size:1em;margin:0 .25rem 0 0}.ui.accordion .accordion .active.title .dropdown.icon,.ui.accordion .active.title .dropdown.icon{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.ui.accordion.menu .item .active.title>.dropdown.icon{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.ui.styled.accordion{width:600px}.ui.styled.accordion,.ui.styled.accordion .accordion{border-radius:.28571429rem;background:#fff;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15)}.ui.styled.accordion .accordion .title,.ui.styled.accordion .title{margin:0;padding:.75em 1em;color:rgba(0,0,0,.4);font-weight:700;border-top:1px solid rgba(34,36,38,.15);-webkit-transition:background .1s ease,color .1s ease;transition:background .1s ease,color .1s ease}.ui.styled.accordion .accordion .title:first-child,.ui.styled.accordion>.title:first-child{border-top:none}.ui.styled.accordion .accordion .content,.ui.styled.accordion .content{margin:0;padding:.5em 1em 1.5em}.ui.styled.accordion .accordion .content{margin:0;padding:.5em 1em 1.5em}.ui.styled.accordion .accordion .active.title,.ui.styled.accordion .accordion .title:hover,.ui.styled.accordion .active.title,.ui.styled.accordion .title:hover{background:0 0;color:rgba(0,0,0,.87)}.ui.styled.accordion .accordion .active.title,.ui.styled.accordion .accordion .title:hover{background:0 0;color:rgba(0,0,0,.87)}.ui.styled.accordion .active.title{background:0 0;color:rgba(0,0,0,.95)}.ui.styled.accordion .accordion .active.title{background:0 0;color:rgba(0,0,0,.95)}.ui.accordion .accordion .title~.content:not(.active),.ui.accordion .title~.content:not(.active){display:none}.ui.fluid.accordion,.ui.fluid.accordion .accordion{width:100%}.ui.inverted.accordion .title:not(.ui){color:rgba(255,255,255,.9)}@font-face{font-family:Accordion;src:url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfOIKAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zryj6HgAAAFwAAAAyGhlYWT/0IhHAAACOAAAADZoaGVhApkB5wAAAnAAAAAkaG10eAJuABIAAAKUAAAAGGxvY2EAjABWAAACrAAAAA5tYXhwAAgAFgAAArwAAAAgbmFtZfC1n04AAALcAAABPHBvc3QAAwAAAAAEGAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDZ//3//wAB/+MPKwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQASAEkAtwFuABMAADc0PwE2FzYXFh0BFAcGJwYvASY1EgaABQgHBQYGBQcIBYAG2wcGfwcBAQcECf8IBAcBAQd/BgYAAAAAAQAAAEkApQFuABMAADcRNDc2MzIfARYVFA8BBiMiJyY1AAUGBwgFgAYGgAUIBwYFWwEACAUGBoAFCAcFgAYGBQcAAAABAAAAAQAAqWYls18PPPUACwIAAAAAAM/9o+4AAAAAz/2j7gAAAAAAtwFuAAAACAACAAAAAAAAAAEAAAHg/+AAAAIAAAAAAAC3AAEAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAQAAAAC3ABIAtwAAAAAAAAAKABQAHgBCAGQAAAABAAAABgAUAAEAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADAAAAAEAAAAAAAIADgBAAAEAAAAAAAMADAAiAAEAAAAAAAQADABOAAEAAAAAAAUAFgAMAAEAAAAAAAYABgAuAAEAAAAAAAoANABaAAMAAQQJAAEADAAAAAMAAQQJAAIADgBAAAMAAQQJAAMADAAiAAMAAQQJAAQADABOAAMAAQQJAAUAFgAMAAMAAQQJAAYADAA0AAMAAQQJAAoANABaAHIAYQB0AGkAbgBnAFYAZQByAHMAaQBvAG4AIAAxAC4AMAByAGEAdABpAG4AZ3JhdGluZwByAGEAdABpAG4AZwBSAGUAZwB1AGwAYQByAHIAYQB0AGkAbgBnAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype'),url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAASwAAoAAAAABGgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAS0AAAEtFpovuE9TLzIAAAIkAAAAYAAAAGAIIweQY21hcAAAAoQAAABMAAAATA984gpnYXNwAAAC0AAAAAgAAAAIAAAAEGhlYWQAAALYAAAANgAAADb/0IhHaGhlYQAAAxAAAAAkAAAAJAKZAedobXR4AAADNAAAABgAAAAYAm4AEm1heHAAAANMAAAABgAAAAYABlAAbmFtZQAAA1QAAAE8AAABPPC1n05wb3N0AAAEkAAAACAAAAAgAAMAAAEABAQAAQEBB3JhdGluZwABAgABADr4HAL4GwP4GAQeCgAZU/+Lix4KABlT/4uLDAeLa/iU+HQFHQAAAHkPHQAAAH4RHQAAAAkdAAABJBIABwEBBw0PERQZHnJhdGluZ3JhdGluZ3UwdTF1MjB1RjBEOXVGMERBAAACAYkABAAGAQEEBwoNVp38lA78lA78lA77lA773Z33bxWLkI2Qj44I9xT3FAWOj5CNkIuQi4+JjoePiI2Gi4YIi/uUBYuGiYeHiIiHh4mGi4aLho2Ijwj7FPcUBYeOiY+LkAgO+92L5hWL95QFi5CNkI6Oj4+PjZCLkIuQiY6HCPcU+xQFj4iNhouGi4aJh4eICPsU+xQFiIeGiYaLhouHjYePiI6Jj4uQCA74lBT4lBWLDAoAAAAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDZ//3//wAB/+MPKwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAEAADfYOJZfDzz1AAsCAAAAAADP/aPuAAAAAM/9o+4AAAAAALcBbgAAAAgAAgAAAAAAAAABAAAB4P/gAAACAAAAAAAAtwABAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAEAAAAAtwASALcAAAAAUAAABgAAAAAADgCuAAEAAAAAAAEADAAAAAEAAAAAAAIADgBAAAEAAAAAAAMADAAiAAEAAAAAAAQADABOAAEAAAAAAAUAFgAMAAEAAAAAAAYABgAuAAEAAAAAAAoANABaAAMAAQQJAAEADAAAAAMAAQQJAAIADgBAAAMAAQQJAAMADAAiAAMAAQQJAAQADABOAAMAAQQJAAUAFgAMAAMAAQQJAAYADAA0AAMAAQQJAAoANABaAHIAYQB0AGkAbgBnAFYAZQByAHMAaQBvAG4AIAAxAC4AMAByAGEAdABpAG4AZ3JhdGluZwByAGEAdABpAG4AZwBSAGUAZwB1AGwAYQByAHIAYQB0AGkAbgBnAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff');font-weight:400;font-style:normal}.ui.accordion .accordion .title .dropdown.icon,.ui.accordion .title .dropdown.icon{font-family:Accordion;line-height:1;-webkit-backface-visibility:hidden;backface-visibility:hidden;font-weight:400;font-style:normal;text-align:center}.ui.accordion .accordion .title .dropdown.icon:before,.ui.accordion .title .dropdown.icon:before{content:'\f0da'}/*! + * # Fomantic-UI - Calendar + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.calendar .ui.popup{max-width:none;padding:0;border:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ui.calendar .calendar:focus{outline:0}.ui.calendar .ui.popup .ui.grid{display:block;white-space:nowrap}.ui.calendar .ui.popup .ui.grid>.column{width:auto}.ui.calendar .ui.table.minute,.ui.calendar .ui.table.month,.ui.calendar .ui.table.year{min-width:15em}.ui.calendar .ui.table.day{min-width:18em}.ui.calendar .ui.table.day.andweek{min-width:22em}.ui.calendar .ui.table.hour{min-width:20em}.ui.calendar .ui.table tr td,.ui.calendar .ui.table tr th{padding:.5em;white-space:nowrap}.ui.calendar .ui.table tr th{border-left:none}.ui.calendar .ui.table tr th .icon{margin:0}.ui.calendar .ui.table tr:first-child th{position:relative;padding-left:0;padding-right:0}.ui.calendar .ui.table.day tr:first-child th{border:none}.ui.calendar .ui.table.day tr:nth-child(2) th{padding-top:.2em;padding-bottom:.3em}.ui.calendar .ui.table tr td{padding-left:.1em;padding-right:.1em}.ui.calendar .ui.table tr .link{cursor:pointer}.ui.calendar .ui.table tr .prev.link{width:14.28571429%;position:absolute;left:0}.ui.calendar .ui.table tr .next.link{width:14.28571429%;position:absolute;right:0}.ui.calendar .ui.table tr .disabled{pointer-events:auto;cursor:default;color:rgba(40,40,40,.3)}.ui.calendar .ui.table tr .adjacent:not(.disabled){color:rgba(0,0,0,.6);background:rgba(0,0,0,.03)}.ui.calendar .ui.table tr td.today{font-weight:700}.ui.calendar .ui.table tr td.range{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);-webkit-box-shadow:none;box-shadow:none}.ui.calendar .ui.table.inverted tr td.range{background:rgba(255,255,255,.08);color:#fff;-webkit-box-shadow:none;box-shadow:none}.ui.calendar:not(.disabled) .calendar.active .ui.table tbody tr td.focus,.ui.calendar:not(.disabled) .calendar:focus .ui.table tbody tr td.focus{-webkit-box-shadow:inset 0 0 0 1px #85b7d9;box-shadow:inset 0 0 0 1px #85b7d9}.ui.calendar:not(.disabled) .calendar.active .ui.table.inverted tbody tr td.focus,.ui.calendar:not(.disabled) .calendar:focus .ui.table.inverted tbody tr td.focus{-webkit-box-shadow:inset 0 0 0 1px #85b7d9;box-shadow:inset 0 0 0 1px #85b7d9}.ui.disabled.calendar{opacity:.45}.ui.disabled.calendar .ui.table tr .link,.ui.disabled.calendar>.input{pointer-events:none}/*! + * # Fomantic-UI - Checkbox + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.checkbox{position:relative;display:inline-block;-webkit-backface-visibility:hidden;backface-visibility:hidden;outline:0;vertical-align:baseline;font-style:normal;min-height:17px;font-size:1em;line-height:17px;min-width:17px}.ui.checkbox input[type=checkbox],.ui.checkbox input[type=radio]{cursor:pointer;position:absolute;top:0;left:0;opacity:0!important;outline:0;z-index:3;width:17px;height:17px}.ui.checkbox label{cursor:auto;position:relative;display:block;padding-left:1.85714em;outline:0;font-size:1em}.ui.checkbox label:before{position:absolute;top:0;left:0;width:17px;height:17px;content:'';background:#fff;border-radius:.21428571rem;-webkit-transition:border .1s ease,opacity .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,transform .1s ease,box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,transform .1s ease,box-shadow .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;border:1px solid #d4d4d5}.ui.checkbox label:after{position:absolute;font-size:14px;top:0;left:0;width:17px;height:17px;text-align:center;opacity:0;color:rgba(0,0,0,.87);-webkit-transition:border .1s ease,opacity .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,transform .1s ease,box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,transform .1s ease,box-shadow .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease}.ui.checkbox label,.ui.checkbox+label{color:rgba(0,0,0,.87);-webkit-transition:color .1s ease;transition:color .1s ease}.ui.checkbox+label{vertical-align:middle}.ui.checkbox label:hover::before{background:#fff;border-color:rgba(34,36,38,.35)}.ui.checkbox label:hover,.ui.checkbox+label:hover{color:rgba(0,0,0,.8)}.ui.checkbox label:active::before{background:#f9fafb;border-color:rgba(34,36,38,.35)}.ui.checkbox label:active::after{color:rgba(0,0,0,.95)}.ui.checkbox input:active~label{color:rgba(0,0,0,.95)}.ui.checkbox input:focus~label:before{background:#fff;border-color:#96c8da}.ui.checkbox input:focus~label:after{color:rgba(0,0,0,.95)}.ui.checkbox input:focus~label{color:rgba(0,0,0,.95)}.ui.checkbox input:checked~label:before{background:#fff;border-color:rgba(34,36,38,.35)}.ui.checkbox input:checked~label:after{opacity:1;color:rgba(0,0,0,.95)}.ui.checkbox input:not([type=radio]):indeterminate~label:before{background:#fff;border-color:rgba(34,36,38,.35)}.ui.checkbox input:not([type=radio]):indeterminate~label:after{opacity:1;color:rgba(0,0,0,.95)}.ui.indeterminate.toggle.checkbox input:not([type=radio]):indeterminate~label:before{background:rgba(0,0,0,.15)}.ui.indeterminate.toggle.checkbox input:not([type=radio])~label:after{left:1.075rem}.ui.checkbox input:checked:focus~label:before,.ui.checkbox input:not([type=radio]):indeterminate:focus~label:before{background:#fff;border-color:#96c8da}.ui.checkbox input:checked:focus~label:after,.ui.checkbox input:not([type=radio]):indeterminate:focus~label:after{color:rgba(0,0,0,.95)}.ui.read-only.checkbox,.ui.read-only.checkbox label{cursor:default}.ui.checkbox input[disabled]~label,.ui.disabled.checkbox label{cursor:default!important;opacity:.5;color:#000}.ui.checkbox input.hidden{z-index:-1}.ui.checkbox input.hidden+label{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ui.radio.checkbox{min-height:15px}.ui.radio.checkbox label{padding-left:1.85714em}.ui.radio.checkbox label:before{content:'';-webkit-transform:none;transform:none;width:15px;height:15px;border-radius:500rem;top:1px;left:0}.ui.radio.checkbox label:after{border:none;content:''!important;line-height:15px;top:1px;left:0;width:15px;height:15px;border-radius:500rem;-webkit-transform:scale(.46666667);transform:scale(.46666667);background-color:rgba(0,0,0,.87)}.ui.radio.checkbox input:focus~label:before{background-color:#fff}.ui.radio.checkbox input:focus~label:after{background-color:rgba(0,0,0,.95)}.ui.radio.checkbox input:indeterminate~label:after{opacity:0}.ui.radio.checkbox input:checked~label:before{background-color:#fff}.ui.radio.checkbox input:checked~label:after{background-color:rgba(0,0,0,.95)}.ui.radio.checkbox input:focus:checked~label:before{background-color:#fff}.ui.radio.checkbox input:focus:checked~label:after{background-color:rgba(0,0,0,.95)}.ui.slider.checkbox{min-height:1.25rem}.ui.slider.checkbox input{width:3.5rem;height:1.25rem}.ui.slider.checkbox label{padding-left:4.5rem;line-height:1rem;color:rgba(0,0,0,.4)}.ui.slider.checkbox label:before{display:block;position:absolute;content:'';-webkit-transform:none;transform:none;border:none!important;left:0;z-index:1;top:.4rem;background-color:rgba(0,0,0,.05);width:3.5rem;height:.21428571rem;border-radius:500rem;-webkit-transition:background .3s ease;transition:background .3s ease}.ui.slider.checkbox label:after{background:#fff -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:#fff -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:#fff linear-gradient(transparent,rgba(0,0,0,.05));position:absolute;content:''!important;opacity:1;z-index:2;border:none;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;width:1.5rem;height:1.5rem;top:-.25rem;left:0;-webkit-transform:none;transform:none;border-radius:500rem;-webkit-transition:left .3s ease;transition:left .3s ease}.ui.slider.checkbox input:focus~label:before{background-color:rgba(0,0,0,.15);border:none}.ui.slider.checkbox label:hover{color:rgba(0,0,0,.8)}.ui.slider.checkbox label:hover::before{background:rgba(0,0,0,.15)}.ui.slider.checkbox input:checked~label{color:rgba(0,0,0,.95)!important}.ui.slider.checkbox input:checked~label:before{background-color:#545454!important}.ui.slider.checkbox input:checked~label:after{left:2rem}.ui.slider.checkbox input:focus:checked~label{color:rgba(0,0,0,.95)!important}.ui.slider.checkbox input:focus:checked~label:before{background-color:#000!important}.ui.toggle.checkbox{min-height:1.5rem}.ui.toggle.checkbox input{width:3.5rem;height:1.5rem}.ui.toggle.checkbox label{min-height:1.5rem;padding-left:4.5rem;color:rgba(0,0,0,.87)}.ui.toggle.checkbox label{padding-top:.15em}.ui.toggle.checkbox label:before{display:block;position:absolute;content:'';z-index:1;-webkit-transform:none;transform:none;border:none;top:0;background:rgba(0,0,0,.05);-webkit-box-shadow:none;box-shadow:none;width:3.5rem;height:1.5rem;border-radius:500rem}.ui.toggle.checkbox label:after{background:#fff -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:#fff -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:#fff linear-gradient(transparent,rgba(0,0,0,.05));position:absolute;content:''!important;opacity:1;z-index:2;border:none;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;width:1.5rem;height:1.5rem;top:0;left:0;border-radius:500rem;-webkit-transition:background .3s ease,left .3s ease;transition:background .3s ease,left .3s ease}.ui.toggle.checkbox input~label:after{left:-.05rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset}.ui.toggle.checkbox input:focus~label:before{background-color:rgba(0,0,0,.15);border:none}.ui.toggle.checkbox label:hover::before{background-color:rgba(0,0,0,.15);border:none}.ui.toggle.checkbox input:checked~label{color:rgba(0,0,0,.95)!important}.ui.toggle.checkbox input:checked~label:before{background-color:#2185d0!important}.ui.toggle.checkbox input:checked~label:after{left:2.15rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset}.ui.toggle.checkbox input:focus:checked~label{color:rgba(0,0,0,.95)!important}.ui.toggle.checkbox input:focus:checked~label:before{background-color:#0d71bb!important}.ui.fitted.checkbox label{padding-left:0!important}.ui.fitted.toggle.checkbox{width:3.5rem}.ui.fitted.slider.checkbox{width:3.5rem}.ui.inverted.checkbox label,.ui.inverted.checkbox+label{color:rgba(255,255,255,.9)!important}.ui.inverted.checkbox label:hover{color:#fff!important}.ui.inverted.checkbox label:hover::before{border-color:rgba(34,36,38,.5)}.ui.inverted.slider.checkbox label{color:rgba(255,255,255,.5)}.ui.inverted.slider.checkbox label:before{background-color:rgba(255,255,255,.5)!important}.ui.inverted.slider.checkbox label:hover::before{background:rgba(255,255,255,.7)!important}.ui.inverted.slider.checkbox input:checked~label{color:#fff!important}.ui.inverted.slider.checkbox input:checked~label:before{background-color:rgba(255,255,255,.8)!important}.ui.inverted.slider.checkbox input:focus:checked~label{color:#fff!important}.ui.inverted.slider.checkbox input:focus:checked~label:before{background-color:rgba(255,255,255,.8)!important}.ui.inverted.toggle.checkbox label:before{background-color:rgba(255,255,255,.9)!important}.ui.inverted.toggle.checkbox label:hover::before{background:#fff!important}.ui.inverted.toggle.checkbox input:checked~label{color:#fff!important}.ui.inverted.toggle.checkbox input:checked~label:before{background-color:#2185d0!important}.ui.inverted.toggle.checkbox input:focus:checked~label{color:#fff!important}.ui.inverted.toggle.checkbox input:focus:checked~label:before{background-color:#0d71bb!important}.ui.mini.checkbox{font-size:.78571429em}.ui.tiny.checkbox{font-size:.85714286em}.ui.small.checkbox{font-size:.92857143em}.ui.large.checkbox{font-size:1.14285714em}.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) label:before,.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before{-webkit-transform:scale(1.14285714);transform:scale(1.14285714);-webkit-transform-origin:left;transform-origin:left}.ui.large.checkbox.radio label:before,.ui.large.form .checkbox.radio label:before{-webkit-transform:scale(1.14285714);transform:scale(1.14285714);-webkit-transform-origin:left;transform-origin:left}.ui.large.checkbox.radio label:after,.ui.large.form .checkbox.radio label:after{-webkit-transform:scale(.57142857);transform:scale(.57142857);-webkit-transform-origin:left;transform-origin:left;left:.33571429em}.ui.big.checkbox{font-size:1.28571429em}.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) label:before,.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before{-webkit-transform:scale(1.28571429);transform:scale(1.28571429);-webkit-transform-origin:left;transform-origin:left}.ui.big.checkbox.radio label:before,.ui.big.form .checkbox.radio label:before{-webkit-transform:scale(1.28571429);transform:scale(1.28571429);-webkit-transform-origin:left;transform-origin:left}.ui.big.checkbox.radio label:after,.ui.big.form .checkbox.radio label:after{-webkit-transform:scale(.64285714);transform:scale(.64285714);-webkit-transform-origin:left;transform-origin:left;left:.37142857em}.ui.huge.checkbox{font-size:1.42857143em}.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) label:before,.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before{-webkit-transform:scale(1.42857143);transform:scale(1.42857143);-webkit-transform-origin:left;transform-origin:left}.ui.huge.checkbox.radio label:before,.ui.huge.form .checkbox.radio label:before{-webkit-transform:scale(1.42857143);transform:scale(1.42857143);-webkit-transform-origin:left;transform-origin:left}.ui.huge.checkbox.radio label:after,.ui.huge.form .checkbox.radio label:after{-webkit-transform:scale(.71428571);transform:scale(.71428571);-webkit-transform-origin:left;transform-origin:left;left:.40714286em}.ui.massive.checkbox{font-size:1.71428571em}.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) label:before,.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before{-webkit-transform:scale(1.71428571);transform:scale(1.71428571);-webkit-transform-origin:left;transform-origin:left}.ui.massive.checkbox.radio label:before,.ui.massive.form .checkbox.radio label:before{-webkit-transform:scale(1.71428571);transform:scale(1.71428571);-webkit-transform-origin:left;transform-origin:left}.ui.massive.checkbox.radio label:after,.ui.massive.form .checkbox.radio label:after{-webkit-transform:scale(.85714286);transform:scale(.85714286);-webkit-transform-origin:left;transform-origin:left;left:.47857143em}@font-face{font-family:Checkbox;src:url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBD8AAAC8AAAAYGNtYXAYVtCJAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5Zn4huwUAAAF4AAABYGhlYWQGPe1ZAAAC2AAAADZoaGVhB30DyAAAAxAAAAAkaG10eBBKAEUAAAM0AAAAHGxvY2EAmgESAAADUAAAABBtYXhwAAkALwAAA2AAAAAgbmFtZSC8IugAAAOAAAABknBvc3QAAwAAAAAFFAAAACAAAwMTAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADoAgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6AL//f//AAAAAAAg6AD//f//AAH/4xgEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAEUAUQO7AvgAGgAAARQHAQYjIicBJjU0PwE2MzIfAQE2MzIfARYVA7sQ/hQQFhcQ/uMQEE4QFxcQqAF2EBcXEE4QAnMWEP4UEBABHRAXFhBOEBCoAXcQEE4QFwAAAAABAAABbgMlAkkAFAAAARUUBwYjISInJj0BNDc2MyEyFxYVAyUQEBf9SRcQEBAQFwK3FxAQAhJtFxAQEBAXbRcQEBAQFwAAAAABAAAASQMlA24ALAAAARUUBwYrARUUBwYrASInJj0BIyInJj0BNDc2OwE1NDc2OwEyFxYdATMyFxYVAyUQEBfuEBAXbhYQEO4XEBAQEBfuEBAWbhcQEO4XEBACEm0XEBDuFxAQEBAX7hAQF20XEBDuFxAQEBAX7hAQFwAAAQAAAAIAAHRSzT9fDzz1AAsEAAAAAADRsdR3AAAAANGx1HcAAAAAA7sDbgAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAADuwABAAAAAAAAAAAAAAAAAAAABwQAAAAAAAAAAAAAAAIAAAAEAABFAyUAAAMlAAAAAAAAAAoAFAAeAE4AcgCwAAEAAAAHAC0AAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAIAAAAAQAAAAAAAgAHAGkAAQAAAAAAAwAIADkAAQAAAAAABAAIAH4AAQAAAAAABQALABgAAQAAAAAABgAIAFEAAQAAAAAACgAaAJYAAwABBAkAAQAQAAgAAwABBAkAAgAOAHAAAwABBAkAAwAQAEEAAwABBAkABAAQAIYAAwABBAkABQAWACMAAwABBAkABgAQAFkAAwABBAkACgA0ALBDaGVja2JveABDAGgAZQBjAGsAYgBvAHhWZXJzaW9uIDIuMABWAGUAcgBzAGkAbwBuACAAMgAuADBDaGVja2JveABDAGgAZQBjAGsAYgBvAHhDaGVja2JveABDAGgAZQBjAGsAYgBvAHhSZWd1bGFyAFIAZQBnAHUAbABhAHJDaGVja2JveABDAGgAZQBjAGsAYgBvAHhGb250IGdlbmVyYXRlZCBieSBJY29Nb29uLgBGAG8AbgB0ACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAC4AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype')}.ui.checkbox .box:after,.ui.checkbox label:after{font-family:Checkbox}.ui.checkbox input:checked~.box:after,.ui.checkbox input:checked~label:after{content:'\e800'}.ui.checkbox input:indeterminate~.box:after,.ui.checkbox input:indeterminate~label:after{font-size:12px;content:'\e801'}/*! + * # Fomantic-UI - Dimmer + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.dimmable:not(body){position:relative}.ui.dimmer{display:none;position:absolute;top:0!important;left:0!important;width:100%;height:100%;text-align:center;vertical-align:middle;padding:1em;background-color:rgba(0,0,0,.85);opacity:0;line-height:1;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-transition:background-color .5s linear;transition:background-color .5s linear;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;will-change:opacity;z-index:1000}.ui.dimmer>.content{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;color:#fff}.ui.segment>.ui.dimmer:not(.page){border-radius:inherit}.ui.dimmer:not(.inverted)::-webkit-scrollbar-track{background:rgba(255,255,255,.1)}.ui.dimmer:not(.inverted)::-webkit-scrollbar-thumb{background:rgba(255,255,255,.25)}.ui.dimmer:not(.inverted)::-webkit-scrollbar-thumb:window-inactive{background:rgba(255,255,255,.15)}.ui.dimmer:not(.inverted)::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.35)}.animating.dimmable:not(body),.dimmed.dimmable:not(body){overflow:hidden}.dimmed.dimmable>.ui.animating.dimmer,.dimmed.dimmable>.ui.visible.dimmer,.ui.active.dimmer{display:-webkit-box;display:-ms-flexbox;display:flex;opacity:1}.ui.disabled.dimmer{width:0!important;height:0!important}.dimmed.dimmable>.ui.animating.legacy.dimmer,.dimmed.dimmable>.ui.visible.legacy.dimmer,.ui.active.legacy.dimmer{display:block}.ui[class*="top aligned"].dimmer{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.ui[class*="bottom aligned"].dimmer{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.ui.page.dimmer{position:fixed;-webkit-transform-style:'';transform-style:'';-webkit-perspective:2000px;perspective:2000px;-webkit-transform-origin:center center;transform-origin:center center}body.animating.in.dimmable,body.dimmed.dimmable{overflow:hidden}body.dimmable>.dimmer{position:fixed}.blurring.dimmable>:not(.dimmer){-webkit-filter:initial;filter:initial;-webkit-transition:.8s -webkit-filter ease;transition:.8s -webkit-filter ease;transition:.8s filter ease;transition:.8s filter ease,.8s -webkit-filter ease}.blurring.dimmed.dimmable>:not(.dimmer):not(.popup){-webkit-filter:blur(5px) grayscale(.7);filter:blur(5px) grayscale(.7)}.blurring.dimmable>.dimmer{background-color:rgba(0,0,0,.6)}.blurring.dimmable>.inverted.dimmer{background-color:rgba(255,255,255,.6)}.ui.dimmer>.top.aligned.content>*{vertical-align:top}.ui.dimmer>.bottom.aligned.content>*{vertical-align:bottom}.medium.medium.medium.medium.medium.dimmer{background-color:rgba(0,0,0,.65)}.light.light.light.light.light.dimmer{background-color:rgba(0,0,0,.45)}.very.light.light.light.light.dimmer{background-color:rgba(0,0,0,.25)}.ui.inverted.dimmer{background-color:rgba(255,255,255,.85)}.ui.inverted.dimmer>.content,.ui.inverted.dimmer>.content>*{color:#000}.medium.medium.medium.medium.medium.inverted.dimmer{background-color:rgba(255,255,255,.65)}.light.light.light.light.light.inverted.dimmer{background-color:rgba(255,255,255,.45)}.very.light.light.light.light.inverted.dimmer{background-color:rgba(255,255,255,.25)}.ui.simple.dimmer{display:block;overflow:hidden;opacity:0;width:0;height:0;z-index:-100;background-color:rgba(0,0,0,0)}.dimmed.dimmable>.ui.simple.dimmer{overflow:visible;opacity:1;width:100%;height:100%;background-color:rgba(0,0,0,.85);z-index:1}.ui.simple.inverted.dimmer{background-color:rgba(255,255,255,0)}.dimmed.dimmable>.ui.simple.inverted.dimmer{background-color:rgba(255,255,255,.85)}.ui[class*="bottom dimmer"],.ui[class*="center dimmer"],.ui[class*="top dimmer"]{height:auto}.ui[class*="bottom dimmer"]{top:auto!important;bottom:0}.ui[class*="center dimmer"]{top:50%!important;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}.ui.segment>.ui.ui[class*="top dimmer"]{border-bottom-left-radius:0;border-bottom-right-radius:0}.ui.segment>.ui.ui[class*="center dimmer"]{border-radius:0}.ui.segment>.ui.ui[class*="bottom dimmer"]{border-top-left-radius:0;border-top-right-radius:0}.ui[class*="center dimmer"].transition[class*="fade up"].in{-webkit-animation-name:fadeInUpCenter;animation-name:fadeInUpCenter}.ui[class*="center dimmer"].transition[class*="fade down"].in{-webkit-animation-name:fadeInDownCenter;animation-name:fadeInDownCenter}.ui[class*="center dimmer"].transition[class*="fade up"].out{-webkit-animation-name:fadeOutUpCenter;animation-name:fadeOutUpCenter}.ui[class*="center dimmer"].transition[class*="fade down"].out{-webkit-animation-name:fadeOutDownCenter;animation-name:fadeOutDownCenter}.ui[class*="center dimmer"].bounce.transition{-webkit-animation-name:bounceCenter;animation-name:bounceCenter}@-webkit-keyframes fadeInUpCenter{0%{opacity:0;transform:translateY(-40%);-webkit-transform:translateY(calc(-40% - .5px))}100%{opacity:1;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}}@keyframes fadeInUpCenter{0%{opacity:0;transform:translateY(-40%);-webkit-transform:translateY(calc(-40% - .5px))}100%{opacity:1;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}}@-webkit-keyframes fadeInDownCenter{0%{opacity:0;transform:translateY(-60%);-webkit-transform:translateY(calc(-60% - .5px))}100%{opacity:1;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}}@keyframes fadeInDownCenter{0%{opacity:0;transform:translateY(-60%);-webkit-transform:translateY(calc(-60% - .5px))}100%{opacity:1;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}}@-webkit-keyframes fadeOutUpCenter{0%{opacity:1;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}100%{opacity:0;transform:translateY(-45%);-webkit-transform:translateY(calc(-45% - .5px))}}@keyframes fadeOutUpCenter{0%{opacity:1;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}100%{opacity:0;transform:translateY(-45%);-webkit-transform:translateY(calc(-45% - .5px))}}@-webkit-keyframes fadeOutDownCenter{0%{opacity:1;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}100%{opacity:0;transform:translateY(-55%);-webkit-transform:translateY(calc(-55% - .5px))}}@keyframes fadeOutDownCenter{0%{opacity:1;transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}100%{opacity:0;transform:translateY(-55%);-webkit-transform:translateY(calc(-55% - .5px))}}@-webkit-keyframes bounceCenter{0%,100%,20%,50%,80%{transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}40%{-webkit-transform:translateY(calc(-50% - 30px));transform:translateY(calc(-50% - 30px))}60%{-webkit-transform:translateY(calc(-50% - 15px));transform:translateY(calc(-50% - 15px))}}@keyframes bounceCenter{0%,100%,20%,50%,80%{transform:translateY(-50%);-webkit-transform:translateY(calc(-50% - .5px))}40%{-webkit-transform:translateY(calc(-50% - 30px));transform:translateY(calc(-50% - 30px))}60%{-webkit-transform:translateY(calc(-50% - 15px));transform:translateY(calc(-50% - 15px))}}/*! + * # Fomantic-UI - Dropdown + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.dropdown{cursor:pointer;position:relative;display:inline-block;outline:0;text-align:left;-webkit-transition:width .1s ease,-webkit-box-shadow .1s ease;transition:width .1s ease,-webkit-box-shadow .1s ease;transition:box-shadow .1s ease,width .1s ease;transition:box-shadow .1s ease,width .1s ease,-webkit-box-shadow .1s ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ui.dropdown .menu{cursor:auto;position:absolute;display:none;outline:0;top:100%;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;margin:0;padding:0 0;background:#fff;font-size:1em;text-shadow:none;text-align:left;-webkit-box-shadow:0 2px 3px 0 rgba(34,36,38,.15);box-shadow:0 2px 3px 0 rgba(34,36,38,.15);border:1px solid rgba(34,36,38,.15);border-radius:.28571429rem;-webkit-transition:opacity .1s ease;transition:opacity .1s ease;z-index:11;will-change:transform,opacity}.ui.dropdown .menu>*{white-space:nowrap}.ui.dropdown>input:not(.search):first-child,.ui.dropdown>select{display:none!important}.ui.dropdown:not(.labeled)>.dropdown.icon{position:relative;width:auto;font-size:.85714286em;margin:0 0 0 1em}.ui.dropdown .menu>.item .dropdown.icon{width:auto;float:right;margin:0 0 0 1em}.ui.dropdown .menu>.item .dropdown.icon+.text{margin-right:1em}.ui.dropdown>.text{display:inline-block;-webkit-transition:none;transition:none}.ui.dropdown .menu>.item{position:relative;cursor:pointer;display:block;border:none;height:auto;min-height:2.57142857rem;text-align:left;border-top:none;line-height:1em;font-size:1rem;color:rgba(0,0,0,.87);padding:.78571429rem 1.14285714rem!important;text-transform:none;font-weight:400;-webkit-box-shadow:none;box-shadow:none;-webkit-touch-callout:none}.ui.dropdown .menu>.item:first-child{border-top-width:0}.ui.dropdown .menu .item>[class*="right floated"],.ui.dropdown>.text>[class*="right floated"]{float:right!important;margin-right:0!important;margin-left:1em!important}.ui.dropdown .menu .item>[class*="left floated"],.ui.dropdown>.text>[class*="left floated"]{float:left!important;margin-left:0!important;margin-right:1em!important}.ui.dropdown .menu .item>.flag.floated,.ui.dropdown .menu .item>.icon.floated,.ui.dropdown .menu .item>.image.floated,.ui.dropdown .menu .item>img.floated{margin-top:0}.ui.dropdown .menu>.header{margin:1rem 0 .75rem;padding:0 1.14285714rem;font-weight:700;text-transform:uppercase}.ui.dropdown .menu>.header:not(.ui){color:rgba(0,0,0,.85);font-size:.78571429em}.ui.dropdown .menu>.divider{border-top:1px solid rgba(34,36,38,.1);height:0;margin:.5em 0}.ui.dropdown .menu>.horizontal.divider{border-top:none}.ui.dropdown.dropdown .menu>.input{width:auto;display:-webkit-box;display:-ms-flexbox;display:flex;margin:1.14285714rem .78571429rem;min-width:10rem}.ui.dropdown .menu>.header+.input{margin-top:0}.ui.dropdown .menu>.input:not(.transparent) input{padding:.5em 1em}.ui.dropdown .menu>.input:not(.transparent) .button,.ui.dropdown .menu>.input:not(.transparent) .icon,.ui.dropdown .menu>.input:not(.transparent) .label{padding-top:.5em;padding-bottom:.5em}.ui.dropdown .menu>.item>.description,.ui.dropdown>.text>.description{float:right;margin:0 0 0 1em;color:rgba(0,0,0,.4)}.ui.dropdown .menu>.message{padding:.78571429rem 1.14285714rem;font-weight:400}.ui.dropdown .menu>.message:not(.ui){color:rgba(0,0,0,.4)}.ui.dropdown .menu .menu{top:0;left:100%;right:auto;margin:0 0 0 -.5em!important;border-radius:.28571429rem!important;z-index:21!important}.ui.dropdown .menu .menu:after{display:none}.ui.dropdown>.text>.flag,.ui.dropdown>.text>.icon,.ui.dropdown>.text>.image,.ui.dropdown>.text>.label,.ui.dropdown>.text>img{margin-top:0}.ui.dropdown .menu>.item>.flag,.ui.dropdown .menu>.item>.icon,.ui.dropdown .menu>.item>.image,.ui.dropdown .menu>.item>.label,.ui.dropdown .menu>.item>img{margin-top:0}.ui.dropdown .menu>.item>.flag,.ui.dropdown .menu>.item>.icon,.ui.dropdown .menu>.item>.image,.ui.dropdown .menu>.item>.label,.ui.dropdown .menu>.item>img,.ui.dropdown>.text>.flag,.ui.dropdown>.text>.icon,.ui.dropdown>.text>.image,.ui.dropdown>.text>.label,.ui.dropdown>.text>img{margin-left:0;float:none;margin-right:.78571429rem}.ui.dropdown .menu>.item>.image:not(.icon),.ui.dropdown .menu>.item>img,.ui.dropdown>.text>.image:not(.icon),.ui.dropdown>.text>img{display:inline-block;vertical-align:top;width:auto;margin-top:-.5em;margin-bottom:-.5em;max-height:2em}.ui.dropdown .ui.menu>.item:before,.ui.menu .ui.dropdown .menu>.item:before{display:none}.ui.menu .ui.dropdown .menu .active.item{border-left:none}.ui.buttons>.ui.dropdown:last-child .menu,.ui.menu .right.dropdown.item .menu,.ui.menu .right.menu .dropdown:last-child .menu{left:auto;right:0}.ui.label.dropdown .menu{min-width:100%}.ui.dropdown.icon.button>.dropdown.icon{margin:0}.ui.button.dropdown .menu{min-width:100%}select.ui.dropdown{height:38px;padding:.5em;border:1px solid rgba(34,36,38,.15);visibility:visible}.ui.selection.dropdown{cursor:pointer;word-wrap:break-word;line-height:1em;white-space:normal;outline:0;-webkit-transform:rotateZ(0);transform:rotateZ(0);min-width:14em;min-height:2.71428571em;background:#fff;display:inline-block;padding:.78571429em 3.2em .78571429em 1em;color:rgba(0,0,0,.87);-webkit-box-shadow:none;box-shadow:none;border:1px solid rgba(34,36,38,.15);border-radius:.28571429rem;-webkit-transition:width .1s ease,-webkit-box-shadow .1s ease;transition:width .1s ease,-webkit-box-shadow .1s ease;transition:box-shadow .1s ease,width .1s ease;transition:box-shadow .1s ease,width .1s ease,-webkit-box-shadow .1s ease}.ui.selection.dropdown.active,.ui.selection.dropdown.visible{z-index:10}.ui.selection.dropdown>.delete.icon,.ui.selection.dropdown>.dropdown.icon,.ui.selection.dropdown>.search.icon{cursor:pointer;position:absolute;width:auto;height:auto;line-height:1.21428571em;top:.78571429em;right:1em;z-index:3;margin:-.78571429em;padding:.91666667em;opacity:.8;-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.compact.selection.dropdown{min-width:0}.ui.selection.dropdown .menu{overflow-x:hidden;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-overflow-scrolling:touch;border-top-width:0!important;width:auto;outline:0;margin:0 -1px;min-width:calc(100% + 2px);width:calc(100% + 2px);border-radius:0 0 .28571429rem .28571429rem;-webkit-box-shadow:0 2px 3px 0 rgba(34,36,38,.15);box-shadow:0 2px 3px 0 rgba(34,36,38,.15);-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.selection.dropdown .menu:after,.ui.selection.dropdown .menu:before{display:none}.ui.selection.dropdown .menu>.message{padding:.78571429rem 1.14285714rem}@media only screen and (max-width:767.98px){.ui.selection.dropdown[class*="very short"] .menu{max-height:4.00714286rem}.ui.selection.dropdown.short .menu{max-height:6.01071429rem}.ui.selection.dropdown .menu{max-height:8.01428571rem}.ui.selection.dropdown.long .menu{max-height:16.02857143rem}.ui.selection.dropdown[class*="very long"] .menu{max-height:24.04285714rem}}@media only screen and (min-width:768px){.ui.selection.dropdown[class*="very short"] .menu{max-height:5.34285714rem}.ui.selection.dropdown.short .menu{max-height:8.01428571rem}.ui.selection.dropdown .menu{max-height:10.68571429rem}.ui.selection.dropdown.long .menu{max-height:21.37142857rem}.ui.selection.dropdown[class*="very long"] .menu{max-height:32.05714286rem}}@media only screen and (min-width:992px){.ui.selection.dropdown[class*="very short"] .menu{max-height:8.01428571rem}.ui.selection.dropdown.short .menu{max-height:12.02142857rem}.ui.selection.dropdown .menu{max-height:16.02857143rem}.ui.selection.dropdown.long .menu{max-height:32.05714286rem}.ui.selection.dropdown[class*="very long"] .menu{max-height:48.08571429rem}}@media only screen and (min-width:1920px){.ui.selection.dropdown[class*="very short"] .menu{max-height:10.68571429rem}.ui.selection.dropdown.short .menu{max-height:16.02857143rem}.ui.selection.dropdown .menu{max-height:21.37142857rem}.ui.selection.dropdown.long .menu{max-height:42.74285714rem}.ui.selection.dropdown[class*="very long"] .menu{max-height:64.11428571rem}}.ui.selection.dropdown .menu>.item{border-top:1px solid #fafafa;padding:.78571429rem 1.14285714rem!important;white-space:normal;word-wrap:normal}.ui.selection.dropdown .menu>.hidden.addition.item{display:none}.ui.selection.dropdown:hover{border-color:rgba(34,36,38,.35);-webkit-box-shadow:none;box-shadow:none}.ui.selection.active.dropdown{border-color:#96c8da;-webkit-box-shadow:0 2px 3px 0 rgba(34,36,38,.15);box-shadow:0 2px 3px 0 rgba(34,36,38,.15)}.ui.selection.active.dropdown .menu{border-color:#96c8da;-webkit-box-shadow:0 2px 3px 0 rgba(34,36,38,.15);box-shadow:0 2px 3px 0 rgba(34,36,38,.15)}.ui.selection.dropdown:focus{border-color:#96c8da;-webkit-box-shadow:none;box-shadow:none}.ui.selection.dropdown:focus .menu{border-color:#96c8da;-webkit-box-shadow:0 2px 3px 0 rgba(34,36,38,.15);box-shadow:0 2px 3px 0 rgba(34,36,38,.15)}.ui.selection.visible.dropdown>.text:not(.default){font-weight:400;color:rgba(0,0,0,.8)}.ui.selection.active.dropdown:hover{border-color:#96c8da;-webkit-box-shadow:0 2px 3px 0 rgba(34,36,38,.15);box-shadow:0 2px 3px 0 rgba(34,36,38,.15)}.ui.selection.active.dropdown:hover .menu{border-color:#96c8da;-webkit-box-shadow:0 2px 3px 0 rgba(34,36,38,.15);box-shadow:0 2px 3px 0 rgba(34,36,38,.15)}.ui.active.selection.dropdown>.dropdown.icon,.ui.visible.selection.dropdown>.dropdown.icon{opacity:'';z-index:3}.ui.active.selection.dropdown{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}.ui.active.empty.selection.dropdown{border-radius:.28571429rem!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.active.empty.selection.dropdown .menu{border:none!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.search.dropdown{min-width:''}.ui.search.dropdown>input.search{background:none transparent!important;border:none!important;-webkit-box-shadow:none!important;box-shadow:none!important;cursor:text;top:0;left:1px;width:100%;outline:0;-webkit-tap-highlight-color:rgba(255,255,255,0);padding:inherit}.ui.search.dropdown>input.search{position:absolute;z-index:2}.ui.search.dropdown>.text{cursor:text;position:relative;left:1px;z-index:3}.ui.search.selection.dropdown>input.search{line-height:1.21428571em;padding:.67857143em 3.2em .67857143em 1em}.ui.search.selection.dropdown>span.sizer{line-height:1.21428571em;padding:.67857143em 3.2em .67857143em 1em;display:none;white-space:pre}.ui.search.dropdown.active>input.search,.ui.search.dropdown.visible>input.search{cursor:auto}.ui.search.dropdown.active>.text,.ui.search.dropdown.visible>.text{pointer-events:none}.ui.active.search.dropdown input.search:focus+.text .flag,.ui.active.search.dropdown input.search:focus+.text .icon{opacity:.45}.ui.active.search.dropdown input.search:focus+.text{color:rgba(115,115,115,.87)!important}.ui.search.dropdown .menu{overflow-x:hidden;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-overflow-scrolling:touch}@media only screen and (max-width:767.98px){.ui.search.dropdown .menu{max-height:8.01428571rem}}@media only screen and (min-width:768px){.ui.search.dropdown .menu{max-height:10.68571429rem}}@media only screen and (min-width:992px){.ui.search.dropdown .menu{max-height:16.02857143rem}}@media only screen and (min-width:1920px){.ui.search.dropdown .menu{max-height:21.37142857rem}}.ui.dropdown>.remove.icon{cursor:pointer;font-size:.85714286em;margin:-.78571429em;padding:.91666667em;right:3em;top:.78571429em;position:absolute;opacity:.6;z-index:3}.ui.clearable.dropdown .text,.ui.clearable.dropdown a:last-of-type{margin-right:1.5em}.ui.dropdown input:not([value])~.remove.icon,.ui.dropdown input[value='']~.remove.icon,.ui.dropdown select.noselection~.remove.icon,.ui.dropdown.loading>.remove.icon{display:none}.ui.multiple.dropdown{padding:.22619048em 3.2em .22619048em .35714286em}.ui.multiple.dropdown .menu{cursor:auto}.ui.multiple.dropdown>.label{display:inline-block;white-space:normal;font-size:1em;padding:.35714286em .78571429em;margin:.14285714rem .28571429rem .14285714rem 0;-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset}.ui.multiple.dropdown .dropdown.icon{margin:'';padding:''}.ui.multiple.dropdown>.text{position:static;padding:0;max-width:100%;margin:.45238095em 0 .45238095em .64285714em;line-height:1.21428571em}.ui.multiple.dropdown>.label~input.search{margin-left:.14285714em!important}.ui.multiple.dropdown>.label~.text{display:none}.ui.multiple.dropdown>.label:not(.image)>img:not(.centered){margin-right:.78571429rem}.ui.multiple.dropdown>.label:not(.image)>img.ui:not(.avatar){margin-bottom:.39285714rem}.ui.multiple.dropdown>.image.label img{margin:-.35714286em .78571429em -.35714286em -.78571429em;height:1.71428571em}.ui.multiple.search.dropdown,.ui.multiple.search.dropdown>input.search{cursor:text}.ui.multiple.search.dropdown>.text{display:inline-block;position:absolute;top:0;left:0;padding:inherit;margin:.45238095em 0 .45238095em .64285714em;line-height:1.21428571em}.ui.multiple.search.dropdown>.label~.text{display:none}.ui.multiple.search.dropdown>input.search{position:static;padding:0;max-width:100%;margin:.45238095em 0 .45238095em .64285714em;width:2.2em;line-height:1.21428571em}.ui.inline.dropdown{cursor:pointer;display:inline-block;color:inherit}.ui.inline.dropdown .dropdown.icon{margin:0 .21428571em 0 .21428571em;vertical-align:baseline}.ui.inline.dropdown>.text{font-weight:700}.ui.inline.dropdown .menu{cursor:auto;margin-top:.21428571em;border-radius:.28571429rem}.ui.dropdown .menu .active.item{background:0 0;font-weight:700;color:rgba(0,0,0,.95);-webkit-box-shadow:none;box-shadow:none;z-index:12}.ui.dropdown .menu>.item:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);z-index:13}.ui.default.dropdown:not(.button)>.text,.ui.dropdown:not(.button)>.default.text{color:rgba(191,191,191,.87)}.ui.default.dropdown:not(.button)>input:focus~.text,.ui.dropdown:not(.button)>input:focus~.default.text{color:rgba(115,115,115,.87)}.ui.loading.dropdown>i.icon{height:1em!important}.ui.loading.selection.dropdown>i.icon{padding:1.5em 1.28571429em!important}.ui.loading.dropdown>i.icon:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loading.dropdown>i.icon:after{position:absolute;content:'';top:50%;left:50%;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem}.ui.loading.dropdown.button>i.icon:after,.ui.loading.dropdown.button>i.icon:before{display:none}.ui.loading.dropdown>.text{-webkit-transition:none;transition:none}.ui.dropdown .loading.menu{display:block;visibility:hidden;z-index:-1}.ui.dropdown>.loading.menu{left:0!important;right:auto!important}.ui.dropdown>.menu .loading.menu{left:100%!important;right:auto!important}.ui.dropdown .menu .selected.item,.ui.dropdown.selected{background:rgba(0,0,0,.03);color:rgba(0,0,0,.95)}.ui.dropdown>.filtered.text{visibility:hidden}.ui.dropdown .filtered.item{display:none!important}.ui.dropdown.error,.ui.dropdown.error>.default.text,.ui.dropdown.error>.text{color:#9f3a38}.ui.selection.dropdown.error{background:#fff6f6;border-color:#e0b4b4}.ui.selection.dropdown.error:hover{border-color:#e0b4b4}.ui.multiple.selection.error.dropdown>.label{border-color:#e0b4b4}.ui.dropdown.error>.menu,.ui.dropdown.error>.menu .menu{border-color:#e0b4b4}.ui.dropdown.error>.menu>.item{color:#9f3a38}.ui.dropdown.error>.menu>.item:hover{background-color:#fbe7e7}.ui.dropdown.error>.menu .active.item{background-color:#fdcfcf}.ui.dropdown.info,.ui.dropdown.info>.default.text,.ui.dropdown.info>.text{color:#276f86}.ui.selection.dropdown.info{background:#f8ffff;border-color:#a9d5de}.ui.selection.dropdown.info:hover{border-color:#a9d5de}.ui.multiple.selection.info.dropdown>.label{border-color:#a9d5de}.ui.dropdown.info>.menu,.ui.dropdown.info>.menu .menu{border-color:#a9d5de}.ui.dropdown.info>.menu>.item{color:#276f86}.ui.dropdown.info>.menu>.item:hover{background-color:#e9f2fb}.ui.dropdown.info>.menu .active.item{background-color:#cef1fd}.ui.dropdown.success,.ui.dropdown.success>.default.text,.ui.dropdown.success>.text{color:#2c662d}.ui.selection.dropdown.success{background:#fcfff5;border-color:#a3c293}.ui.selection.dropdown.success:hover{border-color:#a3c293}.ui.multiple.selection.success.dropdown>.label{border-color:#a3c293}.ui.dropdown.success>.menu,.ui.dropdown.success>.menu .menu{border-color:#a3c293}.ui.dropdown.success>.menu>.item{color:#2c662d}.ui.dropdown.success>.menu>.item:hover{background-color:#e9fbe9}.ui.dropdown.success>.menu .active.item{background-color:#dafdce}.ui.dropdown.warning,.ui.dropdown.warning>.default.text,.ui.dropdown.warning>.text{color:#573a08}.ui.selection.dropdown.warning{background:#fffaf3;border-color:#c9ba9b}.ui.selection.dropdown.warning:hover{border-color:#c9ba9b}.ui.multiple.selection.warning.dropdown>.label{border-color:#c9ba9b}.ui.dropdown.warning>.menu,.ui.dropdown.warning>.menu .menu{border-color:#c9ba9b}.ui.dropdown.warning>.menu>.item{color:#573a08}.ui.dropdown.warning>.menu>.item:hover{background-color:#fbfbe9}.ui.dropdown.warning>.menu .active.item{background-color:#fdfdce}.ui.dropdown>.clear.dropdown.icon{opacity:.8;-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.dropdown>.clear.dropdown.icon:hover{opacity:1}.ui.disabled.dropdown,.ui.dropdown .menu>.disabled.item{cursor:default;pointer-events:none;opacity:.45}.ui.dropdown .menu{left:0}.ui.dropdown .menu .right.menu,.ui.dropdown .right.menu>.menu{left:100%!important;right:auto!important;border-radius:.28571429rem!important}.ui.dropdown>.left.menu{left:auto!important;right:0!important}.ui.dropdown .menu .left.menu,.ui.dropdown>.left.menu .menu{left:auto;right:100%;margin:0 -.5em 0 0!important;border-radius:.28571429rem!important}.ui.dropdown .item .left.dropdown.icon,.ui.dropdown .left.menu .item .dropdown.icon{width:auto;float:left;margin:0}.ui.dropdown .item .left.dropdown.icon,.ui.dropdown .left.menu .item .dropdown.icon{width:auto;float:left;margin:0}.ui.dropdown .item .left.dropdown.icon+.text,.ui.dropdown .left.menu .item .dropdown.icon+.text{margin-left:1em;margin-right:0}.ui.upward.dropdown>.menu{top:auto;bottom:100%;-webkit-box-shadow:0 0 3px 0 rgba(0,0,0,.08);box-shadow:0 0 3px 0 rgba(0,0,0,.08);border-radius:.28571429rem .28571429rem 0 0}.ui.dropdown .upward.menu{top:auto!important;bottom:0!important}.ui.simple.upward.active.dropdown,.ui.simple.upward.dropdown:hover{border-radius:.28571429rem .28571429rem 0 0!important}.ui.upward.dropdown.button:not(.pointing):not(.floating).active{border-radius:.28571429rem .28571429rem 0 0}.ui.upward.selection.dropdown .menu{border-top-width:1px!important;border-bottom-width:0!important;-webkit-box-shadow:0 -2px 3px 0 rgba(0,0,0,.08);box-shadow:0 -2px 3px 0 rgba(0,0,0,.08)}.ui.upward.selection.dropdown:hover{-webkit-box-shadow:0 0 2px 0 rgba(0,0,0,.05);box-shadow:0 0 2px 0 rgba(0,0,0,.05)}.ui.active.upward.selection.dropdown{border-radius:0 0 .28571429rem .28571429rem!important}.ui.upward.selection.dropdown.visible{-webkit-box-shadow:0 0 3px 0 rgba(0,0,0,.08);box-shadow:0 0 3px 0 rgba(0,0,0,.08);border-radius:0 0 .28571429rem .28571429rem!important}.ui.upward.active.selection.dropdown:hover{-webkit-box-shadow:0 0 3px 0 rgba(0,0,0,.05);box-shadow:0 0 3px 0 rgba(0,0,0,.05)}.ui.upward.active.selection.dropdown:hover .menu{-webkit-box-shadow:0 -2px 3px 0 rgba(0,0,0,.08);box-shadow:0 -2px 3px 0 rgba(0,0,0,.08)}.ui.dropdown .scrolling.menu,.ui.scrolling.dropdown .menu{overflow-x:hidden;overflow-y:auto}.ui.scrolling.dropdown .menu{overflow-x:hidden;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-overflow-scrolling:touch;min-width:100%!important;width:auto!important}.ui.dropdown .scrolling.menu{position:static;overflow-y:auto;border:none;-webkit-box-shadow:none!important;box-shadow:none!important;border-radius:0!important;margin:0!important;min-width:100%!important;width:auto!important;border-top:1px solid rgba(34,36,38,.15)}.ui.dropdown .scrolling.menu>.item.item.item,.ui.scrolling.dropdown .menu .item.item.item{border-top:none}.ui.dropdown .scrolling.menu .item:first-child,.ui.scrolling.dropdown .menu .item:first-child{border-top:none}.ui.dropdown>.animating.menu .scrolling.menu,.ui.dropdown>.visible.menu .scrolling.menu{display:block}@media all and (-ms-high-contrast:none){.ui.dropdown .scrolling.menu,.ui.scrolling.dropdown .menu{min-width:calc(100% - 17px)}}@media only screen and (max-width:767.98px){.ui.dropdown .scrolling.menu,.ui.scrolling.dropdown .menu{max-height:10.28571429rem}}@media only screen and (min-width:768px){.ui.dropdown .scrolling.menu,.ui.scrolling.dropdown .menu{max-height:15.42857143rem}}@media only screen and (min-width:992px){.ui.dropdown .scrolling.menu,.ui.scrolling.dropdown .menu{max-height:20.57142857rem}}@media only screen and (min-width:1920px){.ui.dropdown .scrolling.menu,.ui.scrolling.dropdown .menu{max-height:20.57142857rem}}.ui.dropdown[class*="two column"]>.menu>.item{display:inline-block;width:50%}.ui.dropdown[class*="three column"]>.menu>.item{display:inline-block;width:33%}.ui.dropdown[class*="four column"]>.menu>.item{display:inline-block;width:25%}.ui.dropdown[class*="five column"]>.menu>.item{display:inline-block;width:20%}.ui.simple.dropdown .menu:after,.ui.simple.dropdown .menu:before{display:none}.ui.simple.dropdown .menu{position:absolute;display:-ms-inline-flexbox!important;display:block;overflow:hidden;top:-9999px;opacity:0;width:0;height:0;-webkit-transition:opacity .1s ease;transition:opacity .1s ease;margin-top:0!important}.ui.simple.active.dropdown,.ui.simple.dropdown:hover{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}.ui.simple.active.dropdown>.menu,.ui.simple.dropdown:hover>.menu{overflow:visible;width:auto;height:auto;top:100%;opacity:1}.ui.simple.dropdown:hover>.menu>.item:hover>.menu,.ui.simple.dropdown>.menu>.item:active>.menu{overflow:visible;width:auto;height:auto;top:0!important;left:100%!important;opacity:1}.ui.simple.disabled.dropdown:hover .menu{display:none;height:0;width:0;overflow:hidden}.ui.simple.visible.dropdown>.menu{display:block}.ui.simple.scrolling.active.dropdown>.menu,.ui.simple.scrolling.dropdown:hover>.menu{overflow-x:hidden;overflow-y:auto}.ui.fluid.dropdown{display:block;width:100%!important;min-width:0}.ui.fluid.dropdown>.dropdown.icon{float:right}.ui.floating.dropdown .menu{left:0;right:auto;-webkit-box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)!important;box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)!important;border-radius:.28571429rem!important}.ui.floating.dropdown>.menu{border-radius:.28571429rem!important}.ui:not(.upward).floating.dropdown>.menu{margin-top:.5em}.ui.upward.floating.dropdown>.menu{margin-bottom:.5em}.ui.pointing.dropdown>.menu{top:100%;margin-top:.78571429rem;border-radius:.28571429rem}.ui.pointing.dropdown>.menu:not(.hidden):after{display:block;position:absolute;pointer-events:none;content:'';visibility:visible;-webkit-transform:rotate(45deg);transform:rotate(45deg);width:.5em;height:.5em;-webkit-box-shadow:-1px -1px 0 0 rgba(34,36,38,.15);box-shadow:-1px -1px 0 0 rgba(34,36,38,.15);background:#fff;z-index:2}.ui.pointing.dropdown>.menu:not(.hidden):after{top:-.25em;left:50%;margin:0 0 0 -.25em}.ui.top.left.pointing.dropdown>.menu{top:100%;bottom:auto;left:0;right:auto;margin:1em 0 0}.ui.top.left.pointing.dropdown>.menu{top:100%;bottom:auto;left:0;right:auto;margin:1em 0 0}.ui.top.left.pointing.dropdown>.menu:after{top:-.25em;left:1em;right:auto;margin:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ui.top.right.pointing.dropdown>.menu{top:100%;bottom:auto;right:0;left:auto;margin:1em 0 0}.ui.top.pointing.dropdown>.left.menu:after,.ui.top.right.pointing.dropdown>.menu:after{top:-.25em;left:auto!important;right:1em!important;margin:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ui.left.pointing.dropdown>.menu{top:0;left:100%;right:auto;margin:0 0 0 1em}.ui.left.pointing.dropdown>.menu:after{top:1em;left:-.25em;margin:0;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.ui.left:not(.top):not(.bottom).pointing.dropdown>.left.menu{left:auto!important;right:100%!important;margin:0 1em 0 0}.ui.left:not(.top):not(.bottom).pointing.dropdown>.left.menu:after{top:1em;left:auto;right:-.25em;margin:0;-webkit-transform:rotate(135deg);transform:rotate(135deg)}.ui.right.pointing.dropdown>.menu{top:0;left:auto;right:100%;margin:0 1em 0 0}.ui.right.pointing.dropdown>.menu:after{top:1em;left:auto;right:-.25em;margin:0;-webkit-transform:rotate(135deg);transform:rotate(135deg)}.ui.bottom.pointing.dropdown>.menu{top:auto;bottom:100%;left:0;right:auto;margin:0 0 1em}.ui.bottom.pointing.dropdown>.menu:after{top:auto;bottom:-.25em;right:auto;margin:0;-webkit-transform:rotate(-135deg);transform:rotate(-135deg)}.ui.bottom.pointing.dropdown>.menu .menu{top:auto!important;bottom:0!important}.ui.bottom.left.pointing.dropdown>.menu{left:0;right:auto}.ui.bottom.left.pointing.dropdown>.menu:after{left:1em;right:auto}.ui.bottom.right.pointing.dropdown>.menu{right:0;left:auto}.ui.bottom.right.pointing.dropdown>.menu:after{left:auto;right:1em}.ui.pointing.upward.dropdown .menu,.ui.top.pointing.upward.dropdown .menu{top:auto!important;bottom:100%!important;margin:0 0 .78571429rem;border-radius:.28571429rem}.ui.pointing.upward.dropdown .menu:after,.ui.top.pointing.upward.dropdown .menu:after{top:100%!important;bottom:auto!important;-webkit-box-shadow:1px 1px 0 0 rgba(34,36,38,.15);box-shadow:1px 1px 0 0 rgba(34,36,38,.15);margin:-.25em 0 0}.ui.right.pointing.upward.dropdown:not(.top):not(.bottom) .menu{top:auto!important;bottom:0!important;margin:0 1em 0 0}.ui.right.pointing.upward.dropdown:not(.top):not(.bottom) .menu:after{top:auto!important;bottom:0!important;margin:0 0 1em 0;-webkit-box-shadow:-1px -1px 0 0 rgba(34,36,38,.15);box-shadow:-1px -1px 0 0 rgba(34,36,38,.15)}.ui.left.pointing.upward.dropdown:not(.top):not(.bottom) .menu{top:auto!important;bottom:0!important;margin:0 0 0 1em}.ui.left.pointing.upward.dropdown:not(.top):not(.bottom) .menu:after{top:auto!important;bottom:0!important;margin:0 0 1em 0;-webkit-box-shadow:-1px -1px 0 0 rgba(34,36,38,.15);box-shadow:-1px -1px 0 0 rgba(34,36,38,.15)}.ui.dropdown,.ui.dropdown .menu>.item{font-size:1rem}.ui.mini.dropdown,.ui.mini.dropdown .menu>.item{font-size:.78571429rem}.ui.tiny.dropdown,.ui.tiny.dropdown .menu>.item{font-size:.85714286rem}.ui.small.dropdown,.ui.small.dropdown .menu>.item{font-size:.92857143rem}.ui.large.dropdown,.ui.large.dropdown .menu>.item{font-size:1.14285714rem}.ui.big.dropdown,.ui.big.dropdown .menu>.item{font-size:1.28571429rem}.ui.huge.dropdown,.ui.huge.dropdown .menu>.item{font-size:1.42857143rem}.ui.massive.dropdown,.ui.massive.dropdown .menu>.item{font-size:1.71428571rem}.ui.inverted.dropdown .menu{background:#1b1c1d;-webkit-box-shadow:none;box-shadow:none;border:1px solid rgba(255,255,255,.15)}.ui.inverted.dropdown .menu>.item{color:rgba(255,255,255,.8)}.ui.inverted.dropdown .menu .active.item{background:0 0;color:rgba(255,255,255,.8);-webkit-box-shadow:none;box-shadow:none}.ui.inverted.dropdown .menu>.item:hover{background:rgba(255,255,255,.08);color:rgba(255,255,255,.8)}.ui.inverted.dropdown .menu .selected.item,.ui.inverted.dropdown.selected{background:rgba(255,255,255,.15);color:rgba(255,255,255,.8)}.ui.inverted.dropdown .menu>.header{color:#fff}.ui.inverted.dropdown .menu>.item>.description,.ui.inverted.dropdown>.text>.description{color:rgba(255,255,255,.5)}.ui.inverted.dropdown .menu>.divider{border-top:1px solid rgba(255,255,255,.15)}.ui.inverted.dropdown .scrolling.menu{border:none;border-top:1px solid rgba(255,255,255,.15)}.ui.inverted.selection.dropdown{border:1px solid rgba(255,255,255,.15);background:#1b1c1d;color:rgba(255,255,255,.8)}.ui.inverted.selection.dropdown:hover{border-color:rgba(255,255,255,.25);-webkit-box-shadow:none;box-shadow:none}.ui.inverted.selection.dropdown input{color:#fff}.ui.inverted.selection.visible.dropdown>.text:not(.default){color:rgba(255,255,255,.9)}.ui.inverted.selection.active.dropdown .menu,.ui.inverted.selection.active.dropdown:hover{border-color:rgba(255,255,255,.15)}.ui.inverted.selection.dropdown .menu>.item{border-top:1px solid #242526}.ui.inverted.default.dropdown:not(.button)>.text,.ui.inverted.dropdown:not(.button)>.default.text{color:rgba(255,255,255,.5)}.ui.inverted.default.dropdown:not(.button)>input:focus~.text,.ui.inverted.dropdown:not(.button)>input:focus~.default.text{color:rgba(255,255,255,.7)}.ui.inverted.active.search.dropdown input.search:focus+.text .flag,.ui.inverted.active.search.dropdown input.search:focus+.text .icon{opacity:.45}.ui.inverted.active.search.dropdown input.search:focus+.text{color:rgba(255,255,255,.7)!important}.ui.inverted.dropdown .menu>.message:not(.ui){color:rgba(255,255,255,.5)}.ui.inverted.dropdown .menu>.item:first-child{border-top-width:0}.ui.inverted.multiple.dropdown>.label{background-color:rgba(255,255,255,.7);background-image:none;color:#000;-webkit-box-shadow:0 0 0 1px rgba(255,255,255,0) inset;box-shadow:0 0 0 1px rgba(255,255,255,0) inset}.ui.inverted.multiple.dropdown>.label:hover{background-color:rgba(255,255,255,.9);border-color:rgba(255,255,255,.9);background-image:none;color:#000}.ui.inverted.multiple.dropdown>.label>.close.icon,.ui.inverted.multiple.dropdown>.label>.delete.icon{opacity:.6}.ui.inverted.multiple.dropdown>.label>.close.icon:hover,.ui.inverted.multiple.dropdown>.label>.delete.icon:hover{opacity:.8}.ui.inverted.dropdown input::-webkit-selection,.ui.inverted.dropdown textarea::-webkit-selection{background-color:rgba(255,255,255,.25);color:rgba(255,255,255,.8)}.ui.inverted.dropdown input::-moz-selection,.ui.inverted.dropdown textarea::-moz-selection{background-color:rgba(255,255,255,.25);color:rgba(255,255,255,.8)}.ui.inverted.dropdown input::selection,.ui.inverted.dropdown textarea::selection{background-color:rgba(255,255,255,.25);color:rgba(255,255,255,.8)}.ui.inverted.dropdown .menu::-webkit-scrollbar-track{background:rgba(255,255,255,.1)}.ui.inverted.dropdown .menu::-webkit-scrollbar-thumb{background:rgba(255,255,255,.25)}.ui.inverted.dropdown .menu::-webkit-scrollbar-thumb:window-inactive{background:rgba(255,255,255,.15)}.ui.inverted.dropdown .menu::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.35)}.ui.inverted.pointing.dropdown>.menu:after{background:#1b1c1d;-webkit-box-shadow:-1px -1px 0 0 rgba(255,255,255,.15);box-shadow:-1px -1px 0 0 rgba(255,255,255,.15)}@font-face{font-family:Dropdown;src:url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfuIIAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zjo82LgAAAFwAAABVGhlYWQAQ88bAAACxAAAADZoaGVhAwcB6QAAAvwAAAAkaG10eAS4ABIAAAMgAAAAIGxvY2EBNgDeAAADQAAAABJtYXhwAAoAFgAAA1QAAAAgbmFtZVcZpu4AAAN0AAABRXBvc3QAAwAAAAAEvAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDX//3//wAB/+MPLQADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAIABJQElABMAABM0NzY3BTYXFhUUDwEGJwYvASY1AAUGBwEACAUGBoAFCAcGgAUBEgcGBQEBAQcECQYHfwYBAQZ/BwYAAQAAAG4BJQESABMAADc0PwE2MzIfARYVFAcGIyEiJyY1AAWABgcIBYAGBgUI/wAHBgWABwaABQWABgcHBgUFBgcAAAABABIASQC3AW4AEwAANzQ/ATYXNhcWHQEUBwYnBi8BJjUSBoAFCAcFBgYFBwgFgAbbBwZ/BwEBBwQJ/wgEBwEBB38GBgAAAAABAAAASQClAW4AEwAANxE0NzYzMh8BFhUUDwEGIyInJjUABQYHCAWABgaABQgHBgVbAQAIBQYGgAUIBwWABgYFBwAAAAEAAAABAADZuaKOXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAAAAACgAUAB4AQgBkAIgAqgAAAAEAAAAIABQAAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAOAAAAAQAAAAAAAgAOAEcAAQAAAAAAAwAOACQAAQAAAAAABAAOAFUAAQAAAAAABQAWAA4AAQAAAAAABgAHADIAAQAAAAAACgA0AGMAAwABBAkAAQAOAAAAAwABBAkAAgAOAEcAAwABBAkAAwAOACQAAwABBAkABAAOAFUAAwABBAkABQAWAA4AAwABBAkABgAOADkAAwABBAkACgA0AGMAaQBjAG8AbQBvAG8AbgBWAGUAcgBzAGkAbwBuACAAMQAuADAAaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AbgBSAGUAZwB1AGwAYQByAGkAYwBvAG0AbwBvAG4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format('truetype'),url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAAVwAAoAAAAABSgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAdkAAAHZLDXE/09TLzIAAALQAAAAYAAAAGAIIweQY21hcAAAAzAAAABMAAAATA9+4ghnYXNwAAADfAAAAAgAAAAIAAAAEGhlYWQAAAOEAAAANgAAADYAQ88baGhlYQAAA7wAAAAkAAAAJAMHAelobXR4AAAD4AAAACAAAAAgBLgAEm1heHAAAAQAAAAABgAAAAYACFAAbmFtZQAABAgAAAFFAAABRVcZpu5wb3N0AAAFUAAAACAAAAAgAAMAAAEABAQAAQEBCGljb21vb24AAQIAAQA6+BwC+BsD+BgEHgoAGVP/i4seCgAZU/+LiwwHi2v4lPh0BR0AAACIDx0AAACNER0AAAAJHQAAAdASAAkBAQgPERMWGyAlKmljb21vb25pY29tb29udTB1MXUyMHVGMEQ3dUYwRDh1RjBEOXVGMERBAAACAYkABgAIAgABAAQABwAKAA0AVgCfAOgBL/yUDvyUDvyUDvuUDvtvi/emFYuQjZCOjo+Pj42Qiwj3lIsFkIuQiY6Hj4iNhouGi4aJh4eHCPsU+xQFiIiGiYaLhouHjYeOCPsU9xQFiI+Jj4uQCA77b4v3FBWLkI2Pjo8I9xT3FAWPjo+NkIuQi5CJjogI9xT7FAWPh42Hi4aLhomHh4eIiIaJhosI+5SLBYaLh42HjoiPiY+LkAgO+92d928Vi5CNkI+OCPcU9xQFjo+QjZCLkIuPiY6Hj4iNhouGCIv7lAWLhomHh4iIh4eJhouGi4aNiI8I+xT3FAWHjomPi5AIDvvdi+YVi/eUBYuQjZCOjo+Pj42Qi5CLkImOhwj3FPsUBY+IjYaLhouGiYeHiAj7FPsUBYiHhomGi4aLh42Hj4iOiY+LkAgO+JQU+JQViwwKAAAAAAMCAAGQAAUAAAFMAWYAAABHAUwBZgAAAPUAGQCEAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA8NoB4P/g/+AB4AAgAAAAAQAAAAAAAAAAAAAAIAAAAAAAAgAAAAMAAAAUAAMAAQAAABQABAA4AAAACgAIAAIAAgABACDw2v/9//8AAAAAACDw1//9//8AAf/jDy0AAwABAAAAAAAAAAAAAAABAAH//wAPAAEAAAABAAA5emozXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAUAAACAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoANABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoANABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff');font-weight:400;font-style:normal}.ui.dropdown>.dropdown.icon{font-family:Dropdown;line-height:1;height:1em;width:1.23em;-webkit-backface-visibility:hidden;backface-visibility:hidden;font-weight:400;font-style:normal;text-align:center}.ui.dropdown>.dropdown.icon{width:auto}.ui.dropdown>.dropdown.icon:before{content:'\f0d7'}.ui.dropdown .menu .item .dropdown.icon:before{content:'\f0da'}.ui.dropdown .item .left.dropdown.icon:before,.ui.dropdown .left.menu .item .dropdown.icon:before{content:"\f0d9"}.ui.vertical.menu .dropdown.item>.dropdown.icon:before{content:"\f0da"}/*! + * # Fomantic-UI - Video + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.embed{position:relative;max-width:100%;height:0;overflow:hidden;background:#dcddde;padding-bottom:56.25%}.ui.embed embed,.ui.embed iframe,.ui.embed object{position:absolute;border:none;width:100%;height:100%;top:0;left:0;margin:0;padding:0;overflow:hidden}.ui.embed>.embed{display:none}.ui.embed>.placeholder{position:absolute;cursor:pointer;top:0;left:0;display:block;width:100%;height:100%;background-color:radial-gradient(transparent 45%,rgba(0,0,0,.3))}.ui.embed>.icon{cursor:pointer;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2}.ui.embed>.icon:after{position:absolute;top:0;left:0;width:100%;height:100%;z-index:3;content:'';background:-webkit-radial-gradient(transparent 45%,rgba(0,0,0,.3));background:radial-gradient(transparent 45%,rgba(0,0,0,.3));opacity:.5;-webkit-transition:opacity .5s ease;transition:opacity .5s ease}.ui.embed>.icon:before{position:absolute;top:50%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);color:#fff;font-size:6rem;text-shadow:0 2px 10px rgba(34,36,38,.2);-webkit-transition:opacity .5s ease,color .5s ease;transition:opacity .5s ease,color .5s ease;z-index:10}.ui.embed .icon:hover:after{background:-webkit-radial-gradient(transparent 45%,rgba(0,0,0,.3));background:radial-gradient(transparent 45%,rgba(0,0,0,.3));opacity:1}.ui.embed .icon:hover:before{color:#fff}.ui.active.embed>.icon,.ui.active.embed>.placeholder{display:none}.ui.active.embed>.embed{display:block}.ui.square.embed{padding-bottom:100%}.ui[class*="4:3"].embed{padding-bottom:75%}.ui[class*="16:9"].embed{padding-bottom:56.25%}.ui[class*="21:9"].embed{padding-bottom:42.85714286%}/*! + * # Fomantic-UI - Modal + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.modal{position:absolute;display:none;z-index:1001;text-align:left;background:#fff;border:none;-webkit-box-shadow:1px 3px 3px 0 rgba(0,0,0,.2),1px 3px 15px 2px rgba(0,0,0,.2);box-shadow:1px 3px 3px 0 rgba(0,0,0,.2),1px 3px 15px 2px rgba(0,0,0,.2);-webkit-transform-origin:50% 25%;transform-origin:50% 25%;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;border-radius:.28571429rem;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;will-change:top,left,margin,transform,opacity}.ui.modal>.dimmer:first-child+.icon+*,.ui.modal>.dimmer:first-child+:not(.icon),.ui.modal>.icon:first-child+*,.ui.modal>:first-child:not(.icon):not(.dimmer){border-top-left-radius:.28571429rem;border-top-right-radius:.28571429rem}.ui.modal>:last-child{border-bottom-left-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.modal>.close{cursor:pointer;position:absolute;top:-2.5rem;right:-2.5rem;z-index:1;opacity:.8;font-size:1.25em;color:#fff;width:2.25rem;height:2.25rem;padding:.625rem 0 0 0}.ui.modal>.close:hover{opacity:1}.ui.modal>.header{display:block;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;background:#fff;margin:0;padding:1.25rem 1.5rem;-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.85);border-bottom:1px solid rgba(34,36,38,.15)}.ui.modal>.header:not(.ui){font-size:1.42857143rem;line-height:1.28571429em;font-weight:700}.ui.modal>.content{display:block;width:100%;font-size:1em;line-height:1.4;padding:1.5rem;background:#fff}.ui.modal>.image.content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.ui.modal>.content>.image{display:block;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:'';-ms-flex-item-align:start;align-self:start;max-width:100%}.ui.modal>[class*="top aligned"]{-ms-flex-item-align:start;align-self:start}.ui.modal>[class*="middle aligned"]{-ms-flex-item-align:center;align-self:center}.ui.modal>[class*=stretched]{-ms-flex-item-align:stretch;align-self:stretch}.ui.modal>.content>.description{display:block;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;min-width:0;-ms-flex-item-align:start;align-self:start}.ui.modal>.content>.icon+.description,.ui.modal>.content>.image+.description{-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;min-width:'';width:auto;padding-left:2em}.ui.modal>.content>.image>i.icon{margin:0;opacity:1;width:auto;line-height:1;font-size:8rem}.ui.modal>.actions{background:#f9fafb;padding:1rem 1rem;border-top:1px solid rgba(34,36,38,.15);text-align:right}.ui.modal .actions>.button{margin-left:.75em}.ui.basic.modal>.actions{border-top:none}@media only screen and (max-width:767.98px){.ui.modal:not(.fullscreen){width:95%;margin:0}}@media only screen and (min-width:768px){.ui.modal:not(.fullscreen){width:88%;margin:0}}@media only screen and (min-width:992px){.ui.modal:not(.fullscreen){width:850px;margin:0}}@media only screen and (min-width:1200px){.ui.modal:not(.fullscreen){width:900px;margin:0}}@media only screen and (min-width:1920px){.ui.modal:not(.fullscreen){width:950px;margin:0}}@media only screen and (max-width:991.98px){.ui.modal>.header{padding-right:2.25rem}.ui.modal>.close{top:1.0535rem;right:1rem;color:rgba(0,0,0,.87)}}@media only screen and (max-width:767.98px){.ui.modal>.header{padding:.75rem 1rem!important;padding-right:2.25rem!important}.ui.overlay.fullscreen.modal>.content.content.content{min-height:calc(100vh - 8.1rem)}.ui.overlay.fullscreen.modal>.scrolling.content.content.content{max-height:calc(100vh - 8.1rem)}.ui.modal>.content{display:block;padding:1rem!important}.ui.modal>.close{top:.5rem!important;right:.5rem!important}.ui.modal .image.content{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.modal .content>.image{display:block;max-width:100%;margin:0 auto!important;text-align:center;padding:0 0 1rem!important}.ui.modal>.content>.image>i.icon{font-size:5rem;text-align:center}.ui.modal .content>.description{display:block;width:100%!important;margin:0!important;padding:1rem 0!important;-webkit-box-shadow:none;box-shadow:none}.ui.modal>.actions{padding:1rem 1rem 0!important}.ui.modal .actions>.button,.ui.modal .actions>.buttons{margin-bottom:1rem}}.ui.inverted.dimmer>.ui.modal{-webkit-box-shadow:1px 3px 10px 2px rgba(0,0,0,.2);box-shadow:1px 3px 10px 2px rgba(0,0,0,.2)}.ui.basic.modal{background-color:transparent;border:none;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;color:#fff}.ui.basic.modal>.actions,.ui.basic.modal>.content,.ui.basic.modal>.header{background-color:transparent}.ui.basic.modal>.header{color:#fff}.ui.basic.modal>.close{top:1rem;right:1.5rem;color:#fff}.ui.inverted.dimmer>.basic.modal{color:rgba(0,0,0,.87)}.ui.inverted.dimmer>.ui.basic.modal>.header{color:rgba(0,0,0,.85)}.ui.legacy.legacy.modal,.ui.legacy.legacy.page.dimmer>.ui.modal{left:50%!important}.ui.legacy.legacy.modal:not(.aligned),.ui.legacy.legacy.page.dimmer>.ui.modal:not(.aligned){top:50%}.ui.legacy.legacy.page.dimmer>.ui.scrolling.modal:not(.aligned),.ui.page.dimmer>.ui.scrolling.legacy.legacy.modal:not(.aligned),.ui.top.aligned.dimmer>.ui.legacy.legacy.modal:not(.aligned),.ui.top.aligned.legacy.legacy.page.dimmer>.ui.modal:not(.aligned){top:auto}.ui.legacy.overlay.fullscreen.modal{margin-top:-2rem!important}.ui.loading.modal{display:block;visibility:hidden;z-index:-1}.ui.active.modal{display:block}.modals.dimmer .ui.top.aligned.modal{top:5vh}.modals.dimmer .ui.bottom.aligned.modal{bottom:5vh}@media only screen and (max-width:767.98px){.modals.dimmer .ui.top.aligned.modal{top:1rem}.modals.dimmer .ui.bottom.aligned.modal{bottom:1rem}}.scrolling.dimmable.dimmed{overflow:hidden}.scrolling.dimmable>.dimmer{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;position:fixed}.scrolling.dimmable.dimmed>.dimmer{overflow:auto;-webkit-overflow-scrolling:touch}.modals.dimmer .ui.scrolling.modal:not(.fullscreen){margin:2rem auto}.modals.dimmer .ui.scrolling.modal:not([class*="overlay fullscreen"])::after{content:'\00A0';position:absolute;height:2rem}.scrolling.undetached.dimmable.dimmed{overflow:auto;-webkit-overflow-scrolling:touch}.scrolling.undetached.dimmable.dimmed>.dimmer{overflow:hidden}.scrolling.undetached.dimmable .ui.scrolling.modal:not(.fullscreen){position:absolute;left:50%}.ui.modal>.scrolling.content{max-height:calc(80vh - 10rem);overflow:auto}.ui.overlay.fullscreen.modal>.content{min-height:calc(100vh - 9.1rem)}.ui.overlay.fullscreen.modal>.scrolling.content{max-height:calc(100vh - 9.1rem)}.ui.fullscreen.modal{width:95%;left:2.5%;margin:1em auto}.ui.overlay.fullscreen.modal{width:100%;left:0;margin:0 auto;top:0;border-radius:0}.ui.fullscreen.modal>.header,.ui.modal>.close.inside+.header{padding-right:2.25rem}.ui.fullscreen.modal>.close,.ui.modal>.close.inside{top:1.0535rem;right:1rem;color:rgba(0,0,0,.87)}.ui.basic.fullscreen.modal>.close{color:#fff}.ui.modal{font-size:1rem}.ui.mini.modal>.header:not(.ui){font-size:1.3em}@media only screen and (max-width:767.98px){.ui.mini.modal{width:95%;margin:0}}@media only screen and (min-width:768px){.ui.mini.modal{width:35.2%;margin:0}}@media only screen and (min-width:992px){.ui.mini.modal{width:340px;margin:0}}@media only screen and (min-width:1200px){.ui.mini.modal{width:360px;margin:0}}@media only screen and (min-width:1920px){.ui.mini.modal{width:380px;margin:0}}.ui.tiny.modal>.header:not(.ui){font-size:1.3em}@media only screen and (max-width:767.98px){.ui.tiny.modal{width:95%;margin:0}}@media only screen and (min-width:768px){.ui.tiny.modal{width:52.8%;margin:0}}@media only screen and (min-width:992px){.ui.tiny.modal{width:510px;margin:0}}@media only screen and (min-width:1200px){.ui.tiny.modal{width:540px;margin:0}}@media only screen and (min-width:1920px){.ui.tiny.modal{width:570px;margin:0}}.ui.small.modal>.header:not(.ui){font-size:1.3em}@media only screen and (max-width:767.98px){.ui.small.modal{width:95%;margin:0}}@media only screen and (min-width:768px){.ui.small.modal{width:70.4%;margin:0}}@media only screen and (min-width:992px){.ui.small.modal{width:680px;margin:0}}@media only screen and (min-width:1200px){.ui.small.modal{width:720px;margin:0}}@media only screen and (min-width:1920px){.ui.small.modal{width:760px;margin:0}}.ui.large.modal>.header:not(.ui){font-size:1.6em}@media only screen and (max-width:767.98px){.ui.large.modal{width:95%;margin:0}}@media only screen and (min-width:768px){.ui.large.modal{width:88%;margin:0}}@media only screen and (min-width:992px){.ui.large.modal{width:1020px;margin:0}}@media only screen and (min-width:1200px){.ui.large.modal{width:1080px;margin:0}}@media only screen and (min-width:1920px){.ui.large.modal{width:1140px;margin:0}}.ui.big.modal>.header:not(.ui){font-size:1.6em}@media only screen and (max-width:767.98px){.ui.big.modal{width:95%;margin:0}}@media only screen and (min-width:768px){.ui.big.modal{width:88%;margin:0}}@media only screen and (min-width:992px){.ui.big.modal{width:1190px;margin:0}}@media only screen and (min-width:1200px){.ui.big.modal{width:1260px;margin:0}}@media only screen and (min-width:1920px){.ui.big.modal{width:1330px;margin:0}}.ui.huge.modal>.header:not(.ui){font-size:1.6em}@media only screen and (max-width:767.98px){.ui.huge.modal{width:95%;margin:0}}@media only screen and (min-width:768px){.ui.huge.modal{width:88%;margin:0}}@media only screen and (min-width:992px){.ui.huge.modal{width:1360px;margin:0}}@media only screen and (min-width:1200px){.ui.huge.modal{width:1440px;margin:0}}@media only screen and (min-width:1920px){.ui.huge.modal{width:1520px;margin:0}}.ui.massive.modal>.header:not(.ui){font-size:1.8em}@media only screen and (max-width:767.98px){.ui.massive.modal{width:95%;margin:0}}@media only screen and (min-width:768px){.ui.massive.modal{width:88%;margin:0}}@media only screen and (min-width:992px){.ui.massive.modal{width:1530px;margin:0}}@media only screen and (min-width:1200px){.ui.massive.modal{width:1620px;margin:0}}@media only screen and (min-width:1920px){.ui.massive.modal{width:1710px;margin:0}}.ui.inverted.modal{background:rgba(0,0,0,.9)}.ui.inverted.modal>.content,.ui.inverted.modal>.header{background:rgba(0,0,0,.9);color:#fff}.ui.inverted.modal>.actions{background:#191a1b;border-top:1px solid rgba(34,36,38,.85);color:#fff}.ui.inverted.dimmer>.modal>.close{color:rgba(0,0,0,.85)}@media only screen and (max-width:991.98px){.ui.dimmer .inverted.modal>.close{color:#fff}}.ui.inverted.fullscreen.modal>.close,.ui.inverted.modal>.close.inside{color:#fff}/*! + * # Fomantic-UI - Nag + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.nag{display:none;opacity:.95;position:relative;top:0;left:0;z-index:999;min-height:0;width:100%;margin:0;padding:.75em 1em;background:#555;-webkit-box-shadow:0 1px 2px 0 rgba(0,0,0,.2);box-shadow:0 1px 2px 0 rgba(0,0,0,.2);font-size:1rem;text-align:center;color:rgba(0,0,0,.87);border-radius:0 0 .28571429rem .28571429rem;-webkit-transition:.2s background ease;transition:.2s background ease}a.ui.nag{cursor:pointer}.ui.nag>.title{display:inline-block;margin:0 .5em;color:#fff}.ui.nag>.close.icon{cursor:pointer;opacity:.4;position:absolute;top:50%;right:1em;font-size:1em;margin:-.5em 0 0;color:#fff;-webkit-transition:opacity .2s ease;transition:opacity .2s ease}.ui.nag:hover{background:#555;opacity:1}.ui.nag .close:hover{opacity:1}.ui.overlay.nag{position:absolute;display:block}.ui.fixed.nag{position:fixed}.ui.bottom.nag,.ui.bottom.nags{border-radius:.28571429rem .28571429rem 0 0;top:auto;bottom:0}.ui.inverted.nag,.ui.inverted.nags .nag{background-color:#f3f4f5;color:rgba(0,0,0,.85)}.ui.inverted.nag .close,.ui.inverted.nag .title,.ui.inverted.nags .nag .close,.ui.inverted.nags .nag .title{color:rgba(0,0,0,.4)}.ui.nags .nag{border-radius:0!important}.ui.nags .nag:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.bottom.nags .nag:last-child{border-radius:.28571429rem .28571429rem 0 0}/*! + * # Fomantic-UI - Popup + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.popup{display:none;position:absolute;top:0;right:0;min-width:-webkit-min-content;min-width:-moz-min-content;min-width:min-content;z-index:1900;border:1px solid #d4d4d5;line-height:1.4285em;max-width:250px;background:#fff;padding:.833em 1em;font-weight:400;font-style:normal;color:rgba(0,0,0,.87);border-radius:.28571429rem;-webkit-box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.popup>.header{padding:0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1.14285714em;line-height:1.2;font-weight:700}.ui.popup>.header+.content{padding-top:.5em}.ui.popup:before{position:absolute;content:'';width:.71428571em;height:.71428571em;background:#fff;-webkit-transform:rotate(45deg);transform:rotate(45deg);z-index:1901;-webkit-box-shadow:1px 1px 0 0 #bababc;box-shadow:1px 1px 0 0 #bababc}[data-tooltip]{position:relative}[data-tooltip]:before{pointer-events:none;position:absolute;content:'';font-size:1rem;width:.71428571em;height:.71428571em;background:#fff;-webkit-transform:rotate(45deg);transform:rotate(45deg);z-index:1901;-webkit-box-shadow:1px 1px 0 0 #bababc;box-shadow:1px 1px 0 0 #bababc}[data-tooltip]:after{pointer-events:none;content:attr(data-tooltip);position:absolute;text-transform:none;text-align:left;white-space:nowrap;font-size:1rem;border:1px solid #d4d4d5;line-height:1.4285em;max-width:none;background:#fff;padding:.833em 1em;font-weight:400;font-style:normal;color:rgba(0,0,0,.87);border-radius:.28571429rem;-webkit-box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);z-index:1900}[data-tooltip]:not([data-position]):before{top:auto;right:auto;bottom:100%;left:50%;background:#fff;margin-left:-.07142857rem;margin-bottom:.14285714rem}[data-tooltip]:not([data-position]):after{left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);bottom:100%;margin-bottom:.5em}[data-tooltip]:after,[data-tooltip]:before{pointer-events:none;visibility:hidden;opacity:0;-webkit-transition:opacity .1s ease,-webkit-transform .1s ease;transition:opacity .1s ease,-webkit-transform .1s ease;transition:transform .1s ease,opacity .1s ease;transition:transform .1s ease,opacity .1s ease,-webkit-transform .1s ease}[data-tooltip]:before{-webkit-transform:rotate(45deg) scale(0)!important;transform:rotate(45deg) scale(0)!important;-webkit-transform-origin:center top;transform-origin:center top}[data-tooltip]:after{-webkit-transform-origin:center bottom;transform-origin:center bottom}[data-tooltip]:hover:after,[data-tooltip]:hover:before{visibility:visible;pointer-events:auto;opacity:1}[data-tooltip]:hover:before{-webkit-transform:rotate(45deg) scale(1)!important;transform:rotate(45deg) scale(1)!important}[data-tooltip]:after,[data-tooltip][data-position="bottom center"]:after,[data-tooltip][data-position="top center"]:after{-webkit-transform:translateX(-50%) scale(0)!important;transform:translateX(-50%) scale(0)!important}[data-tooltip]:hover:after,[data-tooltip][data-position="bottom center"]:hover:after{-webkit-transform:translateX(-50%) scale(1)!important;transform:translateX(-50%) scale(1)!important}[data-tooltip][data-position="left center"]:after,[data-tooltip][data-position="right center"]:after{-webkit-transform:translateY(-50%) scale(0)!important;transform:translateY(-50%) scale(0)!important}[data-tooltip][data-position="left center"]:hover:after,[data-tooltip][data-position="right center"]:hover:after{-webkit-transform:translateY(-50%) scale(1)!important;transform:translateY(-50%) scale(1)!important}[data-tooltip][data-position="bottom left"]:after,[data-tooltip][data-position="bottom right"]:after,[data-tooltip][data-position="top left"]:after,[data-tooltip][data-position="top right"]:after{-webkit-transform:scale(0)!important;transform:scale(0)!important}[data-tooltip][data-position="bottom left"]:hover:after,[data-tooltip][data-position="bottom right"]:hover:after,[data-tooltip][data-position="top left"]:hover:after,[data-tooltip][data-position="top right"]:hover:after{-webkit-transform:scale(1)!important;transform:scale(1)!important}[data-tooltip][data-variation~=fixed]:after{white-space:normal;width:250px}[data-tooltip][data-variation*="wide fixed"]:after{width:350px}[data-tooltip][data-variation*="very wide fixed"]:after{width:550px}@media only screen and (max-width:767.98px){[data-tooltip][data-variation~=fixed]:after{width:250px}}[data-tooltip][data-inverted]:before{-webkit-box-shadow:none!important;box-shadow:none!important}[data-tooltip][data-inverted]:before{background:#1b1c1d}[data-tooltip][data-inverted]:after{background:#1b1c1d;color:#fff;border:none;-webkit-box-shadow:none;box-shadow:none}[data-tooltip][data-inverted]:after .header{background:0 0;color:#fff}[data-position~=top][data-tooltip]:before{background:#fff}[data-position="top center"][data-tooltip]:after{top:auto;right:auto;left:50%;bottom:100%;-webkit-transform:translateX(-50%);transform:translateX(-50%);margin-bottom:.5em}[data-position="top center"][data-tooltip]:before{top:auto;right:auto;bottom:100%;left:50%;background:#fff;margin-left:-.07142857rem;margin-bottom:.14285714rem}[data-position="top left"][data-tooltip]:after{top:auto;right:auto;left:0;bottom:100%;margin-bottom:.5em}[data-position="top left"][data-tooltip]:before{top:auto;right:auto;bottom:100%;left:1em;margin-left:-.07142857rem;margin-bottom:.14285714rem}[data-position="top right"][data-tooltip]:after{top:auto;left:auto;right:0;bottom:100%;margin-bottom:.5em}[data-position="top right"][data-tooltip]:before{top:auto;left:auto;bottom:100%;right:1em;margin-left:-.07142857rem;margin-bottom:.14285714rem}[data-position~=bottom][data-tooltip]:before{background:#fff;-webkit-box-shadow:-1px -1px 0 0 #bababc;box-shadow:-1px -1px 0 0 #bababc}[data-position="bottom center"][data-tooltip]:after{bottom:auto;right:auto;left:50%;top:100%;-webkit-transform:translateX(-50%);transform:translateX(-50%);margin-top:.5em}[data-position="bottom center"][data-tooltip]:before{bottom:auto;right:auto;top:100%;left:50%;margin-left:-.07142857rem;margin-top:.14285714rem}[data-position="bottom left"][data-tooltip]:after{left:0;top:100%;margin-top:.5em}[data-position="bottom left"][data-tooltip]:before{bottom:auto;right:auto;top:100%;left:1em;margin-left:-.07142857rem;margin-top:.14285714rem}[data-position="bottom right"][data-tooltip]:after{right:0;top:100%;margin-top:.5em}[data-position="bottom right"][data-tooltip]:before{bottom:auto;left:auto;top:100%;right:1em;margin-left:-.14285714rem;margin-top:.07142857rem}[data-position="left center"][data-tooltip]:after{right:100%;top:50%;margin-right:.5em;-webkit-transform:translateY(-50%);transform:translateY(-50%)}[data-position="left center"][data-tooltip]:before{right:100%;top:50%;margin-top:-.14285714rem;margin-right:-.07142857rem;background:#fff;-webkit-box-shadow:1px -1px 0 0 #bababc;box-shadow:1px -1px 0 0 #bababc}[data-position="right center"][data-tooltip]:after{left:100%;top:50%;margin-left:.5em;-webkit-transform:translateY(-50%);transform:translateY(-50%)}[data-position="right center"][data-tooltip]:before{left:100%;top:50%;margin-top:-.07142857rem;margin-left:.14285714rem;background:#fff;-webkit-box-shadow:-1px 1px 0 0 #bababc;box-shadow:-1px 1px 0 0 #bababc}[data-inverted][data-position~=bottom][data-tooltip]:before{background:#1b1c1d;-webkit-box-shadow:-1px -1px 0 0 #bababc;box-shadow:-1px -1px 0 0 #bababc}[data-inverted][data-position="left center"][data-tooltip]:before{background:#1b1c1d;-webkit-box-shadow:1px -1px 0 0 #bababc;box-shadow:1px -1px 0 0 #bababc}[data-inverted][data-position="right center"][data-tooltip]:before{background:#1b1c1d;-webkit-box-shadow:-1px 1px 0 0 #bababc;box-shadow:-1px 1px 0 0 #bababc}[data-inverted][data-position~=top][data-tooltip]:before{background:#1b1c1d}[data-position~=bottom][data-tooltip]:before{-webkit-transform-origin:center bottom;transform-origin:center bottom}[data-position~=bottom][data-tooltip]:after{-webkit-transform-origin:center top;transform-origin:center top}[data-position="left center"][data-tooltip]:before{-webkit-transform-origin:top center;transform-origin:top center}[data-position="left center"][data-tooltip]:after{-webkit-transform-origin:right center;transform-origin:right center}[data-position="right center"][data-tooltip]:before{-webkit-transform-origin:right center;transform-origin:right center}[data-position="right center"][data-tooltip]:after{-webkit-transform-origin:left center;transform-origin:left center}[data-tooltip][data-variation~=basic]:before{display:none}.ui.popup{margin:0}.ui.top.popup{margin:0 0 .71428571em}.ui.top.left.popup{-webkit-transform-origin:left bottom;transform-origin:left bottom}.ui.top.center.popup{-webkit-transform-origin:center bottom;transform-origin:center bottom}.ui.top.right.popup{-webkit-transform-origin:right bottom;transform-origin:right bottom}.ui.left.center.popup{margin:0 .71428571em 0 0;-webkit-transform-origin:right 50%;transform-origin:right 50%}.ui.right.center.popup{margin:0 0 0 .71428571em;-webkit-transform-origin:left 50%;transform-origin:left 50%}.ui.bottom.popup{margin:.71428571em 0 0}.ui.bottom.left.popup{-webkit-transform-origin:left top;transform-origin:left top}.ui.bottom.center.popup{-webkit-transform-origin:center top;transform-origin:center top}.ui.bottom.right.popup{-webkit-transform-origin:right top;transform-origin:right top}.ui.bottom.center.popup:before{margin-left:-.30714286em;top:-.30714286em;left:50%;right:auto;bottom:auto;-webkit-box-shadow:-1px -1px 0 0 #bababc;box-shadow:-1px -1px 0 0 #bababc}.ui.bottom.left.popup{margin-left:0}.ui.bottom.left.popup:before{top:-.30714286em;left:1em;right:auto;bottom:auto;margin-left:0;-webkit-box-shadow:-1px -1px 0 0 #bababc;box-shadow:-1px -1px 0 0 #bababc}.ui.bottom.right.popup{margin-right:0}.ui.bottom.right.popup:before{top:-.30714286em;right:1em;bottom:auto;left:auto;margin-left:0;-webkit-box-shadow:-1px -1px 0 0 #bababc;box-shadow:-1px -1px 0 0 #bababc}.ui.top.center.popup:before{top:auto;right:auto;bottom:-.30714286em;left:50%;margin-left:-.30714286em}.ui.top.left.popup{margin-left:0}.ui.top.left.popup:before{bottom:-.30714286em;left:1em;top:auto;right:auto;margin-left:0}.ui.top.right.popup{margin-right:0}.ui.top.right.popup:before{bottom:-.30714286em;right:1em;top:auto;left:auto;margin-left:0}.ui.left.center.popup:before{top:50%;right:-.30714286em;bottom:auto;left:auto;margin-top:-.30714286em;-webkit-box-shadow:1px -1px 0 0 #bababc;box-shadow:1px -1px 0 0 #bababc}.ui.right.center.popup:before{top:50%;left:-.30714286em;bottom:auto;right:auto;margin-top:-.30714286em;-webkit-box-shadow:-1px 1px 0 0 #bababc;box-shadow:-1px 1px 0 0 #bababc}.ui.left.center.popup:before,.ui.right.center.popup:before{background:#fff}.ui.bottom.popup:before{background:#fff}.ui.top.popup:before{background:#fff}.ui.inverted.bottom.popup:before{background:#1b1c1d}.ui.inverted.left.center.popup:before,.ui.inverted.right.center.popup:before{background:#1b1c1d}.ui.inverted.top.popup:before{background:#1b1c1d}.ui.popup>.ui.grid:not(.padded){width:calc(100% + 1.75rem);margin:-.7rem -.875rem}.ui.loading.popup{display:block;visibility:hidden;z-index:-1}.ui.animating.popup,.ui.visible.popup{display:block}.ui.visible.popup{-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.ui.basic.popup:before{display:none}.ui.fixed.popup{width:250px}.ui.wide.popup{max-width:350px}.ui.wide.popup.fixed{width:350px}.ui[class*="very wide"].popup{max-width:550px}.ui[class*="very wide"].popup.fixed{width:550px}@media only screen and (max-width:767.98px){.ui.wide.popup,.ui[class*="very wide"].popup{max-width:250px}.ui.wide.popup.fixed,.ui[class*="very wide"].popup.fixed{width:250px}}.ui.fluid.popup{width:100%;max-width:none}.ui.inverted.popup{background:#1b1c1d;color:#fff;border:none;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.popup .header{background-color:none;color:#fff}.ui.inverted.popup:before{background-color:#1b1c1d;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.flowing.popup{max-width:none}.ui.popup{font-size:1rem}.ui.mini.popup{font-size:.78571429rem}[data-tooltip][data-variation~=mini]:after,[data-tooltip][data-variation~=mini]:before{font-size:.78571429rem}.ui.tiny.popup{font-size:.85714286rem}[data-tooltip][data-variation~=tiny]:after,[data-tooltip][data-variation~=tiny]:before{font-size:.85714286rem}.ui.small.popup{font-size:.92857143rem}[data-tooltip][data-variation~=small]:after,[data-tooltip][data-variation~=small]:before{font-size:.92857143rem}.ui.large.popup{font-size:1.14285714rem}[data-tooltip][data-variation~=large]:after,[data-tooltip][data-variation~=large]:before{font-size:1.14285714rem}.ui.big.popup{font-size:1.28571429rem}[data-tooltip][data-variation~=big]:after,[data-tooltip][data-variation~=big]:before{font-size:1.28571429rem}.ui.huge.popup{font-size:1.42857143rem}[data-tooltip][data-variation~=huge]:after,[data-tooltip][data-variation~=huge]:before{font-size:1.42857143rem}.ui.massive.popup{font-size:1.71428571rem}[data-tooltip][data-variation~=massive]:after,[data-tooltip][data-variation~=massive]:before{font-size:1.71428571rem}/*! + * # Fomantic-UI - Progress Bar + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.progress{position:relative;display:block;max-width:100%;border:none;margin:1em 0 2.5em;-webkit-box-shadow:none;box-shadow:none;background:rgba(0,0,0,.1);padding:0;border-radius:.28571429rem}.ui.progress:first-child{margin:0 0 2.5em}.ui.progress:last-child{margin:0 0 1.5em}.ui.progress .bar{display:block;line-height:1;position:relative;width:0;min-width:2em;background:#888;border-radius:.28571429rem;-webkit-transition:width .1s ease,background-color .1s ease;transition:width .1s ease,background-color .1s ease;overflow:hidden}.ui.ui.ui.progress:not([data-percent]):not(.indeterminate) .bar,.ui.ui.ui.progress[data-percent="0"]:not(.indeterminate) .bar{background:0 0}.ui.progress[data-percent="0"] .bar .progress{color:rgba(0,0,0,.87)}.ui.inverted.progress[data-percent="0"] .bar .progress{color:rgba(255,255,255,.9)}.ui.progress .bar>.progress{white-space:nowrap;position:absolute;width:auto;font-size:.92857143em;top:50%;right:.5em;left:auto;bottom:auto;color:rgba(255,255,255,.7);text-shadow:none;margin-top:-.5em;font-weight:700;text-align:left}.ui.progress>.label{position:absolute;width:100%;font-size:1em;top:100%;right:auto;left:0;bottom:auto;color:rgba(0,0,0,.87);font-weight:700;text-shadow:none;margin-top:.2em;text-align:center;-webkit-transition:color .4s ease;transition:color .4s ease}.ui.indicating.progress[data-percent^="1"] .bar,.ui.indicating.progress[data-percent^="2"] .bar{background-color:#d95c5c}.ui.indicating.progress[data-percent^="3"] .bar{background-color:#efbc72}.ui.indicating.progress[data-percent^="4"] .bar,.ui.indicating.progress[data-percent^="5"] .bar{background-color:#e6bb48}.ui.indicating.progress[data-percent^="6"] .bar{background-color:#ddc928}.ui.indicating.progress[data-percent^="7"] .bar,.ui.indicating.progress[data-percent^="8"] .bar{background-color:#b4d95c}.ui.indicating.progress[data-percent^="100"] .bar,.ui.indicating.progress[data-percent^="9"] .bar{background-color:#66da81}.ui.indicating.progress[data-percent^="1"] .label,.ui.indicating.progress[data-percent^="2"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="3"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="4"] .label,.ui.indicating.progress[data-percent^="5"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="6"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="7"] .label,.ui.indicating.progress[data-percent^="8"] .label{color:rgba(0,0,0,.87)}.ui.indicating.progress[data-percent^="100"] .label,.ui.indicating.progress[data-percent^="9"] .label{color:rgba(0,0,0,.87)}.ui.inverted.indicating.progress[data-percent^="1"] .label,.ui.inverted.indicating.progress[data-percent^="2"] .label{color:rgba(255,255,255,.9)}.ui.inverted.indicating.progress[data-percent^="3"] .label{color:rgba(255,255,255,.9)}.ui.inverted.indicating.progress[data-percent^="4"] .label,.ui.inverted.indicating.progress[data-percent^="5"] .label{color:rgba(255,255,255,.9)}.ui.inverted.indicating.progress[data-percent^="6"] .label{color:rgba(255,255,255,.9)}.ui.inverted.indicating.progress[data-percent^="7"] .label,.ui.inverted.indicating.progress[data-percent^="8"] .label{color:rgba(255,255,255,.9)}.ui.inverted.indicating.progress[data-percent^="100"] .label,.ui.inverted.indicating.progress[data-percent^="9"] .label{color:rgba(255,255,255,.9)}.ui.indicating.progress[data-percent="1"] .bar,.ui.indicating.progress[data-percent="2"] .bar,.ui.indicating.progress[data-percent="3"] .bar,.ui.indicating.progress[data-percent="4"] .bar,.ui.indicating.progress[data-percent="5"] .bar,.ui.indicating.progress[data-percent="6"] .bar,.ui.indicating.progress[data-percent="7"] .bar,.ui.indicating.progress[data-percent="8"] .bar,.ui.indicating.progress[data-percent="9"] .bar{background-color:#d95c5c}.ui.indicating.progress[data-percent="0"] .label,.ui.indicating.progress[data-percent="1"] .label,.ui.indicating.progress[data-percent="2"] .label,.ui.indicating.progress[data-percent="3"] .label,.ui.indicating.progress[data-percent="4"] .label,.ui.indicating.progress[data-percent="5"] .label,.ui.indicating.progress[data-percent="6"] .label,.ui.indicating.progress[data-percent="7"] .label,.ui.indicating.progress[data-percent="8"] .label,.ui.indicating.progress[data-percent="9"] .label{color:rgba(0,0,0,.87)}.ui.inverted.indicating.progress[data-percent="0"] .label,.ui.inverted.indicating.progress[data-percent="1"] .label,.ui.inverted.indicating.progress[data-percent="2"] .label,.ui.inverted.indicating.progress[data-percent="3"] .label,.ui.inverted.indicating.progress[data-percent="4"] .label,.ui.inverted.indicating.progress[data-percent="5"] .label,.ui.inverted.indicating.progress[data-percent="6"] .label,.ui.inverted.indicating.progress[data-percent="7"] .label,.ui.inverted.indicating.progress[data-percent="8"] .label,.ui.inverted.indicating.progress[data-percent="9"] .label{color:rgba(255,255,255,.9)}.ui.ui.indicating.progress.success .label{color:#1a531b}.ui.multiple.progress{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.progress.success .bar{background-color:#21ba45}.ui.ui.progress.success .bar,.ui.ui.progress.success .bar::after{-webkit-animation:none;animation:none}.ui.progress.success>.label{color:#1a531b}.ui.ui.progress.warning .bar{background-color:#f2c037}.ui.ui.progress.warning .bar,.ui.ui.progress.warning .bar::after{-webkit-animation:none;animation:none}.ui.progress.warning>.label{color:#794b02}.ui.ui.progress.error .bar{background-color:#db2828}.ui.ui.progress.error .bar,.ui.ui.progress.error .bar::after{-webkit-animation:none;animation:none}.ui.progress.error>.label{color:#912d2b}.ui.active.progress .bar{position:relative;min-width:2em}.ui.active.progress .bar::after{content:'';opacity:0;position:absolute;top:0;left:0;right:0;bottom:0;background:#fff;border-radius:.28571429rem;-webkit-animation:progress-active 2s ease infinite;animation:progress-active 2s ease infinite;-webkit-transform-origin:left;transform-origin:left}@-webkit-keyframes progress-active{0%{opacity:.3;-webkit-transform:scale(0,1);transform:scale(0,1)}100%{opacity:0;-webkit-transform:scale(1);transform:scale(1)}}@keyframes progress-active{0%{opacity:.3;-webkit-transform:scale(0,1);transform:scale(0,1)}100%{opacity:0;-webkit-transform:scale(1);transform:scale(1)}}.ui.disabled.progress{opacity:.35}.ui.ui.disabled.progress .bar,.ui.ui.disabled.progress .bar::after{-webkit-animation:none;animation:none}.ui.inverted.progress{background:rgba(255,255,255,.08);border:none}.ui.inverted.progress .bar{background:#888}.ui.inverted.progress .bar>.progress{color:#1b1c1d}.ui.inverted.progress>.label{color:#fff}.ui.inverted.progress.success>.label{color:#21ba45}.ui.inverted.progress.warning>.label{color:#f2c037}.ui.inverted.progress.error>.label{color:#db2828}.ui.progress.attached{background:0 0;position:relative;border:none;margin:0}.ui.progress.attached,.ui.progress.attached .bar{display:block;height:.2rem;padding:0;overflow:hidden;border-radius:0 0 .28571429rem .28571429rem}.ui.progress.attached .bar{border-radius:0}.ui.progress.top.attached,.ui.progress.top.attached .bar{top:0;border-radius:.28571429rem .28571429rem 0 0}.ui.progress.top.attached .bar{border-radius:0}.ui.card>.ui.attached.progress,.ui.segment>.ui.attached.progress{position:absolute;top:auto;left:0;bottom:100%;width:100%}.ui.card>.ui.bottom.attached.progress,.ui.segment>.ui.bottom.attached.progress{top:100%;bottom:auto}.ui.indeterminate.primary.progress .bar::before,.ui.primary.progress .bar,.ui.progress .primary.bar{background-color:#2185d0}.ui.inverted.indeterminate.primary.progress .bar::before,.ui.inverted.progress .primary.bar,.ui.primary.inverted.progress .bar{background-color:#54c8ff}.ui.indeterminate.secondary.progress .bar::before,.ui.progress .secondary.bar,.ui.secondary.progress .bar{background-color:#1b1c1d}.ui.inverted.indeterminate.secondary.progress .bar::before,.ui.inverted.progress .secondary.bar,.ui.secondary.inverted.progress .bar{background-color:#545454}.ui.indeterminate.red.progress .bar::before,.ui.progress .red.bar,.ui.red.progress .bar{background-color:#db2828}.ui.inverted.indeterminate.red.progress .bar::before,.ui.inverted.progress .red.bar,.ui.red.inverted.progress .bar{background-color:#ff695e}.ui.indeterminate.orange.progress .bar::before,.ui.orange.progress .bar,.ui.progress .orange.bar{background-color:#f2711c}.ui.inverted.indeterminate.orange.progress .bar::before,.ui.inverted.progress .orange.bar,.ui.orange.inverted.progress .bar{background-color:#ff851b}.ui.indeterminate.yellow.progress .bar::before,.ui.progress .yellow.bar,.ui.yellow.progress .bar{background-color:#fbbd08}.ui.inverted.indeterminate.yellow.progress .bar::before,.ui.inverted.progress .yellow.bar,.ui.yellow.inverted.progress .bar{background-color:#ffe21f}.ui.indeterminate.olive.progress .bar::before,.ui.olive.progress .bar,.ui.progress .olive.bar{background-color:#b5cc18}.ui.inverted.indeterminate.olive.progress .bar::before,.ui.inverted.progress .olive.bar,.ui.olive.inverted.progress .bar{background-color:#d9e778}.ui.green.progress .bar,.ui.indeterminate.green.progress .bar::before,.ui.progress .green.bar{background-color:#21ba45}.ui.green.inverted.progress .bar,.ui.inverted.indeterminate.green.progress .bar::before,.ui.inverted.progress .green.bar{background-color:#2ecc40}.ui.indeterminate.teal.progress .bar::before,.ui.progress .teal.bar,.ui.teal.progress .bar{background-color:#00b5ad}.ui.inverted.indeterminate.teal.progress .bar::before,.ui.inverted.progress .teal.bar,.ui.teal.inverted.progress .bar{background-color:#6dffff}.ui.blue.progress .bar,.ui.indeterminate.blue.progress .bar::before,.ui.progress .blue.bar{background-color:#2185d0}.ui.blue.inverted.progress .bar,.ui.inverted.indeterminate.blue.progress .bar::before,.ui.inverted.progress .blue.bar{background-color:#54c8ff}.ui.indeterminate.violet.progress .bar::before,.ui.progress .violet.bar,.ui.violet.progress .bar{background-color:#6435c9}.ui.inverted.indeterminate.violet.progress .bar::before,.ui.inverted.progress .violet.bar,.ui.violet.inverted.progress .bar{background-color:#a291fb}.ui.indeterminate.purple.progress .bar::before,.ui.progress .purple.bar,.ui.purple.progress .bar{background-color:#a333c8}.ui.inverted.indeterminate.purple.progress .bar::before,.ui.inverted.progress .purple.bar,.ui.purple.inverted.progress .bar{background-color:#dc73ff}.ui.indeterminate.pink.progress .bar::before,.ui.pink.progress .bar,.ui.progress .pink.bar{background-color:#e03997}.ui.inverted.indeterminate.pink.progress .bar::before,.ui.inverted.progress .pink.bar,.ui.pink.inverted.progress .bar{background-color:#ff8edf}.ui.brown.progress .bar,.ui.indeterminate.brown.progress .bar::before,.ui.progress .brown.bar{background-color:#a5673f}.ui.brown.inverted.progress .bar,.ui.inverted.indeterminate.brown.progress .bar::before,.ui.inverted.progress .brown.bar{background-color:#d67c1c}.ui.grey.progress .bar,.ui.indeterminate.grey.progress .bar::before,.ui.progress .grey.bar{background-color:#767676}.ui.grey.inverted.progress .bar,.ui.inverted.indeterminate.grey.progress .bar::before,.ui.inverted.progress .grey.bar{background-color:#dcddde}.ui.black.progress .bar,.ui.indeterminate.black.progress .bar::before,.ui.progress .black.bar{background-color:#1b1c1d}.ui.black.inverted.progress .bar,.ui.inverted.indeterminate.black.progress .bar::before,.ui.inverted.progress .black.bar{background-color:#545454}.ui.progress{font-size:1rem}.ui.progress .bar{height:1.75em}.ui.mini.progress{font-size:.78571429rem}.ui.mini.progress .bar{height:.3em}.ui.tiny.progress{font-size:.85714286rem}.ui.tiny.progress .bar{height:.5em}.ui.small.progress{font-size:.92857143rem}.ui.small.progress .bar{height:1em}.ui.large.progress{font-size:1.14285714rem}.ui.large.progress .bar{height:2.5em}.ui.big.progress{font-size:1.28571429rem}.ui.big.progress .bar{height:3.5em}.ui.huge.progress{font-size:1.42857143rem}.ui.huge.progress .bar{height:4em}.ui.massive.progress{font-size:1.71428571rem}.ui.massive.progress .bar{height:5em}.ui.indeterminate.progress .bar{width:100%}.ui.indeterminate.progress .bar .progress,.ui.progress .bar .centered.progress{text-align:center;position:relative}.ui.indeterminate.progress .bar::before{content:'';position:absolute;top:0;bottom:0;border-radius:.28571429rem;-webkit-animation:progress-pulsating 2s ease infinite;animation:progress-pulsating 2s ease infinite;-webkit-transform-origin:center;transform-origin:center;width:100%}.ui.slow.indeterminate.progress .bar::before{-webkit-animation-duration:4s;animation-duration:4s}.ui.fast.indeterminate.progress .bar::before{-webkit-animation-duration:1s;animation-duration:1s}.ui.swinging.indeterminate.progress .bar::before{-webkit-transform-origin:left;transform-origin:left;-webkit-animation-name:progress-swinging;animation-name:progress-swinging}.ui.sliding.indeterminate.progress .bar::before{-webkit-transform-origin:left;transform-origin:left;-webkit-animation-name:progress-sliding;animation-name:progress-sliding}.ui.filling.indeterminate.progress .bar::before{-webkit-animation-name:progress-filling;animation-name:progress-filling}.ui.indeterminate.progress:not(.sliding):not(.filling):not(.swinging) .bar::before{background:#fff}.ui.filling.indeterminate.progress .bar,.ui.sliding.indeterminate.progress .bar,.ui.swinging.indeterminate.progress .bar{background:rgba(0,0,0,.1)}.ui.sliding.indeterminate.progress .bar .progress,.ui.swinging.indeterminate.progress .bar .progress{color:#1b1c1d}.ui.inverted.filling.indeterminate.progress .bar,.ui.inverted.sliding.indeterminate.progress .bar,.ui.inverted.swinging.indeterminate.progress .bar{background:rgba(255,255,255,.08)}.ui.inverted.sliding.indeterminate.progress .bar .progress,.ui.inverted.swinging.indeterminate.progress .bar .progress{color:rgba(255,255,255,.7)}@-webkit-keyframes progress-swinging{0%,100%{width:10%;left:-25%}25%,65%{width:70%}50%{width:10%;left:100%}}@keyframes progress-swinging{0%,100%{width:10%;left:-25%}25%,65%{width:70%}50%{width:10%;left:100%}}@-webkit-keyframes progress-sliding{0%{width:10%;left:-25%}50%{width:70%}100%{width:10%;left:100%}}@keyframes progress-sliding{0%{width:10%;left:-25%}50%{width:70%}100%{width:10%;left:100%}}@-webkit-keyframes progress-filling{0%{-webkit-transform:scale(0,1);transform:scale(0,1)}80%{-webkit-transform:scale(1);transform:scale(1);opacity:1}100%{opacity:0}}@keyframes progress-filling{0%{-webkit-transform:scale(0,1);transform:scale(0,1)}80%{-webkit-transform:scale(1);transform:scale(1);opacity:1}100%{opacity:0}}@-webkit-keyframes progress-pulsating{0%{-webkit-transform:scale(0,1);transform:scale(0,1);opacity:.7}100%{-webkit-transform:scale(1);transform:scale(1);opacity:0}}@keyframes progress-pulsating{0%{-webkit-transform:scale(0,1);transform:scale(0,1);opacity:.7}100%{-webkit-transform:scale(1);transform:scale(1);opacity:0}}.ui.slider:not(.vertical):not(.checkbox){width:100%;padding:1em .5em}.ui.slider:not(.checkbox){position:relative}.ui.slider:not(.checkbox):focus{outline:0}.ui.slider .inner{position:relative;z-index:2}.ui.slider:not(.vertical) .inner{height:1.5em}.ui.slider .inner:hover{cursor:auto}.ui.slider .inner .track{position:absolute;border-radius:4px;background-color:rgba(0,0,0,.05)}.ui.slider:not(.vertical) .inner .track{width:100%;height:.4em;top:.55em;left:0}.ui.slider .inner .track-fill{position:absolute;border-radius:4px;background-color:#1b1c1d}.ui.slider:not(.vertical) .inner .track-fill{height:.4em;top:.55em;left:0}.ui.slider .inner .thumb{position:absolute;left:0;top:0;height:1.5em;width:1.5em;background:#fff -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:#fff -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:#fff linear-gradient(transparent,rgba(0,0,0,.05));border-radius:100%;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;-webkit-transition:background .3s ease;transition:background .3s ease}.ui.slider:not(.disabled) .inner .thumb:hover{cursor:pointer;background:#f2f2f2 -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:#f2f2f2 -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:#f2f2f2 linear-gradient(transparent,rgba(0,0,0,.05))}.ui.slider:not(.disabled):focus .inner .thumb{background:#f2f2f2 -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:#f2f2f2 -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:#f2f2f2 linear-gradient(transparent,rgba(0,0,0,.05))}.ui.disabled.slider:not(.checkbox){opacity:.5}.ui.disabled.slider .inner:hover{cursor:auto}.ui.disabled.slider .inner .track-fill{background:#ccc}.ui.reversed.slider .inner .track-fill{left:auto;right:0}.ui.reversed.slider:not(.vertical) .inner .thumb{left:auto;right:0}.ui.reversed.vertical.slider .inner .thumb{left:.03em}.ui.labeled.reversed.slider>.labels .label{-webkit-transform:translate(-100%,-100%);transform:translate(-100%,-100%)}.ui.vertical.slider{height:100%;width:1.5em;padding:.5em 1em}.ui.vertical.slider .inner{height:100%}.ui.vertical.slider .inner .track{height:100%;width:.4em;left:.55em;top:0}.ui.vertical.slider .inner .track-fill{width:.4em;left:.55em;top:0}.ui.vertical.reversed.slider .inner .thumb{top:auto;bottom:0}.ui.vertical.reversed.slider .inner .track-fill{top:auto;bottom:0}.ui.labeled.slider>.labels{height:1.5em;width:auto;margin:0;padding:0;position:absolute;top:50%;left:0;right:0}.ui.labeled.slider:not(.vertical)>.labels{-webkit-transform:translateY(-50%);transform:translateY(-50%)}.ui.labeled.slider>.labels .label{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;position:absolute;-webkit-transform:translate(-50%,-100%);transform:translate(-50%,-100%)}.ui.labeled.ticked.slider>.labels .label:after{content:' ';height:1.5em;width:1px;background:#ccc;position:absolute;top:100%;left:50%}.ui.labeled.ticked.slider>.labels .halftick.label:after{height:.75em}.ui.labeled.vertical.slider>.labels{width:1.5em;height:auto;left:50%;top:0;bottom:0;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.ui.labeled.vertical.slider>.labels .label{-webkit-transform:translate(-100%,-50%);transform:translate(-100%,-50%)}.ui.labeled.vertical.slider>.labels .label:after{width:1.5em;height:1px;left:100%;top:50%}.ui.labeled.vertical.slider>.labels .halftick.label:after{width:.75em;height:1px}.ui.labeled.vertical.reversed.slider>.labels .label{-webkit-transform:translate(-100%,50%);transform:translate(-100%,50%)}.ui.hover.slider .inner .thumb{opacity:0;-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.ui.hover.slider:not(.disabled):focus .inner .thumb,.ui.hover.slider:not(.disabled):hover .inner .thumb{opacity:1}.ui.inverted.slider .inner .track-fill{background-color:#545454}.ui.inverted.slider .inner .track{background-color:rgba(255,255,255,.08)}.ui.primary.slider .inner .track-fill{background-color:#2185d0}.ui.primary.inverted.slider .inner .track-fill{background-color:#54c8ff}.ui.primary.slider.basic .inner .thumb{background-color:#2185d0}.ui.primary.slider.basic .inner .thumb:hover,.ui.primary.slider.basic:focus .inner .thumb{background-color:#1678c2}.ui.primary.inverted.slider.basic .inner .thumb{background-color:#54c8ff}.ui.primary.inverted.slider.basic .inner .thumb:hover,.ui.primary.inverted.slider.basic:focus .inner .thumb{background-color:#21b8ff}.ui.secondary.slider .inner .track-fill{background-color:#1b1c1d}.ui.secondary.inverted.slider .inner .track-fill{background-color:#545454}.ui.secondary.slider.basic .inner .thumb{background-color:#1b1c1d}.ui.secondary.slider.basic .inner .thumb:hover,.ui.secondary.slider.basic:focus .inner .thumb{background-color:#27292a}.ui.secondary.inverted.slider.basic .inner .thumb{background-color:#545454}.ui.secondary.inverted.slider.basic .inner .thumb:hover,.ui.secondary.inverted.slider.basic:focus .inner .thumb{background-color:#6e6e6e}.ui.red.slider .inner .track-fill{background-color:#db2828}.ui.red.inverted.slider .inner .track-fill{background-color:#ff695e}.ui.red.slider.basic .inner .thumb{background-color:#db2828}.ui.red.slider.basic .inner .thumb:hover,.ui.red.slider.basic:focus .inner .thumb{background-color:#d01919}.ui.red.inverted.slider.basic .inner .thumb{background-color:#ff695e}.ui.red.inverted.slider.basic .inner .thumb:hover,.ui.red.inverted.slider.basic:focus .inner .thumb{background-color:#ff392b}.ui.orange.slider .inner .track-fill{background-color:#f2711c}.ui.orange.inverted.slider .inner .track-fill{background-color:#ff851b}.ui.orange.slider.basic .inner .thumb{background-color:#f2711c}.ui.orange.slider.basic .inner .thumb:hover,.ui.orange.slider.basic:focus .inner .thumb{background-color:#f26202}.ui.orange.inverted.slider.basic .inner .thumb{background-color:#ff851b}.ui.orange.inverted.slider.basic .inner .thumb:hover,.ui.orange.inverted.slider.basic:focus .inner .thumb{background-color:#e76b00}.ui.yellow.slider .inner .track-fill{background-color:#fbbd08}.ui.yellow.inverted.slider .inner .track-fill{background-color:#ffe21f}.ui.yellow.slider.basic .inner .thumb{background-color:#fbbd08}.ui.yellow.slider.basic .inner .thumb:hover,.ui.yellow.slider.basic:focus .inner .thumb{background-color:#eaae00}.ui.yellow.inverted.slider.basic .inner .thumb{background-color:#ffe21f}.ui.yellow.inverted.slider.basic .inner .thumb:hover,.ui.yellow.inverted.slider.basic:focus .inner .thumb{background-color:#ebcd00}.ui.olive.slider .inner .track-fill{background-color:#b5cc18}.ui.olive.inverted.slider .inner .track-fill{background-color:#d9e778}.ui.olive.slider.basic .inner .thumb{background-color:#b5cc18}.ui.olive.slider.basic .inner .thumb:hover,.ui.olive.slider.basic:focus .inner .thumb{background-color:#a7bd0d}.ui.olive.inverted.slider.basic .inner .thumb{background-color:#d9e778}.ui.olive.inverted.slider.basic .inner .thumb:hover,.ui.olive.inverted.slider.basic:focus .inner .thumb{background-color:#d2e745}.ui.green.slider .inner .track-fill{background-color:#21ba45}.ui.green.inverted.slider .inner .track-fill{background-color:#2ecc40}.ui.green.slider.basic .inner .thumb{background-color:#21ba45}.ui.green.slider.basic .inner .thumb:hover,.ui.green.slider.basic:focus .inner .thumb{background-color:#16ab39}.ui.green.inverted.slider.basic .inner .thumb{background-color:#2ecc40}.ui.green.inverted.slider.basic .inner .thumb:hover,.ui.green.inverted.slider.basic:focus .inner .thumb{background-color:#1ea92e}.ui.teal.slider .inner .track-fill{background-color:#00b5ad}.ui.teal.inverted.slider .inner .track-fill{background-color:#6dffff}.ui.teal.slider.basic .inner .thumb{background-color:#00b5ad}.ui.teal.slider.basic .inner .thumb:hover,.ui.teal.slider.basic:focus .inner .thumb{background-color:#009c95}.ui.teal.inverted.slider.basic .inner .thumb{background-color:#6dffff}.ui.teal.inverted.slider.basic .inner .thumb:hover,.ui.teal.inverted.slider.basic:focus .inner .thumb{background-color:#3affff}.ui.blue.slider .inner .track-fill{background-color:#2185d0}.ui.blue.inverted.slider .inner .track-fill{background-color:#54c8ff}.ui.blue.slider.basic .inner .thumb{background-color:#2185d0}.ui.blue.slider.basic .inner .thumb:hover,.ui.blue.slider.basic:focus .inner .thumb{background-color:#1678c2}.ui.blue.inverted.slider.basic .inner .thumb{background-color:#54c8ff}.ui.blue.inverted.slider.basic .inner .thumb:hover,.ui.blue.inverted.slider.basic:focus .inner .thumb{background-color:#21b8ff}.ui.violet.slider .inner .track-fill{background-color:#6435c9}.ui.violet.inverted.slider .inner .track-fill{background-color:#a291fb}.ui.violet.slider.basic .inner .thumb{background-color:#6435c9}.ui.violet.slider.basic .inner .thumb:hover,.ui.violet.slider.basic:focus .inner .thumb{background-color:#5829bb}.ui.violet.inverted.slider.basic .inner .thumb{background-color:#a291fb}.ui.violet.inverted.slider.basic .inner .thumb:hover,.ui.violet.inverted.slider.basic:focus .inner .thumb{background-color:#745aff}.ui.purple.slider .inner .track-fill{background-color:#a333c8}.ui.purple.inverted.slider .inner .track-fill{background-color:#dc73ff}.ui.purple.slider.basic .inner .thumb{background-color:#a333c8}.ui.purple.slider.basic .inner .thumb:hover,.ui.purple.slider.basic:focus .inner .thumb{background-color:#9627ba}.ui.purple.inverted.slider.basic .inner .thumb{background-color:#dc73ff}.ui.purple.inverted.slider.basic .inner .thumb:hover,.ui.purple.inverted.slider.basic:focus .inner .thumb{background-color:#cf40ff}.ui.pink.slider .inner .track-fill{background-color:#e03997}.ui.pink.inverted.slider .inner .track-fill{background-color:#ff8edf}.ui.pink.slider.basic .inner .thumb{background-color:#e03997}.ui.pink.slider.basic .inner .thumb:hover,.ui.pink.slider.basic:focus .inner .thumb{background-color:#e61a8d}.ui.pink.inverted.slider.basic .inner .thumb{background-color:#ff8edf}.ui.pink.inverted.slider.basic .inner .thumb:hover,.ui.pink.inverted.slider.basic:focus .inner .thumb{background-color:#ff5bd1}.ui.brown.slider .inner .track-fill{background-color:#a5673f}.ui.brown.inverted.slider .inner .track-fill{background-color:#d67c1c}.ui.brown.slider.basic .inner .thumb{background-color:#a5673f}.ui.brown.slider.basic .inner .thumb:hover,.ui.brown.slider.basic:focus .inner .thumb{background-color:#975b33}.ui.brown.inverted.slider.basic .inner .thumb{background-color:#d67c1c}.ui.brown.inverted.slider.basic .inner .thumb:hover,.ui.brown.inverted.slider.basic:focus .inner .thumb{background-color:#b0620f}.ui.grey.slider .inner .track-fill{background-color:#767676}.ui.grey.inverted.slider .inner .track-fill{background-color:#dcddde}.ui.grey.slider.basic .inner .thumb{background-color:#767676}.ui.grey.slider.basic .inner .thumb:hover,.ui.grey.slider.basic:focus .inner .thumb{background-color:#838383}.ui.grey.inverted.slider.basic .inner .thumb{background-color:#dcddde}.ui.grey.inverted.slider.basic .inner .thumb:hover,.ui.grey.inverted.slider.basic:focus .inner .thumb{background-color:#c2c4c5}.ui.black.slider .inner .track-fill{background-color:#1b1c1d}.ui.black.inverted.slider .inner .track-fill{background-color:#545454}.ui.black.slider.basic .inner .thumb{background-color:#1b1c1d}.ui.black.slider.basic .inner .thumb:hover,.ui.black.slider.basic:focus .inner .thumb{background-color:#27292a}.ui.black.inverted.slider.basic .inner .thumb{background-color:#545454}.ui.black.inverted.slider.basic .inner .thumb:hover,.ui.black.inverted.slider.basic:focus .inner .thumb{background-color:#000}.ui.slider.basic .inner .thumb{background-color:#1b1c1d}.ui.slider.basic .inner .thumb:hover,.ui.slider.basic:focus .inner .thumb{background-color:#27292a}.ui.inverted.slider.basic .inner .thumb{background-color:#545454}.ui.inverted.slider.basic .inner .thumb:hover,.ui.inverted.slider.basic:focus .inner .thumb{background-color:#000}.ui.slider.small .inner .thumb{height:1em;width:1em}.ui.slider.small:not(.vertical) .inner{height:1em}.ui.slider.small:not(.vertical) .inner .track,.ui.slider.small:not(.vertical) .inner .track-fill{height:.3em;top:.35em}.ui.small.labeled.slider:not(.vertical)>.labels,.ui.small.labeled.slider:not(.vertical)>.labels .label:after{height:1em}.ui.small.labeled.slider:not(.vertical)>.labels .halftick.label:after{height:.5em}.ui.slider.small.vertical .inner{width:1em}.ui.slider.small.vertical .inner .track,.ui.slider.small.vertical .inner .track-fill{width:.3em;left:.35em}.ui.small.labeled.vertical.slider>.labels,.ui.small.labeled.vertical.slider>.labels .label:after{width:1em}.ui.small.labeled.vertical.slider>.labels .halftick.label:after{width:.5em}.ui.slider.large .inner .thumb{height:2em;width:2em}.ui.slider.large:not(.vertical) .inner{height:2em}.ui.slider.large:not(.vertical) .inner .track,.ui.slider.large:not(.vertical) .inner .track-fill{height:.5em;top:.75em}.ui.large.labeled.slider:not(.vertical)>.labels,.ui.large.labeled.slider:not(.vertical)>.labels .label:after{height:2em}.ui.large.labeled.slider:not(.vertical)>.labels .halftick.label:after{height:1em}.ui.slider.large.vertical .inner{width:2em}.ui.slider.large.vertical .inner .track,.ui.slider.large.vertical .inner .track-fill{width:.5em;left:.75em}.ui.large.labeled.vertical.slider>.labels,.ui.large.labeled.vertical.slider>.labels .label:after{width:2em}.ui.large.labeled.vertical.slider>.labels .halftick.label:after{width:1em}.ui.slider.big .inner .thumb{height:2.5em;width:2.5em}.ui.slider.big:not(.vertical) .inner{height:2.5em}.ui.slider.big:not(.vertical) .inner .track,.ui.slider.big:not(.vertical) .inner .track-fill{height:.6em;top:.95em}.ui.big.labeled.slider:not(.vertical)>.labels,.ui.big.labeled.slider:not(.vertical)>.labels .label:after{height:2.5em}.ui.big.labeled.slider:not(.vertical)>.labels .halftick.label:after{height:1.25em}.ui.slider.big.vertical .inner{width:2.5em}.ui.slider.big.vertical .inner .track,.ui.slider.big.vertical .inner .track-fill{width:.6em;left:.95em}.ui.big.labeled.vertical.slider>.labels,.ui.big.labeled.vertical.slider>.labels .label:after{width:2.5em}.ui.big.labeled.vertical.slider>.labels .halftick.label:after{width:1.25em}/*! + * # Fomantic-UI - Rating + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.rating{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;white-space:nowrap;vertical-align:baseline}.ui.rating:last-child{margin-right:0}.ui.rating .icon{padding:0;margin:0;text-align:center;font-weight:400;font-style:normal;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;cursor:pointer;width:1.25em;height:auto;-webkit-transition:opacity .1s ease,background .1s ease,text-shadow .1s ease,color .1s ease;transition:opacity .1s ease,background .1s ease,text-shadow .1s ease,color .1s ease;line-height:1;-webkit-backface-visibility:hidden;backface-visibility:hidden}.ui.rating .icon{background:0 0;color:rgba(0,0,0,.15)}.ui.rating .active.icon{background:0 0;color:rgba(0,0,0,.85)}.ui.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.85)),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,rgba(0,0,0,.85) 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,rgba(0,0,0,.85) 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);-webkit-background-clip:text;background-clip:text;color:transparent}.ui.rating .icon.selected,.ui.rating .icon.selected.active,.ui.rating .icon.selected.partial.active{background:0 0;color:rgba(0,0,0,.87);background-clip:unset}.ui.primary.rating .active.icon{color:#54c8ff;text-shadow:0 -1px 0 #2185d0,-1px 0 0 #2185d0,0 1px 0 #2185d0,1px 0 0 #2185d0}.ui.primary.rating .icon.selected,.ui.primary.rating .icon.selected.active,.ui.primary.rating .icon.selected.partial.active{background:inherit;color:#21b8ff;text-shadow:0 -1px 0 #1678c2,-1px 0 0 #1678c2,0 1px 0 #1678c2,1px 0 0 #1678c2;-webkit-text-stroke:unset;background-clip:unset}.ui.primary.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#54c8ff),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#54c8ff 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#54c8ff 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#2185d0 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.secondary.rating .active.icon{color:#545454;text-shadow:0 -1px 0 #1b1c1d,-1px 0 0 #1b1c1d,0 1px 0 #1b1c1d,1px 0 0 #1b1c1d}.ui.secondary.rating .icon.selected,.ui.secondary.rating .icon.selected.active,.ui.secondary.rating .icon.selected.partial.active{background:inherit;color:#6e6e6e;text-shadow:0 -1px 0 #27292a,-1px 0 0 #27292a,0 1px 0 #27292a,1px 0 0 #27292a;-webkit-text-stroke:unset;background-clip:unset}.ui.secondary.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#545454),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#545454 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#545454 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#1b1c1d .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.red.rating .active.icon{color:#ff695e;text-shadow:0 -1px 0 #db2828,-1px 0 0 #db2828,0 1px 0 #db2828,1px 0 0 #db2828}.ui.red.rating .icon.selected,.ui.red.rating .icon.selected.active,.ui.red.rating .icon.selected.partial.active{background:inherit;color:#ff392b;text-shadow:0 -1px 0 #d01919,-1px 0 0 #d01919,0 1px 0 #d01919,1px 0 0 #d01919;-webkit-text-stroke:unset;background-clip:unset}.ui.red.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#ff695e),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#ff695e 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#ff695e 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#db2828 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.orange.rating .active.icon{color:#ff851b;text-shadow:0 -1px 0 #f2711c,-1px 0 0 #f2711c,0 1px 0 #f2711c,1px 0 0 #f2711c}.ui.orange.rating .icon.selected,.ui.orange.rating .icon.selected.active,.ui.orange.rating .icon.selected.partial.active{background:inherit;color:#e76b00;text-shadow:0 -1px 0 #f26202,-1px 0 0 #f26202,0 1px 0 #f26202,1px 0 0 #f26202;-webkit-text-stroke:unset;background-clip:unset}.ui.orange.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#ff851b),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#ff851b 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#ff851b 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#f2711c .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.yellow.rating .active.icon{color:#ffe21f;text-shadow:0 -1px 0 #fbbd08,-1px 0 0 #fbbd08,0 1px 0 #fbbd08,1px 0 0 #fbbd08}.ui.yellow.rating .icon.selected,.ui.yellow.rating .icon.selected.active,.ui.yellow.rating .icon.selected.partial.active{background:inherit;color:#ebcd00;text-shadow:0 -1px 0 #eaae00,-1px 0 0 #eaae00,0 1px 0 #eaae00,1px 0 0 #eaae00;-webkit-text-stroke:unset;background-clip:unset}.ui.yellow.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#ffe21f),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#ffe21f 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#ffe21f 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#fbbd08 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.olive.rating .active.icon{color:#d9e778;text-shadow:0 -1px 0 #b5cc18,-1px 0 0 #b5cc18,0 1px 0 #b5cc18,1px 0 0 #b5cc18}.ui.olive.rating .icon.selected,.ui.olive.rating .icon.selected.active,.ui.olive.rating .icon.selected.partial.active{background:inherit;color:#d2e745;text-shadow:0 -1px 0 #a7bd0d,-1px 0 0 #a7bd0d,0 1px 0 #a7bd0d,1px 0 0 #a7bd0d;-webkit-text-stroke:unset;background-clip:unset}.ui.olive.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#d9e778),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#d9e778 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#d9e778 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#b5cc18 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.green.rating .active.icon{color:#2ecc40;text-shadow:0 -1px 0 #21ba45,-1px 0 0 #21ba45,0 1px 0 #21ba45,1px 0 0 #21ba45}.ui.green.rating .icon.selected,.ui.green.rating .icon.selected.active,.ui.green.rating .icon.selected.partial.active{background:inherit;color:#1ea92e;text-shadow:0 -1px 0 #16ab39,-1px 0 0 #16ab39,0 1px 0 #16ab39,1px 0 0 #16ab39;-webkit-text-stroke:unset;background-clip:unset}.ui.green.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#2ecc40),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#2ecc40 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#2ecc40 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#21ba45 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.teal.rating .active.icon{color:#6dffff;text-shadow:0 -1px 0 #00b5ad,-1px 0 0 #00b5ad,0 1px 0 #00b5ad,1px 0 0 #00b5ad}.ui.teal.rating .icon.selected,.ui.teal.rating .icon.selected.active,.ui.teal.rating .icon.selected.partial.active{background:inherit;color:#3affff;text-shadow:0 -1px 0 #009c95,-1px 0 0 #009c95,0 1px 0 #009c95,1px 0 0 #009c95;-webkit-text-stroke:unset;background-clip:unset}.ui.teal.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#6dffff),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#6dffff 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#6dffff 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#00b5ad .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.blue.rating .active.icon{color:#54c8ff;text-shadow:0 -1px 0 #2185d0,-1px 0 0 #2185d0,0 1px 0 #2185d0,1px 0 0 #2185d0}.ui.blue.rating .icon.selected,.ui.blue.rating .icon.selected.active,.ui.blue.rating .icon.selected.partial.active{background:inherit;color:#21b8ff;text-shadow:0 -1px 0 #1678c2,-1px 0 0 #1678c2,0 1px 0 #1678c2,1px 0 0 #1678c2;-webkit-text-stroke:unset;background-clip:unset}.ui.blue.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#54c8ff),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#54c8ff 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#54c8ff 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#2185d0 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.violet.rating .active.icon{color:#a291fb;text-shadow:0 -1px 0 #6435c9,-1px 0 0 #6435c9,0 1px 0 #6435c9,1px 0 0 #6435c9}.ui.violet.rating .icon.selected,.ui.violet.rating .icon.selected.active,.ui.violet.rating .icon.selected.partial.active{background:inherit;color:#745aff;text-shadow:0 -1px 0 #5829bb,-1px 0 0 #5829bb,0 1px 0 #5829bb,1px 0 0 #5829bb;-webkit-text-stroke:unset;background-clip:unset}.ui.violet.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#a291fb),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#a291fb 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#a291fb 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#6435c9 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.purple.rating .active.icon{color:#dc73ff;text-shadow:0 -1px 0 #a333c8,-1px 0 0 #a333c8,0 1px 0 #a333c8,1px 0 0 #a333c8}.ui.purple.rating .icon.selected,.ui.purple.rating .icon.selected.active,.ui.purple.rating .icon.selected.partial.active{background:inherit;color:#cf40ff;text-shadow:0 -1px 0 #9627ba,-1px 0 0 #9627ba,0 1px 0 #9627ba,1px 0 0 #9627ba;-webkit-text-stroke:unset;background-clip:unset}.ui.purple.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#dc73ff),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#dc73ff 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#dc73ff 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#a333c8 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.pink.rating .active.icon{color:#ff8edf;text-shadow:0 -1px 0 #e03997,-1px 0 0 #e03997,0 1px 0 #e03997,1px 0 0 #e03997}.ui.pink.rating .icon.selected,.ui.pink.rating .icon.selected.active,.ui.pink.rating .icon.selected.partial.active{background:inherit;color:#ff5bd1;text-shadow:0 -1px 0 #e61a8d,-1px 0 0 #e61a8d,0 1px 0 #e61a8d,1px 0 0 #e61a8d;-webkit-text-stroke:unset;background-clip:unset}.ui.pink.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#ff8edf),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#ff8edf 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#ff8edf 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#e03997 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.brown.rating .active.icon{color:#d67c1c;text-shadow:0 -1px 0 #a5673f,-1px 0 0 #a5673f,0 1px 0 #a5673f,1px 0 0 #a5673f}.ui.brown.rating .icon.selected,.ui.brown.rating .icon.selected.active,.ui.brown.rating .icon.selected.partial.active{background:inherit;color:#b0620f;text-shadow:0 -1px 0 #975b33,-1px 0 0 #975b33,0 1px 0 #975b33,1px 0 0 #975b33;-webkit-text-stroke:unset;background-clip:unset}.ui.brown.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#d67c1c),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#d67c1c 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#d67c1c 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#a5673f .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.grey.rating .active.icon{color:#dcddde;text-shadow:0 -1px 0 #767676,-1px 0 0 #767676,0 1px 0 #767676,1px 0 0 #767676}.ui.grey.rating .icon.selected,.ui.grey.rating .icon.selected.active,.ui.grey.rating .icon.selected.partial.active{background:inherit;color:#c2c4c5;text-shadow:0 -1px 0 #838383,-1px 0 0 #838383,0 1px 0 #838383,1px 0 0 #838383;-webkit-text-stroke:unset;background-clip:unset}.ui.grey.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#dcddde),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#dcddde 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#dcddde 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#767676 .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.black.rating .active.icon{color:#545454;text-shadow:0 -1px 0 #1b1c1d,-1px 0 0 #1b1c1d,0 1px 0 #1b1c1d,1px 0 0 #1b1c1d}.ui.black.rating .icon.selected,.ui.black.rating .icon.selected.active,.ui.black.rating .icon.selected.partial.active{background:inherit;color:#000;text-shadow:0 -1px 0 #27292a,-1px 0 0 #27292a,0 1px 0 #27292a,1px 0 0 #27292a;-webkit-text-stroke:unset;background-clip:unset}.ui.black.rating .icon.partial.active{background:-webkit-gradient(linear,left top,right top,from(#545454),to(rgba(0,0,0,.15)));background:-webkit-linear-gradient(left,#545454 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);background:linear-gradient(to right,#545454 0 var(--full),rgba(0,0,0,.15) var(--full) 100%);text-shadow:none;-webkit-text-stroke:#1b1c1d .78px;-webkit-background-clip:text;background-clip:text;color:transparent}.ui.disabled.rating .icon{cursor:default}.ui.rating.selected .active.icon{opacity:1}.ui.rating .icon.selected,.ui.rating.selected .icon.selected{opacity:1}.ui.rating{font-size:1rem}.ui.mini.rating{font-size:.78571429rem}.ui.tiny.rating{font-size:.85714286rem}.ui.small.rating{font-size:.92857143rem}.ui.large.rating{font-size:1.14285714rem}.ui.big.rating{font-size:1.28571429rem}.ui.huge.rating{font-size:1.42857143rem}.ui.massive.rating{font-size:2rem}/*! + * # Fomantic-UI - Search + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.search{position:relative}.ui.search>.prompt{margin:0;outline:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);text-shadow:none;font-style:normal;font-weight:400;line-height:1.21428571em;padding:.67857143em 1em;font-size:1em;background:#fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);-webkit-box-shadow:0 0 0 0 transparent inset;box-shadow:0 0 0 0 transparent inset;-webkit-transition:background-color .1s ease,color .1s ease,border-color .1s ease,-webkit-box-shadow .1s ease;transition:background-color .1s ease,color .1s ease,border-color .1s ease,-webkit-box-shadow .1s ease;transition:background-color .1s ease,color .1s ease,box-shadow .1s ease,border-color .1s ease;transition:background-color .1s ease,color .1s ease,box-shadow .1s ease,border-color .1s ease,-webkit-box-shadow .1s ease}.ui.search .prompt{border-radius:500rem}.ui.search .prompt~.search.icon{cursor:pointer}.ui.search>.results{display:none;position:absolute;top:100%;left:0;-webkit-transform-origin:center top;transform-origin:center top;white-space:normal;text-align:left;text-transform:none;background:#fff;margin-top:.5em;width:18em;border-radius:.28571429rem;-webkit-box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);border:1px solid #d4d4d5;z-index:998}.ui.search>.results>:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.search>.results>:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.search>.results .result{cursor:pointer;display:block;overflow:hidden;font-size:1em;padding:.85714286em 1.14285714em;color:rgba(0,0,0,.87);line-height:1.33;border-bottom:1px solid rgba(34,36,38,.1)}.ui.search>.results .result:last-child{border-bottom:none!important}.ui.search>.results .result .image{float:right;overflow:hidden;background:0 0;width:5em;height:3em;border-radius:.25em}.ui.search>.results .result .image img{display:block;width:auto;height:100%}.ui.search>.results .result .image+.content{margin:0 6em 0 0}.ui.search>.results .result .title{margin:-.14285714em 0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;font-size:1em;color:rgba(0,0,0,.85)}.ui.search>.results .result .description{margin-top:0;font-size:.92857143em;color:rgba(0,0,0,.4)}.ui.search>.results .result .price{float:right;color:#21ba45}.ui.search>.results>.message{padding:1em 1em}.ui.search>.results>.message .header{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1rem;font-weight:700;color:rgba(0,0,0,.87)}.ui.search>.results>.message .description{margin-top:.25rem;font-size:1em;color:rgba(0,0,0,.87)}.ui.search>.results>.action{display:block;border-top:none;background:#f3f4f5;padding:.92857143em 1em;color:rgba(0,0,0,.87);font-weight:700;text-align:center}.ui.search>.prompt:focus{border-color:rgba(34,36,38,.35);background:#fff;color:rgba(0,0,0,.95)}.ui.loading.search .input>i.icon:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loading.search .input>i.icon:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent}.ui.category.search>.results .category .result:hover,.ui.search>.results .result:hover{background:#f9fafb}.ui.search .action:hover:not(div){background:#e0e0e0}.ui.category.search>.results .category.active{background:#f3f4f5}.ui.category.search>.results .category.active>.name{color:rgba(0,0,0,.87)}.ui.category.search>.results .category .result.active,.ui.search>.results .result.active{position:relative;border-left-color:rgba(34,36,38,.1);background:#f3f4f5;-webkit-box-shadow:none;box-shadow:none}.ui.search>.results .result.active .title{color:rgba(0,0,0,.85)}.ui.search>.results .result.active .description{color:rgba(0,0,0,.85)}.ui.disabled.search{cursor:default;pointer-events:none;opacity:.45}.ui.search.selection .prompt{border-radius:.28571429rem}.ui.search.selection>.icon.input>.remove.icon{pointer-events:none;position:absolute;left:auto;opacity:0;color:'';top:0;right:0;-webkit-transition:color .1s ease,opacity .1s ease;transition:color .1s ease,opacity .1s ease}.ui.search.selection>.icon.input>.active.remove.icon{cursor:pointer;opacity:.8;pointer-events:auto}.ui.search.selection>.icon.input:not([class*="left icon"])>.icon~.remove.icon{right:1.85714em}.ui.search.selection>.icon.input>.remove.icon:hover{opacity:1;color:#db2828}.ui.category.search .results{width:28em}.ui.category.search .results.animating,.ui.category.search .results.visible{display:table}.ui.category.search>.results .category{display:table-row;background:#f3f4f5;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:background .1s ease,border-color .1s ease;transition:background .1s ease,border-color .1s ease}.ui.category.search>.results .category:last-child{border-bottom:none}.ui.category.search>.results .category:first-child .name+.result{border-radius:0 .28571429rem 0 0}.ui.category.search>.results .category:last-child .result:last-child{border-radius:0 0 .28571429rem 0}.ui.category.search>.results .category>.name{display:table-cell;text-overflow:ellipsis;width:100px;white-space:nowrap;background:0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-size:1em;padding:.4em 1em;font-weight:700;color:rgba(0,0,0,.4);border-bottom:1px solid rgba(34,36,38,.1)}.ui.category.search>.results .category .results{display:table-cell;background:#fff;border-left:1px solid rgba(34,36,38,.15);border-bottom:1px solid rgba(34,36,38,.1)}.ui.category.search>.results .category .result{border-bottom:1px solid rgba(34,36,38,.1);-webkit-transition:background .1s ease,border-color .1s ease;transition:background .1s ease,border-color .1s ease;padding:.85714286em 1.14285714em}.ui[class*="left aligned"].search>.results{right:auto;left:0}.ui[class*="right aligned"].search>.results{right:0;left:auto}.ui.fluid.search .results{width:100%}.ui.search{font-size:1em}.ui.mini.search{font-size:.78571429em}.ui.tiny.search{font-size:.85714286em}.ui.small.search{font-size:.92857143em}.ui.large.search{font-size:1.14285714em}.ui.big.search{font-size:1.28571429em}.ui.huge.search{font-size:1.42857143em}.ui.massive.search{font-size:1.71428571em}@media only screen and (max-width:767.98px){.ui.search .results{max-width:calc(100vw - 2rem)}}/*! + * # Fomantic-UI - Shape + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.shape{position:relative;vertical-align:top;display:inline-block;-webkit-perspective:2000px;perspective:2000px;-webkit-transition:left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out;transition:left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out,left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out;transition:transform .6s ease-in-out,left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out}.ui.shape .sides{-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.ui.shape .side{display:none;opacity:1;width:100%;margin:0!important;-webkit-backface-visibility:hidden;backface-visibility:hidden}.ui.shape .side *{-webkit-backface-visibility:visible!important;backface-visibility:visible!important}.ui.cube.shape .side{min-width:15em;height:15em;padding:2em;background-color:#e6e6e6;color:rgba(0,0,0,.87);-webkit-box-shadow:0 0 2px rgba(0,0,0,.3);box-shadow:0 0 2px rgba(0,0,0,.3)}.ui.cube.shape .side>.content{width:100%;height:100%;display:table;text-align:center;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.ui.cube.shape .side>.content>div{display:table-cell;vertical-align:middle;font-size:2em}.ui.text.shape.animating .sides{position:static}.ui.text.shape .side{white-space:nowrap}.ui.text.shape .side>*{white-space:normal}.ui.loading.shape{position:absolute;top:-9999px;left:-9999px}.ui.shape .animating.side{position:absolute;top:0;left:0;display:block;z-index:100}.ui.shape .hidden.side{opacity:.6}.ui.shape.animating .sides{position:absolute;-webkit-transition:left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out;transition:left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out,left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out;transition:transform .6s ease-in-out,left .6s ease-in-out,width .6s ease-in-out,height .6s ease-in-out,-webkit-transform .6s ease-in-out}.ui.shape.animating .side{-webkit-transition:opacity .6s ease-in-out;transition:opacity .6s ease-in-out}.ui.shape .animating.side *,.ui.shape.animating .side *{-webkit-transition:none;transition:none}.ui.shape .active.side{display:block}/*! + * # Fomantic-UI - Sidebar + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.sidebar{position:fixed;top:0;left:0;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transition:none;transition:none;will-change:transform;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);visibility:hidden;-webkit-overflow-scrolling:touch;height:100%!important;max-height:100%;border-radius:0!important;margin:0!important;overflow-y:auto!important;z-index:102}.ui.sidebar>*{-webkit-backface-visibility:hidden;backface-visibility:hidden}.ui.left.sidebar{right:auto;left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.ui.right.sidebar{right:0!important;left:auto!important;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.ui.bottom.sidebar,.ui.top.sidebar{width:100%!important;height:auto!important}.ui.top.sidebar{top:0!important;bottom:auto!important;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}.ui.bottom.sidebar{top:auto!important;bottom:0!important;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}.pushable{height:100%;overflow-x:hidden;padding:0!important}body.pushable{background:#545454}body.pushable.dimmed{background:inherit}.pushable:not(body){-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.pushable:not(body)>.fixed,.pushable:not(body)>.pusher:after,.pushable:not(body)>.ui.sidebar{position:absolute}.pushable>.fixed{position:fixed;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease;will-change:transform;z-index:101}.pushable>.pusher{position:relative;-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden;min-height:100%;-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease;z-index:2;background:inherit}body.pushable>.pusher{background:#fff}.pushable>.pusher:after{position:fixed;top:0;right:0;content:'';background-color:rgba(0,0,0,.4);overflow:hidden;opacity:0;-webkit-transition:opacity .5s;transition:opacity .5s;will-change:opacity;z-index:1000}.ui.sidebar.menu .item{border-radius:0!important}.pushable>.pusher.dimmed:after{width:100%!important;height:100%!important;opacity:1!important}.ui.animating.sidebar{visibility:visible}.ui.visible.sidebar{visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.left.visible.sidebar,.ui.right.visible.sidebar{-webkit-box-shadow:0 0 20px rgba(34,36,38,.15);box-shadow:0 0 20px rgba(34,36,38,.15)}.ui.bottom.visible.sidebar,.ui.top.visible.sidebar{-webkit-box-shadow:0 0 20px rgba(34,36,38,.15);box-shadow:0 0 20px rgba(34,36,38,.15)}.ui.visible.left.sidebar~.fixed,.ui.visible.left.sidebar~.pusher{-webkit-transform:translate3d(260px,0,0);transform:translate3d(260px,0,0)}.ui.visible.right.sidebar~.fixed,.ui.visible.right.sidebar~.pusher{-webkit-transform:translate3d(-260px,0,0);transform:translate3d(-260px,0,0)}.ui.visible.top.sidebar~.fixed,.ui.visible.top.sidebar~.pusher{-webkit-transform:translate3d(0,36px,0);transform:translate3d(0,36px,0)}.ui.visible.bottom.sidebar~.fixed,.ui.visible.bottom.sidebar~.pusher{-webkit-transform:translate3d(0,-36px,0);transform:translate3d(0,-36px,0)}.ui.visible.left.sidebar~.ui.visible.right.sidebar~.fixed,.ui.visible.left.sidebar~.ui.visible.right.sidebar~.pusher,.ui.visible.right.sidebar~.ui.visible.left.sidebar~.fixed,.ui.visible.right.sidebar~.ui.visible.left.sidebar~.pusher{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.thin.left.sidebar,.ui.thin.right.sidebar{width:150px}.ui[class*="very thin"].left.sidebar,.ui[class*="very thin"].right.sidebar{width:60px}.ui.left.sidebar,.ui.right.sidebar{width:260px}.ui.wide.left.sidebar,.ui.wide.right.sidebar{width:350px}.ui[class*="very wide"].left.sidebar,.ui[class*="very wide"].right.sidebar{width:475px}.ui.visible.thin.left.sidebar~.fixed,.ui.visible.thin.left.sidebar~.pusher{-webkit-transform:translate3d(150px,0,0);transform:translate3d(150px,0,0)}.ui.visible[class*="very thin"].left.sidebar~.fixed,.ui.visible[class*="very thin"].left.sidebar~.pusher{-webkit-transform:translate3d(60px,0,0);transform:translate3d(60px,0,0)}.ui.visible.wide.left.sidebar~.fixed,.ui.visible.wide.left.sidebar~.pusher{-webkit-transform:translate3d(350px,0,0);transform:translate3d(350px,0,0)}.ui.visible[class*="very wide"].left.sidebar~.fixed,.ui.visible[class*="very wide"].left.sidebar~.pusher{-webkit-transform:translate3d(475px,0,0);transform:translate3d(475px,0,0)}.ui.visible.thin.right.sidebar~.fixed,.ui.visible.thin.right.sidebar~.pusher{-webkit-transform:translate3d(-150px,0,0);transform:translate3d(-150px,0,0)}.ui.visible[class*="very thin"].right.sidebar~.fixed,.ui.visible[class*="very thin"].right.sidebar~.pusher{-webkit-transform:translate3d(-60px,0,0);transform:translate3d(-60px,0,0)}.ui.visible.wide.right.sidebar~.fixed,.ui.visible.wide.right.sidebar~.pusher{-webkit-transform:translate3d(-350px,0,0);transform:translate3d(-350px,0,0)}.ui.visible[class*="very wide"].right.sidebar~.fixed,.ui.visible[class*="very wide"].right.sidebar~.pusher{-webkit-transform:translate3d(-475px,0,0);transform:translate3d(-475px,0,0)}.ui.overlay.sidebar{z-index:102}.ui.left.overlay.sidebar{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.ui.right.overlay.sidebar{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.ui.top.overlay.sidebar{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}.ui.bottom.overlay.sidebar{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}.animating.ui.overlay.sidebar,.ui.visible.overlay.sidebar{-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease}.ui.visible.left.overlay.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.visible.right.overlay.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.visible.top.overlay.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.visible.bottom.overlay.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.visible.overlay.sidebar~.fixed,.ui.visible.overlay.sidebar~.pusher{-webkit-transform:none!important;transform:none!important}.ui.push.sidebar{-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease;z-index:102}.ui.left.push.sidebar{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.ui.right.push.sidebar{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.ui.top.push.sidebar{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}.ui.bottom.push.sidebar{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}.ui.visible.push.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.uncover.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);z-index:1}.ui.visible.uncover.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease}.ui.slide.along.sidebar{z-index:1}.ui.left.slide.along.sidebar{-webkit-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}.ui.right.slide.along.sidebar{-webkit-transform:translate3d(50%,0,0);transform:translate3d(50%,0,0)}.ui.top.slide.along.sidebar{-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0)}.ui.bottom.slide.along.sidebar{-webkit-transform:translate3d(0,50%,0);transform:translate3d(0,50%,0)}.ui.animating.slide.along.sidebar{-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease}.ui.visible.slide.along.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.slide.out.sidebar{z-index:1}.ui.left.slide.out.sidebar{-webkit-transform:translate3d(50%,0,0);transform:translate3d(50%,0,0)}.ui.right.slide.out.sidebar{-webkit-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0)}.ui.top.slide.out.sidebar{-webkit-transform:translate3d(0,50%,0);transform:translate3d(0,50%,0)}.ui.bottom.slide.out.sidebar{-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0)}.ui.animating.slide.out.sidebar{-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease}.ui.visible.slide.out.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.scale.down.sidebar{-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease;z-index:102}.ui.left.scale.down.sidebar{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.ui.right.scale.down.sidebar{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.ui.top.scale.down.sidebar{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}.ui.bottom.scale.down.sidebar{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}.ui.scale.down.left.sidebar~.pusher{-webkit-transform-origin:75% 50%;transform-origin:75% 50%}.ui.scale.down.right.sidebar~.pusher{-webkit-transform-origin:25% 50%;transform-origin:25% 50%}.ui.scale.down.top.sidebar~.pusher{-webkit-transform-origin:50% 75%;transform-origin:50% 75%}.ui.scale.down.bottom.sidebar~.pusher{-webkit-transform-origin:50% 25%;transform-origin:50% 25%}.ui.animating.scale.down>.visible.ui.sidebar{-webkit-transition:-webkit-transform .5s ease;transition:-webkit-transform .5s ease;transition:transform .5s ease;transition:transform .5s ease,-webkit-transform .5s ease}.ui.animating.scale.down.sidebar~.pusher,.ui.visible.scale.down.sidebar~.pusher{display:block!important;width:100%;height:100%;overflow:hidden!important}.ui.visible.scale.down.sidebar{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.ui.visible.scale.down.sidebar~.pusher{-webkit-transform:scale(.75);transform:scale(.75)}/*! + * # Fomantic-UI - Sticky + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.sticky{position:static;-webkit-transition:none;transition:none;z-index:800}.ui.sticky.bound{position:absolute;left:auto;right:auto}.ui.sticky.fixed{position:fixed;left:auto;right:auto}.ui.sticky.bound.top,.ui.sticky.fixed.top{top:0;bottom:auto}.ui.sticky.bound.bottom,.ui.sticky.fixed.bottom{top:auto;bottom:0}.ui.native.sticky{position:-webkit-sticky;position:-moz-sticky;position:-ms-sticky;position:-o-sticky;position:sticky}/*! + * # Fomantic-UI - Tab + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.tab{display:none}.ui.tab.active,.ui.tab.open{display:block}.ui.tab.loading{position:relative;overflow:hidden;display:block;min-height:250px}.ui.tab.loading *{position:relative!important;left:-10000px!important}.ui.tab.loading.segment:before,.ui.tab.loading:before{position:absolute;content:'';top:50%;left:50%;margin:-1.25em 0 0 -1.25em;width:2.5em;height:2.5em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.tab.loading.segment:after,.ui.tab.loading:after{position:absolute;content:'';top:50%;left:50%;margin:-1.25em 0 0 -1.25em;width:2.5em;height:2.5em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent}/*! + * # Fomantic-UI - Toast + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.ui.toast-container{position:fixed;z-index:9999}.ui.toast-container.top.right{top:.85714286em;right:.85714286em;margin-left:.85714286em}.ui.toast-container.top.left{top:.85714286em;left:.85714286em;margin-right:.85714286em}.ui.toast-container.top.center{left:50%;-webkit-transform:translate(-50%,0);transform:translate(-50%,0);top:.85714286em}.ui.toast-container.bottom.right{bottom:.85714286em;right:.85714286em;margin-left:.85714286em}.ui.toast-container.bottom.left{bottom:.85714286em;left:.85714286em;margin-right:.85714286em}.ui.toast-container.bottom.center{left:50%;-webkit-transform:translate(-50%,0);transform:translate(-50%,0);bottom:.85714286em}.ui.toast-container .animating.toast-box,.ui.toast-container .toast-box,.ui.toast-container .visible.toast-box{display:table!important}.ui.toast-container .toast-box{margin-bottom:.5em;border-radius:.28571429rem;cursor:default}.ui.toast-container .toast-box:hover{opacity:1}.ui.toast-container .toast-box:not(.unclickable):hover{cursor:pointer}.ui.toast-container .toast-box.floating,.ui.toast-container .toast-box.hoverfloating:hover{-webkit-box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);border:1px solid rgba(34,36,38,.12)}.ui.toast-container .toast-box.compact,.ui.toast-container .toast-box>.compact{width:350px}.ui.toast-container .toast-box>.ui.message,.ui.toast-container .toast-box>.ui.toast{margin:0 -1px -.01em;position:relative}.ui.toast-container .toast-box>.attached.progress{z-index:1}.ui.toast-container .toast-box>.attached.progress.bottom{margin:-.2em -1px -.01em}.ui.toast-container .toast-box>.attached.progress.top{margin:-.01em -1px -.2em}.ui.toast-container .toast-box>.attached.progress .bar{min-width:0}.ui.toast-container .toast-box>.attached.progress.info .bar.bar.bar{background:#12a1bf}.ui.toast-container .toast-box>.attached.progress.warning .bar.bar.bar{background:#cf9b0d}.ui.toast-container .toast-box>.attached.progress.success .bar.bar.bar{background:#15792d}.ui.toast-container .toast-box>.attached.progress .error .bar.bar.bar{background:#9c1a1a}.ui.toast-container .toast-box>.attached.progress.neutral .bar.bar.bar{background:#d9d9d9}.ui.toast-container .toast-box>.ui.message>.close.icon{top:.3em;right:.3em}.ui.toast-container .toast-box>.ui.message>.actions:last-child{margin-bottom:-1em}.ui.toast-container .toast-box>.ui.message.icon{-webkit-box-align:inherit;-ms-flex-align:inherit;align-items:inherit}.ui.toast-container .toast-box>.ui.message.icon>:not(.icon):not(.actions){padding-left:5rem}.ui.toast-container .toast-box>.ui.message.icon>.icon:not(.close){display:inline-block;position:absolute;width:4rem;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.ui.toast-container .toast-box>.ui.message.icon:not(.vertical).actions>.icon:not(.close){top:calc(50% - 1.2em);-webkit-transform:none;transform:none}.ui.toast-container .toast-box>.ui.message.icon:not(.vertical).icon.icon.icon{display:block}.ui.toast-container .toast-box .ui.toast>.close.icon{cursor:pointer;margin:0;opacity:.7;-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.toast-container .toast-box .ui.toast>.close.icon:hover{opacity:1}.ui.toast-container .toast-box .ui.toast.vertical>.close.icon{margin-top:-.3em;margin-right:-.3em}.ui.toast-container .toast-box .ui.toast:not(.vertical)>.close.icon{position:absolute;top:.3em}.ui.toast-container .toast-box .ui.toast:not(.vertical)>.close.icon:not(.left){right:.3em}.ui.toast-container .toast-box .ui.toast:not(.vertical)>.close.icon.left{margin-left:-.3em}.ui.toast-container .toast-box .ui.card{margin:0}.ui.toast-container .toast-box .ui.card.attached:not(.vertical).bottom{border-top-left-radius:0;border-top-right-radius:0}.ui.toast-container .toast-box .ui.card.attached:not(.vertical).bottom.horizontal>.image>img{border-top-left-radius:0}.ui.toast-container .toast-box .ui.card.attached:not(.vertical).bottom.horizontal>.image:last-child>img{border-top-right-radius:0}.ui.toast-container .toast-box .ui.card.attached:not(.vertical).top{border-bottom-left-radius:0;border-bottom-right-radius:0}.ui.toast-container .toast-box .ui.card.attached:not(.vertical).top.horizontal>.image>img{border-bottom-left-radius:0}.ui.toast-container .toast-box .ui.card.attached:not(.vertical).top.horizontal>.image:last-child>img{border-bottom-right-radius:0}.ui.toast-container .toast-box .ui.card.horizontal.actions>.image>img{border-bottom-left-radius:0}.ui.toast-container .toast-box .ui.card.horizontal.actions>.image:last-child>img{border-bottom-right-radius:0}.ui.toast-container .toast-box .progressing{-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-animation-timing-function:linear;animation-timing-function:linear}.ui.toast-container .toast-box .progressing.up{-webkit-animation-name:progressUp;animation-name:progressUp}.ui.toast-container .toast-box .progressing.down{-webkit-animation-name:progressDown;animation-name:progressDown}.ui.toast-container .toast-box .progressing.wait{-webkit-animation-name:progressWait;animation-name:progressWait}.ui.toast-container .toast-box:hover .pausable.progressing{-webkit-animation-play-state:paused;animation-play-state:paused}.ui.toast-container .toast-box .ui.toast:not(.vertical){display:block}.ui.toast-container .toast-box :not(.comment):not(.card) .actions{margin:.5em -1em -1em -1em}.ui.toast-container .toast-box :not(.comment) .actions{padding:.5em .5em .75em .5em;text-align:right}.ui.toast-container .toast-box :not(.comment) .actions.attached:not(.vertical){margin-right:1px}.ui.toast-container .toast-box :not(.comment) .actions:not(.basic):not(.attached){background:rgba(255,255,255,.25);border-top:1px solid rgba(0,0,0,.2)}.ui.toast-container .toast-box :not(.comment) .actions.left{text-align:left}.ui.toast-container .toast-box .vertical.actions>.button,.ui.toast-container .toast-box>.vertical.vertical.vertical,.ui.toast-container .toast-box>.vertical>.vertical.vertical{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.toast-container .toast-box :not(.comment) .vertical.actions{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.toast-container .toast-box :not(.comment) .vertical.actions>.button{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.toast-container .toast-box :not(.comment) .vertical.actions.attached>.button{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.toast-container .toast-box :not(.comment) .vertical.actions:not(.attached){border-top:0;margin-top:-.75em;margin-bottom:-.75em;margin-left:1em;-ms-flex-pack:distribute;justify-content:space-around}.ui.toast-container .toast-box :not(.comment) .vertical.actions:not(.attached):not(.basic){border-left:1px solid rgba(0,0,0,.2)}.ui.toast-container .toast-box :not(.comment) .vertical.actions:not(.attached)>.button:not(:last-child){margin-bottom:.3em}.ui.toast-container .toast-box :not(.comment) .vertical.actions:not(.attached).top{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.ui.toast-container .toast-box :not(.comment) .vertical.actions:not(.attached).bottom{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.ui.vertical.attached:not(.left).card>.image>img{border-top-right-radius:0}.ui.vertical.attached:not(.left).card.horizontal>.image:last-child>img{border-top-right-radius:0;border-bottom-right-radius:0}.ui.vertical.attached:not(.left).card,.ui.vertical.attached:not(.left).toast{border-top-right-radius:0;border-bottom-right-radius:0}.ui.vertical.attached:not(.left).actions{border-top-right-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.vertical.attached:not(.left).actions .button:first-child,.ui.vertical.attached:not(.left).actions .button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.ui.vertical.attached:not(.left).message{border-top-right-radius:0;border-bottom-left-radius:.28571429rem}.ui.vertical.attached.left.card>.image>img{border-top-left-radius:0}.ui.vertical.attached.left.card.horizontal>.image>img{border-top-left-radius:0;border-bottom-left-radius:0}.ui.vertical.attached.left.card,.ui.vertical.attached.left.toast{border-top-left-radius:0;border-bottom-left-radius:0}.ui.vertical.attached.left.actions{border-top-left-radius:.28571429rem;border-bottom-left-radius:.28571429rem}.ui.vertical.attached.left.actions .button:first-child,.ui.vertical.attached.left.actions .button:last-child{border-top-right-radius:0;border-bottom-right-radius:0}.ui.vertical.attached.left.actions .button:not(:first-child):not(:last-child){margin-left:-1px}.ui.vertical.attached.left.message.message.message{border-top-right-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.attached:not(.vertical):not(.top).actions{border-bottom-left-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.attached:not(.vertical):not(.top).actions .button:first-child{border-bottom-left-radius:.28571429rem}.ui.attached:not(.vertical):not(.top).actions .button:last-child{border-bottom-right-radius:.28571429rem}.ui.attached:not(.vertical).top.actions{border-top-left-radius:.28571429rem;border-top-right-radius:.28571429rem}.ui.attached:not(.vertical).top.actions .button:first-child{border-top-left-radius:.28571429rem}.ui.attached:not(.vertical).top.actions .button:last-child{border-top-right-radius:.28571429rem}.ui.toast{display:none;border-radius:.28571429rem;padding:.78571429em 1em;margin:0 -1px -.01em;color:rgba(0,0,0,.87);background-color:#fff}.ui.toast>.content>.header{font-weight:700;color:inherit;margin:0}.ui.toast.info{background-color:#31ccec;color:rgba(255,255,255,.9)}.ui.toast.warning{background-color:#f2c037;color:rgba(255,255,255,.9)}.ui.toast.success{background-color:#21ba45;color:rgba(255,255,255,.9)}.ui.toast.error{background-color:#db2828;color:rgba(255,255,255,.9)}.ui.toast.neutral{background-color:#fff;color:rgba(0,0,0,.87)}.ui.toast>.icon:not(.close){font-size:1.5em}.ui.toast:not(.vertical)>.icon:not(.close){position:absolute}.ui.toast:not(.vertical)>.icon:not(.close)+.content{padding-left:3em}.ui.toast:not(.vertical)>.close.icon+.content{padding-left:1.5em}.ui.toast:not(.vertical)>.ui.image{position:absolute}.ui.toast:not(.vertical)>.ui.image.avatar+.content{padding-left:3em;min-height:2em}.ui.toast:not(.vertical)>.ui.image.mini+.content{padding-left:3.4em;min-height:35px}.ui.toast:not(.vertical)>.ui.image.tiny+.content{padding-left:7em;min-height:80px}.ui.toast:not(.vertical)>.ui.image.small+.content{padding-left:12em;min-height:150px}.ui.toast:not(.vertical)>.centered.icon,.ui.toast:not(.vertical)>.centered.image{-webkit-transform:translateY(-50%);transform:translateY(-50%);top:50%}.ui.toast:not(.vertical).actions>.centered.image{top:calc(50% - 2em)}.ui.toast:not(.vertical).actions>.centered.icon{top:calc(50% - 1.2em)}.ui.toast.vertical>.close.icon+.content{padding-left:1em}.ui.toast.vertical>.icon:not(.close)+.content,.ui.toast.vertical>.ui.image+.content{padding-left:1em}.ui.toast.vertical>.ui.image{-ms-flex-item-align:start;align-self:flex-start;-ms-flex-negative:0;flex-shrink:0}.ui.toast.vertical>.centered.icon,.ui.toast.vertical>.centered.image{-ms-flex-item-align:center;align-self:center}.ui.toast.attached.bottom{border-top-left-radius:0;border-top-right-radius:0}.ui.toast.attached.top{border-bottom-left-radius:0;border-bottom-right-radius:0}.ui.hoverfloating.message:hover{-webkit-box-shadow:0 0 0 1px inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 0 0 1px inset,0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.center.toast-container .toast-box,.ui.right.toast-container .toast-box{margin-left:auto}.ui.center.toast-container .toast-box{margin-right:auto}.ui.primary.toast{background-color:#2185d0;color:rgba(255,255,255,.9)}.ui.inverted.primary.toast,.ui.toast-container .toast-box>.inverted.primary.attached.progress .bar{background-color:#54c8ff;color:rgba(0,0,0,.87)}.ui.secondary.toast{background-color:#1b1c1d;color:rgba(255,255,255,.9)}.ui.inverted.secondary.toast,.ui.toast-container .toast-box>.inverted.secondary.attached.progress .bar{background-color:#545454;color:rgba(0,0,0,.87)}.ui.red.toast{background-color:#db2828;color:rgba(255,255,255,.9)}.ui.inverted.red.toast,.ui.toast-container .toast-box>.inverted.red.attached.progress .bar{background-color:#ff695e;color:rgba(0,0,0,.87)}.ui.orange.toast{background-color:#f2711c;color:rgba(255,255,255,.9)}.ui.inverted.orange.toast,.ui.toast-container .toast-box>.inverted.orange.attached.progress .bar{background-color:#ff851b;color:rgba(0,0,0,.87)}.ui.yellow.toast{background-color:#fbbd08;color:rgba(255,255,255,.9)}.ui.inverted.yellow.toast,.ui.toast-container .toast-box>.inverted.yellow.attached.progress .bar{background-color:#ffe21f;color:rgba(0,0,0,.87)}.ui.olive.toast{background-color:#b5cc18;color:rgba(255,255,255,.9)}.ui.inverted.olive.toast,.ui.toast-container .toast-box>.inverted.olive.attached.progress .bar{background-color:#d9e778;color:rgba(0,0,0,.87)}.ui.green.toast{background-color:#21ba45;color:rgba(255,255,255,.9)}.ui.inverted.green.toast,.ui.toast-container .toast-box>.inverted.green.attached.progress .bar{background-color:#2ecc40;color:rgba(0,0,0,.87)}.ui.teal.toast{background-color:#00b5ad;color:rgba(255,255,255,.9)}.ui.inverted.teal.toast,.ui.toast-container .toast-box>.inverted.teal.attached.progress .bar{background-color:#6dffff;color:rgba(0,0,0,.87)}.ui.blue.toast{background-color:#2185d0;color:rgba(255,255,255,.9)}.ui.inverted.blue.toast,.ui.toast-container .toast-box>.inverted.blue.attached.progress .bar{background-color:#54c8ff;color:rgba(0,0,0,.87)}.ui.violet.toast{background-color:#6435c9;color:rgba(255,255,255,.9)}.ui.inverted.violet.toast,.ui.toast-container .toast-box>.inverted.violet.attached.progress .bar{background-color:#a291fb;color:rgba(0,0,0,.87)}.ui.purple.toast{background-color:#a333c8;color:rgba(255,255,255,.9)}.ui.inverted.purple.toast,.ui.toast-container .toast-box>.inverted.purple.attached.progress .bar{background-color:#dc73ff;color:rgba(0,0,0,.87)}.ui.pink.toast{background-color:#e03997;color:rgba(255,255,255,.9)}.ui.inverted.pink.toast,.ui.toast-container .toast-box>.inverted.pink.attached.progress .bar{background-color:#ff8edf;color:rgba(0,0,0,.87)}.ui.brown.toast{background-color:#a5673f;color:rgba(255,255,255,.9)}.ui.inverted.brown.toast,.ui.toast-container .toast-box>.inverted.brown.attached.progress .bar{background-color:#d67c1c;color:rgba(0,0,0,.87)}.ui.grey.toast{background-color:#767676;color:rgba(255,255,255,.9)}.ui.inverted.grey.toast,.ui.toast-container .toast-box>.inverted.grey.attached.progress .bar{background-color:#dcddde;color:rgba(0,0,0,.87)}.ui.black.toast{background-color:#1b1c1d;color:rgba(255,255,255,.9)}.ui.inverted.black.toast,.ui.toast-container .toast-box>.inverted.black.attached.progress .bar{background-color:#545454;color:rgba(0,0,0,.87)}.ui.inverted.toast{color:rgba(255,255,255,.9);background-color:#1b1c1d}@media only screen and (max-width:420px){.ui.toast-container .toast-box.toast-box,.ui.toast-container .toast-box>*,.ui.toast-container .toast-box>.compact,.ui.toast-container .toast-box>.vertical>*{width:auto;max-width:100%}.ui.toast-container .toast-box>:not(.vertical){min-width:280px}.ui.toast-container .toast-box>.ui.card.horizontal,.ui.toast-container .toast-box>.vertical>.ui.horizontal.card{min-width:initial}}@-webkit-keyframes progressDown{0%{width:100%}100%{width:0}}@keyframes progressDown{0%{width:100%}100%{width:0}}@-webkit-keyframes progressUp{0%{width:0}100%{width:100%}}@keyframes progressUp{0%{width:0}100%{width:100%}}@-webkit-keyframes progressWait{0%{opacity:1}100%{opacity:0}}@keyframes progressWait{0%{opacity:1}100%{opacity:0}}/*! + * # Fomantic-UI - Transition + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */.transition{-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-timing-function:ease;animation-timing-function:ease;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animating.transition{-webkit-backface-visibility:hidden;backface-visibility:hidden;visibility:visible!important}.loading.transition{position:absolute;top:-99999px;left:-99999px}.hidden.transition{display:none;visibility:hidden}.visible.transition{display:block!important;visibility:visible!important}.disabled.transition{-webkit-animation-play-state:paused;animation-play-state:paused}.looping.transition{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.transition.browse{-webkit-animation-duration:.5s;animation-duration:.5s}.transition.browse.in{-webkit-animation-name:browseIn;animation-name:browseIn}.transition.browse.left.out,.transition.browse.out{-webkit-animation-name:browseOutLeft;animation-name:browseOutLeft}.transition.browse.right.out{-webkit-animation-name:browseOutRight;animation-name:browseOutRight}@-webkit-keyframes browseIn{0%{-webkit-transform:scale(.8) translateZ(0);transform:scale(.8) translateZ(0);z-index:-1}10%{-webkit-transform:scale(.8) translateZ(0);transform:scale(.8) translateZ(0);z-index:-1;opacity:.7}80%{-webkit-transform:scale(1.05) translateZ(0);transform:scale(1.05) translateZ(0);opacity:1;z-index:999}100%{-webkit-transform:scale(1) translateZ(0);transform:scale(1) translateZ(0);z-index:999}}@keyframes browseIn{0%{-webkit-transform:scale(.8) translateZ(0);transform:scale(.8) translateZ(0);z-index:-1}10%{-webkit-transform:scale(.8) translateZ(0);transform:scale(.8) translateZ(0);z-index:-1;opacity:.7}80%{-webkit-transform:scale(1.05) translateZ(0);transform:scale(1.05) translateZ(0);opacity:1;z-index:999}100%{-webkit-transform:scale(1) translateZ(0);transform:scale(1) translateZ(0);z-index:999}}@-webkit-keyframes browseOutLeft{0%{z-index:999;-webkit-transform:translateX(0) rotateY(0) rotateX(0);transform:translateX(0) rotateY(0) rotateX(0)}50%{z-index:-1;-webkit-transform:translateX(-105%) rotateY(35deg) rotateX(10deg) translateZ(-10px);transform:translateX(-105%) rotateY(35deg) rotateX(10deg) translateZ(-10px)}80%{opacity:1}100%{z-index:-1;-webkit-transform:translateX(0) rotateY(0) rotateX(0) translateZ(-10px);transform:translateX(0) rotateY(0) rotateX(0) translateZ(-10px);opacity:0}}@keyframes browseOutLeft{0%{z-index:999;-webkit-transform:translateX(0) rotateY(0) rotateX(0);transform:translateX(0) rotateY(0) rotateX(0)}50%{z-index:-1;-webkit-transform:translateX(-105%) rotateY(35deg) rotateX(10deg) translateZ(-10px);transform:translateX(-105%) rotateY(35deg) rotateX(10deg) translateZ(-10px)}80%{opacity:1}100%{z-index:-1;-webkit-transform:translateX(0) rotateY(0) rotateX(0) translateZ(-10px);transform:translateX(0) rotateY(0) rotateX(0) translateZ(-10px);opacity:0}}@-webkit-keyframes browseOutRight{0%{z-index:999;-webkit-transform:translateX(0) rotateY(0) rotateX(0);transform:translateX(0) rotateY(0) rotateX(0)}50%{z-index:1;-webkit-transform:translateX(105%) rotateY(35deg) rotateX(10deg) translateZ(-10px);transform:translateX(105%) rotateY(35deg) rotateX(10deg) translateZ(-10px)}80%{opacity:1}100%{z-index:1;-webkit-transform:translateX(0) rotateY(0) rotateX(0) translateZ(-10px);transform:translateX(0) rotateY(0) rotateX(0) translateZ(-10px);opacity:0}}@keyframes browseOutRight{0%{z-index:999;-webkit-transform:translateX(0) rotateY(0) rotateX(0);transform:translateX(0) rotateY(0) rotateX(0)}50%{z-index:1;-webkit-transform:translateX(105%) rotateY(35deg) rotateX(10deg) translateZ(-10px);transform:translateX(105%) rotateY(35deg) rotateX(10deg) translateZ(-10px)}80%{opacity:1}100%{z-index:1;-webkit-transform:translateX(0) rotateY(0) rotateX(0) translateZ(-10px);transform:translateX(0) rotateY(0) rotateX(0) translateZ(-10px);opacity:0}}.drop.transition{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-timing-function:cubic-bezier(.34,1.61,.7,1);animation-timing-function:cubic-bezier(.34,1.61,.7,1)}.drop.transition.in{-webkit-animation-name:dropIn;animation-name:dropIn}.drop.transition.out{-webkit-animation-name:dropOut;animation-name:dropOut}@-webkit-keyframes dropIn{0%{opacity:0;-webkit-transform:scale(0);transform:scale(0)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes dropIn{0%{opacity:0;-webkit-transform:scale(0);transform:scale(0)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes dropOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(0);transform:scale(0)}}@keyframes dropOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(0);transform:scale(0)}}.transition.fade.in{-webkit-animation-name:fadeIn;animation-name:fadeIn}.transition[class*="fade up"].in{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}.transition[class*="fade down"].in{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}.transition[class*="fade left"].in{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}.transition[class*="fade right"].in{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}.transition.fade.out{-webkit-animation-name:fadeOut;animation-name:fadeOut}.transition[class*="fade up"].out{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}.transition[class*="fade down"].out{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}.transition[class*="fade left"].out{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}.transition[class*="fade right"].out{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(10%);transform:translateY(10%)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translateY(10%);transform:translateY(10%)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translateY(-10%);transform:translateY(-10%)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translateY(-10%);transform:translateY(-10%)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translateX(10%);transform:translateX(10%)}100%{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translateX(10%);transform:translateX(10%)}100%{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translateX(-10%);transform:translateX(-10%)}100%{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translateX(-10%);transform:translateX(-10%)}100%{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@-webkit-keyframes fadeOutUp{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(5%);transform:translateY(5%)}}@keyframes fadeOutUp{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(5%);transform:translateY(5%)}}@-webkit-keyframes fadeOutDown{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-5%);transform:translateY(-5%)}}@keyframes fadeOutDown{0%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}100%{opacity:0;-webkit-transform:translateY(-5%);transform:translateY(-5%)}}@-webkit-keyframes fadeOutLeft{0%{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}100%{opacity:0;-webkit-transform:translateX(5%);transform:translateX(5%)}}@keyframes fadeOutLeft{0%{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}100%{opacity:0;-webkit-transform:translateX(5%);transform:translateX(5%)}}@-webkit-keyframes fadeOutRight{0%{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}100%{opacity:0;-webkit-transform:translateX(-5%);transform:translateX(-5%)}}@keyframes fadeOutRight{0%{opacity:1;-webkit-transform:translateX(0);transform:translateX(0)}100%{opacity:0;-webkit-transform:translateX(-5%);transform:translateX(-5%)}}.flip.transition.in,.flip.transition.out{-webkit-animation-duration:.6s;animation-duration:.6s}.horizontal.flip.transition.in{-webkit-animation-name:horizontalFlipIn;animation-name:horizontalFlipIn}.horizontal.flip.transition.out{-webkit-animation-name:horizontalFlipOut;animation-name:horizontalFlipOut}.vertical.flip.transition.in{-webkit-animation-name:verticalFlipIn;animation-name:verticalFlipIn}.vertical.flip.transition.out{-webkit-animation-name:verticalFlipOut;animation-name:verticalFlipOut}@-webkit-keyframes horizontalFlipIn{0%{-webkit-transform:perspective(2000px) rotateY(-90deg);transform:perspective(2000px) rotateY(-90deg);opacity:0}100%{-webkit-transform:perspective(2000px) rotateY(0);transform:perspective(2000px) rotateY(0);opacity:1}}@keyframes horizontalFlipIn{0%{-webkit-transform:perspective(2000px) rotateY(-90deg);transform:perspective(2000px) rotateY(-90deg);opacity:0}100%{-webkit-transform:perspective(2000px) rotateY(0);transform:perspective(2000px) rotateY(0);opacity:1}}@-webkit-keyframes verticalFlipIn{0%{-webkit-transform:perspective(2000px) rotateX(-90deg);transform:perspective(2000px) rotateX(-90deg);opacity:0}100%{-webkit-transform:perspective(2000px) rotateX(0);transform:perspective(2000px) rotateX(0);opacity:1}}@keyframes verticalFlipIn{0%{-webkit-transform:perspective(2000px) rotateX(-90deg);transform:perspective(2000px) rotateX(-90deg);opacity:0}100%{-webkit-transform:perspective(2000px) rotateX(0);transform:perspective(2000px) rotateX(0);opacity:1}}@-webkit-keyframes horizontalFlipOut{0%{-webkit-transform:perspective(2000px) rotateY(0);transform:perspective(2000px) rotateY(0);opacity:1}100%{-webkit-transform:perspective(2000px) rotateY(90deg);transform:perspective(2000px) rotateY(90deg);opacity:0}}@keyframes horizontalFlipOut{0%{-webkit-transform:perspective(2000px) rotateY(0);transform:perspective(2000px) rotateY(0);opacity:1}100%{-webkit-transform:perspective(2000px) rotateY(90deg);transform:perspective(2000px) rotateY(90deg);opacity:0}}@-webkit-keyframes verticalFlipOut{0%{-webkit-transform:perspective(2000px) rotateX(0);transform:perspective(2000px) rotateX(0);opacity:1}100%{-webkit-transform:perspective(2000px) rotateX(-90deg);transform:perspective(2000px) rotateX(-90deg);opacity:0}}@keyframes verticalFlipOut{0%{-webkit-transform:perspective(2000px) rotateX(0);transform:perspective(2000px) rotateX(0);opacity:1}100%{-webkit-transform:perspective(2000px) rotateX(-90deg);transform:perspective(2000px) rotateX(-90deg);opacity:0}}.scale.transition.in{-webkit-animation-name:scaleIn;animation-name:scaleIn}.scale.transition.out{-webkit-animation-name:scaleOut;animation-name:scaleOut}@-webkit-keyframes scaleIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes scaleIn{0%{opacity:0;-webkit-transform:scale(.8);transform:scale(.8)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes scaleOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(.9);transform:scale(.9)}}@keyframes scaleOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:0;-webkit-transform:scale(.9);transform:scale(.9)}}.transition.fly{-webkit-animation-duration:.6s;animation-duration:.6s;-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1)}.transition.fly.in{-webkit-animation-name:flyIn;animation-name:flyIn}.transition[class*="fly up"].in{-webkit-animation-name:flyInUp;animation-name:flyInUp}.transition[class*="fly down"].in{-webkit-animation-name:flyInDown;animation-name:flyInDown}.transition[class*="fly left"].in{-webkit-animation-name:flyInLeft;animation-name:flyInLeft}.transition[class*="fly right"].in{-webkit-animation-name:flyInRight;animation-name:flyInRight}.transition.fly.out{-webkit-animation-name:flyOut;animation-name:flyOut}.transition[class*="fly up"].out{-webkit-animation-name:flyOutUp;animation-name:flyOutUp}.transition[class*="fly down"].out{-webkit-animation-name:flyOutDown;animation-name:flyOutDown}.transition[class*="fly left"].out{-webkit-animation-name:flyOutLeft;animation-name:flyOutLeft}.transition[class*="fly right"].out{-webkit-animation-name:flyOutRight;animation-name:flyOutRight}@-webkit-keyframes flyIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes flyIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@-webkit-keyframes flyInUp{0%{opacity:0;-webkit-transform:translate3d(0,1500px,0);transform:translate3d(0,1500px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes flyInUp{0%{opacity:0;-webkit-transform:translate3d(0,1500px,0);transform:translate3d(0,1500px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@-webkit-keyframes flyInDown{0%{opacity:0;-webkit-transform:translate3d(0,-1500px,0);transform:translate3d(0,-1500px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}@keyframes flyInDown{0%{opacity:0;-webkit-transform:translate3d(0,-1500px,0);transform:translate3d(0,-1500px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}@-webkit-keyframes flyInLeft{0%{opacity:0;-webkit-transform:translate3d(1500px,0,0);transform:translate3d(1500px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes flyInLeft{0%{opacity:0;-webkit-transform:translate3d(1500px,0,0);transform:translate3d(1500px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}@-webkit-keyframes flyInRight{0%{opacity:0;-webkit-transform:translate3d(-1500px,0,0);transform:translate3d(-1500px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes flyInRight{0%{opacity:0;-webkit-transform:translate3d(-1500px,0,0);transform:translate3d(-1500px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}@-webkit-keyframes flyOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes flyOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@-webkit-keyframes flyOutUp{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes flyOutUp{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@-webkit-keyframes flyOutDown{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes flyOutDown{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@-webkit-keyframes flyOutRight{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes flyOutRight{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@-webkit-keyframes flyOutLeft{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes flyOutLeft{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.transition.slide.in,.transition[class*="slide down"].in{-webkit-animation-name:slideInY;animation-name:slideInY;-webkit-transform-origin:top center;transform-origin:top center}.transition[class*="slide up"].in{-webkit-animation-name:slideInY;animation-name:slideInY;-webkit-transform-origin:bottom center;transform-origin:bottom center}.transition[class*="slide left"].in{-webkit-animation-name:slideInX;animation-name:slideInX;-webkit-transform-origin:center right;transform-origin:center right}.transition[class*="slide right"].in{-webkit-animation-name:slideInX;animation-name:slideInX;-webkit-transform-origin:center left;transform-origin:center left}.transition.slide.out,.transition[class*="slide down"].out{-webkit-animation-name:slideOutY;animation-name:slideOutY;-webkit-transform-origin:top center;transform-origin:top center}.transition[class*="slide up"].out{-webkit-animation-name:slideOutY;animation-name:slideOutY;-webkit-transform-origin:bottom center;transform-origin:bottom center}.transition[class*="slide left"].out{-webkit-animation-name:slideOutX;animation-name:slideOutX;-webkit-transform-origin:center right;transform-origin:center right}.transition[class*="slide right"].out{-webkit-animation-name:slideOutX;animation-name:slideOutX;-webkit-transform-origin:center left;transform-origin:center left}@-webkit-keyframes slideInY{0%{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}100%{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1)}}@keyframes slideInY{0%{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}100%{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1)}}@-webkit-keyframes slideInX{0%{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}100%{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes slideInX{0%{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}100%{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}@-webkit-keyframes slideOutY{0%{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1)}100%{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}}@keyframes slideOutY{0%{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1)}100%{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}}@-webkit-keyframes slideOutX{0%{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}100%{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}}@keyframes slideOutX{0%{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}100%{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}}.transition.swing{-webkit-animation-duration:.8s;animation-duration:.8s}.transition[class*="swing down"].in{-webkit-animation-name:swingInX;animation-name:swingInX;-webkit-transform-origin:top center;transform-origin:top center}.transition[class*="swing up"].in{-webkit-animation-name:swingInX;animation-name:swingInX;-webkit-transform-origin:bottom center;transform-origin:bottom center}.transition[class*="swing left"].in{-webkit-animation-name:swingInY;animation-name:swingInY;-webkit-transform-origin:center right;transform-origin:center right}.transition[class*="swing right"].in{-webkit-animation-name:swingInY;animation-name:swingInY;-webkit-transform-origin:center left;transform-origin:center left}.transition.swing.out,.transition[class*="swing down"].out{-webkit-animation-name:swingOutX;animation-name:swingOutX;-webkit-transform-origin:top center;transform-origin:top center}.transition[class*="swing up"].out{-webkit-animation-name:swingOutX;animation-name:swingOutX;-webkit-transform-origin:bottom center;transform-origin:bottom center}.transition[class*="swing left"].out{-webkit-animation-name:swingOutY;animation-name:swingOutY;-webkit-transform-origin:center right;transform-origin:center right}.transition[class*="swing right"].out{-webkit-animation-name:swingOutY;animation-name:swingOutY;-webkit-transform-origin:center left;transform-origin:center left}@-webkit-keyframes swingInX{0%{-webkit-transform:perspective(1000px) rotateX(90deg);transform:perspective(1000px) rotateX(90deg);opacity:0}40%{-webkit-transform:perspective(1000px) rotateX(-30deg);transform:perspective(1000px) rotateX(-30deg);opacity:1}60%{-webkit-transform:perspective(1000px) rotateX(15deg);transform:perspective(1000px) rotateX(15deg)}80%{-webkit-transform:perspective(1000px) rotateX(-7.5deg);transform:perspective(1000px) rotateX(-7.5deg)}100%{-webkit-transform:perspective(1000px) rotateX(0);transform:perspective(1000px) rotateX(0)}}@keyframes swingInX{0%{-webkit-transform:perspective(1000px) rotateX(90deg);transform:perspective(1000px) rotateX(90deg);opacity:0}40%{-webkit-transform:perspective(1000px) rotateX(-30deg);transform:perspective(1000px) rotateX(-30deg);opacity:1}60%{-webkit-transform:perspective(1000px) rotateX(15deg);transform:perspective(1000px) rotateX(15deg)}80%{-webkit-transform:perspective(1000px) rotateX(-7.5deg);transform:perspective(1000px) rotateX(-7.5deg)}100%{-webkit-transform:perspective(1000px) rotateX(0);transform:perspective(1000px) rotateX(0)}}@-webkit-keyframes swingInY{0%{-webkit-transform:perspective(1000px) rotateY(-90deg);transform:perspective(1000px) rotateY(-90deg);opacity:0}40%{-webkit-transform:perspective(1000px) rotateY(30deg);transform:perspective(1000px) rotateY(30deg);opacity:1}60%{-webkit-transform:perspective(1000px) rotateY(-17.5deg);transform:perspective(1000px) rotateY(-17.5deg)}80%{-webkit-transform:perspective(1000px) rotateY(7.5deg);transform:perspective(1000px) rotateY(7.5deg)}100%{-webkit-transform:perspective(1000px) rotateY(0);transform:perspective(1000px) rotateY(0)}}@keyframes swingInY{0%{-webkit-transform:perspective(1000px) rotateY(-90deg);transform:perspective(1000px) rotateY(-90deg);opacity:0}40%{-webkit-transform:perspective(1000px) rotateY(30deg);transform:perspective(1000px) rotateY(30deg);opacity:1}60%{-webkit-transform:perspective(1000px) rotateY(-17.5deg);transform:perspective(1000px) rotateY(-17.5deg)}80%{-webkit-transform:perspective(1000px) rotateY(7.5deg);transform:perspective(1000px) rotateY(7.5deg)}100%{-webkit-transform:perspective(1000px) rotateY(0);transform:perspective(1000px) rotateY(0)}}@-webkit-keyframes swingOutX{0%{-webkit-transform:perspective(1000px) rotateX(0);transform:perspective(1000px) rotateX(0)}40%{-webkit-transform:perspective(1000px) rotateX(-7.5deg);transform:perspective(1000px) rotateX(-7.5deg)}60%{-webkit-transform:perspective(1000px) rotateX(17.5deg);transform:perspective(1000px) rotateX(17.5deg)}80%{-webkit-transform:perspective(1000px) rotateX(-30deg);transform:perspective(1000px) rotateX(-30deg);opacity:1}100%{-webkit-transform:perspective(1000px) rotateX(90deg);transform:perspective(1000px) rotateX(90deg);opacity:0}}@keyframes swingOutX{0%{-webkit-transform:perspective(1000px) rotateX(0);transform:perspective(1000px) rotateX(0)}40%{-webkit-transform:perspective(1000px) rotateX(-7.5deg);transform:perspective(1000px) rotateX(-7.5deg)}60%{-webkit-transform:perspective(1000px) rotateX(17.5deg);transform:perspective(1000px) rotateX(17.5deg)}80%{-webkit-transform:perspective(1000px) rotateX(-30deg);transform:perspective(1000px) rotateX(-30deg);opacity:1}100%{-webkit-transform:perspective(1000px) rotateX(90deg);transform:perspective(1000px) rotateX(90deg);opacity:0}}@-webkit-keyframes swingOutY{0%{-webkit-transform:perspective(1000px) rotateY(0);transform:perspective(1000px) rotateY(0)}40%{-webkit-transform:perspective(1000px) rotateY(7.5deg);transform:perspective(1000px) rotateY(7.5deg)}60%{-webkit-transform:perspective(1000px) rotateY(-10deg);transform:perspective(1000px) rotateY(-10deg)}80%{-webkit-transform:perspective(1000px) rotateY(30deg);transform:perspective(1000px) rotateY(30deg);opacity:1}100%{-webkit-transform:perspective(1000px) rotateY(-90deg);transform:perspective(1000px) rotateY(-90deg);opacity:0}}@keyframes swingOutY{0%{-webkit-transform:perspective(1000px) rotateY(0);transform:perspective(1000px) rotateY(0)}40%{-webkit-transform:perspective(1000px) rotateY(7.5deg);transform:perspective(1000px) rotateY(7.5deg)}60%{-webkit-transform:perspective(1000px) rotateY(-10deg);transform:perspective(1000px) rotateY(-10deg)}80%{-webkit-transform:perspective(1000px) rotateY(30deg);transform:perspective(1000px) rotateY(30deg);opacity:1}100%{-webkit-transform:perspective(1000px) rotateY(-90deg);transform:perspective(1000px) rotateY(-90deg);opacity:0}}.transition.zoom.in{-webkit-animation-name:zoomIn;animation-name:zoomIn}.transition.zoom.out{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomIn{0%{opacity:1;-webkit-transform:scale(0);transform:scale(0)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes zoomIn{0%{opacity:1;-webkit-transform:scale(0);transform:scale(0)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes zoomOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:1;-webkit-transform:scale(0);transform:scale(0)}}@keyframes zoomOut{0%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}100%{opacity:1;-webkit-transform:scale(0);transform:scale(0)}}.flash.transition{-webkit-animation-duration:750ms;animation-duration:750ms;-webkit-animation-name:flash;animation-name:flash}.shake.transition{-webkit-animation-duration:750ms;animation-duration:750ms;-webkit-animation-name:shake;animation-name:shake}.bounce.transition{-webkit-animation-duration:750ms;animation-duration:750ms;-webkit-animation-name:bounce;animation-name:bounce}.tada.transition{-webkit-animation-duration:750ms;animation-duration:750ms;-webkit-animation-name:tada;animation-name:tada}.pulse.transition{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-name:pulse;animation-name:pulse}.jiggle.transition{-webkit-animation-duration:750ms;animation-duration:750ms;-webkit-animation-name:jiggle;animation-name:jiggle}.transition.glow{-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:cubic-bezier(.19,1,.22,1);animation-timing-function:cubic-bezier(.19,1,.22,1)}.transition.glow{-webkit-animation-name:glow;animation-name:glow}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@-webkit-keyframes bounce{0%,100%,20%,50%,80%{-webkit-transform:translateY(0);transform:translateY(0)}40%{-webkit-transform:translateY(-30px);transform:translateY(-30px)}60%{-webkit-transform:translateY(-15px);transform:translateY(-15px)}}@keyframes bounce{0%,100%,20%,50%,80%{-webkit-transform:translateY(0);transform:translateY(0)}40%{-webkit-transform:translateY(-30px);transform:translateY(-30px)}60%{-webkit-transform:translateY(-15px);transform:translateY(-15px)}}@-webkit-keyframes tada{0%{-webkit-transform:scale(1);transform:scale(1)}10%,20%{-webkit-transform:scale(.9) rotate(-3deg);transform:scale(.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale(1.1) rotate(3deg);transform:scale(1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale(1.1) rotate(-3deg);transform:scale(1.1) rotate(-3deg)}100%{-webkit-transform:scale(1) rotate(0);transform:scale(1) rotate(0)}}@keyframes tada{0%{-webkit-transform:scale(1);transform:scale(1)}10%,20%{-webkit-transform:scale(.9) rotate(-3deg);transform:scale(.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale(1.1) rotate(3deg);transform:scale(1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale(1.1) rotate(-3deg);transform:scale(1.1) rotate(-3deg)}100%{-webkit-transform:scale(1) rotate(0);transform:scale(1) rotate(0)}}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}50%{-webkit-transform:scale(.9);transform:scale(.9);opacity:.7}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes pulse{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}50%{-webkit-transform:scale(.9);transform:scale(.9);opacity:.7}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@-webkit-keyframes jiggle{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes jiggle{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@-webkit-keyframes glow{0%{background-color:#fcfcfd}30%{background-color:#fff6cd}100%{background-color:#fcfcfd}}@keyframes glow{0%{background-color:#fcfcfd}30%{background-color:#fff6cd}100%{background-color:#fcfcfd}} \ No newline at end of file diff --git a/code/modules/vchat/css/semantic.vc.min.css b/code/modules/vchat/css/semantic.vc.min.css new file mode 100644 index 0000000000..2bafead4c5 --- /dev/null +++ b/code/modules/vchat/css/semantic.vc.min.css @@ -0,0 +1,110 @@ +/* + * # Fomantic-UI - Menu + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin:1rem 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;background:#fff;font-weight:400;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);border-radius:.28571429rem;min-height:2.85714286em}.ui.menu:after{content:'';display:block;height:0;clear:both;visibility:hidden}.ui.menu:first-child{margin-top:0}.ui.menu:last-child{margin-bottom:0}.ui.menu .menu{margin:0}.ui.menu:not(.vertical)>.menu{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.menu:not(.vertical) .item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.menu .item{position:relative;vertical-align:middle;line-height:1;text-decoration:none;-webkit-tap-highlight-color:transparent;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:0 0;padding:.92857143em 1.14285714em;text-transform:none;color:rgba(0,0,0,.87);font-weight:400;-webkit-transition:background .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background .1s ease,box-shadow .1s ease,color .1s ease;transition:background .1s ease,box-shadow .1s ease,color .1s ease,-webkit-box-shadow .1s ease}.ui.menu>.item:first-child{border-radius:.28571429rem 0 0 .28571429rem}.ui.menu .item:before{position:absolute;content:'';top:0;right:0;height:100%;width:1px;background:rgba(34,36,38,.1)}.ui.menu .item>a:not(.ui),.ui.menu .item>p:only-child,.ui.menu .text.item>*{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;line-height:1.3}.ui.menu .item>p:first-child{margin-top:0}.ui.menu .item>p:last-child{margin-bottom:0}.ui.menu .item>i.icon{opacity:.9;float:none;margin:0 .35714286em 0 0}.ui.menu:not(.vertical) .item>.button{position:relative;top:0;margin:-.5em 0;padding-bottom:.78571429em;padding-top:.78571429em;font-size:1em}.ui.menu>.container,.ui.menu>.grid{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:inherit;-ms-flex-align:inherit;align-items:inherit;-webkit-box-orient:inherit;-webkit-box-direction:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.menu .item>.input{width:100%}.ui.menu:not(.vertical) .item>.input{position:relative;top:0;margin:-.5em 0}.ui.menu .item>.input input{font-size:1em;padding-top:.57142857em;padding-bottom:.57142857em}.ui.menu .header.item,.ui.vertical.menu .header.item{margin:0;background:'';text-transform:normal;font-weight:700}.ui.vertical.menu .item>.header:not(.ui){margin:0 0 .5em;font-size:1em;font-weight:700}.ui.menu .item>i.dropdown.icon{padding:0;float:right;margin:0 0 0 1em}.ui.menu .dropdown.item .menu{min-width:calc(100% - 1px);border-radius:0 0 .28571429rem .28571429rem;background:#fff;margin:0 0 0;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.08);box-shadow:0 1px 3px 0 rgba(0,0,0,.08);-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.ui.menu .ui.dropdown .menu>.item{margin:0;text-align:left;font-size:1em!important;padding:.78571429em 1.14285714em!important;background:0 0!important;color:rgba(0,0,0,.87)!important;text-transform:none!important;font-weight:400!important;-webkit-box-shadow:none!important;box-shadow:none!important;-webkit-transition:none!important;transition:none!important}.ui.menu .ui.dropdown .menu>.item:hover{background:rgba(0,0,0,.05)!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown .menu>.selected.item{background:rgba(0,0,0,.05)!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown .menu>.active.item{background:rgba(0,0,0,.03)!important;font-weight:700!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown.item .menu .item:not(.filtered){display:block}.ui.menu .ui.dropdown .menu>.item .icon:not(.dropdown){display:inline-block;font-size:1em!important;float:none;margin:0 .75em 0 0!important}.ui.secondary.menu .dropdown.item>.menu,.ui.text.menu .dropdown.item>.menu{border-radius:.28571429rem;margin-top:.35714286em}.ui.menu .pointing.dropdown.item .menu{margin-top:.75em}.ui.inverted.menu .search.dropdown.item>.search,.ui.inverted.menu .search.dropdown.item>.text{color:rgba(255,255,255,.9)}.ui.vertical.menu .dropdown.item>.icon{float:right;content:"\f0da";margin-left:1em}.ui.vertical.menu .dropdown.item .menu{left:100%;min-width:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;margin:0;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.08);box-shadow:0 1px 3px 0 rgba(0,0,0,.08);border-radius:0 .28571429rem .28571429rem .28571429rem}.ui.vertical.menu .dropdown.item.upward .menu{bottom:0}.ui.vertical.menu .dropdown.item:not(.upward) .menu{top:0}.ui.vertical.menu .active.dropdown.item{border-top-right-radius:0;border-bottom-right-radius:0}.ui.vertical.menu .dropdown.active.item{-webkit-box-shadow:none;box-shadow:none}.ui.item.menu .dropdown .menu .item{width:100%}.ui.menu .item>.label:not(.floating){margin-left:1em;padding:.3em .78571429em}.ui.vertical.menu .item>.label{margin-top:-.15em;margin-bottom:-.15em;padding:.3em .78571429em}.ui.menu .item>.floating.label{padding:.3em .78571429em}.ui.menu .item>.label{background:#999;color:#fff}.ui.menu .item>.image.label img{margin:-.2833em .8em -.2833em -.8em;height:1.5666em}.ui.menu .item>img:not(.ui){display:inline-block;vertical-align:middle;margin:-.3em 0;width:2.5em}.ui.vertical.menu .item>img:not(.ui):only-child{display:block;max-width:100%;width:auto}.ui.menu .list .item:before{background:0 0!important}.ui.vertical.sidebar.menu>.item:first-child:before{display:block!important}.ui.vertical.sidebar.menu>.item::before{top:auto;bottom:0}@media only screen and (max-width:767.98px){.ui.menu>.ui.container{width:100%!important;margin-left:0!important;margin-right:0!important}}@media only screen and (min-width:768px){.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.item:not(.right):not(.borderless):first-child{border-left:1px solid rgba(34,36,38,.1)}.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.right.item:not(.borderless):last-child,.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.right.menu>.item:not(.borderless):last-child{border-right:1px solid rgba(34,36,38,.1)}}.ui.link.menu .item:hover,.ui.menu .dropdown.item:hover,.ui.menu .link.item:hover,.ui.menu a.item:hover{cursor:pointer;background:rgba(0,0,0,.03);color:rgba(0,0,0,.95)}.ui.link.menu .item:active,.ui.menu .link.item:active,.ui.menu a.item:active{background:rgba(0,0,0,.03);color:rgba(0,0,0,.95)}.ui.menu .active.item{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);font-weight:400;-webkit-box-shadow:none;box-shadow:none}.ui.menu .active.item>i.icon{opacity:1}.ui.menu .active.item:hover,.ui.vertical.menu .active.item:hover{background-color:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.ui.menu .item.disabled{cursor:default;background-color:transparent;color:rgba(40,40,40,.3);pointer-events:none}.ui.menu:not(.vertical) .left.item,.ui.menu:not(.vertical) .left.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-right:auto!important}.ui.menu:not(.vertical) .right.item,.ui.menu:not(.vertical) .right.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-left:auto!important}.ui.menu:not(.vertical) :not(.dropdown)>.left.menu,.ui.menu:not(.vertical) :not(.dropdown)>.right.menu{display:inherit}.ui.menu:not(.vertical) .center.item,.ui.menu:not(.vertical) .center.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-left:auto!important;margin-right:auto!important}.ui.menu .right.item::before,.ui.menu .right.menu>.item::before{right:auto;left:0}.ui.menu .center.item:last-child::before,.ui.menu .center.menu>.item:last-child::before{display:none}.ui.vertical.menu{display:block;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;background:#fff;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.ui.vertical.menu .item{display:block;background:0 0;border-top:none;border-right:none}.ui.vertical.menu>.item:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.vertical.menu>.item:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.vertical.menu .item>.label{float:right;text-align:center}.ui.vertical.menu .item>i.icon,.ui.vertical.menu .item>i.icons{width:1.18em;float:right;margin:0 0 0 .5em}.ui.vertical.menu .item>.label+i.icon{float:none;margin:0 .5em 0 0}.ui.vertical.menu .item:before{position:absolute;content:'';top:0;left:0;width:100%;height:1px;background:rgba(34,36,38,.1)}.ui.vertical.menu .item:first-child:before{display:none!important}.ui.vertical.menu .item>.menu{margin:.5em -1.14285714em 0}.ui.vertical.menu .menu .item{background:0 0;padding:.5em 1.33333333em;font-size:.85714286em;color:rgba(0,0,0,.5)}.ui.vertical.menu .item .menu .link.item:hover,.ui.vertical.menu .item .menu a.item:hover{color:rgba(0,0,0,.85)}.ui.vertical.menu .menu .item:before{display:none}.ui.vertical.menu .active.item{background:rgba(0,0,0,.05);border-radius:0;-webkit-box-shadow:none;box-shadow:none}.ui.vertical.menu>.active.item:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.vertical.menu>.active.item:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.vertical.menu>.active.item:only-child{border-radius:.28571429rem}.ui.vertical.menu .active.item .menu .active.item{border-left:none}.ui.vertical.menu .item .menu .active.item{background-color:transparent;font-weight:700;color:rgba(0,0,0,.95)}.ui.tabular.menu{border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border:none;background:none transparent;border-bottom:1px solid #d4d4d5}.ui.tabular.fluid.menu{width:calc(100% + 2px)!important}.ui.tabular.menu .item{background:0 0;border-bottom:none;border-left:1px solid transparent;border-right:1px solid transparent;border-top:2px solid transparent;padding:.92857143em 1.42857143em;color:rgba(0,0,0,.87)}.ui.tabular.menu .item:before{display:none}.ui.tabular.menu .item:hover{background-color:transparent;color:rgba(0,0,0,.8)}.ui.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-top-width:1px;border-color:#d4d4d5;font-weight:700;margin-bottom:-1px;-webkit-box-shadow:none;box-shadow:none;border-radius:.28571429rem .28571429rem 0 0!important}.ui.tabular.menu+.attached:not(.top).segment,.ui.tabular.menu+.attached:not(.top).segment+.attached:not(.top).segment{border-top:none;margin-left:0;margin-top:0;margin-right:0;width:100%}.top.attached.segment+.ui.bottom.tabular.menu{position:relative;width:calc(100% + 2px);left:-1px}.ui.bottom.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-top:1px solid #d4d4d5}.ui.bottom.tabular.menu .item{background:0 0;border-left:1px solid transparent;border-right:1px solid transparent;border-bottom:1px solid transparent;border-top:none}.ui.bottom.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:-1px 0 0 0;border-radius:0 0 .28571429rem .28571429rem!important}.ui.vertical.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-right:1px solid #d4d4d5}.ui.vertical.tabular.menu .item{background:0 0;border-left:1px solid transparent;border-bottom:1px solid transparent;border-top:1px solid transparent;border-right:none}.ui.vertical.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:0 -1px 0 0;border-radius:.28571429rem 0 0 .28571429rem!important}.ui.vertical.right.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-right:none;border-left:1px solid #d4d4d5}.ui.vertical.right.tabular.menu .item{background:0 0;border-right:1px solid transparent;border-bottom:1px solid transparent;border-top:1px solid transparent;border-left:none}.ui.vertical.right.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:0 0 0 -1px;border-radius:0 .28571429rem .28571429rem 0!important}.ui.tabular.menu .active.dropdown.item{margin-bottom:0;border-left:1px solid transparent;border-right:1px solid transparent;border-top:2px solid transparent;border-bottom:none}.ui.pagination.menu{margin:0;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.ui.pagination.menu .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.compact.menu .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.pagination.menu .item:last-child:before{display:none}.ui.pagination.menu .item{min-width:3em;text-align:center}.ui.pagination.menu .icon.item i.icon{vertical-align:top}.ui.pagination.menu .active.item{border-top:none;padding-top:.92857143em;background-color:rgba(0,0,0,.05);color:rgba(0,0,0,.95);-webkit-box-shadow:none;box-shadow:none}.ui.secondary.menu{background:0 0;margin-left:-.35714286em;margin-right:-.35714286em;border-radius:0;border:none;-webkit-box-shadow:none;box-shadow:none}.ui.secondary.menu .item{-ms-flex-item-align:center;align-self:center;-webkit-box-shadow:none;box-shadow:none;border:none;padding:.78571429em .92857143em;margin:0 .35714286em;background:0 0;-webkit-transition:color .1s ease;transition:color .1s ease;border-radius:.28571429rem}.ui.secondary.menu .item:before{display:none!important}.ui.secondary.menu .header.item{border-radius:0;border-right:none;background:none transparent}.ui.secondary.menu .item>img:not(.ui){margin:0}.ui.secondary.menu .dropdown.item:hover,.ui.secondary.menu .link.item:hover,.ui.secondary.menu a.item:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.secondary.menu .active.item{-webkit-box-shadow:none;box-shadow:none;background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);border-radius:.28571429rem}.ui.secondary.menu .active.item:hover{-webkit-box-shadow:none;box-shadow:none;background:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.secondary.inverted.menu .link.item:not(.disabled),.ui.secondary.inverted.menu a.item:not(.disabled){color:rgba(255,255,255,.7)}.ui.secondary.inverted.menu .dropdown.item:hover,.ui.secondary.inverted.menu .link.item:hover,.ui.secondary.inverted.menu a.item:hover{background:rgba(255,255,255,.08);color:#fff}.ui.secondary.inverted.menu .active.item{background:rgba(255,255,255,.15);color:#fff}.ui.secondary.item.menu{margin-left:0;margin-right:0}.ui.secondary.item.menu .item:last-child{margin-right:0}.ui.secondary.attached.menu{-webkit-box-shadow:none;box-shadow:none}.ui.vertical.secondary.menu .item:not(.dropdown)>.menu{margin:0 -.92857143em}.ui.vertical.secondary.menu .item:not(.dropdown)>.menu>.item{margin:0;padding:.5em 1.33333333em}.ui.secondary.vertical.menu>.item{border:none;margin:0 0 .35714286em;border-radius:.28571429rem!important}.ui.secondary.vertical.menu>.header.item{border-radius:0}.ui.vertical.secondary.menu .item>.menu .item{background-color:transparent}.ui.secondary.inverted.menu{background-color:transparent}.ui.secondary.pointing.menu{margin-left:0;margin-right:0;border-bottom:2px solid rgba(34,36,38,.15)}.ui.secondary.pointing.menu .item{border-bottom-color:transparent;border-bottom-style:solid;border-radius:0;-ms-flex-item-align:end;align-self:flex-end;margin:0 0 -2px;padding:.85714286em 1.14285714em;border-bottom-width:2px;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.secondary.pointing.menu .ui.dropdown .menu .item{border-bottom-width:0}.ui.secondary.pointing.menu .item>.label:not(.floating){margin-top:-.3em;margin-bottom:-.3em}.ui.secondary.pointing.menu .item>.circular.label{margin-top:-.5em;margin-bottom:-.5em}.ui.secondary.pointing.menu .header.item{color:rgba(0,0,0,.85)!important}.ui.secondary.pointing.menu .text.item{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.secondary.pointing.menu .item:after{display:none}.ui.secondary.pointing.menu .dropdown.item:hover,.ui.secondary.pointing.menu .link.item:hover,.ui.secondary.pointing.menu a.item:hover{background-color:transparent;color:rgba(0,0,0,.87)}.ui.secondary.pointing.menu .dropdown.item:active,.ui.secondary.pointing.menu .link.item:active,.ui.secondary.pointing.menu a.item:active{background-color:transparent;border-color:rgba(34,36,38,.15)}.ui.secondary.pointing.menu .active.item{background-color:transparent;-webkit-box-shadow:none;box-shadow:none;border-color:currentColor;font-weight:700;color:rgba(0,0,0,.95)}.ui.secondary.pointing.menu .active.item:hover{border-color:currentColor;color:rgba(0,0,0,.95)}.ui.secondary.pointing.menu .active.dropdown.item{border-color:transparent}.ui.secondary.vertical.pointing.menu{border-bottom-width:0;border-right-width:2px;border-right-style:solid;border-right-color:rgba(34,36,38,.15)}.ui.secondary.vertical.pointing.menu .item{border-bottom:none;border-right-style:solid;border-right-color:transparent;border-radius:0!important;margin:0 -2px 0 0;border-right-width:2px}.ui.secondary.vertical.pointing.menu .active.item{border-color:currentColor}.ui.secondary.inverted.pointing.menu{border-color:rgba(255,255,255,.1)}.ui.secondary.inverted.pointing.menu .item:not(.disabled){color:rgba(255,255,255,.9)}.ui.secondary.inverted.pointing.menu .header.item{color:#fff!important}.ui.secondary.inverted.pointing.menu .link.item:hover,.ui.secondary.inverted.pointing.menu a.item:hover{color:#fff}.ui.ui.secondary.inverted.pointing.menu .active.item{border-color:#fff;color:#fff;background-color:transparent}.ui.text.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none;box-shadow:none;border:none;margin:1em -.5em}.ui.text.menu .item{border-radius:0;-webkit-box-shadow:none;box-shadow:none;-ms-flex-item-align:center;align-self:center;margin:0 0;padding:.35714286em .5em;font-weight:400;color:rgba(0,0,0,.6);-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.text.menu .item:before,.ui.text.menu .menu .item:before{display:none!important}.ui.text.menu .header.item{background-color:transparent;opacity:1;color:rgba(0,0,0,.85);font-size:.92857143em;text-transform:uppercase;font-weight:700}.ui.text.menu .item>img:not(.ui){margin:0}.ui.text.item.menu .item{margin:0}.ui.vertical.text.menu{margin:1em 0}.ui.vertical.text.menu:first-child{margin-top:0}.ui.vertical.text.menu:last-child{margin-bottom:0}.ui.vertical.text.menu .item{margin:.57142857em 0;padding-left:0;padding-right:0}.ui.vertical.text.menu .item>i.icon{float:none;margin:0 .35714286em 0 0}.ui.vertical.text.menu .header.item{margin:.57142857em 0 .71428571em}.ui.vertical.text.menu .item:not(.dropdown)>.menu{margin:0}.ui.vertical.text.menu .item:not(.dropdown)>.menu>.item{margin:0;padding:.5em 0}.ui.text.menu .item:hover{opacity:1;background-color:transparent}.ui.text.menu .active.item{background-color:transparent;border:none;-webkit-box-shadow:none;box-shadow:none;font-weight:400;color:rgba(0,0,0,.95)}.ui.text.menu .active.item:hover{background-color:transparent}.ui.text.pointing.menu .active.item:after{-webkit-box-shadow:none;box-shadow:none}.ui.text.attached.menu{-webkit-box-shadow:none;box-shadow:none}.ui.inverted.text.menu,.ui.inverted.text.menu .active.item,.ui.inverted.text.menu .item,.ui.inverted.text.menu .item:hover{background-color:transparent}.ui.fluid.text.menu{margin-left:0;margin-right:0}.ui.vertical.icon.menu{display:inline-block;width:auto}.ui.icon.menu .item{height:auto;text-align:center;color:#1b1c1d}.ui.icon.menu .item>.icon:not(.dropdown){margin:0;opacity:1}.ui.icon.menu .icon:before{opacity:1}.ui.menu .icon.item>.icon{width:auto;margin:0 auto}.ui.vertical.icon.menu .item>.icon:not(.dropdown){display:block;opacity:1;margin:0 auto;float:none}.ui.inverted.icon.menu .item{color:#fff}.ui.labeled.icon.menu{text-align:center}.ui.labeled.icon.menu .item{min-width:6em;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.labeled.icon.menu>.item>.icon:not(.dropdown){height:1em;display:block;font-size:1.71428571em!important;margin:0 auto .5rem!important}.ui.fluid.labeled.icon.menu>.item{min-width:0}@media only screen and (max-width:767.98px){.ui.stackable.menu{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.stackable.menu .item{width:100%!important}.ui.stackable.menu .item:before{position:absolute;content:'';top:auto;bottom:0;left:0;width:100%;height:1px;background:rgba(34,36,38,.1)}.ui.stackable.menu .left.item,.ui.stackable.menu .left.menu{margin-right:0!important}.ui.stackable.menu .right.item,.ui.stackable.menu .right.menu{margin-left:0!important}.ui.stackable.menu .center.item,.ui.stackable.menu .center.menu{margin-left:0!important;margin-right:0!important}.ui.stackable.menu .center.menu,.ui.stackable.menu .left.menu,.ui.stackable.menu .right.menu{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.ui.ui.menu .primary.active.item,.ui.ui.primary.menu .active.item,.ui.ui.primary.menu .active.item:hover{color:#2185d0}.ui.ui.menu .red.active.item,.ui.ui.red.menu .active.item,.ui.ui.red.menu .active.item:hover{color:#db2828}.ui.ui.menu .orange.active.item,.ui.ui.orange.menu .active.item,.ui.ui.orange.menu .active.item:hover{color:#f2711c}.ui.ui.menu .yellow.active.item,.ui.ui.yellow.menu .active.item,.ui.ui.yellow.menu .active.item:hover{color:#fbbd08}.ui.ui.menu .olive.active.item,.ui.ui.olive.menu .active.item,.ui.ui.olive.menu .active.item:hover{color:#b5cc18}.ui.ui.green.menu .active.item,.ui.ui.green.menu .active.item:hover,.ui.ui.menu .green.active.item{color:#21ba45}.ui.ui.menu .teal.active.item,.ui.ui.teal.menu .active.item,.ui.ui.teal.menu .active.item:hover{color:#00b5ad}.ui.ui.blue.menu .active.item,.ui.ui.blue.menu .active.item:hover,.ui.ui.menu .blue.active.item{color:#2185d0}.ui.ui.menu .violet.active.item,.ui.ui.violet.menu .active.item,.ui.ui.violet.menu .active.item:hover{color:#6435c9}.ui.ui.menu .purple.active.item,.ui.ui.purple.menu .active.item,.ui.ui.purple.menu .active.item:hover{color:#a333c8}.ui.ui.menu .pink.active.item,.ui.ui.pink.menu .active.item,.ui.ui.pink.menu .active.item:hover{color:#e03997}.ui.ui.brown.menu .active.item,.ui.ui.brown.menu .active.item:hover,.ui.ui.menu .brown.active.item{color:#a5673f}.ui.ui.grey.menu .active.item,.ui.ui.grey.menu .active.item:hover,.ui.ui.menu .grey.active.item{color:#767676}.ui.ui.black.menu .active.item,.ui.ui.black.menu .active.item:hover,.ui.ui.menu .black.active.item{color:#1b1c1d}.ui.inverted.menu{border:0 solid transparent;background:#1b1c1d;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.menu .item,.ui.inverted.menu .item>a:not(.ui){background:0 0;color:rgba(255,255,255,.9)}.ui.inverted.menu .item.menu{background:0 0}.ui.inverted.menu .item:before{background:rgba(255,255,255,.08)}.ui.vertical.inverted.menu .item:before{background:rgba(255,255,255,.08)}.ui.vertical.inverted.menu .menu .item,.ui.vertical.inverted.menu .menu .item a:not(.ui){color:rgba(255,255,255,.5)}.ui.inverted.menu .header.item{margin:0;background:0 0;-webkit-box-shadow:none;box-shadow:none}.ui.ui.inverted.menu .item.disabled{color:rgba(225,225,225,.3)}.ui.inverted.menu .dropdown.item:hover,.ui.inverted.menu .link.item:hover,.ui.inverted.menu a.item:hover,.ui.link.inverted.menu .item:hover{background:rgba(255,255,255,.08);color:#fff}.ui.vertical.inverted.menu .item .menu .link.item:hover,.ui.vertical.inverted.menu .item .menu a.item:hover{background:0 0;color:#fff}.ui.inverted.menu .link.item:active,.ui.inverted.menu a.item:active{background:rgba(255,255,255,.08);color:#fff}.ui.inverted.menu .active.item{background:#3d3e3f;color:#fff!important}.ui.inverted.vertical.menu .item .menu .active.item{background:0 0;color:#fff}.ui.inverted.pointing.menu .active.item:after{background:#3d3e3f;margin:0!important;-webkit-box-shadow:none!important;box-shadow:none!important;border:none!important}.ui.inverted.menu .active.item:hover{background:#3d3e3f;color:#fff!important}.ui.inverted.pointing.menu .active.item:hover:after{background:#3d3e3f}.ui.floated.menu{float:left;margin:0 .5rem 0 0}.ui.floated.menu .item:last-child:before{display:none}.ui.right.floated.menu{float:right;margin:0 0 0 .5rem}.ui.ui.inverted.menu .primary.active.item,.ui.ui.inverted.primary.menu{background-color:#2185d0}.ui.inverted.primary.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.primary.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.primary.menu .active.item{background-color:#1678c2}.ui.ui.inverted.menu .red.active.item,.ui.ui.inverted.red.menu{background-color:#db2828}.ui.inverted.red.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.red.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.red.menu .active.item{background-color:#d01919}.ui.ui.inverted.menu .orange.active.item,.ui.ui.inverted.orange.menu{background-color:#f2711c}.ui.inverted.orange.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.orange.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.orange.menu .active.item{background-color:#f26202}.ui.ui.inverted.menu .yellow.active.item,.ui.ui.inverted.yellow.menu{background-color:#fbbd08}.ui.inverted.yellow.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.yellow.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.yellow.menu .active.item{background-color:#eaae00}.ui.ui.inverted.menu .olive.active.item,.ui.ui.inverted.olive.menu{background-color:#b5cc18}.ui.inverted.olive.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.olive.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.olive.menu .active.item{background-color:#a7bd0d}.ui.ui.inverted.green.menu,.ui.ui.inverted.menu .green.active.item{background-color:#21ba45}.ui.inverted.green.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.green.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.green.menu .active.item{background-color:#16ab39}.ui.ui.inverted.menu .teal.active.item,.ui.ui.inverted.teal.menu{background-color:#00b5ad}.ui.inverted.teal.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.teal.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.teal.menu .active.item{background-color:#009c95}.ui.ui.inverted.blue.menu,.ui.ui.inverted.menu .blue.active.item{background-color:#2185d0}.ui.inverted.blue.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.blue.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.blue.menu .active.item{background-color:#1678c2}.ui.ui.inverted.menu .violet.active.item,.ui.ui.inverted.violet.menu{background-color:#6435c9}.ui.inverted.violet.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.violet.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.violet.menu .active.item{background-color:#5829bb}.ui.ui.inverted.menu .purple.active.item,.ui.ui.inverted.purple.menu{background-color:#a333c8}.ui.inverted.purple.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.purple.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.purple.menu .active.item{background-color:#9627ba}.ui.ui.inverted.menu .pink.active.item,.ui.ui.inverted.pink.menu{background-color:#e03997}.ui.inverted.pink.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.pink.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.pink.menu .active.item{background-color:#e61a8d}.ui.ui.inverted.brown.menu,.ui.ui.inverted.menu .brown.active.item{background-color:#a5673f}.ui.inverted.brown.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.brown.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.brown.menu .active.item{background-color:#975b33}.ui.ui.inverted.grey.menu,.ui.ui.inverted.menu .grey.active.item{background-color:#767676}.ui.inverted.grey.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.grey.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.grey.menu .active.item{background-color:#838383}.ui.ui.inverted.black.menu,.ui.ui.inverted.menu .black.active.item{background-color:#1b1c1d}.ui.inverted.black.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.black.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.black.menu .active.item{background-color:#27292a}.ui.ui.ui.inverted.pointing.menu .active.item:after{background-color:inherit}.ui.fitted.menu .item,.ui.fitted.menu .item .menu .item,.ui.menu .fitted.item{padding:0}.ui.horizontally.fitted.menu .item,.ui.horizontally.fitted.menu .item .menu .item,.ui.menu .horizontally.fitted.item{padding-top:.92857143em;padding-bottom:.92857143em}.ui.menu .vertically.fitted.item,.ui.vertically.fitted.menu .item,.ui.vertically.fitted.menu .item .menu .item{padding-left:1.14285714em;padding-right:1.14285714em}.ui.borderless.menu .item .menu .item:before,.ui.borderless.menu .item:before,.ui.menu .borderless.item:before{background:0 0!important}.ui.compact.menu{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin:0;vertical-align:middle}.ui.compact.vertical.menu{display:-ms-inline-flexbox!important;display:inline-block}.ui.compact.menu:not(.secondary) .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.compact.menu .item:last-child:before{display:none}.ui.compact.vertical.menu{width:auto!important}.ui.compact.vertical.menu .item:last-child::before{display:block}.ui.menu.fluid,.ui.vertical.menu.fluid{width:100%!important}.ui.item.menu,.ui.item.menu .item{width:100%;padding-left:0!important;padding-right:0!important;margin-left:0!important;margin-right:0!important;text-align:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.attached.item.menu:not(.tabular){margin:0 -1px!important}.ui.item.menu .item:last-child:before{display:none}.ui.menu.two.item .item{width:50%}.ui.menu.three.item .item{width:33.333%}.ui.menu.four.item .item{width:25%}.ui.menu.five.item .item{width:20%}.ui.menu.six.item .item{width:16.666%}.ui.menu.seven.item .item{width:14.285%}.ui.menu.eight.item .item{width:12.5%}.ui.menu.nine.item .item{width:11.11%}.ui.menu.ten.item .item{width:10%}.ui.menu.eleven.item .item{width:9.09%}.ui.menu.twelve.item .item{width:8.333%}.ui.menu.fixed{position:fixed;z-index:101;margin:0;width:100%}.ui.menu.fixed,.ui.menu.fixed .item:first-child,.ui.menu.fixed .item:last-child{border-radius:0!important}.ui.fixed.menu,.ui[class*="top fixed"].menu{top:0;left:0;right:auto;bottom:auto}.ui[class*="top fixed"].menu{border-top:none;border-left:none;border-right:none}.ui[class*="right fixed"].menu{border-top:none;border-bottom:none;border-right:none;top:0;right:0;left:auto;bottom:auto;width:auto;height:100%}.ui[class*="bottom fixed"].menu{border-bottom:none;border-left:none;border-right:none;bottom:0;left:0;top:auto;right:auto}.ui[class*="left fixed"].menu{border-top:none;border-bottom:none;border-left:none;top:0;left:0;right:auto;bottom:auto;width:auto;height:100%}.ui.fixed.menu+.ui.grid{padding-top:2.75rem}.ui.pointing.menu .item:after{visibility:hidden;position:absolute;content:'';top:100%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);background:0 0;margin:.5px 0 0;width:.57142857em;height:.57142857em;border:none;border-bottom:1px solid #d4d4d5;border-right:1px solid #d4d4d5;z-index:2;-webkit-transition:background .1s ease;transition:background .1s ease}.ui.vertical.pointing.menu .item:after{position:absolute;top:50%;right:0;bottom:auto;left:auto;-webkit-transform:translateX(50%) translateY(-50%) rotate(45deg);transform:translateX(50%) translateY(-50%) rotate(45deg);margin:0 -.5px 0 0;border:none;border-top:1px solid #d4d4d5;border-right:1px solid #d4d4d5}.ui.pointing.menu .ui.dropdown .menu .item:after,.ui.vertical.pointing.menu .ui.dropdown .menu .item:after{display:none}.ui.pointing.menu .active.item:after{visibility:visible}.ui.pointing.menu .active.dropdown.item:after{visibility:hidden}.ui.pointing.menu .active.item .menu .active.item:after,.ui.pointing.menu .dropdown.active.item:after{display:none}.ui.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.pointing.menu .active.item:after{background-color:#f2f2f2}.ui.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .active.item:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .menu .active.item:after{background-color:#fff}.ui.inverted.pointing.menu .primary.active.item:after{background-color:#2185d0}.ui.inverted.pointing.menu .secondary.active.item:after{background-color:#1b1c1d}.ui.inverted.pointing.menu .red.active.item:after{background-color:#db2828}.ui.inverted.pointing.menu .orange.active.item:after{background-color:#f2711c}.ui.inverted.pointing.menu .yellow.active.item:after{background-color:#fbbd08}.ui.inverted.pointing.menu .olive.active.item:after{background-color:#b5cc18}.ui.inverted.pointing.menu .green.active.item:after{background-color:#21ba45}.ui.inverted.pointing.menu .teal.active.item:after{background-color:#00b5ad}.ui.inverted.pointing.menu .blue.active.item:after{background-color:#2185d0}.ui.inverted.pointing.menu .violet.active.item:after{background-color:#6435c9}.ui.inverted.pointing.menu .purple.active.item:after{background-color:#a333c8}.ui.inverted.pointing.menu .pink.active.item:after{background-color:#e03997}.ui.inverted.pointing.menu .brown.active.item:after{background-color:#a5673f}.ui.inverted.pointing.menu .grey.active.item:after{background-color:#767676}.ui.inverted.pointing.menu .black.active.item:after{background-color:#1b1c1d}.ui.attached.menu{top:0;bottom:0;border-radius:0;margin:0 -1px;width:calc(100% + 2px);max-width:calc(100% + 2px);-webkit-box-shadow:none;box-shadow:none}.ui.attached+.ui.attached.menu:not(.top){border-top:none}.ui[class*="top attached"].menu{bottom:0;margin-bottom:0;top:0;margin-top:1rem;border-radius:.28571429rem .28571429rem 0 0}.ui.menu[class*="top attached"]:first-child{margin-top:0}.ui[class*="bottom attached"].menu{bottom:0;margin-top:0;top:0;margin-bottom:1rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;border-radius:0 0 .28571429rem .28571429rem}.ui[class*="bottom attached"].menu:last-child{margin-bottom:0}.ui.top.attached.menu>.item:first-child{border-radius:.28571429rem 0 0 0}.ui.bottom.attached.menu>.item:first-child{border-radius:0 0 0 .28571429rem}.ui.attached.menu:not(.tabular){border:1px solid #d4d4d5}.ui.attached.inverted.menu{border:none}.ui.attached.tabular.menu{margin-left:0;margin-right:0;width:100%}.ui.menu{font-size:1rem}.ui.vertical.menu{width:15rem}.ui.mini.menu,.ui.mini.menu .dropdown,.ui.mini.menu .dropdown .menu>.item{font-size:.78571429rem}.ui.mini.vertical.menu:not(.icon){width:9rem}.ui.tiny.menu,.ui.tiny.menu .dropdown,.ui.tiny.menu .dropdown .menu>.item{font-size:.85714286rem}.ui.tiny.vertical.menu:not(.icon){width:11rem}.ui.small.menu,.ui.small.menu .dropdown,.ui.small.menu .dropdown .menu>.item{font-size:.92857143rem}.ui.small.vertical.menu:not(.icon){width:13rem}.ui.large.menu,.ui.large.menu .dropdown,.ui.large.menu .dropdown .menu>.item{font-size:1.07142857rem}.ui.large.vertical.menu:not(.icon){width:18rem}.ui.big.menu,.ui.big.menu .dropdown,.ui.big.menu .dropdown .menu>.item{font-size:1.14285714rem}.ui.big.vertical.menu:not(.icon){width:20rem}.ui.huge.menu,.ui.huge.menu .dropdown,.ui.huge.menu .dropdown .menu>.item{font-size:1.21428571rem}.ui.huge.vertical.menu:not(.icon){width:22rem}.ui.massive.menu,.ui.massive.menu .dropdown,.ui.massive.menu .dropdown .menu>.item{font-size:1.28571429rem}.ui.massive.vertical.menu:not(.icon){width:25rem}.ui.menu .ui.inverted.inverted.dropdown.item .menu{background:#1b1c1d;-webkit-box-shadow:none;box-shadow:none}.ui.menu .ui.inverted.dropdown .menu>.item{color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.active.item{background:0 0!important;color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.item:hover{background:rgba(255,255,255,.08)!important;color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.selected.item{background:rgba(255,255,255,.15)!important;color:rgba(255,255,255,.8)!important}.ui.vertical.menu .inverted.dropdown.item .menu{-webkit-box-shadow:none;box-shadow:none} +/* + * # Fomantic-UI - Button + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ +.ui.button{cursor:pointer;display:inline-block;min-height:1em;outline:0;border:none;vertical-align:baseline;background:#e0e1e2 none;color:rgba(0,0,0,.6);font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;margin:0 .25em 0 0;padding:.78571429em 1.5em .78571429em;text-transform:none;text-shadow:none;font-weight:700;line-height:1em;font-style:normal;text-align:center;text-decoration:none;border-radius:.28571429rem;-webkit-box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease,-webkit-box-shadow .1s ease;will-change:auto;-webkit-tap-highlight-color:transparent}.ui.button:hover{background-color:#cacbcd;background-image:none;-webkit-box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;color:rgba(0,0,0,.8)}.ui.button:hover .icon{opacity:.85}.ui.button:focus{background-color:#cacbcd;color:rgba(0,0,0,.8);background-image:none;-webkit-box-shadow:'';box-shadow:''}.ui.button:focus .icon{opacity:.85}.ui.active.button:active,.ui.button:active{background-color:#babbbc;background-image:'';color:rgba(0,0,0,.9);-webkit-box-shadow:0 0 0 1px transparent inset,none;box-shadow:0 0 0 1px transparent inset,none}.ui.active.button{background-color:#c0c1c2;background-image:none;-webkit-box-shadow:0 0 0 1px transparent inset;box-shadow:0 0 0 1px transparent inset;color:rgba(0,0,0,.95)}.ui.active.button:hover{background-color:#c0c1c2;background-image:none;color:rgba(0,0,0,.95)}.ui.active.button:active{background-color:#c0c1c2;background-image:none}.ui.loading.loading.loading.loading.loading.loading.button{position:relative;cursor:default;text-shadow:none!important;color:transparent;opacity:1;pointer-events:auto;-webkit-transition:all 0s linear,opacity .1s ease;transition:all 0s linear,opacity .1s ease}.ui.loading.button:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;border:.2em solid rgba(0,0,0,.15)}.ui.loading.button:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid currentColor;color:#fff;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent}.ui.labeled.icon.loading.button .icon{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.ui.basic.loading.button:not(.inverted):before{border-color:rgba(0,0,0,.1)}.ui.basic.loading.button:not(.inverted):after{border-color:#767676}.ui.button:disabled,.ui.buttons .disabled.button:not(.basic),.ui.disabled.active.button,.ui.disabled.button,.ui.disabled.button:hover{cursor:default;opacity:.45!important;background-image:none;-webkit-box-shadow:none;box-shadow:none;pointer-events:none!important}.ui.basic.buttons .ui.disabled.button{border-color:rgba(34,36,38,.5)}.ui.animated.button{position:relative;overflow:hidden;padding-right:0!important;vertical-align:middle;z-index:1}.ui.animated.button .content{will-change:transform,opacity}.ui.animated.button .visible.content{position:relative;margin-right:1.5em}.ui.animated.button .hidden.content{position:absolute;width:100%}.ui.animated.button .hidden.content,.ui.animated.button .visible.content{-webkit-transition:right .3s ease 0s;transition:right .3s ease 0s}.ui.animated.button .visible.content{left:auto;right:0}.ui.animated.button .hidden.content{top:50%;left:auto;right:-100%;margin-top:-.5em}.ui.animated.button:focus .visible.content,.ui.animated.button:hover .visible.content{left:auto;right:200%}.ui.animated.button:focus .hidden.content,.ui.animated.button:hover .hidden.content{left:auto;right:0}.ui.vertical.animated.button .hidden.content,.ui.vertical.animated.button .visible.content{-webkit-transition:top .3s ease,-webkit-transform .3s ease;transition:top .3s ease,-webkit-transform .3s ease;transition:top .3s ease,transform .3s ease;transition:top .3s ease,transform .3s ease,-webkit-transform .3s ease}.ui.vertical.animated.button .visible.content{-webkit-transform:translateY(0);transform:translateY(0);right:auto}.ui.vertical.animated.button .hidden.content{top:-50%;left:0;right:auto}.ui.vertical.animated.button:focus .visible.content,.ui.vertical.animated.button:hover .visible.content{-webkit-transform:translateY(200%);transform:translateY(200%);right:auto}.ui.vertical.animated.button:focus .hidden.content,.ui.vertical.animated.button:hover .hidden.content{top:50%;right:auto}.ui.fade.animated.button .hidden.content,.ui.fade.animated.button .visible.content{-webkit-transition:opacity .3s ease,-webkit-transform .3s ease;transition:opacity .3s ease,-webkit-transform .3s ease;transition:opacity .3s ease,transform .3s ease;transition:opacity .3s ease,transform .3s ease,-webkit-transform .3s ease}.ui.fade.animated.button .visible.content{left:auto;right:auto;opacity:1;-webkit-transform:scale(1);transform:scale(1)}.ui.fade.animated.button .hidden.content{opacity:0;left:0;right:auto;-webkit-transform:scale(1.5);transform:scale(1.5)}.ui.fade.animated.button:focus .visible.content,.ui.fade.animated.button:hover .visible.content{left:auto;right:auto;opacity:0;-webkit-transform:scale(.75);transform:scale(.75)}.ui.fade.animated.button:focus .hidden.content,.ui.fade.animated.button:hover .hidden.content{left:0;right:auto;opacity:1;-webkit-transform:scale(1);transform:scale(1)}.ui.inverted.button{-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset;background:transparent none;color:#fff;text-shadow:none!important}.ui.inverted.buttons .button{margin:0 0 0 -2px}.ui.inverted.buttons .button:first-child{margin-left:0}.ui.inverted.vertical.buttons .button{margin:0 0 -2px 0}.ui.inverted.vertical.buttons .button:first-child{margin-top:0}.ui.inverted.button:hover{background:#fff;-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset;color:rgba(0,0,0,.8)}.ui.inverted.button.active,.ui.inverted.button:focus{background:#fff;-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset;color:rgba(0,0,0,.8)}.ui.inverted.button.active:focus{background:#dcddde;-webkit-box-shadow:0 0 0 2px #dcddde inset;box-shadow:0 0 0 2px #dcddde inset;color:rgba(0,0,0,.8)}.ui.labeled.button:not(.icon){display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;background:0 0;padding:0!important;border:none;-webkit-box-shadow:none;box-shadow:none}.ui.labeled.button>.button{margin:0}.ui.labeled.button>.label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:0 0 0 -1px!important;font-size:1em;padding:'';border-color:rgba(34,36,38,.15)}.ui.labeled.button>.tag.label:before{width:1.85em;height:1.85em}.ui.labeled.button:not([class*="left labeled"])>.button{border-top-right-radius:0;border-bottom-right-radius:0}.ui.labeled.button:not([class*="left labeled"])>.label{border-top-left-radius:0;border-bottom-left-radius:0}.ui[class*="left labeled"].button>.button{border-top-left-radius:0;border-bottom-left-radius:0}.ui[class*="left labeled"].button>.label{border-top-right-radius:0;border-bottom-right-radius:0}.ui.facebook.button{background-color:#3b5998;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.facebook.button:hover{background-color:#304d8a;color:#fff;text-shadow:none}.ui.facebook.button:active{background-color:#2d4373;color:#fff;text-shadow:none}.ui.twitter.button{background-color:#1da1f2;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.twitter.button:hover{background-color:#0298f3;color:#fff;text-shadow:none}.ui.twitter.button:active{background-color:#0c85d0;color:#fff;text-shadow:none}.ui.google.plus.button{background-color:#dd4b39;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.google.plus.button:hover{background-color:#e0321c;color:#fff;text-shadow:none}.ui.google.plus.button:active{background-color:#c23321;color:#fff;text-shadow:none}.ui.linkedin.button{background-color:#0077b5;color:#fff;text-shadow:none}.ui.linkedin.button:hover{background-color:#00669c;color:#fff;text-shadow:none}.ui.linkedin.button:active{background-color:#005582;color:#fff;text-shadow:none}.ui.youtube.button{background-color:red;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.youtube.button:hover{background-color:#e60000;color:#fff;text-shadow:none}.ui.youtube.button:active{background-color:#c00;color:#fff;text-shadow:none}.ui.instagram.button{background-color:#49769c;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.instagram.button:hover{background-color:#3d698e;color:#fff;text-shadow:none}.ui.instagram.button:active{background-color:#395c79;color:#fff;text-shadow:none}.ui.pinterest.button{background-color:#bd081c;color:#fff;text-shadow:none;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.pinterest.button:hover{background-color:#ac0013;color:#fff;text-shadow:none}.ui.pinterest.button:active{background-color:#8c0615;color:#fff;text-shadow:none}.ui.vk.button{background-color:#45668e;color:#fff;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.vk.button:hover{background-color:#395980;color:#fff}.ui.vk.button:active{background-color:#344d6c;color:#fff}.ui.whatsapp.button{background-color:#25d366;color:#fff;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.whatsapp.button:hover{background-color:#19c55a;color:#fff}.ui.whatsapp.button:active{background-color:#1da851;color:#fff}.ui.telegram.button{background-color:#08c;color:#fff;background-image:none;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.telegram.button:hover{background-color:#0077b3;color:#fff}.ui.telegram.button:active{background-color:#069;color:#fff}.ui.button>.icon:not(.button){height:auto;opacity:.8;margin:0 .42857143em 0 -.21428571em;-webkit-transition:opacity .1s ease;transition:opacity .1s ease;vertical-align:'';color:''}.ui.button:not(.icon)>.icon:not(.button):not(.dropdown),.ui.button:not(.icon)>.icons:not(.button):not(.dropdown){margin:0 .42857143em 0 -.21428571em}.ui.button:not(.icon)>.right.icon:not(.button):not(.dropdown){margin:0 -.21428571em 0 .42857143em}.ui[class*="left floated"].button,.ui[class*="left floated"].buttons{float:left;margin-left:0;margin-right:.25em}.ui[class*="right floated"].button,.ui[class*="right floated"].buttons{float:right;margin-right:0;margin-left:.25em}.ui.compact.button,.ui.compact.buttons .button{padding:.58928571em 1.125em .58928571em}.ui.compact.icon.button,.ui.compact.icon.buttons .button{padding:.58928571em .58928571em .58928571em}.ui.compact.labeled.icon.button,.ui.compact.labeled.icon.buttons .button{padding:.58928571em 3.69642857em .58928571em}.ui.compact.labeled.icon.button>.icon,.ui.compact.labeled.icon.buttons .button>.icon{padding:.58928571em 0 .58928571em 0}.ui.button,.ui.buttons .button,.ui.buttons .or{font-size:1rem}.ui.mini.buttons .button,.ui.mini.buttons .dropdown,.ui.mini.buttons .dropdown .menu>.item,.ui.mini.buttons .or,.ui.ui.ui.ui.mini.button{font-size:.78571429rem}.ui.tiny.buttons .button,.ui.tiny.buttons .dropdown,.ui.tiny.buttons .dropdown .menu>.item,.ui.tiny.buttons .or,.ui.ui.ui.ui.tiny.button{font-size:.85714286rem}.ui.small.buttons .button,.ui.small.buttons .dropdown,.ui.small.buttons .dropdown .menu>.item,.ui.small.buttons .or,.ui.ui.ui.ui.small.button{font-size:.92857143rem}.ui.large.buttons .button,.ui.large.buttons .dropdown,.ui.large.buttons .dropdown .menu>.item,.ui.large.buttons .or,.ui.ui.ui.ui.large.button{font-size:1.14285714rem}.ui.big.buttons .button,.ui.big.buttons .dropdown,.ui.big.buttons .dropdown .menu>.item,.ui.big.buttons .or,.ui.ui.ui.ui.big.button{font-size:1.28571429rem}.ui.huge.buttons .button,.ui.huge.buttons .dropdown,.ui.huge.buttons .dropdown .menu>.item,.ui.huge.buttons .or,.ui.ui.ui.ui.huge.button{font-size:1.42857143rem}.ui.massive.buttons .button,.ui.massive.buttons .dropdown,.ui.massive.buttons .dropdown .menu>.item,.ui.massive.buttons .or,.ui.ui.ui.ui.massive.button{font-size:1.71428571rem}.ui.icon.button,.ui.icon.buttons .button{padding:.78571429em .78571429em .78571429em}.ui.icon.button>.icon,.ui.icon.buttons .button>.icon{opacity:.9;margin:0!important;vertical-align:top}.ui.basic.button,.ui.basic.buttons .button{background:transparent none;color:rgba(0,0,0,.6);font-weight:400;border-radius:.28571429rem;text-transform:none;text-shadow:none!important;-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset}.ui.basic.buttons{-webkit-box-shadow:none;box-shadow:none;border:1px solid rgba(34,36,38,.15);border-radius:.28571429rem}.ui.basic.buttons .button{border-radius:0}.ui.basic.button:hover,.ui.basic.buttons .button:hover{background:#fff;color:rgba(0,0,0,.8);-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset}.ui.basic.button:focus,.ui.basic.buttons .button:focus{background:#fff;color:rgba(0,0,0,.8);-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset}.ui.basic.button:active,.ui.basic.buttons .button:active{background:#f8f8f8;color:rgba(0,0,0,.9);-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 1px 4px 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 1px 4px 0 rgba(34,36,38,.15) inset}.ui.basic.active.button,.ui.basic.buttons .active.button{background:rgba(0,0,0,.05);-webkit-box-shadow:'';box-shadow:'';color:rgba(0,0,0,.95)}.ui.basic.active.button:hover,.ui.basic.buttons .active.button:hover{background-color:rgba(0,0,0,.05)}.ui.basic.buttons .button:hover{-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset inset;box-shadow:0 0 0 1px rgba(34,36,38,.35) inset,0 0 0 0 rgba(34,36,38,.15) inset inset}.ui.basic.buttons .button:active{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 1px 4px 0 rgba(34,36,38,.15) inset inset;box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 1px 4px 0 rgba(34,36,38,.15) inset inset}.ui.basic.buttons .active.button{-webkit-box-shadow:'';box-shadow:''}.ui.basic.inverted.button,.ui.basic.inverted.buttons .button{background-color:transparent;color:#f9fafb;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset}.ui.basic.inverted.button:hover,.ui.basic.inverted.buttons .button:hover{color:#fff;-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset}.ui.basic.inverted.button:focus,.ui.basic.inverted.buttons .button:focus{color:#fff;-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset}.ui.basic.inverted.button:active,.ui.basic.inverted.buttons .button:active{background-color:rgba(255,255,255,.08);color:#fff;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.9) inset;box-shadow:0 0 0 2px rgba(255,255,255,.9) inset}.ui.basic.inverted.active.button,.ui.basic.inverted.buttons .active.button{background-color:rgba(255,255,255,.08);color:#fff;text-shadow:none;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.7) inset;box-shadow:0 0 0 2px rgba(255,255,255,.7) inset}.ui.basic.inverted.active.button:hover,.ui.basic.inverted.buttons .active.button:hover{background-color:rgba(255,255,255,.15);-webkit-box-shadow:0 0 0 2px #fff inset;box-shadow:0 0 0 2px #fff inset}.ui.basic.buttons .button{border-left:1px solid rgba(34,36,38,.15);-webkit-box-shadow:none;box-shadow:none}.ui.basic.vertical.buttons .button{border-left:none;border-left-width:0;border-top:1px solid rgba(34,36,38,.15)}.ui.basic.vertical.buttons .button:first-child{border-top-width:0}.ui.tertiary.button{-webkit-transition:color .1s ease!important;transition:color .1s ease!important;border-radius:0;margin:.28571429em .25em .28571429em 0!important;padding:.5em!important;-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6);background:0 0}.ui.tertiary.button:hover{-webkit-box-shadow:inset 0 -.2em 0 #666;box-shadow:inset 0 -.2em 0 #666;color:#333;background:0 0}.ui.tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #666;box-shadow:inset 0 -.2em 0 #666;color:#333;background:0 0}.ui.tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #999;box-shadow:inset 0 -.2em 0 #999;border-radius:.28571429rem .28571429rem 0 0;color:#666;background:0 0}.ui.labeled.icon.button,.ui.labeled.icon.buttons .button{position:relative;padding-left:4.07142857em!important;padding-right:1.5em!important}.ui.labeled.icon.button>.icon,.ui.labeled.icon.buttons>.button>.icon{position:absolute;top:0;left:0;height:100%;line-height:1;border-radius:0;border-top-left-radius:inherit;border-bottom-left-radius:inherit;text-align:center;-webkit-animation:none;animation:none;padding:.78571429em 0 .78571429em 0;margin:0;width:2.57142857em;background-color:rgba(0,0,0,.05);color:'';-webkit-box-shadow:-1px 0 0 0 transparent inset;box-shadow:-1px 0 0 0 transparent inset}.ui[class*="right labeled"].icon.button{padding-right:4.07142857em!important;padding-left:1.5em!important}.ui[class*="right labeled"].icon.button>.icon{left:auto;right:0;border-radius:0;border-top-right-radius:inherit;border-bottom-right-radius:inherit;-webkit-box-shadow:1px 0 0 0 transparent inset;box-shadow:1px 0 0 0 transparent inset}.ui.labeled.icon.button>.icon:after,.ui.labeled.icon.button>.icon:before,.ui.labeled.icon.buttons>.button>.icon:after,.ui.labeled.icon.buttons>.button>.icon:before{display:block;position:relative;width:100%;top:0;text-align:center}.ui.labeled.icon.buttons .button>.icon{border-radius:0}.ui.labeled.icon.buttons .button:first-child>.icon{border-top-left-radius:.28571429rem;border-bottom-left-radius:.28571429rem}.ui.labeled.icon.buttons .button:last-child>.icon{border-top-right-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.vertical.labeled.icon.buttons .button:first-child>.icon{border-radius:0;border-top-left-radius:.28571429rem}.ui.vertical.labeled.icon.buttons .button:last-child>.icon{border-radius:0;border-bottom-left-radius:.28571429rem}.ui.labeled.icon.button>.loading.icon:before{-webkit-animation:loader 2s linear infinite;animation:loader 2s linear infinite}.ui.button.toggle.active,.ui.buttons .button.toggle.active,.ui.toggle.buttons .active.button{background-color:#21ba45;-webkit-box-shadow:none;box-shadow:none;text-shadow:none;color:#fff}.ui.button.toggle.active:hover{background-color:#16ab39;text-shadow:none;color:#fff}.ui.circular.button{border-radius:10em}.ui.circular.button>.icon{width:1em;vertical-align:baseline}.ui.buttons .or{position:relative;width:.3em;height:2.57142857em;z-index:3}.ui.buttons .or:before{position:absolute;text-align:center;border-radius:500rem;content:'or';top:50%;left:50%;background-color:#fff;text-shadow:none;margin-top:-.89285714em;margin-left:-.89285714em;width:1.78571429em;height:1.78571429em;line-height:1.78571429em;color:rgba(0,0,0,.4);font-style:normal;font-weight:700;-webkit-box-shadow:0 0 0 1px transparent inset;box-shadow:0 0 0 1px transparent inset}.ui.buttons .or[data-text]:before{content:attr(data-text)}.ui.fluid.buttons .or{width:0!important}.ui.fluid.buttons .or:after{display:none}.ui.attached.button{position:relative;display:block;margin:0;border-radius:0;-webkit-box-shadow:0 0 0 1px rgba(34,36,38,.15);box-shadow:0 0 0 1px rgba(34,36,38,.15)}.ui.attached.top.button{border-radius:.28571429rem .28571429rem 0 0}.ui.attached.bottom.button{border-radius:0 0 .28571429rem .28571429rem}.ui.left.attached.button{display:inline-block;border-left:none;text-align:right;padding-right:.75em;border-radius:.28571429rem 0 0 .28571429rem}.ui.right.attached.button{display:inline-block;text-align:left;padding-left:.75em;border-radius:0 .28571429rem .28571429rem 0}.ui.attached.buttons{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;border-radius:0;width:auto!important;z-index:auto;margin-left:-1px;margin-right:-1px}.ui.attached.buttons .button{margin:0}.ui.attached.buttons .button:first-child{border-radius:0}.ui.attached.buttons .button:last-child{border-radius:0}.ui[class*="top attached"].buttons{margin-bottom:-1px;border-radius:.28571429rem .28571429rem 0 0}.ui[class*="top attached"].buttons .button:first-child{border-radius:.28571429rem 0 0 0}.ui[class*="top attached"].buttons .button:last-child{border-radius:0 .28571429rem 0 0}.ui[class*="bottom attached"].buttons{margin-top:-1px;border-radius:0 0 .28571429rem .28571429rem}.ui[class*="bottom attached"].buttons .button:first-child{border-radius:0 0 0 .28571429rem}.ui[class*="bottom attached"].buttons .button:last-child{border-radius:0 0 .28571429rem 0}.ui[class*="left attached"].buttons{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:0;margin-left:-1px;border-radius:0 .28571429rem .28571429rem 0}.ui[class*="left attached"].buttons .button:first-child{margin-left:-1px;border-radius:0 .28571429rem 0 0}.ui[class*="left attached"].buttons .button:last-child{margin-left:-1px;border-radius:0 0 .28571429rem 0}.ui[class*="right attached"].buttons{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-left:0;margin-right:-1px;border-radius:.28571429rem 0 0 .28571429rem}.ui[class*="right attached"].buttons .button:first-child{margin-left:-1px;border-radius:.28571429rem 0 0 0}.ui[class*="right attached"].buttons .button:last-child{margin-left:-1px;border-radius:0 0 0 .28571429rem}.ui.fluid.button,.ui.fluid.buttons{width:100%}.ui.fluid.button{display:block}.ui.two.buttons{width:100%}.ui.two.buttons>.button{width:50%}.ui.three.buttons{width:100%}.ui.three.buttons>.button{width:33.333%}.ui.four.buttons{width:100%}.ui.four.buttons>.button{width:25%}.ui.five.buttons{width:100%}.ui.five.buttons>.button{width:20%}.ui.six.buttons{width:100%}.ui.six.buttons>.button{width:16.666%}.ui.seven.buttons{width:100%}.ui.seven.buttons>.button{width:14.285%}.ui.eight.buttons{width:100%}.ui.eight.buttons>.button{width:12.5%}.ui.nine.buttons{width:100%}.ui.nine.buttons>.button{width:11.11%}.ui.ten.buttons{width:100%}.ui.ten.buttons>.button{width:10%}.ui.eleven.buttons{width:100%}.ui.eleven.buttons>.button{width:9.09%}.ui.twelve.buttons{width:100%}.ui.twelve.buttons>.button{width:8.3333%}.ui.fluid.vertical.buttons,.ui.fluid.vertical.buttons>.button{display:-webkit-box;display:-ms-flexbox;display:flex;width:auto;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.two.vertical.buttons>.button{height:50%}.ui.three.vertical.buttons>.button{height:33.333%}.ui.four.vertical.buttons>.button{height:25%}.ui.five.vertical.buttons>.button{height:20%}.ui.six.vertical.buttons>.button{height:16.666%}.ui.seven.vertical.buttons>.button{height:14.285%}.ui.eight.vertical.buttons>.button{height:12.5%}.ui.nine.vertical.buttons>.button{height:11.11%}.ui.ten.vertical.buttons>.button{height:10%}.ui.eleven.vertical.buttons>.button{height:9.09%}.ui.twelve.vertical.buttons>.button{height:8.3333%}.ui.primary.button,.ui.primary.buttons .button{background-color:#2185d0;color:#fff;text-shadow:none;background-image:none}.ui.primary.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.primary.button:hover,.ui.primary.buttons .button:hover{background-color:#1678c2;color:#fff;text-shadow:none}.ui.primary.button:focus,.ui.primary.buttons .button:focus{background-color:#0d71bb;color:#fff;text-shadow:none}.ui.primary.button:active,.ui.primary.buttons .button:active{background-color:#1a69a4;color:#fff;text-shadow:none}.ui.primary.active.button,.ui.primary.button .active.button:active,.ui.primary.buttons .active.button,.ui.primary.buttons .active.button:active{background-color:#1279c6;color:#fff;text-shadow:none}.ui.basic.primary.button,.ui.basic.primary.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #2185d0 inset;box-shadow:0 0 0 1px #2185d0 inset;color:#2185d0}.ui.basic.primary.button:hover,.ui.basic.primary.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #1678c2 inset;box-shadow:0 0 0 1px #1678c2 inset;color:#1678c2}.ui.basic.primary.button:focus,.ui.basic.primary.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #0d71bb inset;box-shadow:0 0 0 1px #0d71bb inset;color:#1678c2}.ui.basic.primary.active.button,.ui.basic.primary.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #1279c6 inset;box-shadow:0 0 0 1px #1279c6 inset;color:#1a69a4}.ui.basic.primary.button:active,.ui.basic.primary.buttons .button:active{-webkit-box-shadow:0 0 0 1px #1a69a4 inset;box-shadow:0 0 0 1px #1a69a4 inset;color:#1a69a4}.ui.buttons:not(.vertical)>.basic.primary.button:not(:first-child){margin-left:-1px}.ui.inverted.primary.button,.ui.inverted.primary.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #54c8ff inset;box-shadow:0 0 0 2px #54c8ff inset;color:#54c8ff}.ui.inverted.primary.button.active,.ui.inverted.primary.button:active,.ui.inverted.primary.button:focus,.ui.inverted.primary.button:hover,.ui.inverted.primary.buttons .button.active,.ui.inverted.primary.buttons .button:active,.ui.inverted.primary.buttons .button:focus,.ui.inverted.primary.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.primary.button:hover,.ui.inverted.primary.buttons .button:hover{background-color:#21b8ff}.ui.inverted.primary.button:focus,.ui.inverted.primary.buttons .button:focus{background-color:#2bbbff}.ui.inverted.primary.active.button,.ui.inverted.primary.buttons .active.button{background-color:#3ac0ff}.ui.inverted.primary.button:active,.ui.inverted.primary.buttons .button:active{background-color:#21b8ff}.ui.inverted.primary.basic.button,.ui.inverted.primary.basic.buttons .button,.ui.inverted.primary.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.primary.basic.button:hover,.ui.inverted.primary.basic.buttons .button:hover,.ui.inverted.primary.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #21b8ff inset;box-shadow:0 0 0 2px #21b8ff inset;color:#54c8ff}.ui.inverted.primary.basic.button:focus,.ui.inverted.primary.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #2bbbff inset;box-shadow:0 0 0 2px #2bbbff inset;color:#54c8ff}.ui.inverted.primary.basic.active.button,.ui.inverted.primary.basic.buttons .active.button,.ui.inverted.primary.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #3ac0ff inset;box-shadow:0 0 0 2px #3ac0ff inset;color:#54c8ff}.ui.inverted.primary.basic.button:active,.ui.inverted.primary.basic.buttons .button:active,.ui.inverted.primary.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #21b8ff inset;box-shadow:0 0 0 2px #21b8ff inset;color:#54c8ff}.ui.tertiary.primary.button,.ui.tertiary.primary.buttons .button,.ui.tertiary.primary.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#2185d0}.ui.tertiary.primary.button:hover,.ui.tertiary.primary.buttons .button:hover,.ui.tertiary.primary.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #2b75ac;box-shadow:inset 0 -.2em 0 #2b75ac;color:#2b75ac}.ui.tertiary.primary.button:focus,.ui.tertiary.primary.buttons .button:focus,.ui.tertiary.primary.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #216ea7;box-shadow:inset 0 -.2em 0 #216ea7;color:#216ea7}.ui.tertiary.primary.active.button,.ui.tertiary.primary.button:active,.ui.tertiary.primary.buttons .active.button,.ui.tertiary.primary.buttons .button:active,.ui.tertiary.primary.buttons .tertiary.active.button,.ui.tertiary.primary.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #007bd8;box-shadow:inset 0 -.2em 0 #007bd8;color:#1279c6}.ui.secondary.button,.ui.secondary.buttons .button{background-color:#1b1c1d;color:#fff;text-shadow:none;background-image:none}.ui.secondary.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.secondary.button:hover,.ui.secondary.buttons .button:hover{background-color:#27292a;color:#fff;text-shadow:none}.ui.secondary.button:focus,.ui.secondary.buttons .button:focus{background-color:#2e3032;color:#fff;text-shadow:none}.ui.secondary.button:active,.ui.secondary.buttons .button:active{background-color:#343637;color:#fff;text-shadow:none}.ui.secondary.active.button,.ui.secondary.button .active.button:active,.ui.secondary.buttons .active.button,.ui.secondary.buttons .active.button:active{background-color:#27292a;color:#fff;text-shadow:none}.ui.basic.secondary.button,.ui.basic.secondary.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #1b1c1d inset;box-shadow:0 0 0 1px #1b1c1d inset;color:#1b1c1d}.ui.basic.secondary.button:hover,.ui.basic.secondary.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #27292a inset;box-shadow:0 0 0 1px #27292a inset;color:#27292a}.ui.basic.secondary.button:focus,.ui.basic.secondary.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #2e3032 inset;box-shadow:0 0 0 1px #2e3032 inset;color:#27292a}.ui.basic.secondary.active.button,.ui.basic.secondary.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #27292a inset;box-shadow:0 0 0 1px #27292a inset;color:#343637}.ui.basic.secondary.button:active,.ui.basic.secondary.buttons .button:active{-webkit-box-shadow:0 0 0 1px #343637 inset;box-shadow:0 0 0 1px #343637 inset;color:#343637}.ui.buttons:not(.vertical)>.basic.secondary.button:not(:first-child){margin-left:-1px}.ui.inverted.secondary.button,.ui.inverted.secondary.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #545454 inset;box-shadow:0 0 0 2px #545454 inset;color:#545454}.ui.inverted.secondary.button.active,.ui.inverted.secondary.button:active,.ui.inverted.secondary.button:focus,.ui.inverted.secondary.button:hover,.ui.inverted.secondary.buttons .button.active,.ui.inverted.secondary.buttons .button:active,.ui.inverted.secondary.buttons .button:focus,.ui.inverted.secondary.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.secondary.button:hover,.ui.inverted.secondary.buttons .button:hover{background-color:#6e6e6e}.ui.inverted.secondary.button:focus,.ui.inverted.secondary.buttons .button:focus{background-color:#686868}.ui.inverted.secondary.active.button,.ui.inverted.secondary.buttons .active.button{background-color:#616161}.ui.inverted.secondary.button:active,.ui.inverted.secondary.buttons .button:active{background-color:#6e6e6e}.ui.inverted.secondary.basic.button,.ui.inverted.secondary.basic.buttons .button,.ui.inverted.secondary.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.secondary.basic.button:hover,.ui.inverted.secondary.basic.buttons .button:hover,.ui.inverted.secondary.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #6e6e6e inset;box-shadow:0 0 0 2px #6e6e6e inset;color:#545454}.ui.inverted.secondary.basic.button:focus,.ui.inverted.secondary.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #686868 inset;box-shadow:0 0 0 2px #686868 inset;color:#545454}.ui.inverted.secondary.basic.active.button,.ui.inverted.secondary.basic.buttons .active.button,.ui.inverted.secondary.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #616161 inset;box-shadow:0 0 0 2px #616161 inset;color:#545454}.ui.inverted.secondary.basic.button:active,.ui.inverted.secondary.basic.buttons .button:active,.ui.inverted.secondary.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #6e6e6e inset;box-shadow:0 0 0 2px #6e6e6e inset;color:#545454}.ui.tertiary.secondary.button,.ui.tertiary.secondary.buttons .button,.ui.tertiary.secondary.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#1b1c1d}.ui.tertiary.secondary.button:hover,.ui.tertiary.secondary.buttons .button:hover,.ui.tertiary.secondary.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #292929;box-shadow:inset 0 -.2em 0 #292929;color:#292929}.ui.tertiary.secondary.button:focus,.ui.tertiary.secondary.buttons .button:focus,.ui.tertiary.secondary.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #303030;box-shadow:inset 0 -.2em 0 #303030;color:#303030}.ui.tertiary.secondary.active.button,.ui.tertiary.secondary.button:active,.ui.tertiary.secondary.buttons .active.button,.ui.tertiary.secondary.buttons .button:active,.ui.tertiary.secondary.buttons .tertiary.active.button,.ui.tertiary.secondary.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #1f2933;box-shadow:inset 0 -.2em 0 #1f2933;color:#27292a}.ui.red.button,.ui.red.buttons .button{background-color:#db2828;color:#fff;text-shadow:none;background-image:none}.ui.red.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.red.button:hover,.ui.red.buttons .button:hover{background-color:#d01919;color:#fff;text-shadow:none}.ui.red.button:focus,.ui.red.buttons .button:focus{background-color:#ca1010;color:#fff;text-shadow:none}.ui.red.button:active,.ui.red.buttons .button:active{background-color:#b21e1e;color:#fff;text-shadow:none}.ui.red.active.button,.ui.red.button .active.button:active,.ui.red.buttons .active.button,.ui.red.buttons .active.button:active{background-color:#d41515;color:#fff;text-shadow:none}.ui.basic.red.button,.ui.basic.red.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #db2828 inset;box-shadow:0 0 0 1px #db2828 inset;color:#db2828}.ui.basic.red.button:hover,.ui.basic.red.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #d01919 inset;box-shadow:0 0 0 1px #d01919 inset;color:#d01919}.ui.basic.red.button:focus,.ui.basic.red.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #ca1010 inset;box-shadow:0 0 0 1px #ca1010 inset;color:#d01919}.ui.basic.red.active.button,.ui.basic.red.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #d41515 inset;box-shadow:0 0 0 1px #d41515 inset;color:#b21e1e}.ui.basic.red.button:active,.ui.basic.red.buttons .button:active{-webkit-box-shadow:0 0 0 1px #b21e1e inset;box-shadow:0 0 0 1px #b21e1e inset;color:#b21e1e}.ui.buttons:not(.vertical)>.basic.red.button:not(:first-child){margin-left:-1px}.ui.inverted.red.button,.ui.inverted.red.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #ff695e inset;box-shadow:0 0 0 2px #ff695e inset;color:#ff695e}.ui.inverted.red.button.active,.ui.inverted.red.button:active,.ui.inverted.red.button:focus,.ui.inverted.red.button:hover,.ui.inverted.red.buttons .button.active,.ui.inverted.red.buttons .button:active,.ui.inverted.red.buttons .button:focus,.ui.inverted.red.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.red.button:hover,.ui.inverted.red.buttons .button:hover{background-color:#ff392b}.ui.inverted.red.button:focus,.ui.inverted.red.buttons .button:focus{background-color:#ff4335}.ui.inverted.red.active.button,.ui.inverted.red.buttons .active.button{background-color:#ff5144}.ui.inverted.red.button:active,.ui.inverted.red.buttons .button:active{background-color:#ff392b}.ui.inverted.red.basic.button,.ui.inverted.red.basic.buttons .button,.ui.inverted.red.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.red.basic.button:hover,.ui.inverted.red.basic.buttons .button:hover,.ui.inverted.red.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #ff392b inset;box-shadow:0 0 0 2px #ff392b inset;color:#ff695e}.ui.inverted.red.basic.button:focus,.ui.inverted.red.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #ff4335 inset;box-shadow:0 0 0 2px #ff4335 inset;color:#ff695e}.ui.inverted.red.basic.active.button,.ui.inverted.red.basic.buttons .active.button,.ui.inverted.red.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #ff5144 inset;box-shadow:0 0 0 2px #ff5144 inset;color:#ff695e}.ui.inverted.red.basic.button:active,.ui.inverted.red.basic.buttons .button:active,.ui.inverted.red.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #ff392b inset;box-shadow:0 0 0 2px #ff392b inset;color:#ff695e}.ui.tertiary.red.button,.ui.tertiary.red.buttons .button,.ui.tertiary.red.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#db2828}.ui.tertiary.red.button:hover,.ui.tertiary.red.buttons .button:hover,.ui.tertiary.red.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #b93131;box-shadow:inset 0 -.2em 0 #b93131;color:#b93131}.ui.tertiary.red.button:focus,.ui.tertiary.red.buttons .button:focus,.ui.tertiary.red.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #b52626;box-shadow:inset 0 -.2em 0 #b52626;color:#b52626}.ui.tertiary.red.active.button,.ui.tertiary.red.button:active,.ui.tertiary.red.buttons .active.button,.ui.tertiary.red.buttons .button:active,.ui.tertiary.red.buttons .tertiary.active.button,.ui.tertiary.red.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #ea0000;box-shadow:inset 0 -.2em 0 #ea0000;color:#d41515}.ui.orange.button,.ui.orange.buttons .button{background-color:#f2711c;color:#fff;text-shadow:none;background-image:none}.ui.orange.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.orange.button:hover,.ui.orange.buttons .button:hover{background-color:#f26202;color:#fff;text-shadow:none}.ui.orange.button:focus,.ui.orange.buttons .button:focus{background-color:#e55b00;color:#fff;text-shadow:none}.ui.orange.button:active,.ui.orange.buttons .button:active{background-color:#cf590c;color:#fff;text-shadow:none}.ui.orange.active.button,.ui.orange.button .active.button:active,.ui.orange.buttons .active.button,.ui.orange.buttons .active.button:active{background-color:#f56100;color:#fff;text-shadow:none}.ui.basic.orange.button,.ui.basic.orange.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #f2711c inset;box-shadow:0 0 0 1px #f2711c inset;color:#f2711c}.ui.basic.orange.button:hover,.ui.basic.orange.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #f26202 inset;box-shadow:0 0 0 1px #f26202 inset;color:#f26202}.ui.basic.orange.button:focus,.ui.basic.orange.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #e55b00 inset;box-shadow:0 0 0 1px #e55b00 inset;color:#f26202}.ui.basic.orange.active.button,.ui.basic.orange.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #f56100 inset;box-shadow:0 0 0 1px #f56100 inset;color:#cf590c}.ui.basic.orange.button:active,.ui.basic.orange.buttons .button:active{-webkit-box-shadow:0 0 0 1px #cf590c inset;box-shadow:0 0 0 1px #cf590c inset;color:#cf590c}.ui.buttons:not(.vertical)>.basic.orange.button:not(:first-child){margin-left:-1px}.ui.inverted.orange.button,.ui.inverted.orange.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #ff851b inset;box-shadow:0 0 0 2px #ff851b inset;color:#ff851b}.ui.inverted.orange.button.active,.ui.inverted.orange.button:active,.ui.inverted.orange.button:focus,.ui.inverted.orange.button:hover,.ui.inverted.orange.buttons .button.active,.ui.inverted.orange.buttons .button:active,.ui.inverted.orange.buttons .button:focus,.ui.inverted.orange.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.orange.button:hover,.ui.inverted.orange.buttons .button:hover{background-color:#e76b00}.ui.inverted.orange.button:focus,.ui.inverted.orange.buttons .button:focus{background-color:#f17000}.ui.inverted.orange.active.button,.ui.inverted.orange.buttons .active.button{background-color:#ff7701}.ui.inverted.orange.button:active,.ui.inverted.orange.buttons .button:active{background-color:#e76b00}.ui.inverted.orange.basic.button,.ui.inverted.orange.basic.buttons .button,.ui.inverted.orange.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.orange.basic.button:hover,.ui.inverted.orange.basic.buttons .button:hover,.ui.inverted.orange.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #e76b00 inset;box-shadow:0 0 0 2px #e76b00 inset;color:#ff851b}.ui.inverted.orange.basic.button:focus,.ui.inverted.orange.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #f17000 inset;box-shadow:0 0 0 2px #f17000 inset;color:#ff851b}.ui.inverted.orange.basic.active.button,.ui.inverted.orange.basic.buttons .active.button,.ui.inverted.orange.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #ff7701 inset;box-shadow:0 0 0 2px #ff7701 inset;color:#ff851b}.ui.inverted.orange.basic.button:active,.ui.inverted.orange.basic.buttons .button:active,.ui.inverted.orange.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #e76b00 inset;box-shadow:0 0 0 2px #e76b00 inset;color:#ff851b}.ui.tertiary.orange.button,.ui.tertiary.orange.buttons .button,.ui.tertiary.orange.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#f2711c}.ui.tertiary.orange.button:hover,.ui.tertiary.orange.buttons .button:hover,.ui.tertiary.orange.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #da671b;box-shadow:inset 0 -.2em 0 #da671b;color:#da671b}.ui.tertiary.orange.button:focus,.ui.tertiary.orange.buttons .button:focus,.ui.tertiary.orange.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #ce6017;box-shadow:inset 0 -.2em 0 #ce6017;color:#ce6017}.ui.tertiary.orange.active.button,.ui.tertiary.orange.button:active,.ui.tertiary.orange.buttons .active.button,.ui.tertiary.orange.buttons .button:active,.ui.tertiary.orange.buttons .tertiary.active.button,.ui.tertiary.orange.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #f56100;box-shadow:inset 0 -.2em 0 #f56100;color:#f56100}.ui.yellow.button,.ui.yellow.buttons .button{background-color:#fbbd08;color:#fff;text-shadow:none;background-image:none}.ui.yellow.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.yellow.button:hover,.ui.yellow.buttons .button:hover{background-color:#eaae00;color:#fff;text-shadow:none}.ui.yellow.button:focus,.ui.yellow.buttons .button:focus{background-color:#daa300;color:#fff;text-shadow:none}.ui.yellow.button:active,.ui.yellow.buttons .button:active{background-color:#cd9903;color:#fff;text-shadow:none}.ui.yellow.active.button,.ui.yellow.button .active.button:active,.ui.yellow.buttons .active.button,.ui.yellow.buttons .active.button:active{background-color:#eaae00;color:#fff;text-shadow:none}.ui.basic.yellow.button,.ui.basic.yellow.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #fbbd08 inset;box-shadow:0 0 0 1px #fbbd08 inset;color:#fbbd08}.ui.basic.yellow.button:hover,.ui.basic.yellow.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #eaae00 inset;box-shadow:0 0 0 1px #eaae00 inset;color:#eaae00}.ui.basic.yellow.button:focus,.ui.basic.yellow.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #daa300 inset;box-shadow:0 0 0 1px #daa300 inset;color:#eaae00}.ui.basic.yellow.active.button,.ui.basic.yellow.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #eaae00 inset;box-shadow:0 0 0 1px #eaae00 inset;color:#cd9903}.ui.basic.yellow.button:active,.ui.basic.yellow.buttons .button:active{-webkit-box-shadow:0 0 0 1px #cd9903 inset;box-shadow:0 0 0 1px #cd9903 inset;color:#cd9903}.ui.buttons:not(.vertical)>.basic.yellow.button:not(:first-child){margin-left:-1px}.ui.inverted.yellow.button,.ui.inverted.yellow.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #ffe21f inset;box-shadow:0 0 0 2px #ffe21f inset;color:#ffe21f}.ui.inverted.yellow.button.active,.ui.inverted.yellow.button:active,.ui.inverted.yellow.button:focus,.ui.inverted.yellow.button:hover,.ui.inverted.yellow.buttons .button.active,.ui.inverted.yellow.buttons .button:active,.ui.inverted.yellow.buttons .button:focus,.ui.inverted.yellow.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6)}.ui.inverted.yellow.button:hover,.ui.inverted.yellow.buttons .button:hover{background-color:#ebcd00}.ui.inverted.yellow.button:focus,.ui.inverted.yellow.buttons .button:focus{background-color:#f5d500}.ui.inverted.yellow.active.button,.ui.inverted.yellow.buttons .active.button{background-color:#ffdf05}.ui.inverted.yellow.button:active,.ui.inverted.yellow.buttons .button:active{background-color:#ebcd00}.ui.inverted.yellow.basic.button,.ui.inverted.yellow.basic.buttons .button,.ui.inverted.yellow.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.yellow.basic.button:hover,.ui.inverted.yellow.basic.buttons .button:hover,.ui.inverted.yellow.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #ebcd00 inset;box-shadow:0 0 0 2px #ebcd00 inset;color:#ffe21f}.ui.inverted.yellow.basic.button:focus,.ui.inverted.yellow.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #f5d500 inset;box-shadow:0 0 0 2px #f5d500 inset;color:#ffe21f}.ui.inverted.yellow.basic.active.button,.ui.inverted.yellow.basic.buttons .active.button,.ui.inverted.yellow.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #ffdf05 inset;box-shadow:0 0 0 2px #ffdf05 inset;color:#ffe21f}.ui.inverted.yellow.basic.button:active,.ui.inverted.yellow.basic.buttons .button:active,.ui.inverted.yellow.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #ebcd00 inset;box-shadow:0 0 0 2px #ebcd00 inset;color:#ffe21f}.ui.tertiary.yellow.button,.ui.tertiary.yellow.buttons .button,.ui.tertiary.yellow.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#fbbd08}.ui.tertiary.yellow.button:hover,.ui.tertiary.yellow.buttons .button:hover,.ui.tertiary.yellow.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #d2a217;box-shadow:inset 0 -.2em 0 #d2a217;color:#d2a217}.ui.tertiary.yellow.button:focus,.ui.tertiary.yellow.buttons .button:focus,.ui.tertiary.yellow.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #c49816;box-shadow:inset 0 -.2em 0 #c49816;color:#c49816}.ui.tertiary.yellow.active.button,.ui.tertiary.yellow.button:active,.ui.tertiary.yellow.buttons .active.button,.ui.tertiary.yellow.buttons .button:active,.ui.tertiary.yellow.buttons .tertiary.active.button,.ui.tertiary.yellow.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #eaae00;box-shadow:inset 0 -.2em 0 #eaae00;color:#eaae00}.ui.olive.button,.ui.olive.buttons .button{background-color:#b5cc18;color:#fff;text-shadow:none;background-image:none}.ui.olive.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.olive.button:hover,.ui.olive.buttons .button:hover{background-color:#a7bd0d;color:#fff;text-shadow:none}.ui.olive.button:focus,.ui.olive.buttons .button:focus{background-color:#a0b605;color:#fff;text-shadow:none}.ui.olive.button:active,.ui.olive.buttons .button:active{background-color:#8d9e13;color:#fff;text-shadow:none}.ui.olive.active.button,.ui.olive.button .active.button:active,.ui.olive.buttons .active.button,.ui.olive.buttons .active.button:active{background-color:#aac109;color:#fff;text-shadow:none}.ui.basic.olive.button,.ui.basic.olive.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #b5cc18 inset;box-shadow:0 0 0 1px #b5cc18 inset;color:#b5cc18}.ui.basic.olive.button:hover,.ui.basic.olive.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #a7bd0d inset;box-shadow:0 0 0 1px #a7bd0d inset;color:#a7bd0d}.ui.basic.olive.button:focus,.ui.basic.olive.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #a0b605 inset;box-shadow:0 0 0 1px #a0b605 inset;color:#a7bd0d}.ui.basic.olive.active.button,.ui.basic.olive.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #aac109 inset;box-shadow:0 0 0 1px #aac109 inset;color:#8d9e13}.ui.basic.olive.button:active,.ui.basic.olive.buttons .button:active{-webkit-box-shadow:0 0 0 1px #8d9e13 inset;box-shadow:0 0 0 1px #8d9e13 inset;color:#8d9e13}.ui.buttons:not(.vertical)>.basic.olive.button:not(:first-child){margin-left:-1px}.ui.inverted.olive.button,.ui.inverted.olive.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #d9e778 inset;box-shadow:0 0 0 2px #d9e778 inset;color:#d9e778}.ui.inverted.olive.button.active,.ui.inverted.olive.button:active,.ui.inverted.olive.button:focus,.ui.inverted.olive.button:hover,.ui.inverted.olive.buttons .button.active,.ui.inverted.olive.buttons .button:active,.ui.inverted.olive.buttons .button:focus,.ui.inverted.olive.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6)}.ui.inverted.olive.button:hover,.ui.inverted.olive.buttons .button:hover{background-color:#d2e745}.ui.inverted.olive.button:focus,.ui.inverted.olive.buttons .button:focus{background-color:#daef47}.ui.inverted.olive.active.button,.ui.inverted.olive.buttons .active.button{background-color:#daed59}.ui.inverted.olive.button:active,.ui.inverted.olive.buttons .button:active{background-color:#cddf4d}.ui.inverted.olive.basic.button,.ui.inverted.olive.basic.buttons .button,.ui.inverted.olive.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.olive.basic.button:hover,.ui.inverted.olive.basic.buttons .button:hover,.ui.inverted.olive.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #d2e745 inset;box-shadow:0 0 0 2px #d2e745 inset;color:#d9e778}.ui.inverted.olive.basic.button:focus,.ui.inverted.olive.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #daef47 inset;box-shadow:0 0 0 2px #daef47 inset;color:#d9e778}.ui.inverted.olive.basic.active.button,.ui.inverted.olive.basic.buttons .active.button,.ui.inverted.olive.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #daed59 inset;box-shadow:0 0 0 2px #daed59 inset;color:#d9e778}.ui.inverted.olive.basic.button:active,.ui.inverted.olive.basic.buttons .button:active,.ui.inverted.olive.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #cddf4d inset;box-shadow:0 0 0 2px #cddf4d inset;color:#d9e778}.ui.tertiary.olive.button,.ui.tertiary.olive.buttons .button,.ui.tertiary.olive.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#b5cc18}.ui.tertiary.olive.button:hover,.ui.tertiary.olive.buttons .button:hover,.ui.tertiary.olive.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #98a922;box-shadow:inset 0 -.2em 0 #98a922;color:#98a922}.ui.tertiary.olive.button:focus,.ui.tertiary.olive.buttons .button:focus,.ui.tertiary.olive.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #92a418;box-shadow:inset 0 -.2em 0 #92a418;color:#92a418}.ui.tertiary.olive.active.button,.ui.tertiary.olive.button:active,.ui.tertiary.olive.buttons .active.button,.ui.tertiary.olive.buttons .button:active,.ui.tertiary.olive.buttons .tertiary.active.button,.ui.tertiary.olive.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #b1cb00;box-shadow:inset 0 -.2em 0 #b1cb00;color:#aac109}.ui.green.button,.ui.green.buttons .button{background-color:#21ba45;color:#fff;text-shadow:none;background-image:none}.ui.green.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.green.button:hover,.ui.green.buttons .button:hover{background-color:#16ab39;color:#fff;text-shadow:none}.ui.green.button:focus,.ui.green.buttons .button:focus{background-color:#0ea432;color:#fff;text-shadow:none}.ui.green.button:active,.ui.green.buttons .button:active{background-color:#198f35;color:#fff;text-shadow:none}.ui.green.active.button,.ui.green.button .active.button:active,.ui.green.buttons .active.button,.ui.green.buttons .active.button:active{background-color:#13ae38;color:#fff;text-shadow:none}.ui.basic.green.button,.ui.basic.green.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #21ba45 inset;box-shadow:0 0 0 1px #21ba45 inset;color:#21ba45}.ui.basic.green.button:hover,.ui.basic.green.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #16ab39 inset;box-shadow:0 0 0 1px #16ab39 inset;color:#16ab39}.ui.basic.green.button:focus,.ui.basic.green.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #0ea432 inset;box-shadow:0 0 0 1px #0ea432 inset;color:#16ab39}.ui.basic.green.active.button,.ui.basic.green.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #13ae38 inset;box-shadow:0 0 0 1px #13ae38 inset;color:#198f35}.ui.basic.green.button:active,.ui.basic.green.buttons .button:active{-webkit-box-shadow:0 0 0 1px #198f35 inset;box-shadow:0 0 0 1px #198f35 inset;color:#198f35}.ui.buttons:not(.vertical)>.basic.green.button:not(:first-child){margin-left:-1px}.ui.inverted.green.button,.ui.inverted.green.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #2ecc40 inset;box-shadow:0 0 0 2px #2ecc40 inset;color:#2ecc40}.ui.inverted.green.button.active,.ui.inverted.green.button:active,.ui.inverted.green.button:focus,.ui.inverted.green.button:hover,.ui.inverted.green.buttons .button.active,.ui.inverted.green.buttons .button:active,.ui.inverted.green.buttons .button:focus,.ui.inverted.green.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.green.button:hover,.ui.inverted.green.buttons .button:hover{background-color:#1ea92e}.ui.inverted.green.button:focus,.ui.inverted.green.buttons .button:focus{background-color:#19b82b}.ui.inverted.green.active.button,.ui.inverted.green.buttons .active.button{background-color:#1fc231}.ui.inverted.green.button:active,.ui.inverted.green.buttons .button:active{background-color:#25a233}.ui.inverted.green.basic.button,.ui.inverted.green.basic.buttons .button,.ui.inverted.green.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.green.basic.button:hover,.ui.inverted.green.basic.buttons .button:hover,.ui.inverted.green.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #1ea92e inset;box-shadow:0 0 0 2px #1ea92e inset;color:#2ecc40}.ui.inverted.green.basic.button:focus,.ui.inverted.green.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #19b82b inset;box-shadow:0 0 0 2px #19b82b inset;color:#2ecc40}.ui.inverted.green.basic.active.button,.ui.inverted.green.basic.buttons .active.button,.ui.inverted.green.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #1fc231 inset;box-shadow:0 0 0 2px #1fc231 inset;color:#2ecc40}.ui.inverted.green.basic.button:active,.ui.inverted.green.basic.buttons .button:active,.ui.inverted.green.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #25a233 inset;box-shadow:0 0 0 2px #25a233 inset;color:#2ecc40}.ui.tertiary.green.button,.ui.tertiary.green.buttons .button,.ui.tertiary.green.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#21ba45}.ui.tertiary.green.button:hover,.ui.tertiary.green.buttons .button:hover,.ui.tertiary.green.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #2a9844;box-shadow:inset 0 -.2em 0 #2a9844;color:#2a9844}.ui.tertiary.green.button:focus,.ui.tertiary.green.buttons .button:focus,.ui.tertiary.green.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #20923b;box-shadow:inset 0 -.2em 0 #20923b;color:#20923b}.ui.tertiary.green.active.button,.ui.tertiary.green.button:active,.ui.tertiary.green.buttons .active.button,.ui.tertiary.green.buttons .button:active,.ui.tertiary.green.buttons .tertiary.active.button,.ui.tertiary.green.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #00c22e;box-shadow:inset 0 -.2em 0 #00c22e;color:#13ae38}.ui.teal.button,.ui.teal.buttons .button{background-color:#00b5ad;color:#fff;text-shadow:none;background-image:none}.ui.teal.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.teal.button:hover,.ui.teal.buttons .button:hover{background-color:#009c95;color:#fff;text-shadow:none}.ui.teal.button:focus,.ui.teal.buttons .button:focus{background-color:#008c86;color:#fff;text-shadow:none}.ui.teal.button:active,.ui.teal.buttons .button:active{background-color:#00827c;color:#fff;text-shadow:none}.ui.teal.active.button,.ui.teal.button .active.button:active,.ui.teal.buttons .active.button,.ui.teal.buttons .active.button:active{background-color:#009c95;color:#fff;text-shadow:none}.ui.basic.teal.button,.ui.basic.teal.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #00b5ad inset;box-shadow:0 0 0 1px #00b5ad inset;color:#00b5ad}.ui.basic.teal.button:hover,.ui.basic.teal.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #009c95 inset;box-shadow:0 0 0 1px #009c95 inset;color:#009c95}.ui.basic.teal.button:focus,.ui.basic.teal.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #008c86 inset;box-shadow:0 0 0 1px #008c86 inset;color:#009c95}.ui.basic.teal.active.button,.ui.basic.teal.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #009c95 inset;box-shadow:0 0 0 1px #009c95 inset;color:#00827c}.ui.basic.teal.button:active,.ui.basic.teal.buttons .button:active{-webkit-box-shadow:0 0 0 1px #00827c inset;box-shadow:0 0 0 1px #00827c inset;color:#00827c}.ui.buttons:not(.vertical)>.basic.teal.button:not(:first-child){margin-left:-1px}.ui.inverted.teal.button,.ui.inverted.teal.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #6dffff inset;box-shadow:0 0 0 2px #6dffff inset;color:#6dffff}.ui.inverted.teal.button.active,.ui.inverted.teal.button:active,.ui.inverted.teal.button:focus,.ui.inverted.teal.button:hover,.ui.inverted.teal.buttons .button.active,.ui.inverted.teal.buttons .button:active,.ui.inverted.teal.buttons .button:focus,.ui.inverted.teal.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6)}.ui.inverted.teal.button:hover,.ui.inverted.teal.buttons .button:hover{background-color:#3affff}.ui.inverted.teal.button:focus,.ui.inverted.teal.buttons .button:focus{background-color:#4ff}.ui.inverted.teal.active.button,.ui.inverted.teal.buttons .active.button{background-color:#54ffff}.ui.inverted.teal.button:active,.ui.inverted.teal.buttons .button:active{background-color:#3affff}.ui.inverted.teal.basic.button,.ui.inverted.teal.basic.buttons .button,.ui.inverted.teal.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.teal.basic.button:hover,.ui.inverted.teal.basic.buttons .button:hover,.ui.inverted.teal.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #3affff inset;box-shadow:0 0 0 2px #3affff inset;color:#6dffff}.ui.inverted.teal.basic.button:focus,.ui.inverted.teal.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #4ff inset;box-shadow:0 0 0 2px #4ff inset;color:#6dffff}.ui.inverted.teal.basic.active.button,.ui.inverted.teal.basic.buttons .active.button,.ui.inverted.teal.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #54ffff inset;box-shadow:0 0 0 2px #54ffff inset;color:#6dffff}.ui.inverted.teal.basic.button:active,.ui.inverted.teal.basic.buttons .button:active,.ui.inverted.teal.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #3affff inset;box-shadow:0 0 0 2px #3affff inset;color:#6dffff}.ui.tertiary.teal.button,.ui.tertiary.teal.buttons .button,.ui.tertiary.teal.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#00b5ad}.ui.tertiary.teal.button:hover,.ui.tertiary.teal.buttons .button:hover,.ui.tertiary.teal.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #108c86;box-shadow:inset 0 -.2em 0 #108c86;color:#108c86}.ui.tertiary.teal.button:focus,.ui.tertiary.teal.buttons .button:focus,.ui.tertiary.teal.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #0e7e79;box-shadow:inset 0 -.2em 0 #0e7e79;color:#0e7e79}.ui.tertiary.teal.active.button,.ui.tertiary.teal.button:active,.ui.tertiary.teal.buttons .active.button,.ui.tertiary.teal.buttons .button:active,.ui.tertiary.teal.buttons .tertiary.active.button,.ui.tertiary.teal.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #009c95;box-shadow:inset 0 -.2em 0 #009c95;color:#009c95}.ui.blue.button,.ui.blue.buttons .button{background-color:#2185d0;color:#fff;text-shadow:none;background-image:none}.ui.blue.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.blue.button:hover,.ui.blue.buttons .button:hover{background-color:#1678c2;color:#fff;text-shadow:none}.ui.blue.button:focus,.ui.blue.buttons .button:focus{background-color:#0d71bb;color:#fff;text-shadow:none}.ui.blue.button:active,.ui.blue.buttons .button:active{background-color:#1a69a4;color:#fff;text-shadow:none}.ui.blue.active.button,.ui.blue.button .active.button:active,.ui.blue.buttons .active.button,.ui.blue.buttons .active.button:active{background-color:#1279c6;color:#fff;text-shadow:none}.ui.basic.blue.button,.ui.basic.blue.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #2185d0 inset;box-shadow:0 0 0 1px #2185d0 inset;color:#2185d0}.ui.basic.blue.button:hover,.ui.basic.blue.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #1678c2 inset;box-shadow:0 0 0 1px #1678c2 inset;color:#1678c2}.ui.basic.blue.button:focus,.ui.basic.blue.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #0d71bb inset;box-shadow:0 0 0 1px #0d71bb inset;color:#1678c2}.ui.basic.blue.active.button,.ui.basic.blue.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #1279c6 inset;box-shadow:0 0 0 1px #1279c6 inset;color:#1a69a4}.ui.basic.blue.button:active,.ui.basic.blue.buttons .button:active{-webkit-box-shadow:0 0 0 1px #1a69a4 inset;box-shadow:0 0 0 1px #1a69a4 inset;color:#1a69a4}.ui.buttons:not(.vertical)>.basic.blue.button:not(:first-child){margin-left:-1px}.ui.inverted.blue.button,.ui.inverted.blue.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #54c8ff inset;box-shadow:0 0 0 2px #54c8ff inset;color:#54c8ff}.ui.inverted.blue.button.active,.ui.inverted.blue.button:active,.ui.inverted.blue.button:focus,.ui.inverted.blue.button:hover,.ui.inverted.blue.buttons .button.active,.ui.inverted.blue.buttons .button:active,.ui.inverted.blue.buttons .button:focus,.ui.inverted.blue.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.blue.button:hover,.ui.inverted.blue.buttons .button:hover{background-color:#21b8ff}.ui.inverted.blue.button:focus,.ui.inverted.blue.buttons .button:focus{background-color:#2bbbff}.ui.inverted.blue.active.button,.ui.inverted.blue.buttons .active.button{background-color:#3ac0ff}.ui.inverted.blue.button:active,.ui.inverted.blue.buttons .button:active{background-color:#21b8ff}.ui.inverted.blue.basic.button,.ui.inverted.blue.basic.buttons .button,.ui.inverted.blue.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.blue.basic.button:hover,.ui.inverted.blue.basic.buttons .button:hover,.ui.inverted.blue.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #21b8ff inset;box-shadow:0 0 0 2px #21b8ff inset;color:#54c8ff}.ui.inverted.blue.basic.button:focus,.ui.inverted.blue.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #2bbbff inset;box-shadow:0 0 0 2px #2bbbff inset;color:#54c8ff}.ui.inverted.blue.basic.active.button,.ui.inverted.blue.basic.buttons .active.button,.ui.inverted.blue.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #3ac0ff inset;box-shadow:0 0 0 2px #3ac0ff inset;color:#54c8ff}.ui.inverted.blue.basic.button:active,.ui.inverted.blue.basic.buttons .button:active,.ui.inverted.blue.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #21b8ff inset;box-shadow:0 0 0 2px #21b8ff inset;color:#54c8ff}.ui.tertiary.blue.button,.ui.tertiary.blue.buttons .button,.ui.tertiary.blue.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#2185d0}.ui.tertiary.blue.button:hover,.ui.tertiary.blue.buttons .button:hover,.ui.tertiary.blue.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #2b75ac;box-shadow:inset 0 -.2em 0 #2b75ac;color:#2b75ac}.ui.tertiary.blue.button:focus,.ui.tertiary.blue.buttons .button:focus,.ui.tertiary.blue.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #216ea7;box-shadow:inset 0 -.2em 0 #216ea7;color:#216ea7}.ui.tertiary.blue.active.button,.ui.tertiary.blue.button:active,.ui.tertiary.blue.buttons .active.button,.ui.tertiary.blue.buttons .button:active,.ui.tertiary.blue.buttons .tertiary.active.button,.ui.tertiary.blue.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #007bd8;box-shadow:inset 0 -.2em 0 #007bd8;color:#1279c6}.ui.violet.button,.ui.violet.buttons .button{background-color:#6435c9;color:#fff;text-shadow:none;background-image:none}.ui.violet.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.violet.button:hover,.ui.violet.buttons .button:hover{background-color:#5829bb;color:#fff;text-shadow:none}.ui.violet.button:focus,.ui.violet.buttons .button:focus{background-color:#4f20b5;color:#fff;text-shadow:none}.ui.violet.button:active,.ui.violet.buttons .button:active{background-color:#502aa1;color:#fff;text-shadow:none}.ui.violet.active.button,.ui.violet.button .active.button:active,.ui.violet.buttons .active.button,.ui.violet.buttons .active.button:active{background-color:#5626bf;color:#fff;text-shadow:none}.ui.basic.violet.button,.ui.basic.violet.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #6435c9 inset;box-shadow:0 0 0 1px #6435c9 inset;color:#6435c9}.ui.basic.violet.button:hover,.ui.basic.violet.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #5829bb inset;box-shadow:0 0 0 1px #5829bb inset;color:#5829bb}.ui.basic.violet.button:focus,.ui.basic.violet.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #4f20b5 inset;box-shadow:0 0 0 1px #4f20b5 inset;color:#5829bb}.ui.basic.violet.active.button,.ui.basic.violet.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #5626bf inset;box-shadow:0 0 0 1px #5626bf inset;color:#502aa1}.ui.basic.violet.button:active,.ui.basic.violet.buttons .button:active{-webkit-box-shadow:0 0 0 1px #502aa1 inset;box-shadow:0 0 0 1px #502aa1 inset;color:#502aa1}.ui.buttons:not(.vertical)>.basic.violet.button:not(:first-child){margin-left:-1px}.ui.inverted.violet.button,.ui.inverted.violet.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #a291fb inset;box-shadow:0 0 0 2px #a291fb inset;color:#a291fb}.ui.inverted.violet.button.active,.ui.inverted.violet.button:active,.ui.inverted.violet.button:focus,.ui.inverted.violet.button:hover,.ui.inverted.violet.buttons .button.active,.ui.inverted.violet.buttons .button:active,.ui.inverted.violet.buttons .button:focus,.ui.inverted.violet.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.violet.button:hover,.ui.inverted.violet.buttons .button:hover{background-color:#745aff}.ui.inverted.violet.button:focus,.ui.inverted.violet.buttons .button:focus{background-color:#7d64ff}.ui.inverted.violet.active.button,.ui.inverted.violet.buttons .active.button{background-color:#8a73ff}.ui.inverted.violet.button:active,.ui.inverted.violet.buttons .button:active{background-color:#7860f9}.ui.inverted.violet.basic.button,.ui.inverted.violet.basic.buttons .button,.ui.inverted.violet.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.violet.basic.button:hover,.ui.inverted.violet.basic.buttons .button:hover,.ui.inverted.violet.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #745aff inset;box-shadow:0 0 0 2px #745aff inset;color:#a291fb}.ui.inverted.violet.basic.button:focus,.ui.inverted.violet.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #7d64ff inset;box-shadow:0 0 0 2px #7d64ff inset;color:#a291fb}.ui.inverted.violet.basic.active.button,.ui.inverted.violet.basic.buttons .active.button,.ui.inverted.violet.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #8a73ff inset;box-shadow:0 0 0 2px #8a73ff inset;color:#a291fb}.ui.inverted.violet.basic.button:active,.ui.inverted.violet.basic.buttons .button:active,.ui.inverted.violet.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #7860f9 inset;box-shadow:0 0 0 2px #7860f9 inset;color:#a291fb}.ui.tertiary.violet.button,.ui.tertiary.violet.buttons .button,.ui.tertiary.violet.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#6435c9}.ui.tertiary.violet.button:hover,.ui.tertiary.violet.buttons .button:hover,.ui.tertiary.violet.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #6040a5;box-shadow:inset 0 -.2em 0 #6040a5;color:#6040a5}.ui.tertiary.violet.button:focus,.ui.tertiary.violet.buttons .button:focus,.ui.tertiary.violet.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #5735a0;box-shadow:inset 0 -.2em 0 #5735a0;color:#5735a0}.ui.tertiary.violet.active.button,.ui.tertiary.violet.button:active,.ui.tertiary.violet.buttons .active.button,.ui.tertiary.violet.buttons .button:active,.ui.tertiary.violet.buttons .tertiary.active.button,.ui.tertiary.violet.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #4e0fd6;box-shadow:inset 0 -.2em 0 #4e0fd6;color:#5626bf}.ui.purple.button,.ui.purple.buttons .button{background-color:#a333c8;color:#fff;text-shadow:none;background-image:none}.ui.purple.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.purple.button:hover,.ui.purple.buttons .button:hover{background-color:#9627ba;color:#fff;text-shadow:none}.ui.purple.button:focus,.ui.purple.buttons .button:focus{background-color:#8f1eb4;color:#fff;text-shadow:none}.ui.purple.button:active,.ui.purple.buttons .button:active{background-color:#82299f;color:#fff;text-shadow:none}.ui.purple.active.button,.ui.purple.button .active.button:active,.ui.purple.buttons .active.button,.ui.purple.buttons .active.button:active{background-color:#9724be;color:#fff;text-shadow:none}.ui.basic.purple.button,.ui.basic.purple.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #a333c8 inset;box-shadow:0 0 0 1px #a333c8 inset;color:#a333c8}.ui.basic.purple.button:hover,.ui.basic.purple.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #9627ba inset;box-shadow:0 0 0 1px #9627ba inset;color:#9627ba}.ui.basic.purple.button:focus,.ui.basic.purple.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #8f1eb4 inset;box-shadow:0 0 0 1px #8f1eb4 inset;color:#9627ba}.ui.basic.purple.active.button,.ui.basic.purple.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #9724be inset;box-shadow:0 0 0 1px #9724be inset;color:#82299f}.ui.basic.purple.button:active,.ui.basic.purple.buttons .button:active{-webkit-box-shadow:0 0 0 1px #82299f inset;box-shadow:0 0 0 1px #82299f inset;color:#82299f}.ui.buttons:not(.vertical)>.basic.purple.button:not(:first-child){margin-left:-1px}.ui.inverted.purple.button,.ui.inverted.purple.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #dc73ff inset;box-shadow:0 0 0 2px #dc73ff inset;color:#dc73ff}.ui.inverted.purple.button.active,.ui.inverted.purple.button:active,.ui.inverted.purple.button:focus,.ui.inverted.purple.button:hover,.ui.inverted.purple.buttons .button.active,.ui.inverted.purple.buttons .button:active,.ui.inverted.purple.buttons .button:focus,.ui.inverted.purple.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.purple.button:hover,.ui.inverted.purple.buttons .button:hover{background-color:#cf40ff}.ui.inverted.purple.button:focus,.ui.inverted.purple.buttons .button:focus{background-color:#d24aff}.ui.inverted.purple.active.button,.ui.inverted.purple.buttons .active.button{background-color:#d65aff}.ui.inverted.purple.button:active,.ui.inverted.purple.buttons .button:active{background-color:#cf40ff}.ui.inverted.purple.basic.button,.ui.inverted.purple.basic.buttons .button,.ui.inverted.purple.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.purple.basic.button:hover,.ui.inverted.purple.basic.buttons .button:hover,.ui.inverted.purple.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #cf40ff inset;box-shadow:0 0 0 2px #cf40ff inset;color:#dc73ff}.ui.inverted.purple.basic.button:focus,.ui.inverted.purple.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #d24aff inset;box-shadow:0 0 0 2px #d24aff inset;color:#dc73ff}.ui.inverted.purple.basic.active.button,.ui.inverted.purple.basic.buttons .active.button,.ui.inverted.purple.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #d65aff inset;box-shadow:0 0 0 2px #d65aff inset;color:#dc73ff}.ui.inverted.purple.basic.button:active,.ui.inverted.purple.basic.buttons .button:active,.ui.inverted.purple.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #cf40ff inset;box-shadow:0 0 0 2px #cf40ff inset;color:#dc73ff}.ui.tertiary.purple.button,.ui.tertiary.purple.buttons .button,.ui.tertiary.purple.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#a333c8}.ui.tertiary.purple.button:hover,.ui.tertiary.purple.buttons .button:hover,.ui.tertiary.purple.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #8a3ea4;box-shadow:inset 0 -.2em 0 #8a3ea4;color:#8a3ea4}.ui.tertiary.purple.button:focus,.ui.tertiary.purple.buttons .button:focus,.ui.tertiary.purple.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #84339f;box-shadow:inset 0 -.2em 0 #84339f;color:#84339f}.ui.tertiary.purple.active.button,.ui.tertiary.purple.button:active,.ui.tertiary.purple.buttons .active.button,.ui.tertiary.purple.buttons .button:active,.ui.tertiary.purple.buttons .tertiary.active.button,.ui.tertiary.purple.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #a30dd4;box-shadow:inset 0 -.2em 0 #a30dd4;color:#9724be}.ui.pink.button,.ui.pink.buttons .button{background-color:#e03997;color:#fff;text-shadow:none;background-image:none}.ui.pink.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.pink.button:hover,.ui.pink.buttons .button:hover{background-color:#e61a8d;color:#fff;text-shadow:none}.ui.pink.button:focus,.ui.pink.buttons .button:focus{background-color:#e10f85;color:#fff;text-shadow:none}.ui.pink.button:active,.ui.pink.buttons .button:active{background-color:#c71f7e;color:#fff;text-shadow:none}.ui.pink.active.button,.ui.pink.button .active.button:active,.ui.pink.buttons .active.button,.ui.pink.buttons .active.button:active{background-color:#ea158d;color:#fff;text-shadow:none}.ui.basic.pink.button,.ui.basic.pink.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #e03997 inset;box-shadow:0 0 0 1px #e03997 inset;color:#e03997}.ui.basic.pink.button:hover,.ui.basic.pink.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #e61a8d inset;box-shadow:0 0 0 1px #e61a8d inset;color:#e61a8d}.ui.basic.pink.button:focus,.ui.basic.pink.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #e10f85 inset;box-shadow:0 0 0 1px #e10f85 inset;color:#e61a8d}.ui.basic.pink.active.button,.ui.basic.pink.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #ea158d inset;box-shadow:0 0 0 1px #ea158d inset;color:#c71f7e}.ui.basic.pink.button:active,.ui.basic.pink.buttons .button:active{-webkit-box-shadow:0 0 0 1px #c71f7e inset;box-shadow:0 0 0 1px #c71f7e inset;color:#c71f7e}.ui.buttons:not(.vertical)>.basic.pink.button:not(:first-child){margin-left:-1px}.ui.inverted.pink.button,.ui.inverted.pink.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #ff8edf inset;box-shadow:0 0 0 2px #ff8edf inset;color:#ff8edf}.ui.inverted.pink.button.active,.ui.inverted.pink.button:active,.ui.inverted.pink.button:focus,.ui.inverted.pink.button:hover,.ui.inverted.pink.buttons .button.active,.ui.inverted.pink.buttons .button:active,.ui.inverted.pink.buttons .button:focus,.ui.inverted.pink.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.pink.button:hover,.ui.inverted.pink.buttons .button:hover{background-color:#ff5bd1}.ui.inverted.pink.button:focus,.ui.inverted.pink.buttons .button:focus{background-color:#ff65d3}.ui.inverted.pink.active.button,.ui.inverted.pink.buttons .active.button{background-color:#ff74d8}.ui.inverted.pink.button:active,.ui.inverted.pink.buttons .button:active{background-color:#ff5bd1}.ui.inverted.pink.basic.button,.ui.inverted.pink.basic.buttons .button,.ui.inverted.pink.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.pink.basic.button:hover,.ui.inverted.pink.basic.buttons .button:hover,.ui.inverted.pink.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #ff5bd1 inset;box-shadow:0 0 0 2px #ff5bd1 inset;color:#ff8edf}.ui.inverted.pink.basic.button:focus,.ui.inverted.pink.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #ff65d3 inset;box-shadow:0 0 0 2px #ff65d3 inset;color:#ff8edf}.ui.inverted.pink.basic.active.button,.ui.inverted.pink.basic.buttons .active.button,.ui.inverted.pink.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #ff74d8 inset;box-shadow:0 0 0 2px #ff74d8 inset;color:#ff8edf}.ui.inverted.pink.basic.button:active,.ui.inverted.pink.basic.buttons .button:active,.ui.inverted.pink.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #ff5bd1 inset;box-shadow:0 0 0 2px #ff5bd1 inset;color:#ff8edf}.ui.tertiary.pink.button,.ui.tertiary.pink.buttons .button,.ui.tertiary.pink.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#e03997}.ui.tertiary.pink.button:hover,.ui.tertiary.pink.buttons .button:hover,.ui.tertiary.pink.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #cc3389;box-shadow:inset 0 -.2em 0 #cc3389;color:#cc3389}.ui.tertiary.pink.button:focus,.ui.tertiary.pink.buttons .button:focus,.ui.tertiary.pink.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #c92782;box-shadow:inset 0 -.2em 0 #c92782;color:#c92782}.ui.tertiary.pink.active.button,.ui.tertiary.pink.button:active,.ui.tertiary.pink.buttons .active.button,.ui.tertiary.pink.buttons .button:active,.ui.tertiary.pink.buttons .tertiary.active.button,.ui.tertiary.pink.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #ff0090;box-shadow:inset 0 -.2em 0 #ff0090;color:#ea158d}.ui.brown.button,.ui.brown.buttons .button{background-color:#a5673f;color:#fff;text-shadow:none;background-image:none}.ui.brown.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.brown.button:hover,.ui.brown.buttons .button:hover{background-color:#975b33;color:#fff;text-shadow:none}.ui.brown.button:focus,.ui.brown.buttons .button:focus{background-color:#90532b;color:#fff;text-shadow:none}.ui.brown.button:active,.ui.brown.buttons .button:active{background-color:#805031;color:#fff;text-shadow:none}.ui.brown.active.button,.ui.brown.button .active.button:active,.ui.brown.buttons .active.button,.ui.brown.buttons .active.button:active{background-color:#995a31;color:#fff;text-shadow:none}.ui.basic.brown.button,.ui.basic.brown.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #a5673f inset;box-shadow:0 0 0 1px #a5673f inset;color:#a5673f}.ui.basic.brown.button:hover,.ui.basic.brown.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #975b33 inset;box-shadow:0 0 0 1px #975b33 inset;color:#975b33}.ui.basic.brown.button:focus,.ui.basic.brown.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #90532b inset;box-shadow:0 0 0 1px #90532b inset;color:#975b33}.ui.basic.brown.active.button,.ui.basic.brown.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #995a31 inset;box-shadow:0 0 0 1px #995a31 inset;color:#805031}.ui.basic.brown.button:active,.ui.basic.brown.buttons .button:active{-webkit-box-shadow:0 0 0 1px #805031 inset;box-shadow:0 0 0 1px #805031 inset;color:#805031}.ui.buttons:not(.vertical)>.basic.brown.button:not(:first-child){margin-left:-1px}.ui.inverted.brown.button,.ui.inverted.brown.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #d67c1c inset;box-shadow:0 0 0 2px #d67c1c inset;color:#d67c1c}.ui.inverted.brown.button.active,.ui.inverted.brown.button:active,.ui.inverted.brown.button:focus,.ui.inverted.brown.button:hover,.ui.inverted.brown.buttons .button.active,.ui.inverted.brown.buttons .button:active,.ui.inverted.brown.buttons .button:focus,.ui.inverted.brown.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.brown.button:hover,.ui.inverted.brown.buttons .button:hover{background-color:#b0620f}.ui.inverted.brown.button:focus,.ui.inverted.brown.buttons .button:focus{background-color:#c16808}.ui.inverted.brown.active.button,.ui.inverted.brown.buttons .active.button{background-color:#cc6f0d}.ui.inverted.brown.button:active,.ui.inverted.brown.buttons .button:active{background-color:#a96216}.ui.inverted.brown.basic.button,.ui.inverted.brown.basic.buttons .button,.ui.inverted.brown.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.brown.basic.button:hover,.ui.inverted.brown.basic.buttons .button:hover,.ui.inverted.brown.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #b0620f inset;box-shadow:0 0 0 2px #b0620f inset;color:#d67c1c}.ui.inverted.brown.basic.button:focus,.ui.inverted.brown.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #c16808 inset;box-shadow:0 0 0 2px #c16808 inset;color:#d67c1c}.ui.inverted.brown.basic.active.button,.ui.inverted.brown.basic.buttons .active.button,.ui.inverted.brown.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #cc6f0d inset;box-shadow:0 0 0 2px #cc6f0d inset;color:#d67c1c}.ui.inverted.brown.basic.button:active,.ui.inverted.brown.basic.buttons .button:active,.ui.inverted.brown.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #a96216 inset;box-shadow:0 0 0 2px #a96216 inset;color:#d67c1c}.ui.tertiary.brown.button,.ui.tertiary.brown.buttons .button,.ui.tertiary.brown.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#a5673f}.ui.tertiary.brown.button:hover,.ui.tertiary.brown.buttons .button:hover,.ui.tertiary.brown.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #835f48;box-shadow:inset 0 -.2em 0 #835f48;color:#835f48}.ui.tertiary.brown.button:focus,.ui.tertiary.brown.buttons .button:focus,.ui.tertiary.brown.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #7d573e;box-shadow:inset 0 -.2em 0 #7d573e;color:#7d573e}.ui.tertiary.brown.active.button,.ui.tertiary.brown.button:active,.ui.tertiary.brown.buttons .active.button,.ui.tertiary.brown.buttons .button:active,.ui.tertiary.brown.buttons .tertiary.active.button,.ui.tertiary.brown.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #ae561d;box-shadow:inset 0 -.2em 0 #ae561d;color:#995a31}.ui.grey.button,.ui.grey.buttons .button{background-color:#767676;color:#fff;text-shadow:none;background-image:none}.ui.grey.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.grey.button:hover,.ui.grey.buttons .button:hover{background-color:#838383;color:#fff;text-shadow:none}.ui.grey.button:focus,.ui.grey.buttons .button:focus{background-color:#8a8a8a;color:#fff;text-shadow:none}.ui.grey.button:active,.ui.grey.buttons .button:active{background-color:#909090;color:#fff;text-shadow:none}.ui.grey.active.button,.ui.grey.button .active.button:active,.ui.grey.buttons .active.button,.ui.grey.buttons .active.button:active{background-color:#696969;color:#fff;text-shadow:none}.ui.basic.grey.button,.ui.basic.grey.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #767676 inset;box-shadow:0 0 0 1px #767676 inset;color:#767676}.ui.basic.grey.button:hover,.ui.basic.grey.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #838383 inset;box-shadow:0 0 0 1px #838383 inset;color:#838383}.ui.basic.grey.button:focus,.ui.basic.grey.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #8a8a8a inset;box-shadow:0 0 0 1px #8a8a8a inset;color:#838383}.ui.basic.grey.active.button,.ui.basic.grey.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #696969 inset;box-shadow:0 0 0 1px #696969 inset;color:#909090}.ui.basic.grey.button:active,.ui.basic.grey.buttons .button:active{-webkit-box-shadow:0 0 0 1px #909090 inset;box-shadow:0 0 0 1px #909090 inset;color:#909090}.ui.buttons:not(.vertical)>.basic.grey.button:not(:first-child){margin-left:-1px}.ui.inverted.grey.button,.ui.inverted.grey.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #d4d4d5 inset;box-shadow:0 0 0 2px #d4d4d5 inset;color:#fff}.ui.inverted.grey.button.active,.ui.inverted.grey.button:active,.ui.inverted.grey.button:focus,.ui.inverted.grey.button:hover,.ui.inverted.grey.buttons .button.active,.ui.inverted.grey.buttons .button:active,.ui.inverted.grey.buttons .button:focus,.ui.inverted.grey.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.6)}.ui.inverted.grey.button:hover,.ui.inverted.grey.buttons .button:hover{background-color:#c2c4c5}.ui.inverted.grey.button:focus,.ui.inverted.grey.buttons .button:focus{background-color:#c7c9cb}.ui.inverted.grey.active.button,.ui.inverted.grey.buttons .active.button{background-color:#cfd0d2}.ui.inverted.grey.button:active,.ui.inverted.grey.buttons .button:active{background-color:#c2c4c5}.ui.inverted.grey.basic.button,.ui.inverted.grey.basic.buttons .button,.ui.inverted.grey.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.grey.basic.button:hover,.ui.inverted.grey.basic.buttons .button:hover,.ui.inverted.grey.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #c2c4c5 inset;box-shadow:0 0 0 2px #c2c4c5 inset;color:#fff}.ui.inverted.grey.basic.button:focus,.ui.inverted.grey.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #c7c9cb inset;box-shadow:0 0 0 2px #c7c9cb inset;color:#dcddde}.ui.inverted.grey.basic.active.button,.ui.inverted.grey.basic.buttons .active.button,.ui.inverted.grey.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #cfd0d2 inset;box-shadow:0 0 0 2px #cfd0d2 inset;color:#fff}.ui.inverted.grey.basic.button:active,.ui.inverted.grey.basic.buttons .button:active,.ui.inverted.grey.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #c2c4c5 inset;box-shadow:0 0 0 2px #c2c4c5 inset;color:#fff}.ui.tertiary.grey.button,.ui.tertiary.grey.buttons .button,.ui.tertiary.grey.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#767676}.ui.tertiary.grey.button:hover,.ui.tertiary.grey.buttons .button:hover,.ui.tertiary.grey.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #838383;box-shadow:inset 0 -.2em 0 #838383;color:#838383}.ui.tertiary.grey.button:focus,.ui.tertiary.grey.buttons .button:focus,.ui.tertiary.grey.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #8a8a8a;box-shadow:inset 0 -.2em 0 #8a8a8a;color:#8a8a8a}.ui.tertiary.grey.active.button,.ui.tertiary.grey.button:active,.ui.tertiary.grey.buttons .active.button,.ui.tertiary.grey.buttons .button:active,.ui.tertiary.grey.buttons .tertiary.active.button,.ui.tertiary.grey.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #7e5454;box-shadow:inset 0 -.2em 0 #7e5454;color:#696969}.ui.black.button,.ui.black.buttons .button{background-color:#1b1c1d;color:#fff;text-shadow:none;background-image:none}.ui.black.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.black.button:hover,.ui.black.buttons .button:hover{background-color:#27292a;color:#fff;text-shadow:none}.ui.black.button:focus,.ui.black.buttons .button:focus{background-color:#2f3032;color:#fff;text-shadow:none}.ui.black.button:active,.ui.black.buttons .button:active{background-color:#343637;color:#fff;text-shadow:none}.ui.black.active.button,.ui.black.button .active.button:active,.ui.black.buttons .active.button,.ui.black.buttons .active.button:active{background-color:#0f0f10;color:#fff;text-shadow:none}.ui.basic.black.button,.ui.basic.black.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #1b1c1d inset;box-shadow:0 0 0 1px #1b1c1d inset;color:#1b1c1d}.ui.basic.black.button:hover,.ui.basic.black.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #27292a inset;box-shadow:0 0 0 1px #27292a inset;color:#27292a}.ui.basic.black.button:focus,.ui.basic.black.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #2f3032 inset;box-shadow:0 0 0 1px #2f3032 inset;color:#27292a}.ui.basic.black.active.button,.ui.basic.black.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #0f0f10 inset;box-shadow:0 0 0 1px #0f0f10 inset;color:#343637}.ui.basic.black.button:active,.ui.basic.black.buttons .button:active{-webkit-box-shadow:0 0 0 1px #343637 inset;box-shadow:0 0 0 1px #343637 inset;color:#343637}.ui.buttons:not(.vertical)>.basic.black.button:not(:first-child){margin-left:-1px}.ui.inverted.black.button,.ui.inverted.black.buttons .button{background-color:transparent;-webkit-box-shadow:0 0 0 2px #d4d4d5 inset;box-shadow:0 0 0 2px #d4d4d5 inset;color:#fff}.ui.inverted.black.button.active,.ui.inverted.black.button:active,.ui.inverted.black.button:focus,.ui.inverted.black.button:hover,.ui.inverted.black.buttons .button.active,.ui.inverted.black.buttons .button:active,.ui.inverted.black.buttons .button:focus,.ui.inverted.black.buttons .button:hover{-webkit-box-shadow:none;box-shadow:none;color:#fff}.ui.inverted.black.button:hover,.ui.inverted.black.buttons .button:hover{background-color:#000}.ui.inverted.black.button:focus,.ui.inverted.black.buttons .button:focus{background-color:#000}.ui.inverted.black.active.button,.ui.inverted.black.buttons .active.button{background-color:#000}.ui.inverted.black.button:active,.ui.inverted.black.buttons .button:active{background-color:#000}.ui.inverted.black.basic.button,.ui.inverted.black.basic.buttons .button,.ui.inverted.black.buttons .basic.button{background-color:transparent;-webkit-box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;box-shadow:0 0 0 2px rgba(255,255,255,.5) inset;color:#fff}.ui.inverted.black.basic.button:hover,.ui.inverted.black.basic.buttons .button:hover,.ui.inverted.black.buttons .basic.button:hover{-webkit-box-shadow:0 0 0 2px #000 inset;box-shadow:0 0 0 2px #000 inset;color:#fff}.ui.inverted.black.basic.button:focus,.ui.inverted.black.basic.buttons .button:focus{-webkit-box-shadow:0 0 0 2px #000 inset;box-shadow:0 0 0 2px #000 inset;color:#545454}.ui.inverted.black.basic.active.button,.ui.inverted.black.basic.buttons .active.button,.ui.inverted.black.buttons .basic.active.button{-webkit-box-shadow:0 0 0 2px #000 inset;box-shadow:0 0 0 2px #000 inset;color:#fff}.ui.inverted.black.basic.button:active,.ui.inverted.black.basic.buttons .button:active,.ui.inverted.black.buttons .basic.button:active{-webkit-box-shadow:0 0 0 2px #000 inset;box-shadow:0 0 0 2px #000 inset;color:#fff}.ui.tertiary.black.button,.ui.tertiary.black.buttons .button,.ui.tertiary.black.buttons .tertiary.button{background:0 0;-webkit-box-shadow:none;box-shadow:none;color:#1b1c1d}.ui.tertiary.black.button:hover,.ui.tertiary.black.buttons .button:hover,.ui.tertiary.black.buttons button:hover{-webkit-box-shadow:inset 0 -.2em 0 #8b8f93;box-shadow:inset 0 -.2em 0 #8b8f93;color:#8b8f93}.ui.tertiary.black.button:focus,.ui.tertiary.black.buttons .button:focus,.ui.tertiary.black.buttons .tertiary.button:focus{-webkit-box-shadow:inset 0 -.2em 0 #93969a;box-shadow:inset 0 -.2em 0 #93969a;color:#93969a}.ui.tertiary.black.active.button,.ui.tertiary.black.button:active,.ui.tertiary.black.buttons .active.button,.ui.tertiary.black.buttons .button:active,.ui.tertiary.black.buttons .tertiary.active.button,.ui.tertiary.black.buttons .tertiary.button:active{-webkit-box-shadow:inset 0 -.2em 0 #404245;box-shadow:inset 0 -.2em 0 #404245;color:#0f0f10}.ui.positive.button,.ui.positive.buttons .button{background-color:#21ba45;color:#fff;text-shadow:none;background-image:none}.ui.positive.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.positive.button:hover,.ui.positive.buttons .button:hover{background-color:#16ab39;color:#fff;text-shadow:none}.ui.positive.button:focus,.ui.positive.buttons .button:focus{background-color:#0ea432;color:#fff;text-shadow:none}.ui.positive.button:active,.ui.positive.buttons .button:active{background-color:#198f35;color:#fff;text-shadow:none}.ui.positive.active.button,.ui.positive.button .active.button:active,.ui.positive.buttons .active.button,.ui.positive.buttons .active.button:active{background-color:#13ae38;color:#fff;text-shadow:none}.ui.basic.positive.button,.ui.basic.positive.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #21ba45 inset;box-shadow:0 0 0 1px #21ba45 inset;color:#21ba45}.ui.basic.positive.button:hover,.ui.basic.positive.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #16ab39 inset;box-shadow:0 0 0 1px #16ab39 inset;color:#16ab39}.ui.basic.positive.button:focus,.ui.basic.positive.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #0ea432 inset;box-shadow:0 0 0 1px #0ea432 inset;color:#16ab39}.ui.basic.positive.active.button,.ui.basic.positive.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #13ae38 inset;box-shadow:0 0 0 1px #13ae38 inset;color:#198f35}.ui.basic.positive.button:active,.ui.basic.positive.buttons .button:active{-webkit-box-shadow:0 0 0 1px #198f35 inset;box-shadow:0 0 0 1px #198f35 inset;color:#198f35}.ui.buttons:not(.vertical)>.basic.positive.button:not(:first-child){margin-left:-1px}.ui.negative.button,.ui.negative.buttons .button{background-color:#db2828;color:#fff;text-shadow:none;background-image:none}.ui.negative.button{-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 0 rgba(34,36,38,.15) inset}.ui.negative.button:hover,.ui.negative.buttons .button:hover{background-color:#d01919;color:#fff;text-shadow:none}.ui.negative.button:focus,.ui.negative.buttons .button:focus{background-color:#ca1010;color:#fff;text-shadow:none}.ui.negative.button:active,.ui.negative.buttons .button:active{background-color:#b21e1e;color:#fff;text-shadow:none}.ui.negative.active.button,.ui.negative.button .active.button:active,.ui.negative.buttons .active.button,.ui.negative.buttons .active.button:active{background-color:#d41515;color:#fff;text-shadow:none}.ui.basic.negative.button,.ui.basic.negative.buttons .button{background:0 0;-webkit-box-shadow:0 0 0 1px #db2828 inset;box-shadow:0 0 0 1px #db2828 inset;color:#db2828}.ui.basic.negative.button:hover,.ui.basic.negative.buttons .button:hover{background:0 0;-webkit-box-shadow:0 0 0 1px #d01919 inset;box-shadow:0 0 0 1px #d01919 inset;color:#d01919}.ui.basic.negative.button:focus,.ui.basic.negative.buttons .button:focus{background:0 0;-webkit-box-shadow:0 0 0 1px #ca1010 inset;box-shadow:0 0 0 1px #ca1010 inset;color:#d01919}.ui.basic.negative.active.button,.ui.basic.negative.buttons .active.button{background:0 0;-webkit-box-shadow:0 0 0 1px #d41515 inset;box-shadow:0 0 0 1px #d41515 inset;color:#b21e1e}.ui.basic.negative.button:active,.ui.basic.negative.buttons .button:active{-webkit-box-shadow:0 0 0 1px #b21e1e inset;box-shadow:0 0 0 1px #b21e1e inset;color:#b21e1e}.ui.buttons:not(.vertical)>.basic.negative.button:not(:first-child){margin-left:-1px}.ui.buttons{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;font-size:0;vertical-align:baseline;margin:0 .25em 0 0}.ui.buttons:not(.basic):not(.inverted){-webkit-box-shadow:none;box-shadow:none}.ui.buttons:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui.buttons .button{-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;border-radius:0;margin:0}.ui.buttons:not(.basic):not(.inverted)>.button:not(.basic):not(.inverted){-webkit-box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset}.ui.buttons .button:first-child{border-left:none;margin-left:0;border-top-left-radius:.28571429rem;border-bottom-left-radius:.28571429rem}.ui.buttons .button:last-child{border-top-right-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.vertical.buttons{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.vertical.buttons .button{display:block;float:none;width:100%;margin:0;-webkit-box-shadow:none;box-shadow:none;border-radius:0}.ui.vertical.buttons .button:first-child{border-top-left-radius:.28571429rem;border-top-right-radius:.28571429rem}.ui.vertical.buttons .button:last-child{margin-bottom:0;border-bottom-left-radius:.28571429rem;border-bottom-right-radius:.28571429rem}.ui.vertical.buttons .button:only-child{border-radius:.28571429rem} + /* + * # Fomantic-UI - Checkbox + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.checkbox{position:relative;display:inline-block;-webkit-backface-visibility:hidden;backface-visibility:hidden;outline:0;vertical-align:baseline;font-style:normal;min-height:17px;font-size:1em;line-height:17px;min-width:17px}.ui.checkbox input[type=checkbox],.ui.checkbox input[type=radio]{cursor:pointer;position:absolute;top:0;left:0;opacity:0!important;outline:0;z-index:3;width:17px;height:17px}.ui.checkbox label{cursor:auto;position:relative;display:block;padding-left:1.85714em;outline:0;font-size:1em}.ui.checkbox label:before{position:absolute;top:0;left:0;width:17px;height:17px;content:'';background:#fff;border-radius:.21428571rem;-webkit-transition:border .1s ease,opacity .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,transform .1s ease,box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,transform .1s ease,box-shadow .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;border:1px solid #d4d4d5}.ui.checkbox label:after{position:absolute;font-size:14px;top:0;left:0;width:17px;height:17px;text-align:center;opacity:0;color:rgba(0,0,0,.87);-webkit-transition:border .1s ease,opacity .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,transform .1s ease,box-shadow .1s ease;transition:border .1s ease,opacity .1s ease,transform .1s ease,box-shadow .1s ease,-webkit-transform .1s ease,-webkit-box-shadow .1s ease}.ui.checkbox label,.ui.checkbox+label{color:rgba(0,0,0,.87);-webkit-transition:color .1s ease;transition:color .1s ease}.ui.checkbox+label{vertical-align:middle}.ui.checkbox label:hover::before{background:#fff;border-color:rgba(34,36,38,.35)}.ui.checkbox label:hover,.ui.checkbox+label:hover{color:rgba(0,0,0,.8)}.ui.checkbox label:active::before{background:#f9fafb;border-color:rgba(34,36,38,.35)}.ui.checkbox label:active::after{color:rgba(0,0,0,.95)}.ui.checkbox input:active~label{color:rgba(0,0,0,.95)}.ui.checkbox input:focus~label:before{background:#fff;border-color:#96c8da}.ui.checkbox input:focus~label:after{color:rgba(0,0,0,.95)}.ui.checkbox input:focus~label{color:rgba(0,0,0,.95)}.ui.checkbox input:checked~label:before{background:#fff;border-color:rgba(34,36,38,.35)}.ui.checkbox input:checked~label:after{opacity:1;color:rgba(0,0,0,.95)}.ui.checkbox input:not([type=radio]):indeterminate~label:before{background:#fff;border-color:rgba(34,36,38,.35)}.ui.checkbox input:not([type=radio]):indeterminate~label:after{opacity:1;color:rgba(0,0,0,.95)}.ui.indeterminate.toggle.checkbox input:not([type=radio]):indeterminate~label:before{background:rgba(0,0,0,.15)}.ui.indeterminate.toggle.checkbox input:not([type=radio])~label:after{left:1.075rem}.ui.checkbox input:checked:focus~label:before,.ui.checkbox input:not([type=radio]):indeterminate:focus~label:before{background:#fff;border-color:#96c8da}.ui.checkbox input:checked:focus~label:after,.ui.checkbox input:not([type=radio]):indeterminate:focus~label:after{color:rgba(0,0,0,.95)}.ui.read-only.checkbox,.ui.read-only.checkbox label{cursor:default}.ui.checkbox input[disabled]~label,.ui.disabled.checkbox label{cursor:default!important;opacity:.5;color:#000}.ui.checkbox input.hidden{z-index:-1}.ui.checkbox input.hidden+label{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ui.radio.checkbox{min-height:15px}.ui.radio.checkbox label{padding-left:1.85714em}.ui.radio.checkbox label:before{content:'';-webkit-transform:none;transform:none;width:15px;height:15px;border-radius:500rem;top:1px;left:0}.ui.radio.checkbox label:after{border:none;content:''!important;line-height:15px;top:1px;left:0;width:15px;height:15px;border-radius:500rem;-webkit-transform:scale(.46666667);transform:scale(.46666667);background-color:rgba(0,0,0,.87)}.ui.radio.checkbox input:focus~label:before{background-color:#fff}.ui.radio.checkbox input:focus~label:after{background-color:rgba(0,0,0,.95)}.ui.radio.checkbox input:indeterminate~label:after{opacity:0}.ui.radio.checkbox input:checked~label:before{background-color:#fff}.ui.radio.checkbox input:checked~label:after{background-color:rgba(0,0,0,.95)}.ui.radio.checkbox input:focus:checked~label:before{background-color:#fff}.ui.radio.checkbox input:focus:checked~label:after{background-color:rgba(0,0,0,.95)}.ui.slider.checkbox{min-height:1.25rem}.ui.slider.checkbox input{width:3.5rem;height:1.25rem}.ui.slider.checkbox label{padding-left:4.5rem;line-height:1rem;color:rgba(0,0,0,.4)}.ui.slider.checkbox label:before{display:block;position:absolute;content:'';-webkit-transform:none;transform:none;border:none!important;left:0;z-index:1;top:.4rem;background-color:rgba(0,0,0,.05);width:3.5rem;height:.21428571rem;border-radius:500rem;-webkit-transition:background .3s ease;transition:background .3s ease}.ui.slider.checkbox label:after{background:#fff -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:#fff -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:#fff linear-gradient(transparent,rgba(0,0,0,.05));position:absolute;content:''!important;opacity:1;z-index:2;border:none;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;width:1.5rem;height:1.5rem;top:-.25rem;left:0;-webkit-transform:none;transform:none;border-radius:500rem;-webkit-transition:left .3s ease;transition:left .3s ease}.ui.slider.checkbox input:focus~label:before{background-color:rgba(0,0,0,.15);border:none}.ui.slider.checkbox label:hover{color:rgba(0,0,0,.8)}.ui.slider.checkbox label:hover::before{background:rgba(0,0,0,.15)}.ui.slider.checkbox input:checked~label{color:rgba(0,0,0,.95)!important}.ui.slider.checkbox input:checked~label:before{background-color:#545454!important}.ui.slider.checkbox input:checked~label:after{left:2rem}.ui.slider.checkbox input:focus:checked~label{color:rgba(0,0,0,.95)!important}.ui.slider.checkbox input:focus:checked~label:before{background-color:#000!important}.ui.toggle.checkbox{min-height:1.5rem}.ui.toggle.checkbox input{width:3.5rem;height:1.5rem}.ui.toggle.checkbox label{min-height:1.5rem;padding-left:4.5rem;color:rgba(0,0,0,.87)}.ui.toggle.checkbox label{padding-top:.15em}.ui.toggle.checkbox label:before{display:block;position:absolute;content:'';z-index:1;-webkit-transform:none;transform:none;border:none;top:0;background:rgba(0,0,0,.05);-webkit-box-shadow:none;box-shadow:none;width:3.5rem;height:1.5rem;border-radius:500rem}.ui.toggle.checkbox label:after{background:#fff -webkit-gradient(linear,left top,left bottom,from(transparent),to(rgba(0,0,0,.05)));background:#fff -webkit-linear-gradient(transparent,rgba(0,0,0,.05));background:#fff linear-gradient(transparent,rgba(0,0,0,.05));position:absolute;content:''!important;opacity:1;z-index:2;border:none;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;width:1.5rem;height:1.5rem;top:0;left:0;border-radius:500rem;-webkit-transition:background .3s ease,left .3s ease;transition:background .3s ease,left .3s ease}.ui.toggle.checkbox input~label:after{left:-.05rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset}.ui.toggle.checkbox input:focus~label:before{background-color:rgba(0,0,0,.15);border:none}.ui.toggle.checkbox label:hover::before{background-color:rgba(0,0,0,.15);border:none}.ui.toggle.checkbox input:checked~label{color:rgba(0,0,0,.95)!important}.ui.toggle.checkbox input:checked~label:before{background-color:#2185d0!important}.ui.toggle.checkbox input:checked~label:after{left:2.15rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),0 0 0 1px rgba(34,36,38,.15) inset}.ui.toggle.checkbox input:focus:checked~label{color:rgba(0,0,0,.95)!important}.ui.toggle.checkbox input:focus:checked~label:before{background-color:#0d71bb!important}.ui.fitted.checkbox label{padding-left:0!important}.ui.fitted.toggle.checkbox{width:3.5rem}.ui.fitted.slider.checkbox{width:3.5rem}.ui.inverted.checkbox label,.ui.inverted.checkbox+label{color:rgba(255,255,255,.9)!important}.ui.inverted.checkbox label:hover{color:#fff!important}.ui.inverted.checkbox label:hover::before{border-color:rgba(34,36,38,.5)}.ui.inverted.slider.checkbox label{color:rgba(255,255,255,.5)}.ui.inverted.slider.checkbox label:before{background-color:rgba(255,255,255,.5)!important}.ui.inverted.slider.checkbox label:hover::before{background:rgba(255,255,255,.7)!important}.ui.inverted.slider.checkbox input:checked~label{color:#fff!important}.ui.inverted.slider.checkbox input:checked~label:before{background-color:rgba(255,255,255,.8)!important}.ui.inverted.slider.checkbox input:focus:checked~label{color:#fff!important}.ui.inverted.slider.checkbox input:focus:checked~label:before{background-color:rgba(255,255,255,.8)!important}.ui.inverted.toggle.checkbox label:before{background-color:rgba(255,255,255,.9)!important}.ui.inverted.toggle.checkbox label:hover::before{background:#fff!important}.ui.inverted.toggle.checkbox input:checked~label{color:#fff!important}.ui.inverted.toggle.checkbox input:checked~label:before{background-color:#2185d0!important}.ui.inverted.toggle.checkbox input:focus:checked~label{color:#fff!important}.ui.inverted.toggle.checkbox input:focus:checked~label:before{background-color:#0d71bb!important}.ui.mini.checkbox{font-size:.78571429em}.ui.tiny.checkbox{font-size:.85714286em}.ui.small.checkbox{font-size:.92857143em}.ui.large.checkbox{font-size:1.14285714em}.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) label:before,.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before{-webkit-transform:scale(1.14285714);transform:scale(1.14285714);-webkit-transform-origin:left;transform-origin:left}.ui.large.checkbox.radio label:before,.ui.large.form .checkbox.radio label:before{-webkit-transform:scale(1.14285714);transform:scale(1.14285714);-webkit-transform-origin:left;transform-origin:left}.ui.large.checkbox.radio label:after,.ui.large.form .checkbox.radio label:after{-webkit-transform:scale(.57142857);transform:scale(.57142857);-webkit-transform-origin:left;transform-origin:left;left:.33571429em}.ui.big.checkbox{font-size:1.28571429em}.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) label:before,.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before{-webkit-transform:scale(1.28571429);transform:scale(1.28571429);-webkit-transform-origin:left;transform-origin:left}.ui.big.checkbox.radio label:before,.ui.big.form .checkbox.radio label:before{-webkit-transform:scale(1.28571429);transform:scale(1.28571429);-webkit-transform-origin:left;transform-origin:left}.ui.big.checkbox.radio label:after,.ui.big.form .checkbox.radio label:after{-webkit-transform:scale(.64285714);transform:scale(.64285714);-webkit-transform-origin:left;transform-origin:left;left:.37142857em}.ui.huge.checkbox{font-size:1.42857143em}.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) label:before,.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before{-webkit-transform:scale(1.42857143);transform:scale(1.42857143);-webkit-transform-origin:left;transform-origin:left}.ui.huge.checkbox.radio label:before,.ui.huge.form .checkbox.radio label:before{-webkit-transform:scale(1.42857143);transform:scale(1.42857143);-webkit-transform-origin:left;transform-origin:left}.ui.huge.checkbox.radio label:after,.ui.huge.form .checkbox.radio label:after{-webkit-transform:scale(.71428571);transform:scale(.71428571);-webkit-transform-origin:left;transform-origin:left;left:.40714286em}.ui.massive.checkbox{font-size:1.71428571em}.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) label:before,.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before{-webkit-transform:scale(1.71428571);transform:scale(1.71428571);-webkit-transform-origin:left;transform-origin:left}.ui.massive.checkbox.radio label:before,.ui.massive.form .checkbox.radio label:before{-webkit-transform:scale(1.71428571);transform:scale(1.71428571);-webkit-transform-origin:left;transform-origin:left}.ui.massive.checkbox.radio label:after,.ui.massive.form .checkbox.radio label:after{-webkit-transform:scale(.85714286);transform:scale(.85714286);-webkit-transform-origin:left;transform-origin:left;left:.47857143em}@font-face{font-family:Checkbox;src:url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBD8AAAC8AAAAYGNtYXAYVtCJAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5Zn4huwUAAAF4AAABYGhlYWQGPe1ZAAAC2AAAADZoaGVhB30DyAAAAxAAAAAkaG10eBBKAEUAAAM0AAAAHGxvY2EAmgESAAADUAAAABBtYXhwAAkALwAAA2AAAAAgbmFtZSC8IugAAAOAAAABknBvc3QAAwAAAAAFFAAAACAAAwMTAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADoAgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6AL//f//AAAAAAAg6AD//f//AAH/4xgEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAEUAUQO7AvgAGgAAARQHAQYjIicBJjU0PwE2MzIfAQE2MzIfARYVA7sQ/hQQFhcQ/uMQEE4QFxcQqAF2EBcXEE4QAnMWEP4UEBABHRAXFhBOEBCoAXcQEE4QFwAAAAABAAABbgMlAkkAFAAAARUUBwYjISInJj0BNDc2MyEyFxYVAyUQEBf9SRcQEBAQFwK3FxAQAhJtFxAQEBAXbRcQEBAQFwAAAAABAAAASQMlA24ALAAAARUUBwYrARUUBwYrASInJj0BIyInJj0BNDc2OwE1NDc2OwEyFxYdATMyFxYVAyUQEBfuEBAXbhYQEO4XEBAQEBfuEBAWbhcQEO4XEBACEm0XEBDuFxAQEBAX7hAQF20XEBDuFxAQEBAX7hAQFwAAAQAAAAIAAHRSzT9fDzz1AAsEAAAAAADRsdR3AAAAANGx1HcAAAAAA7sDbgAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAADuwABAAAAAAAAAAAAAAAAAAAABwQAAAAAAAAAAAAAAAIAAAAEAABFAyUAAAMlAAAAAAAAAAoAFAAeAE4AcgCwAAEAAAAHAC0AAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAIAAAAAQAAAAAAAgAHAGkAAQAAAAAAAwAIADkAAQAAAAAABAAIAH4AAQAAAAAABQALABgAAQAAAAAABgAIAFEAAQAAAAAACgAaAJYAAwABBAkAAQAQAAgAAwABBAkAAgAOAHAAAwABBAkAAwAQAEEAAwABBAkABAAQAIYAAwABBAkABQAWACMAAwABBAkABgAQAFkAAwABBAkACgA0ALBDaGVja2JveABDAGgAZQBjAGsAYgBvAHhWZXJzaW9uIDIuMABWAGUAcgBzAGkAbwBuACAAMgAuADBDaGVja2JveABDAGgAZQBjAGsAYgBvAHhDaGVja2JveABDAGgAZQBjAGsAYgBvAHhSZWd1bGFyAFIAZQBnAHUAbABhAHJDaGVja2JveABDAGgAZQBjAGsAYgBvAHhGb250IGdlbmVyYXRlZCBieSBJY29Nb29uLgBGAG8AbgB0ACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAC4AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype')}.ui.checkbox .box:after,.ui.checkbox label:after{font-family:Checkbox}.ui.checkbox input:checked~.box:after,.ui.checkbox input:checked~label:after{content:'\e800'}.ui.checkbox input:indeterminate~.box:after,.ui.checkbox input:indeterminate~label:after{font-size:12px;content:'\e801'} + /* + * # Fomantic-UI - Divider + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.divider{margin:1rem 0;line-height:1;height:0;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:rgba(0,0,0,.85);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ui.divider:not(.vertical):not(.horizontal){border-top:1px solid rgba(34,36,38,.15);border-bottom:1px solid rgba(255,255,255,.1)}.ui.grid>.column+.divider,.ui.grid>.row>.column+.divider{left:auto}.ui.horizontal.divider{display:table;white-space:nowrap;height:auto;margin:'';line-height:1;text-align:center}.ui.horizontal.divider:after,.ui.horizontal.divider:before{content:'';display:table-cell;position:relative;top:50%;width:50%;background-repeat:no-repeat}.ui.horizontal.divider:before{background-position:right 1em top 50%}.ui.horizontal.divider:after{background-position:left 1em top 50%}.ui.vertical.divider{position:absolute;z-index:2;top:50%;left:50%;margin:0;padding:0;width:auto;height:50%;line-height:0;text-align:center;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.ui.vertical.divider:after,.ui.vertical.divider:before{position:absolute;left:50%;content:'';z-index:3;border-left:1px solid rgba(34,36,38,.15);border-right:1px solid rgba(255,255,255,.1);width:0;height:calc(100% - 1rem)}.ui.vertical.divider:before{top:-100%}.ui.vertical.divider:after{top:auto;bottom:0}@media only screen and (max-width:767.98px){.ui.grid .stackable.row .ui.vertical.divider,.ui.stackable.grid .ui.vertical.divider{display:table;white-space:nowrap;height:auto;margin:'';overflow:hidden;line-height:1;text-align:center;position:static;top:0;left:0;-webkit-transform:none;transform:none}.ui.grid .stackable.row .ui.vertical.divider:after,.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:before{left:0;border-left:none;border-right:none;content:'';display:table-cell;position:relative;top:50%;width:50%;background-repeat:no-repeat}.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:before{background-position:right 1em top 50%}.ui.grid .stackable.row .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:after{background-position:left 1em top 50%}}.ui.divider>.icon{margin:0;font-size:1rem;height:1em;vertical-align:middle}.ui.horizontal.divider[class*="left aligned"]:before{display:none}.ui.horizontal.divider[class*="left aligned"]:after{width:100%}.ui.horizontal.divider[class*="right aligned"]:before{width:100%}.ui.horizontal.divider[class*="right aligned"]:after{display:none}.ui.hidden.divider{border-color:transparent!important}.ui.hidden.divider:after,.ui.hidden.divider:before{display:none}.ui.divider.inverted,.ui.horizontal.inverted.divider,.ui.vertical.inverted.divider{color:#fff}.ui.divider.inverted,.ui.divider.inverted:after,.ui.divider.inverted:before{border-top-color:rgba(34,36,38,.15)!important;border-left-color:rgba(34,36,38,.15)!important;border-bottom-color:rgba(255,255,255,.15)!important;border-right-color:rgba(255,255,255,.15)!important}.ui.fitted.divider{margin:0}.ui.clearing.divider{clear:both}.ui.section.divider{margin-top:2rem;margin-bottom:2rem}.ui.divider{font-size:1rem}.ui.mini.divider{font-size:.78571429rem}.ui.tiny.divider{font-size:.85714286rem}.ui.small.divider{font-size:.92857143rem}.ui.large.divider{font-size:1.14285714rem}.ui.big.divider{font-size:1.28571429rem}.ui.huge.divider{font-size:1.42857143rem}.ui.massive.divider{font-size:1.71428571rem}.ui.horizontal.divider:after,.ui.horizontal.divider:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC)}@media only screen and (max-width:767px){.ui.grid .stackable.row .ui.vertical.divider:after,.ui.grid .stackable.row .ui.vertical.divider:before,.ui.stackable.grid .ui.vertical.divider:after,.ui.stackable.grid .ui.vertical.divider:before{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC)}} + /* + * # Fomantic-UI - Form + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.form{position:relative;max-width:100%}.ui.form>p{margin:1em 0}.ui.form .field{clear:both;margin:0 0 1em}.ui.form .field:last-child,.ui.form .fields .fields,.ui.form .fields:last-child .field{margin-bottom:0}.ui.form .fields .field{clear:both;margin:0}.ui.form .field>label{display:block;margin:0 0 .28571429rem 0;color:rgba(0,0,0,.87);font-size:.92857143em;font-weight:700;text-transform:none}.ui.form input:not([type]),.ui.form input[type=date],.ui.form input[type=datetime-local],.ui.form input[type=email],.ui.form input[type=file],.ui.form input[type=number],.ui.form input[type=password],.ui.form input[type=search],.ui.form input[type=tel],.ui.form input[type=text],.ui.form input[type=time],.ui.form input[type=url],.ui.form textarea{width:100%;vertical-align:top}.ui.form ::-webkit-datetime-edit,.ui.form ::-webkit-inner-spin-button{height:1.21428571em}.ui.form input:not([type]),.ui.form input[type=date],.ui.form input[type=datetime-local],.ui.form input[type=email],.ui.form input[type=file],.ui.form input[type=number],.ui.form input[type=password],.ui.form input[type=search],.ui.form input[type=tel],.ui.form input[type=text],.ui.form input[type=time],.ui.form input[type=url]{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;margin:0;outline:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);line-height:1.21428571em;padding:.67857143em 1em;font-size:1em;background:#fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);border-radius:.28571429rem;-webkit-box-shadow:0 0 0 0 transparent inset;box-shadow:0 0 0 0 transparent inset;-webkit-transition:color .1s ease,border-color .1s ease;transition:color .1s ease,border-color .1s ease}.ui.form textarea,.ui.input textarea{margin:0;-webkit-appearance:none;-webkit-tap-highlight-color:rgba(255,255,255,0);padding:.78571429em 1em;background:#fff;border:1px solid rgba(34,36,38,.15);outline:0;color:rgba(0,0,0,.87);border-radius:.28571429rem;-webkit-box-shadow:0 0 0 0 transparent inset;box-shadow:0 0 0 0 transparent inset;-webkit-transition:color .1s ease,border-color .1s ease;transition:color .1s ease,border-color .1s ease;font-size:1em;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;line-height:1.2857;resize:vertical}.ui.form textarea:not([rows]){height:12em;min-height:8em;max-height:24em}.ui.form input[type=checkbox],.ui.form textarea{vertical-align:top}.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) label+.ui.ui.checkbox{margin-top:.7em}.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.checkbox{margin-top:2.41428571em}.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.toggle.checkbox{margin-top:2.21428571em}.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.slider.checkbox{margin-top:2.61428571em}.ui.ui.form .field .fields .field:not(:only-child) .ui.checkbox{margin-top:.6em}.ui.ui.form .field .fields .field:not(:only-child) .ui.toggle.checkbox{margin-top:.5em}.ui.ui.form .field .fields .field:not(:only-child) .ui.slider.checkbox{margin-top:.7em}.ui.form .field .transparent.input:not(.icon) input,.ui.form .field input.transparent,.ui.form .field textarea.transparent{padding:.67857143em 1em}.ui.form .field input.transparent,.ui.form .field textarea.transparent{border-color:transparent!important;background-color:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.form input.attached{width:auto}.ui.form select{display:block;height:auto;width:100%;background:#fff;border:1px solid rgba(34,36,38,.15);border-radius:.28571429rem;-webkit-box-shadow:0 0 0 0 transparent inset;box-shadow:0 0 0 0 transparent inset;padding:.62em 1em;color:rgba(0,0,0,.87);-webkit-transition:color .1s ease,border-color .1s ease;transition:color .1s ease,border-color .1s ease}.ui.form .field>.selection.dropdown{min-width:auto;width:100%}.ui.form .field>.selection.dropdown>.dropdown.icon{float:right}.ui.form .inline.field>.selection.dropdown,.ui.form .inline.fields .field>.selection.dropdown{width:auto}.ui.form .inline.field>.selection.dropdown>.dropdown.icon,.ui.form .inline.fields .field>.selection.dropdown>.dropdown.icon{float:none}.ui.form .field .ui.input,.ui.form .fields .field .ui.input,.ui.form .wide.field .ui.input{width:100%}.ui.form .inline.field:not(.wide) .ui.input,.ui.form .inline.fields .field:not(.wide) .ui.input{width:auto;vertical-align:middle}.ui.form .field .ui.input input,.ui.form .fields .field .ui.input input{width:auto}.ui.form .eight.fields .ui.input input,.ui.form .five.fields .ui.input input,.ui.form .four.fields .ui.input input,.ui.form .nine.fields .ui.input input,.ui.form .seven.fields .ui.input input,.ui.form .six.fields .ui.input input,.ui.form .ten.fields .ui.input input,.ui.form .three.fields .ui.input input,.ui.form .two.fields .ui.input input,.ui.form .wide.field .ui.input input{-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;width:0}.ui.form .error.message,.ui.form .error.message:empty{display:none}.ui.form .info.message,.ui.form .info.message:empty{display:none}.ui.form .success.message,.ui.form .success.message:empty{display:none}.ui.form .warning.message,.ui.form .warning.message:empty{display:none}.ui.form .message:first-child{margin-top:0}.ui.form .field .prompt.label{white-space:normal;background:#fff!important;border:1px solid #e0b4b4!important;color:#9f3a38!important}.ui.form .inline.field .prompt,.ui.form .inline.fields .field .prompt{vertical-align:top;margin:-.25em 0 -.5em .5em}.ui.form .inline.field .prompt:before,.ui.form .inline.fields .field .prompt:before{border-width:0 0 1px 1px;bottom:auto;right:auto;top:50%;left:0}.ui.form .field.field input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px ivory inset!important;box-shadow:0 0 0 100px ivory inset!important;border-color:#e5dfa1!important}.ui.form .field.field input:-webkit-autofill:focus{-webkit-box-shadow:0 0 0 100px ivory inset!important;box-shadow:0 0 0 100px ivory inset!important;border-color:#d5c315!important}.ui.form ::-webkit-input-placeholder{color:rgba(191,191,191,.87)}.ui.form :-ms-input-placeholder{color:rgba(191,191,191,.87)!important}.ui.form ::-moz-placeholder{color:rgba(191,191,191,.87)}.ui.form :focus::-webkit-input-placeholder{color:rgba(115,115,115,.87)}.ui.form :focus:-ms-input-placeholder{color:rgba(115,115,115,.87)!important}.ui.form :focus::-moz-placeholder{color:rgba(115,115,115,.87)}.ui.form input:not([type]):focus,.ui.form input[type=date]:focus,.ui.form input[type=datetime-local]:focus,.ui.form input[type=email]:focus,.ui.form input[type=file]:focus,.ui.form input[type=number]:focus,.ui.form input[type=password]:focus,.ui.form input[type=search]:focus,.ui.form input[type=tel]:focus,.ui.form input[type=text]:focus,.ui.form input[type=time]:focus,.ui.form input[type=url]:focus{color:rgba(0,0,0,.95);border-color:#85b7d9;border-radius:.28571429rem;background:#fff;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.35) inset;box-shadow:0 0 0 0 rgba(34,36,38,.35) inset}.ui.form .ui.action.input:not(.left) input:not([type]):focus,.ui.form .ui.action.input:not(.left) input[type=date]:focus,.ui.form .ui.action.input:not(.left) input[type=datetime-local]:focus,.ui.form .ui.action.input:not(.left) input[type=email]:focus,.ui.form .ui.action.input:not(.left) input[type=file]:focus,.ui.form .ui.action.input:not(.left) input[type=number]:focus,.ui.form .ui.action.input:not(.left) input[type=password]:focus,.ui.form .ui.action.input:not(.left) input[type=search]:focus,.ui.form .ui.action.input:not(.left) input[type=tel]:focus,.ui.form .ui.action.input:not(.left) input[type=text]:focus,.ui.form .ui.action.input:not(.left) input[type=time]:focus,.ui.form .ui.action.input:not(.left) input[type=url]:focus{border-top-right-radius:0;border-bottom-right-radius:0}.ui.form .ui.action.input.left input:not([type]),.ui.form .ui.action.input.left input[type=date],.ui.form .ui.action.input.left input[type=datetime-local],.ui.form .ui.action.input.left input[type=email],.ui.form .ui.action.input.left input[type=file],.ui.form .ui.action.input.left input[type=number],.ui.form .ui.action.input.left input[type=password],.ui.form .ui.action.input.left input[type=search],.ui.form .ui.action.input.left input[type=tel],.ui.form .ui.action.input.left input[type=text],.ui.form .ui.action.input.left input[type=time],.ui.form .ui.action.input.left input[type=url]{border-bottom-left-radius:0;border-top-left-radius:0}.ui.form textarea:focus{color:rgba(0,0,0,.95);border-color:#85b7d9;border-radius:.28571429rem;background:#fff;-webkit-box-shadow:0 0 0 0 rgba(34,36,38,.35) inset;box-shadow:0 0 0 0 rgba(34,36,38,.35) inset;-webkit-appearance:none}.ui.form.error .error.message:not(:empty){display:block}.ui.form.error .compact.error.message:not(:empty){display:inline-block}.ui.form.error .icon.error.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.form .field.error .error.message:not(:empty),.ui.form .fields.error .error.message:not(:empty){display:block}.ui.form .field.error .compact.error.message:not(:empty),.ui.form .fields.error .compact.error.message:not(:empty){display:inline-block}.ui.form .field.error .icon.error.message:not(:empty),.ui.form .fields.error .icon.error.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.form .field.error .input,.ui.ui.form .field.error label,.ui.ui.form .fields.error .field .input,.ui.ui.form .fields.error .field label{color:#9f3a38}.ui.form .field.error .corner.label,.ui.form .fields.error .field .corner.label{border-color:#9f3a38;color:#fff}.ui.form .field.error input:not([type]),.ui.form .field.error input[type=date],.ui.form .field.error input[type=datetime-local],.ui.form .field.error input[type=email],.ui.form .field.error input[type=file],.ui.form .field.error input[type=number],.ui.form .field.error input[type=password],.ui.form .field.error input[type=search],.ui.form .field.error input[type=tel],.ui.form .field.error input[type=text],.ui.form .field.error input[type=time],.ui.form .field.error input[type=url],.ui.form .field.error select,.ui.form .field.error textarea,.ui.form .fields.error .field input:not([type]),.ui.form .fields.error .field input[type=date],.ui.form .fields.error .field input[type=datetime-local],.ui.form .fields.error .field input[type=email],.ui.form .fields.error .field input[type=file],.ui.form .fields.error .field input[type=number],.ui.form .fields.error .field input[type=password],.ui.form .fields.error .field input[type=search],.ui.form .fields.error .field input[type=tel],.ui.form .fields.error .field input[type=text],.ui.form .fields.error .field input[type=time],.ui.form .fields.error .field input[type=url],.ui.form .fields.error .field select,.ui.form .fields.error .field textarea{color:#9f3a38;background:#fff6f6;border-color:#e0b4b4;border-radius:'';-webkit-box-shadow:none;box-shadow:none}.ui.form .field.error input:not([type]):focus,.ui.form .field.error input[type=date]:focus,.ui.form .field.error input[type=datetime-local]:focus,.ui.form .field.error input[type=email]:focus,.ui.form .field.error input[type=file]:focus,.ui.form .field.error input[type=number]:focus,.ui.form .field.error input[type=password]:focus,.ui.form .field.error input[type=search]:focus,.ui.form .field.error input[type=tel]:focus,.ui.form .field.error input[type=text]:focus,.ui.form .field.error input[type=time]:focus,.ui.form .field.error input[type=url]:focus,.ui.form .field.error select:focus,.ui.form .field.error textarea:focus{background:#fff6f6;border-color:#e0b4b4;color:#9f3a38;-webkit-box-shadow:none;box-shadow:none}.ui.form .field.error select{-webkit-appearance:menulist-button}.ui.form .field.error .transparent.input input,.ui.form .field.error .transparent.input textarea,.ui.form .field.error input.transparent,.ui.form .field.error textarea.transparent{background-color:#fff6f6;color:#9f3a38}.ui.form .error.error input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #fffaf0 inset!important;box-shadow:0 0 0 100px #fffaf0 inset!important;border-color:#e0b4b4}.ui.form .error ::-webkit-input-placeholder{color:#e7bdbc}.ui.form .error :-ms-input-placeholder{color:#e7bdbc}.ui.form .error ::-moz-placeholder{color:#e7bdbc}.ui.form .error :focus::-webkit-input-placeholder{color:#da9796}.ui.form .error :focus:-ms-input-placeholder{color:#da9796}.ui.form .error :focus::-moz-placeholder{color:#da9796}.ui.form .field.error .ui.dropdown,.ui.form .field.error .ui.dropdown .item,.ui.form .field.error .ui.dropdown .text,.ui.form .fields.error .field .ui.dropdown,.ui.form .fields.error .field .ui.dropdown .item{background:#fff6f6;color:#9f3a38}.ui.form .field.error .ui.dropdown,.ui.form .fields.error .field .ui.dropdown{border-color:#e0b4b4!important}.ui.form .field.error .ui.dropdown:hover,.ui.form .fields.error .field .ui.dropdown:hover{border-color:#e0b4b4!important}.ui.form .field.error .ui.dropdown:hover .menu,.ui.form .fields.error .field .ui.dropdown:hover .menu{border-color:#e0b4b4}.ui.form .field.error .ui.multiple.selection.dropdown>.label,.ui.form .fields.error .field .ui.multiple.selection.dropdown>.label{background-color:#eacbcb;color:#9f3a38}.ui.form .field.error .ui.dropdown .menu .item:hover,.ui.form .fields.error .field .ui.dropdown .menu .item:hover{background-color:#fbe7e7}.ui.form .field.error .ui.dropdown .menu .selected.item,.ui.form .fields.error .field .ui.dropdown .menu .selected.item{background-color:#fbe7e7}.ui.form .field.error .ui.dropdown .menu .active.item,.ui.form .fields.error .field .ui.dropdown .menu .active.item{background-color:#fdcfcf}.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box,.ui.form .field.error .checkbox:not(.toggle):not(.slider) label,.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box,.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label{color:#9f3a38}.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .field.error .checkbox:not(.toggle):not(.slider) label:before,.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label:before{background:#fff6f6;border-color:#e0b4b4}.ui.form .field.error .checkbox .box:after,.ui.form .field.error .checkbox label:after,.ui.form .fields.error .field .checkbox .box:after,.ui.form .fields.error .field .checkbox label:after{color:#9f3a38}.ui.form.info .info.message:not(:empty){display:block}.ui.form.info .compact.info.message:not(:empty){display:inline-block}.ui.form.info .icon.info.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.form .field.info .info.message:not(:empty),.ui.form .fields.info .info.message:not(:empty){display:block}.ui.form .field.info .compact.info.message:not(:empty),.ui.form .fields.info .compact.info.message:not(:empty){display:inline-block}.ui.form .field.info .icon.info.message:not(:empty),.ui.form .fields.info .icon.info.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.form .field.info .input,.ui.ui.form .field.info label,.ui.ui.form .fields.info .field .input,.ui.ui.form .fields.info .field label{color:#276f86}.ui.form .field.info .corner.label,.ui.form .fields.info .field .corner.label{border-color:#276f86;color:#fff}.ui.form .field.info input:not([type]),.ui.form .field.info input[type=date],.ui.form .field.info input[type=datetime-local],.ui.form .field.info input[type=email],.ui.form .field.info input[type=file],.ui.form .field.info input[type=number],.ui.form .field.info input[type=password],.ui.form .field.info input[type=search],.ui.form .field.info input[type=tel],.ui.form .field.info input[type=text],.ui.form .field.info input[type=time],.ui.form .field.info input[type=url],.ui.form .field.info select,.ui.form .field.info textarea,.ui.form .fields.info .field input:not([type]),.ui.form .fields.info .field input[type=date],.ui.form .fields.info .field input[type=datetime-local],.ui.form .fields.info .field input[type=email],.ui.form .fields.info .field input[type=file],.ui.form .fields.info .field input[type=number],.ui.form .fields.info .field input[type=password],.ui.form .fields.info .field input[type=search],.ui.form .fields.info .field input[type=tel],.ui.form .fields.info .field input[type=text],.ui.form .fields.info .field input[type=time],.ui.form .fields.info .field input[type=url],.ui.form .fields.info .field select,.ui.form .fields.info .field textarea{color:#276f86;background:#f8ffff;border-color:#a9d5de;border-radius:'';-webkit-box-shadow:none;box-shadow:none}.ui.form .field.info input:not([type]):focus,.ui.form .field.info input[type=date]:focus,.ui.form .field.info input[type=datetime-local]:focus,.ui.form .field.info input[type=email]:focus,.ui.form .field.info input[type=file]:focus,.ui.form .field.info input[type=number]:focus,.ui.form .field.info input[type=password]:focus,.ui.form .field.info input[type=search]:focus,.ui.form .field.info input[type=tel]:focus,.ui.form .field.info input[type=text]:focus,.ui.form .field.info input[type=time]:focus,.ui.form .field.info input[type=url]:focus,.ui.form .field.info select:focus,.ui.form .field.info textarea:focus{background:#f8ffff;border-color:#a9d5de;color:#276f86;-webkit-box-shadow:none;box-shadow:none}.ui.form .field.info select{-webkit-appearance:menulist-button}.ui.form .field.info .transparent.input input,.ui.form .field.info .transparent.input textarea,.ui.form .field.info input.transparent,.ui.form .field.info textarea.transparent{background-color:#f8ffff;color:#276f86}.ui.form .info.info input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #f0faff inset!important;box-shadow:0 0 0 100px #f0faff inset!important;border-color:#b3e0e0}.ui.form .info ::-webkit-input-placeholder{color:#98cfe1}.ui.form .info :-ms-input-placeholder{color:#98cfe1}.ui.form .info ::-moz-placeholder{color:#98cfe1}.ui.form .info :focus::-webkit-input-placeholder{color:#70bdd6}.ui.form .info :focus:-ms-input-placeholder{color:#70bdd6}.ui.form .info :focus::-moz-placeholder{color:#70bdd6}.ui.form .field.info .ui.dropdown,.ui.form .field.info .ui.dropdown .item,.ui.form .field.info .ui.dropdown .text,.ui.form .fields.info .field .ui.dropdown,.ui.form .fields.info .field .ui.dropdown .item{background:#f8ffff;color:#276f86}.ui.form .field.info .ui.dropdown,.ui.form .fields.info .field .ui.dropdown{border-color:#a9d5de!important}.ui.form .field.info .ui.dropdown:hover,.ui.form .fields.info .field .ui.dropdown:hover{border-color:#a9d5de!important}.ui.form .field.info .ui.dropdown:hover .menu,.ui.form .fields.info .field .ui.dropdown:hover .menu{border-color:#a9d5de}.ui.form .field.info .ui.multiple.selection.dropdown>.label,.ui.form .fields.info .field .ui.multiple.selection.dropdown>.label{background-color:#cce3ea;color:#276f86}.ui.form .field.info .ui.dropdown .menu .item:hover,.ui.form .fields.info .field .ui.dropdown .menu .item:hover{background-color:#e9f2fb}.ui.form .field.info .ui.dropdown .menu .selected.item,.ui.form .fields.info .field .ui.dropdown .menu .selected.item{background-color:#e9f2fb}.ui.form .field.info .ui.dropdown .menu .active.item,.ui.form .fields.info .field .ui.dropdown .menu .active.item{background-color:#cef1fd}.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box,.ui.form .field.info .checkbox:not(.toggle):not(.slider) label,.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box,.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label{color:#276f86}.ui.form .field.info .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .field.info .checkbox:not(.toggle):not(.slider) label:before,.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .fields.info .field .checkbox:not(.toggle):not(.slider) label:before{background:#f8ffff;border-color:#a9d5de}.ui.form .field.info .checkbox .box:after,.ui.form .field.info .checkbox label:after,.ui.form .fields.info .field .checkbox .box:after,.ui.form .fields.info .field .checkbox label:after{color:#276f86}.ui.form.success .success.message:not(:empty){display:block}.ui.form.success .compact.success.message:not(:empty){display:inline-block}.ui.form.success .icon.success.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.form .field.success .success.message:not(:empty),.ui.form .fields.success .success.message:not(:empty){display:block}.ui.form .field.success .compact.success.message:not(:empty),.ui.form .fields.success .compact.success.message:not(:empty){display:inline-block}.ui.form .field.success .icon.success.message:not(:empty),.ui.form .fields.success .icon.success.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.form .field.success .input,.ui.ui.form .field.success label,.ui.ui.form .fields.success .field .input,.ui.ui.form .fields.success .field label{color:#2c662d}.ui.form .field.success .corner.label,.ui.form .fields.success .field .corner.label{border-color:#2c662d;color:#fff}.ui.form .field.success input:not([type]),.ui.form .field.success input[type=date],.ui.form .field.success input[type=datetime-local],.ui.form .field.success input[type=email],.ui.form .field.success input[type=file],.ui.form .field.success input[type=number],.ui.form .field.success input[type=password],.ui.form .field.success input[type=search],.ui.form .field.success input[type=tel],.ui.form .field.success input[type=text],.ui.form .field.success input[type=time],.ui.form .field.success input[type=url],.ui.form .field.success select,.ui.form .field.success textarea,.ui.form .fields.success .field input:not([type]),.ui.form .fields.success .field input[type=date],.ui.form .fields.success .field input[type=datetime-local],.ui.form .fields.success .field input[type=email],.ui.form .fields.success .field input[type=file],.ui.form .fields.success .field input[type=number],.ui.form .fields.success .field input[type=password],.ui.form .fields.success .field input[type=search],.ui.form .fields.success .field input[type=tel],.ui.form .fields.success .field input[type=text],.ui.form .fields.success .field input[type=time],.ui.form .fields.success .field input[type=url],.ui.form .fields.success .field select,.ui.form .fields.success .field textarea{color:#2c662d;background:#fcfff5;border-color:#a3c293;border-radius:'';-webkit-box-shadow:none;box-shadow:none}.ui.form .field.success input:not([type]):focus,.ui.form .field.success input[type=date]:focus,.ui.form .field.success input[type=datetime-local]:focus,.ui.form .field.success input[type=email]:focus,.ui.form .field.success input[type=file]:focus,.ui.form .field.success input[type=number]:focus,.ui.form .field.success input[type=password]:focus,.ui.form .field.success input[type=search]:focus,.ui.form .field.success input[type=tel]:focus,.ui.form .field.success input[type=text]:focus,.ui.form .field.success input[type=time]:focus,.ui.form .field.success input[type=url]:focus,.ui.form .field.success select:focus,.ui.form .field.success textarea:focus{background:#fcfff5;border-color:#a3c293;color:#2c662d;-webkit-box-shadow:none;box-shadow:none}.ui.form .field.success select{-webkit-appearance:menulist-button}.ui.form .field.success .transparent.input input,.ui.form .field.success .transparent.input textarea,.ui.form .field.success input.transparent,.ui.form .field.success textarea.transparent{background-color:#fcfff5;color:#2c662d}.ui.form .success.success input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #f0fff0 inset!important;box-shadow:0 0 0 100px #f0fff0 inset!important;border-color:#bee0b3}.ui.form .success ::-webkit-input-placeholder{color:#8fcf90}.ui.form .success :-ms-input-placeholder{color:#8fcf90}.ui.form .success ::-moz-placeholder{color:#8fcf90}.ui.form .success :focus::-webkit-input-placeholder{color:#6cbf6d}.ui.form .success :focus:-ms-input-placeholder{color:#6cbf6d}.ui.form .success :focus::-moz-placeholder{color:#6cbf6d}.ui.form .field.success .ui.dropdown,.ui.form .field.success .ui.dropdown .item,.ui.form .field.success .ui.dropdown .text,.ui.form .fields.success .field .ui.dropdown,.ui.form .fields.success .field .ui.dropdown .item{background:#fcfff5;color:#2c662d}.ui.form .field.success .ui.dropdown,.ui.form .fields.success .field .ui.dropdown{border-color:#a3c293!important}.ui.form .field.success .ui.dropdown:hover,.ui.form .fields.success .field .ui.dropdown:hover{border-color:#a3c293!important}.ui.form .field.success .ui.dropdown:hover .menu,.ui.form .fields.success .field .ui.dropdown:hover .menu{border-color:#a3c293}.ui.form .field.success .ui.multiple.selection.dropdown>.label,.ui.form .fields.success .field .ui.multiple.selection.dropdown>.label{background-color:#cceacc;color:#2c662d}.ui.form .field.success .ui.dropdown .menu .item:hover,.ui.form .fields.success .field .ui.dropdown .menu .item:hover{background-color:#e9fbe9}.ui.form .field.success .ui.dropdown .menu .selected.item,.ui.form .fields.success .field .ui.dropdown .menu .selected.item{background-color:#e9fbe9}.ui.form .field.success .ui.dropdown .menu .active.item,.ui.form .fields.success .field .ui.dropdown .menu .active.item{background-color:#dafdce}.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box,.ui.form .field.success .checkbox:not(.toggle):not(.slider) label,.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box,.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label{color:#2c662d}.ui.form .field.success .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .field.success .checkbox:not(.toggle):not(.slider) label:before,.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .fields.success .field .checkbox:not(.toggle):not(.slider) label:before{background:#fcfff5;border-color:#a3c293}.ui.form .field.success .checkbox .box:after,.ui.form .field.success .checkbox label:after,.ui.form .fields.success .field .checkbox .box:after,.ui.form .fields.success .field .checkbox label:after{color:#2c662d}.ui.form.warning .warning.message:not(:empty){display:block}.ui.form.warning .compact.warning.message:not(:empty){display:inline-block}.ui.form.warning .icon.warning.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.form .field.warning .warning.message:not(:empty),.ui.form .fields.warning .warning.message:not(:empty){display:block}.ui.form .field.warning .compact.warning.message:not(:empty),.ui.form .fields.warning .compact.warning.message:not(:empty){display:inline-block}.ui.form .field.warning .icon.warning.message:not(:empty),.ui.form .fields.warning .icon.warning.message:not(:empty){display:-webkit-box;display:-ms-flexbox;display:flex}.ui.ui.form .field.warning .input,.ui.ui.form .field.warning label,.ui.ui.form .fields.warning .field .input,.ui.ui.form .fields.warning .field label{color:#573a08}.ui.form .field.warning .corner.label,.ui.form .fields.warning .field .corner.label{border-color:#573a08;color:#fff}.ui.form .field.warning input:not([type]),.ui.form .field.warning input[type=date],.ui.form .field.warning input[type=datetime-local],.ui.form .field.warning input[type=email],.ui.form .field.warning input[type=file],.ui.form .field.warning input[type=number],.ui.form .field.warning input[type=password],.ui.form .field.warning input[type=search],.ui.form .field.warning input[type=tel],.ui.form .field.warning input[type=text],.ui.form .field.warning input[type=time],.ui.form .field.warning input[type=url],.ui.form .field.warning select,.ui.form .field.warning textarea,.ui.form .fields.warning .field input:not([type]),.ui.form .fields.warning .field input[type=date],.ui.form .fields.warning .field input[type=datetime-local],.ui.form .fields.warning .field input[type=email],.ui.form .fields.warning .field input[type=file],.ui.form .fields.warning .field input[type=number],.ui.form .fields.warning .field input[type=password],.ui.form .fields.warning .field input[type=search],.ui.form .fields.warning .field input[type=tel],.ui.form .fields.warning .field input[type=text],.ui.form .fields.warning .field input[type=time],.ui.form .fields.warning .field input[type=url],.ui.form .fields.warning .field select,.ui.form .fields.warning .field textarea{color:#573a08;background:#fffaf3;border-color:#c9ba9b;border-radius:'';-webkit-box-shadow:none;box-shadow:none}.ui.form .field.warning input:not([type]):focus,.ui.form .field.warning input[type=date]:focus,.ui.form .field.warning input[type=datetime-local]:focus,.ui.form .field.warning input[type=email]:focus,.ui.form .field.warning input[type=file]:focus,.ui.form .field.warning input[type=number]:focus,.ui.form .field.warning input[type=password]:focus,.ui.form .field.warning input[type=search]:focus,.ui.form .field.warning input[type=tel]:focus,.ui.form .field.warning input[type=text]:focus,.ui.form .field.warning input[type=time]:focus,.ui.form .field.warning input[type=url]:focus,.ui.form .field.warning select:focus,.ui.form .field.warning textarea:focus{background:#fffaf3;border-color:#c9ba9b;color:#573a08;-webkit-box-shadow:none;box-shadow:none}.ui.form .field.warning select{-webkit-appearance:menulist-button}.ui.form .field.warning .transparent.input input,.ui.form .field.warning .transparent.input textarea,.ui.form .field.warning input.transparent,.ui.form .field.warning textarea.transparent{background-color:#fffaf3;color:#573a08}.ui.form .warning.warning input:-webkit-autofill{-webkit-box-shadow:0 0 0 100px #ffffe0 inset!important;box-shadow:0 0 0 100px #ffffe0 inset!important;border-color:#e0e0b3}.ui.form .warning ::-webkit-input-placeholder{color:#edad3e}.ui.form .warning :-ms-input-placeholder{color:#edad3e}.ui.form .warning ::-moz-placeholder{color:#edad3e}.ui.form .warning :focus::-webkit-input-placeholder{color:#e39715}.ui.form .warning :focus:-ms-input-placeholder{color:#e39715}.ui.form .warning :focus::-moz-placeholder{color:#e39715}.ui.form .field.warning .ui.dropdown,.ui.form .field.warning .ui.dropdown .item,.ui.form .field.warning .ui.dropdown .text,.ui.form .fields.warning .field .ui.dropdown,.ui.form .fields.warning .field .ui.dropdown .item{background:#fffaf3;color:#573a08}.ui.form .field.warning .ui.dropdown,.ui.form .fields.warning .field .ui.dropdown{border-color:#c9ba9b!important}.ui.form .field.warning .ui.dropdown:hover,.ui.form .fields.warning .field .ui.dropdown:hover{border-color:#c9ba9b!important}.ui.form .field.warning .ui.dropdown:hover .menu,.ui.form .fields.warning .field .ui.dropdown:hover .menu{border-color:#c9ba9b}.ui.form .field.warning .ui.multiple.selection.dropdown>.label,.ui.form .fields.warning .field .ui.multiple.selection.dropdown>.label{background-color:#eaeacc;color:#573a08}.ui.form .field.warning .ui.dropdown .menu .item:hover,.ui.form .fields.warning .field .ui.dropdown .menu .item:hover{background-color:#fbfbe9}.ui.form .field.warning .ui.dropdown .menu .selected.item,.ui.form .fields.warning .field .ui.dropdown .menu .selected.item{background-color:#fbfbe9}.ui.form .field.warning .ui.dropdown .menu .active.item,.ui.form .fields.warning .field .ui.dropdown .menu .active.item{background-color:#fdfdce}.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box,.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label,.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box,.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label{color:#573a08}.ui.form .field.warning .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .field.warning .checkbox:not(.toggle):not(.slider) label:before,.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) .box:before,.ui.form .fields.warning .field .checkbox:not(.toggle):not(.slider) label:before{background:#fffaf3;border-color:#c9ba9b}.ui.form .field.warning .checkbox .box:after,.ui.form .field.warning .checkbox label:after,.ui.form .fields.warning .field .checkbox .box:after,.ui.form .fields.warning .field .checkbox label:after{color:#573a08}.ui.form .disabled.field,.ui.form .disabled.fields .field,.ui.form .field :disabled{pointer-events:none;opacity:.45}.ui.form .field.disabled>label,.ui.form .fields.disabled>label{opacity:.45}.ui.form .field.disabled :disabled{opacity:1}.ui.loading.form{position:relative;cursor:default;pointer-events:none}.ui.loading.form:before{position:absolute;content:'';top:0;left:0;background:rgba(255,255,255,.8);width:100%;height:100%;z-index:100}.ui.loading.form.segments:before{border-radius:.28571429rem}.ui.loading.form:after{position:absolute;content:'';top:50%;left:50%;margin:-1.5em 0 0 -1.5em;width:3em;height:3em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent;visibility:visible;z-index:101}.ui.form .required.field>.checkbox:after,.ui.form .required.field>label:after,.ui.form .required.fields.grouped>label:after,.ui.form .required.fields:not(.grouped)>.field>.checkbox:after,.ui.form .required.fields:not(.grouped)>.field>label:after,.ui.form label.required:after{margin:-.2em 0 0 .2em;content:'*';color:#db2828}.ui.form .required.field>label:after,.ui.form .required.fields.grouped>label:after,.ui.form .required.fields:not(.grouped)>.field>label:after,.ui.form label.required:after{display:inline-block;vertical-align:top}.ui.form .required.field>.checkbox:after,.ui.form .required.fields:not(.grouped)>.field>.checkbox:after{position:absolute;top:0;left:100%}.ui.form .inverted.segment .ui.checkbox .box,.ui.form .inverted.segment .ui.checkbox label,.ui.form .inverted.segment label,.ui.inverted.form .inline.field>label,.ui.inverted.form .inline.field>p,.ui.inverted.form .inline.fields .field>label,.ui.inverted.form .inline.fields .field>p,.ui.inverted.form .inline.fields>label,.ui.inverted.form .ui.checkbox .box,.ui.inverted.form .ui.checkbox label,.ui.inverted.form label{color:rgba(255,255,255,.9)}.ui.inverted.loading.form{color:#fff}.ui.inverted.loading.form:before{background:rgba(0,0,0,.85)}.ui.inverted.form input:not([type]),.ui.inverted.form input[type=date],.ui.inverted.form input[type=datetime-local],.ui.inverted.form input[type=email],.ui.inverted.form input[type=file],.ui.inverted.form input[type=number],.ui.inverted.form input[type=password],.ui.inverted.form input[type=search],.ui.inverted.form input[type=tel],.ui.inverted.form input[type=text],.ui.inverted.form input[type=time],.ui.inverted.form input[type=url]{background:#fff;border-color:rgba(255,255,255,.1);color:rgba(0,0,0,.87);-webkit-box-shadow:none;box-shadow:none}.ui.form .grouped.fields{display:block;margin:0 0 1em}.ui.form .grouped.fields:last-child{margin-bottom:0}.ui.form .grouped.fields>label{margin:0 0 .28571429rem 0;color:rgba(0,0,0,.87);font-size:.92857143em;font-weight:700;text-transform:none}.ui.form .grouped.fields .field,.ui.form .grouped.inline.fields .field{display:block;margin:.5em 0;padding:0}.ui.form .fields{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin:0 -.5em 1em}.ui.form .fields>.field{-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;padding-left:.5em;padding-right:.5em}.ui.form .fields>.field:first-child{border-left:none;-webkit-box-shadow:none;box-shadow:none}.ui.form .two.fields>.field,.ui.form .two.fields>.fields{width:50%}.ui.form .three.fields>.field,.ui.form .three.fields>.fields{width:33.33333333%}.ui.form .four.fields>.field,.ui.form .four.fields>.fields{width:25%}.ui.form .five.fields>.field,.ui.form .five.fields>.fields{width:20%}.ui.form .six.fields>.field,.ui.form .six.fields>.fields{width:16.66666667%}.ui.form .seven.fields>.field,.ui.form .seven.fields>.fields{width:14.28571429%}.ui.form .eight.fields>.field,.ui.form .eight.fields>.fields{width:12.5%}.ui.form .nine.fields>.field,.ui.form .nine.fields>.fields{width:11.11111111%}.ui.form .ten.fields>.field,.ui.form .ten.fields>.fields{width:10%}@media only screen and (max-width:767.98px){.ui.form .fields{-ms-flex-wrap:wrap;flex-wrap:wrap}.ui.form:not(.unstackable) .eight.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .eight.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .five.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .five.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .four.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .four.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .nine.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .nine.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .seven.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .seven.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .six.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .six.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .ten.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .ten.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .three.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .three.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .two.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .two.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) [class*="equal width"].fields:not(.unstackable)>.field,.ui[class*="equal width"].form:not(.unstackable) .fields>.field{width:100%!important;margin:0 0 1em}}.ui.form .fields .wide.field{width:6.25%;padding-left:.5em;padding-right:.5em}.ui.form .one.wide.field{width:6.25%!important}.ui.form .two.wide.field{width:12.5%!important}.ui.form .three.wide.field{width:18.75%!important}.ui.form .four.wide.field{width:25%!important}.ui.form .five.wide.field{width:31.25%!important}.ui.form .six.wide.field{width:37.5%!important}.ui.form .seven.wide.field{width:43.75%!important}.ui.form .eight.wide.field{width:50%!important}.ui.form .nine.wide.field{width:56.25%!important}.ui.form .ten.wide.field{width:62.5%!important}.ui.form .eleven.wide.field{width:68.75%!important}.ui.form .twelve.wide.field{width:75%!important}.ui.form .thirteen.wide.field{width:81.25%!important}.ui.form .fourteen.wide.field{width:87.5%!important}.ui.form .fifteen.wide.field{width:93.75%!important}.ui.form .sixteen.wide.field{width:100%!important}@media only screen and (max-width:767.98px){.ui.form:not(.unstackable) .fields:not(.unstackable)>.eight.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.eleven.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.fifteen.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.five.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.four.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.fourteen.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.nine.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.seven.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.six.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.sixteen.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.ten.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.thirteen.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.three.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.twelve.wide.field,.ui.form:not(.unstackable) .fields:not(.unstackable)>.two.wide.field,.ui.form:not(.unstackable) .five.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .five.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .four.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .four.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .three.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .three.fields:not(.unstackable)>.fields,.ui.form:not(.unstackable) .two.fields:not(.unstackable)>.field,.ui.form:not(.unstackable) .two.fields:not(.unstackable)>.fields{width:100%!important}.ui.form .fields{margin-bottom:0}}.ui.form [class*="equal width"].fields>.field,.ui[class*="equal width"].form .fields>.field{width:100%;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.ui.form .inline.fields{margin:0 0 1em;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.form .inline.fields .field{margin:0;padding:0 1em 0 0}.ui.form .inline.field>label,.ui.form .inline.field>p,.ui.form .inline.fields .field>label,.ui.form .inline.fields .field>p,.ui.form .inline.fields>label{display:inline-block;width:auto;margin-top:0;margin-bottom:0;vertical-align:baseline;font-size:.92857143em;font-weight:700;color:rgba(0,0,0,.87);text-transform:none}.ui.form .inline.fields>label{margin:.035714em 1em 0 0}.ui.form .inline.field>input,.ui.form .inline.field>select,.ui.form .inline.fields .field>input,.ui.form .inline.fields .field>select{display:inline-block;width:auto;margin-top:0;margin-bottom:0;vertical-align:middle;font-size:1em}.ui.form .inline.field>:first-child,.ui.form .inline.fields .field>:first-child{margin:0 .85714286em 0 0}.ui.form .inline.field>:only-child,.ui.form .inline.fields .field>:only-child{margin:0}.ui.form .inline.fields .wide.field{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.form .inline.fields .wide.field>input,.ui.form .inline.fields .wide.field>select{width:100%}.ui.form,.ui.form .field .dropdown,.ui.form .field .dropdown .menu>.item{font-size:1rem}.ui.mini.form,.ui.mini.form .field .dropdown,.ui.mini.form .field .dropdown .menu>.item{font-size:.78571429rem}.ui.tiny.form,.ui.tiny.form .field .dropdown,.ui.tiny.form .field .dropdown .menu>.item{font-size:.85714286rem}.ui.small.form,.ui.small.form .field .dropdown,.ui.small.form .field .dropdown .menu>.item{font-size:.92857143rem}.ui.large.form,.ui.large.form .field .dropdown,.ui.large.form .field .dropdown .menu>.item{font-size:1.14285714rem}.ui.big.form,.ui.big.form .field .dropdown,.ui.big.form .field .dropdown .menu>.item{font-size:1.28571429rem}.ui.huge.form,.ui.huge.form .field .dropdown,.ui.huge.form .field .dropdown .menu>.item{font-size:1.42857143rem}.ui.massive.form,.ui.massive.form .field .dropdown,.ui.massive.form .field .dropdown .menu>.item{font-size:1.71428571rem} + /* + * # Fomantic-UI - Grid + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.grid{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;padding:0}.ui.grid{margin-top:-1rem;margin-bottom:-1rem;margin-left:-1rem;margin-right:-1rem}.ui.relaxed.grid{margin-left:-1.5rem;margin-right:-1.5rem}.ui[class*="very relaxed"].grid{margin-left:-2.5rem;margin-right:-2.5rem}.ui.grid+.grid{margin-top:1rem}.ui.grid>.column:not(.row),.ui.grid>.row>.column{position:relative;display:inline-block;width:6.25%;padding-left:1rem;padding-right:1rem;vertical-align:top}.ui.grid>*{padding-left:1rem;padding-right:1rem}.ui.grid>.row{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:inherit;-ms-flex-pack:inherit;justify-content:inherit;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%!important;padding:0;padding-top:1rem;padding-bottom:1rem}.ui.grid>.column:not(.row){padding-top:1rem;padding-bottom:1rem}.ui.grid>.row>.column{margin-top:0;margin-bottom:0}.ui.grid>.row>.column>img,.ui.grid>.row>img{max-width:100%}.ui.grid>.ui.grid:first-child{margin-top:0}.ui.grid>.ui.grid:last-child{margin-bottom:0}.ui.aligned.grid .column>.segment:not(.compact):not(.attached),.ui.grid .aligned.row>.column>.segment:not(.compact):not(.attached){width:100%}.ui.grid .row+.ui.divider{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;margin:1rem 1rem}.ui.grid .column+.ui.vertical.divider{height:calc(50% - 1rem)}.ui.grid>.column:last-child>.horizontal.segment,.ui.grid>.row>.column:last-child>.horizontal.segment{-webkit-box-shadow:none;box-shadow:none}@media only screen and (max-width:767.98px){.ui.page.grid{width:auto;padding-left:0;padding-right:0;margin-left:0;margin-right:0}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.page.grid{width:auto;margin-left:0;margin-right:0;padding-left:2em;padding-right:2em}}@media only screen and (min-width:992px) and (max-width:1199.98px){.ui.page.grid{width:auto;margin-left:0;margin-right:0;padding-left:3%;padding-right:3%}}@media only screen and (min-width:1200px) and (max-width:1919.98px){.ui.page.grid{width:auto;margin-left:0;margin-right:0;padding-left:15%;padding-right:15%}}@media only screen and (min-width:1920px){.ui.page.grid{width:auto;margin-left:0;margin-right:0;padding-left:23%;padding-right:23%}}.ui.grid>.column:only-child,.ui.grid>.row>.column:only-child{width:100%}.ui[class*="one column"].grid>.column:not(.row),.ui[class*="one column"].grid>.row>.column{width:100%}.ui[class*="two column"].grid>.column:not(.row),.ui[class*="two column"].grid>.row>.column{width:50%}.ui[class*="three column"].grid>.column:not(.row),.ui[class*="three column"].grid>.row>.column{width:33.33333333%}.ui[class*="four column"].grid>.column:not(.row),.ui[class*="four column"].grid>.row>.column{width:25%}.ui[class*="five column"].grid>.column:not(.row),.ui[class*="five column"].grid>.row>.column{width:20%}.ui[class*="six column"].grid>.column:not(.row),.ui[class*="six column"].grid>.row>.column{width:16.66666667%}.ui[class*="seven column"].grid>.column:not(.row),.ui[class*="seven column"].grid>.row>.column{width:14.28571429%}.ui[class*="eight column"].grid>.column:not(.row),.ui[class*="eight column"].grid>.row>.column{width:12.5%}.ui[class*="nine column"].grid>.column:not(.row),.ui[class*="nine column"].grid>.row>.column{width:11.11111111%}.ui[class*="ten column"].grid>.column:not(.row),.ui[class*="ten column"].grid>.row>.column{width:10%}.ui[class*="eleven column"].grid>.column:not(.row),.ui[class*="eleven column"].grid>.row>.column{width:9.09090909%}.ui[class*="twelve column"].grid>.column:not(.row),.ui[class*="twelve column"].grid>.row>.column{width:8.33333333%}.ui[class*="thirteen column"].grid>.column:not(.row),.ui[class*="thirteen column"].grid>.row>.column{width:7.69230769%}.ui[class*="fourteen column"].grid>.column:not(.row),.ui[class*="fourteen column"].grid>.row>.column{width:7.14285714%}.ui[class*="fifteen column"].grid>.column:not(.row),.ui[class*="fifteen column"].grid>.row>.column{width:6.66666667%}.ui[class*="sixteen column"].grid>.column:not(.row),.ui[class*="sixteen column"].grid>.row>.column{width:6.25%}.ui.grid>[class*="one column"].row>.column{width:100%!important}.ui.grid>[class*="two column"].row>.column{width:50%!important}.ui.grid>[class*="three column"].row>.column{width:33.33333333%!important}.ui.grid>[class*="four column"].row>.column{width:25%!important}.ui.grid>[class*="five column"].row>.column{width:20%!important}.ui.grid>[class*="six column"].row>.column{width:16.66666667%!important}.ui.grid>[class*="seven column"].row>.column{width:14.28571429%!important}.ui.grid>[class*="eight column"].row>.column{width:12.5%!important}.ui.grid>[class*="nine column"].row>.column{width:11.11111111%!important}.ui.grid>[class*="ten column"].row>.column{width:10%!important}.ui.grid>[class*="eleven column"].row>.column{width:9.09090909%!important}.ui.grid>[class*="twelve column"].row>.column{width:8.33333333%!important}.ui.grid>[class*="thirteen column"].row>.column{width:7.69230769%!important}.ui.grid>[class*="fourteen column"].row>.column{width:7.14285714%!important}.ui.grid>[class*="fifteen column"].row>.column{width:6.66666667%!important}.ui.grid>[class*="sixteen column"].row>.column{width:6.25%!important}.ui.celled.page.grid{-webkit-box-shadow:none;box-shadow:none}.ui.column.grid>[class*="one wide"].column,.ui.grid>.column.row>[class*="one wide"].column,.ui.grid>.row>[class*="one wide"].column,.ui.grid>[class*="one wide"].column{width:6.25%!important}.ui.column.grid>[class*="two wide"].column,.ui.grid>.column.row>[class*="two wide"].column,.ui.grid>.row>[class*="two wide"].column,.ui.grid>[class*="two wide"].column{width:12.5%!important}.ui.column.grid>[class*="three wide"].column,.ui.grid>.column.row>[class*="three wide"].column,.ui.grid>.row>[class*="three wide"].column,.ui.grid>[class*="three wide"].column{width:18.75%!important}.ui.column.grid>[class*="four wide"].column,.ui.grid>.column.row>[class*="four wide"].column,.ui.grid>.row>[class*="four wide"].column,.ui.grid>[class*="four wide"].column{width:25%!important}.ui.column.grid>[class*="five wide"].column,.ui.grid>.column.row>[class*="five wide"].column,.ui.grid>.row>[class*="five wide"].column,.ui.grid>[class*="five wide"].column{width:31.25%!important}.ui.column.grid>[class*="six wide"].column,.ui.grid>.column.row>[class*="six wide"].column,.ui.grid>.row>[class*="six wide"].column,.ui.grid>[class*="six wide"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide"].column,.ui.grid>.column.row>[class*="seven wide"].column,.ui.grid>.row>[class*="seven wide"].column,.ui.grid>[class*="seven wide"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide"].column,.ui.grid>.column.row>[class*="eight wide"].column,.ui.grid>.row>[class*="eight wide"].column,.ui.grid>[class*="eight wide"].column{width:50%!important}.ui.column.grid>[class*="nine wide"].column,.ui.grid>.column.row>[class*="nine wide"].column,.ui.grid>.row>[class*="nine wide"].column,.ui.grid>[class*="nine wide"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide"].column,.ui.grid>.column.row>[class*="ten wide"].column,.ui.grid>.row>[class*="ten wide"].column,.ui.grid>[class*="ten wide"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide"].column,.ui.grid>.column.row>[class*="eleven wide"].column,.ui.grid>.row>[class*="eleven wide"].column,.ui.grid>[class*="eleven wide"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide"].column,.ui.grid>.column.row>[class*="twelve wide"].column,.ui.grid>.row>[class*="twelve wide"].column,.ui.grid>[class*="twelve wide"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide"].column,.ui.grid>.column.row>[class*="thirteen wide"].column,.ui.grid>.row>[class*="thirteen wide"].column,.ui.grid>[class*="thirteen wide"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide"].column,.ui.grid>.column.row>[class*="fourteen wide"].column,.ui.grid>.row>[class*="fourteen wide"].column,.ui.grid>[class*="fourteen wide"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide"].column,.ui.grid>.column.row>[class*="fifteen wide"].column,.ui.grid>.row>[class*="fifteen wide"].column,.ui.grid>[class*="fifteen wide"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide"].column,.ui.grid>.column.row>[class*="sixteen wide"].column,.ui.grid>.row>[class*="sixteen wide"].column,.ui.grid>[class*="sixteen wide"].column{width:100%!important}@media only screen and (min-width:320px) and (max-width:767.98px){.ui.column.grid>[class*="one wide mobile"].column,.ui.grid>.column.row>[class*="one wide mobile"].column,.ui.grid>.row>[class*="one wide mobile"].column,.ui.grid>[class*="one wide mobile"].column{width:6.25%!important}.ui.column.grid>[class*="two wide mobile"].column,.ui.grid>.column.row>[class*="two wide mobile"].column,.ui.grid>.row>[class*="two wide mobile"].column,.ui.grid>[class*="two wide mobile"].column{width:12.5%!important}.ui.column.grid>[class*="three wide mobile"].column,.ui.grid>.column.row>[class*="three wide mobile"].column,.ui.grid>.row>[class*="three wide mobile"].column,.ui.grid>[class*="three wide mobile"].column{width:18.75%!important}.ui.column.grid>[class*="four wide mobile"].column,.ui.grid>.column.row>[class*="four wide mobile"].column,.ui.grid>.row>[class*="four wide mobile"].column,.ui.grid>[class*="four wide mobile"].column{width:25%!important}.ui.column.grid>[class*="five wide mobile"].column,.ui.grid>.column.row>[class*="five wide mobile"].column,.ui.grid>.row>[class*="five wide mobile"].column,.ui.grid>[class*="five wide mobile"].column{width:31.25%!important}.ui.column.grid>[class*="six wide mobile"].column,.ui.grid>.column.row>[class*="six wide mobile"].column,.ui.grid>.row>[class*="six wide mobile"].column,.ui.grid>[class*="six wide mobile"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide mobile"].column,.ui.grid>.column.row>[class*="seven wide mobile"].column,.ui.grid>.row>[class*="seven wide mobile"].column,.ui.grid>[class*="seven wide mobile"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide mobile"].column,.ui.grid>.column.row>[class*="eight wide mobile"].column,.ui.grid>.row>[class*="eight wide mobile"].column,.ui.grid>[class*="eight wide mobile"].column{width:50%!important}.ui.column.grid>[class*="nine wide mobile"].column,.ui.grid>.column.row>[class*="nine wide mobile"].column,.ui.grid>.row>[class*="nine wide mobile"].column,.ui.grid>[class*="nine wide mobile"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide mobile"].column,.ui.grid>.column.row>[class*="ten wide mobile"].column,.ui.grid>.row>[class*="ten wide mobile"].column,.ui.grid>[class*="ten wide mobile"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide mobile"].column,.ui.grid>.column.row>[class*="eleven wide mobile"].column,.ui.grid>.row>[class*="eleven wide mobile"].column,.ui.grid>[class*="eleven wide mobile"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide mobile"].column,.ui.grid>.column.row>[class*="twelve wide mobile"].column,.ui.grid>.row>[class*="twelve wide mobile"].column,.ui.grid>[class*="twelve wide mobile"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide mobile"].column,.ui.grid>.column.row>[class*="thirteen wide mobile"].column,.ui.grid>.row>[class*="thirteen wide mobile"].column,.ui.grid>[class*="thirteen wide mobile"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide mobile"].column,.ui.grid>.column.row>[class*="fourteen wide mobile"].column,.ui.grid>.row>[class*="fourteen wide mobile"].column,.ui.grid>[class*="fourteen wide mobile"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide mobile"].column,.ui.grid>.column.row>[class*="fifteen wide mobile"].column,.ui.grid>.row>[class*="fifteen wide mobile"].column,.ui.grid>[class*="fifteen wide mobile"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide mobile"].column,.ui.grid>.column.row>[class*="sixteen wide mobile"].column,.ui.grid>.row>[class*="sixteen wide mobile"].column,.ui.grid>[class*="sixteen wide mobile"].column{width:100%!important}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.column.grid>[class*="one wide tablet"].column,.ui.grid>.column.row>[class*="one wide tablet"].column,.ui.grid>.row>[class*="one wide tablet"].column,.ui.grid>[class*="one wide tablet"].column{width:6.25%!important}.ui.column.grid>[class*="two wide tablet"].column,.ui.grid>.column.row>[class*="two wide tablet"].column,.ui.grid>.row>[class*="two wide tablet"].column,.ui.grid>[class*="two wide tablet"].column{width:12.5%!important}.ui.column.grid>[class*="three wide tablet"].column,.ui.grid>.column.row>[class*="three wide tablet"].column,.ui.grid>.row>[class*="three wide tablet"].column,.ui.grid>[class*="three wide tablet"].column{width:18.75%!important}.ui.column.grid>[class*="four wide tablet"].column,.ui.grid>.column.row>[class*="four wide tablet"].column,.ui.grid>.row>[class*="four wide tablet"].column,.ui.grid>[class*="four wide tablet"].column{width:25%!important}.ui.column.grid>[class*="five wide tablet"].column,.ui.grid>.column.row>[class*="five wide tablet"].column,.ui.grid>.row>[class*="five wide tablet"].column,.ui.grid>[class*="five wide tablet"].column{width:31.25%!important}.ui.column.grid>[class*="six wide tablet"].column,.ui.grid>.column.row>[class*="six wide tablet"].column,.ui.grid>.row>[class*="six wide tablet"].column,.ui.grid>[class*="six wide tablet"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide tablet"].column,.ui.grid>.column.row>[class*="seven wide tablet"].column,.ui.grid>.row>[class*="seven wide tablet"].column,.ui.grid>[class*="seven wide tablet"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide tablet"].column,.ui.grid>.column.row>[class*="eight wide tablet"].column,.ui.grid>.row>[class*="eight wide tablet"].column,.ui.grid>[class*="eight wide tablet"].column{width:50%!important}.ui.column.grid>[class*="nine wide tablet"].column,.ui.grid>.column.row>[class*="nine wide tablet"].column,.ui.grid>.row>[class*="nine wide tablet"].column,.ui.grid>[class*="nine wide tablet"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide tablet"].column,.ui.grid>.column.row>[class*="ten wide tablet"].column,.ui.grid>.row>[class*="ten wide tablet"].column,.ui.grid>[class*="ten wide tablet"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide tablet"].column,.ui.grid>.column.row>[class*="eleven wide tablet"].column,.ui.grid>.row>[class*="eleven wide tablet"].column,.ui.grid>[class*="eleven wide tablet"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide tablet"].column,.ui.grid>.column.row>[class*="twelve wide tablet"].column,.ui.grid>.row>[class*="twelve wide tablet"].column,.ui.grid>[class*="twelve wide tablet"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide tablet"].column,.ui.grid>.column.row>[class*="thirteen wide tablet"].column,.ui.grid>.row>[class*="thirteen wide tablet"].column,.ui.grid>[class*="thirteen wide tablet"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide tablet"].column,.ui.grid>.column.row>[class*="fourteen wide tablet"].column,.ui.grid>.row>[class*="fourteen wide tablet"].column,.ui.grid>[class*="fourteen wide tablet"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide tablet"].column,.ui.grid>.column.row>[class*="fifteen wide tablet"].column,.ui.grid>.row>[class*="fifteen wide tablet"].column,.ui.grid>[class*="fifteen wide tablet"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide tablet"].column,.ui.grid>.column.row>[class*="sixteen wide tablet"].column,.ui.grid>.row>[class*="sixteen wide tablet"].column,.ui.grid>[class*="sixteen wide tablet"].column{width:100%!important}}@media only screen and (min-width:992px){.ui.column.grid>[class*="one wide computer"].column,.ui.grid>.column.row>[class*="one wide computer"].column,.ui.grid>.row>[class*="one wide computer"].column,.ui.grid>[class*="one wide computer"].column{width:6.25%!important}.ui.column.grid>[class*="two wide computer"].column,.ui.grid>.column.row>[class*="two wide computer"].column,.ui.grid>.row>[class*="two wide computer"].column,.ui.grid>[class*="two wide computer"].column{width:12.5%!important}.ui.column.grid>[class*="three wide computer"].column,.ui.grid>.column.row>[class*="three wide computer"].column,.ui.grid>.row>[class*="three wide computer"].column,.ui.grid>[class*="three wide computer"].column{width:18.75%!important}.ui.column.grid>[class*="four wide computer"].column,.ui.grid>.column.row>[class*="four wide computer"].column,.ui.grid>.row>[class*="four wide computer"].column,.ui.grid>[class*="four wide computer"].column{width:25%!important}.ui.column.grid>[class*="five wide computer"].column,.ui.grid>.column.row>[class*="five wide computer"].column,.ui.grid>.row>[class*="five wide computer"].column,.ui.grid>[class*="five wide computer"].column{width:31.25%!important}.ui.column.grid>[class*="six wide computer"].column,.ui.grid>.column.row>[class*="six wide computer"].column,.ui.grid>.row>[class*="six wide computer"].column,.ui.grid>[class*="six wide computer"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide computer"].column,.ui.grid>.column.row>[class*="seven wide computer"].column,.ui.grid>.row>[class*="seven wide computer"].column,.ui.grid>[class*="seven wide computer"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide computer"].column,.ui.grid>.column.row>[class*="eight wide computer"].column,.ui.grid>.row>[class*="eight wide computer"].column,.ui.grid>[class*="eight wide computer"].column{width:50%!important}.ui.column.grid>[class*="nine wide computer"].column,.ui.grid>.column.row>[class*="nine wide computer"].column,.ui.grid>.row>[class*="nine wide computer"].column,.ui.grid>[class*="nine wide computer"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide computer"].column,.ui.grid>.column.row>[class*="ten wide computer"].column,.ui.grid>.row>[class*="ten wide computer"].column,.ui.grid>[class*="ten wide computer"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide computer"].column,.ui.grid>.column.row>[class*="eleven wide computer"].column,.ui.grid>.row>[class*="eleven wide computer"].column,.ui.grid>[class*="eleven wide computer"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide computer"].column,.ui.grid>.column.row>[class*="twelve wide computer"].column,.ui.grid>.row>[class*="twelve wide computer"].column,.ui.grid>[class*="twelve wide computer"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide computer"].column,.ui.grid>.column.row>[class*="thirteen wide computer"].column,.ui.grid>.row>[class*="thirteen wide computer"].column,.ui.grid>[class*="thirteen wide computer"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide computer"].column,.ui.grid>.column.row>[class*="fourteen wide computer"].column,.ui.grid>.row>[class*="fourteen wide computer"].column,.ui.grid>[class*="fourteen wide computer"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide computer"].column,.ui.grid>.column.row>[class*="fifteen wide computer"].column,.ui.grid>.row>[class*="fifteen wide computer"].column,.ui.grid>[class*="fifteen wide computer"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide computer"].column,.ui.grid>.column.row>[class*="sixteen wide computer"].column,.ui.grid>.row>[class*="sixteen wide computer"].column,.ui.grid>[class*="sixteen wide computer"].column{width:100%!important}}@media only screen and (min-width:1200px) and (max-width:1919.98px){.ui.column.grid>[class*="one wide large screen"].column,.ui.grid>.column.row>[class*="one wide large screen"].column,.ui.grid>.row>[class*="one wide large screen"].column,.ui.grid>[class*="one wide large screen"].column{width:6.25%!important}.ui.column.grid>[class*="two wide large screen"].column,.ui.grid>.column.row>[class*="two wide large screen"].column,.ui.grid>.row>[class*="two wide large screen"].column,.ui.grid>[class*="two wide large screen"].column{width:12.5%!important}.ui.column.grid>[class*="three wide large screen"].column,.ui.grid>.column.row>[class*="three wide large screen"].column,.ui.grid>.row>[class*="three wide large screen"].column,.ui.grid>[class*="three wide large screen"].column{width:18.75%!important}.ui.column.grid>[class*="four wide large screen"].column,.ui.grid>.column.row>[class*="four wide large screen"].column,.ui.grid>.row>[class*="four wide large screen"].column,.ui.grid>[class*="four wide large screen"].column{width:25%!important}.ui.column.grid>[class*="five wide large screen"].column,.ui.grid>.column.row>[class*="five wide large screen"].column,.ui.grid>.row>[class*="five wide large screen"].column,.ui.grid>[class*="five wide large screen"].column{width:31.25%!important}.ui.column.grid>[class*="six wide large screen"].column,.ui.grid>.column.row>[class*="six wide large screen"].column,.ui.grid>.row>[class*="six wide large screen"].column,.ui.grid>[class*="six wide large screen"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide large screen"].column,.ui.grid>.column.row>[class*="seven wide large screen"].column,.ui.grid>.row>[class*="seven wide large screen"].column,.ui.grid>[class*="seven wide large screen"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide large screen"].column,.ui.grid>.column.row>[class*="eight wide large screen"].column,.ui.grid>.row>[class*="eight wide large screen"].column,.ui.grid>[class*="eight wide large screen"].column{width:50%!important}.ui.column.grid>[class*="nine wide large screen"].column,.ui.grid>.column.row>[class*="nine wide large screen"].column,.ui.grid>.row>[class*="nine wide large screen"].column,.ui.grid>[class*="nine wide large screen"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide large screen"].column,.ui.grid>.column.row>[class*="ten wide large screen"].column,.ui.grid>.row>[class*="ten wide large screen"].column,.ui.grid>[class*="ten wide large screen"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide large screen"].column,.ui.grid>.column.row>[class*="eleven wide large screen"].column,.ui.grid>.row>[class*="eleven wide large screen"].column,.ui.grid>[class*="eleven wide large screen"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide large screen"].column,.ui.grid>.column.row>[class*="twelve wide large screen"].column,.ui.grid>.row>[class*="twelve wide large screen"].column,.ui.grid>[class*="twelve wide large screen"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide large screen"].column,.ui.grid>.column.row>[class*="thirteen wide large screen"].column,.ui.grid>.row>[class*="thirteen wide large screen"].column,.ui.grid>[class*="thirteen wide large screen"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide large screen"].column,.ui.grid>.column.row>[class*="fourteen wide large screen"].column,.ui.grid>.row>[class*="fourteen wide large screen"].column,.ui.grid>[class*="fourteen wide large screen"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide large screen"].column,.ui.grid>.column.row>[class*="fifteen wide large screen"].column,.ui.grid>.row>[class*="fifteen wide large screen"].column,.ui.grid>[class*="fifteen wide large screen"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide large screen"].column,.ui.grid>.column.row>[class*="sixteen wide large screen"].column,.ui.grid>.row>[class*="sixteen wide large screen"].column,.ui.grid>[class*="sixteen wide large screen"].column{width:100%!important}}@media only screen and (min-width:1920px){.ui.column.grid>[class*="one wide widescreen"].column,.ui.grid>.column.row>[class*="one wide widescreen"].column,.ui.grid>.row>[class*="one wide widescreen"].column,.ui.grid>[class*="one wide widescreen"].column{width:6.25%!important}.ui.column.grid>[class*="two wide widescreen"].column,.ui.grid>.column.row>[class*="two wide widescreen"].column,.ui.grid>.row>[class*="two wide widescreen"].column,.ui.grid>[class*="two wide widescreen"].column{width:12.5%!important}.ui.column.grid>[class*="three wide widescreen"].column,.ui.grid>.column.row>[class*="three wide widescreen"].column,.ui.grid>.row>[class*="three wide widescreen"].column,.ui.grid>[class*="three wide widescreen"].column{width:18.75%!important}.ui.column.grid>[class*="four wide widescreen"].column,.ui.grid>.column.row>[class*="four wide widescreen"].column,.ui.grid>.row>[class*="four wide widescreen"].column,.ui.grid>[class*="four wide widescreen"].column{width:25%!important}.ui.column.grid>[class*="five wide widescreen"].column,.ui.grid>.column.row>[class*="five wide widescreen"].column,.ui.grid>.row>[class*="five wide widescreen"].column,.ui.grid>[class*="five wide widescreen"].column{width:31.25%!important}.ui.column.grid>[class*="six wide widescreen"].column,.ui.grid>.column.row>[class*="six wide widescreen"].column,.ui.grid>.row>[class*="six wide widescreen"].column,.ui.grid>[class*="six wide widescreen"].column{width:37.5%!important}.ui.column.grid>[class*="seven wide widescreen"].column,.ui.grid>.column.row>[class*="seven wide widescreen"].column,.ui.grid>.row>[class*="seven wide widescreen"].column,.ui.grid>[class*="seven wide widescreen"].column{width:43.75%!important}.ui.column.grid>[class*="eight wide widescreen"].column,.ui.grid>.column.row>[class*="eight wide widescreen"].column,.ui.grid>.row>[class*="eight wide widescreen"].column,.ui.grid>[class*="eight wide widescreen"].column{width:50%!important}.ui.column.grid>[class*="nine wide widescreen"].column,.ui.grid>.column.row>[class*="nine wide widescreen"].column,.ui.grid>.row>[class*="nine wide widescreen"].column,.ui.grid>[class*="nine wide widescreen"].column{width:56.25%!important}.ui.column.grid>[class*="ten wide widescreen"].column,.ui.grid>.column.row>[class*="ten wide widescreen"].column,.ui.grid>.row>[class*="ten wide widescreen"].column,.ui.grid>[class*="ten wide widescreen"].column{width:62.5%!important}.ui.column.grid>[class*="eleven wide widescreen"].column,.ui.grid>.column.row>[class*="eleven wide widescreen"].column,.ui.grid>.row>[class*="eleven wide widescreen"].column,.ui.grid>[class*="eleven wide widescreen"].column{width:68.75%!important}.ui.column.grid>[class*="twelve wide widescreen"].column,.ui.grid>.column.row>[class*="twelve wide widescreen"].column,.ui.grid>.row>[class*="twelve wide widescreen"].column,.ui.grid>[class*="twelve wide widescreen"].column{width:75%!important}.ui.column.grid>[class*="thirteen wide widescreen"].column,.ui.grid>.column.row>[class*="thirteen wide widescreen"].column,.ui.grid>.row>[class*="thirteen wide widescreen"].column,.ui.grid>[class*="thirteen wide widescreen"].column{width:81.25%!important}.ui.column.grid>[class*="fourteen wide widescreen"].column,.ui.grid>.column.row>[class*="fourteen wide widescreen"].column,.ui.grid>.row>[class*="fourteen wide widescreen"].column,.ui.grid>[class*="fourteen wide widescreen"].column{width:87.5%!important}.ui.column.grid>[class*="fifteen wide widescreen"].column,.ui.grid>.column.row>[class*="fifteen wide widescreen"].column,.ui.grid>.row>[class*="fifteen wide widescreen"].column,.ui.grid>[class*="fifteen wide widescreen"].column{width:93.75%!important}.ui.column.grid>[class*="sixteen wide widescreen"].column,.ui.grid>.column.row>[class*="sixteen wide widescreen"].column,.ui.grid>.row>[class*="sixteen wide widescreen"].column,.ui.grid>[class*="sixteen wide widescreen"].column{width:100%!important}}.ui.centered.grid,.ui.centered.grid>.row,.ui.grid>.centered.row{text-align:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.centered.grid>.column:not(.aligned):not(.justified):not(.row),.ui.centered.grid>.row>.column:not(.aligned):not(.justified),.ui.grid .centered.row>.column:not(.aligned):not(.justified){text-align:left}.ui.grid>.centered.column,.ui.grid>.row>.centered.column{display:block;margin-left:auto;margin-right:auto}.ui.grid>.relaxed.row>.column,.ui.relaxed.grid>.column:not(.row),.ui.relaxed.grid>.row>.column{padding-left:1.5rem;padding-right:1.5rem}.ui.grid>[class*="very relaxed"].row>.column,.ui[class*="very relaxed"].grid>.column:not(.row),.ui[class*="very relaxed"].grid>.row>.column{padding-left:2.5rem;padding-right:2.5rem}.ui.grid .relaxed.row+.ui.divider,.ui.relaxed.grid .row+.ui.divider{margin-left:1.5rem;margin-right:1.5rem}.ui.grid [class*="very relaxed"].row+.ui.divider,.ui[class*="very relaxed"].grid .row+.ui.divider{margin-left:2.5rem;margin-right:2.5rem}.ui.padded.grid:not(.vertically):not(.horizontally){margin:0!important}[class*="horizontally padded"].ui.grid{margin-left:0!important;margin-right:0!important}[class*="vertically padded"].ui.grid{margin-top:0!important;margin-bottom:0!important}.ui.grid [class*="left floated"].column{margin-right:auto}.ui.grid [class*="right floated"].column{margin-left:auto}.ui.divided.grid:not([class*="vertically divided"])>.column:not(.row),.ui.divided.grid:not([class*="vertically divided"])>.row>.column{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui[class*="vertically divided"].grid>.column:not(.row),.ui[class*="vertically divided"].grid>.row>.column{margin-top:1rem;margin-bottom:1rem;padding-top:0;padding-bottom:0}.ui[class*="vertically divided"].grid>.row{margin-top:0;margin-bottom:0}.ui.divided.grid:not([class*="vertically divided"])>.column:first-child,.ui.divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui[class*="vertically divided"].grid>.row:first-child>.column{margin-top:0}.ui.grid>.divided.row>.column{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui.grid>.divided.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui[class*="vertically divided"].grid>.row{position:relative}.ui[class*="vertically divided"].grid>.row:before{position:absolute;content:"";top:0;left:0;width:calc(100% - 2rem);height:1px;margin:0 1rem;-webkit-box-shadow:0 -1px 0 0 rgba(34,36,38,.15);box-shadow:0 -1px 0 0 rgba(34,36,38,.15)}.ui.padded.divided.grid:not(.vertically):not(.horizontally),[class*="horizontally padded"].ui.divided.grid{width:100%}.ui[class*="vertically divided"].grid>.row:first-child:before{-webkit-box-shadow:none;box-shadow:none}.ui.inverted.divided.grid:not([class*="vertically divided"])>.column:not(.row),.ui.inverted.divided.grid:not([class*="vertically divided"])>.row>.column{-webkit-box-shadow:-1px 0 0 0 rgba(255,255,255,.1);box-shadow:-1px 0 0 0 rgba(255,255,255,.1)}.ui.inverted.divided.grid:not([class*="vertically divided"])>.column:not(.row):first-child,.ui.inverted.divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui.inverted[class*="vertically divided"].grid>.row:before{-webkit-box-shadow:0 -1px 0 0 rgba(255,255,255,.1);box-shadow:0 -1px 0 0 rgba(255,255,255,.1)}.ui.relaxed[class*="vertically divided"].grid>.row:before{margin-left:1.5rem;margin-right:1.5rem;width:calc(100% - 3rem)}.ui[class*="very relaxed"][class*="vertically divided"].grid>.row:before{margin-left:2.5rem;margin-right:2.5rem;width:calc(100% - 5rem)}.ui.celled.grid{width:100%;margin:1em 0;-webkit-box-shadow:0 0 0 1px #d4d4d5;box-shadow:0 0 0 1px #d4d4d5}.ui.celled.grid>.row{width:100%!important;margin:0;padding:0;-webkit-box-shadow:0 -1px 0 0 #d4d4d5;box-shadow:0 -1px 0 0 #d4d4d5}.ui.celled.grid>.column:not(.row),.ui.celled.grid>.row>.column{-webkit-box-shadow:-1px 0 0 0 #d4d4d5;box-shadow:-1px 0 0 0 #d4d4d5}.ui.celled.grid>.column:first-child,.ui.celled.grid>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui.celled.grid>.column:not(.row),.ui.celled.grid>.row>.column{padding:1em}.ui.relaxed.celled.grid>.column:not(.row),.ui.relaxed.celled.grid>.row>.column{padding:1.5em}.ui[class*="very relaxed"].celled.grid>.column:not(.row),.ui[class*="very relaxed"].celled.grid>.row>.column{padding:2em}.ui[class*="internally celled"].grid{-webkit-box-shadow:none;box-shadow:none;margin:0}.ui[class*="internally celled"].grid>.row:first-child{-webkit-box-shadow:none;box-shadow:none}.ui[class*="internally celled"].grid>.row>.column:first-child{-webkit-box-shadow:none;box-shadow:none}.ui.grid>.row>[class*="top aligned"].column,.ui.grid>[class*="top aligned"].column:not(.row),.ui.grid>[class*="top aligned"].row>.column,.ui[class*="top aligned"].grid>.column:not(.row),.ui[class*="top aligned"].grid>.row>.column{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;vertical-align:top;-ms-flex-item-align:start!important;align-self:flex-start!important}.ui.grid>.row>[class*="middle aligned"].column,.ui.grid>[class*="middle aligned"].column:not(.row),.ui.grid>[class*="middle aligned"].row>.column,.ui[class*="middle aligned"].grid>.column:not(.row),.ui[class*="middle aligned"].grid>.row>.column{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;vertical-align:middle;-ms-flex-item-align:center!important;align-self:center!important}.ui.grid>.row>[class*="bottom aligned"].column,.ui.grid>[class*="bottom aligned"].column:not(.row),.ui.grid>[class*="bottom aligned"].row>.column,.ui[class*="bottom aligned"].grid>.column:not(.row),.ui[class*="bottom aligned"].grid>.row>.column{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;vertical-align:bottom;-ms-flex-item-align:end!important;align-self:flex-end!important}.ui.grid>.row>.stretched.column,.ui.grid>.stretched.column:not(.row),.ui.grid>.stretched.row>.column,.ui.stretched.grid>.column,.ui.stretched.grid>.row>.column{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important;-ms-flex-item-align:stretch;align-self:stretch;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.grid>.row>.stretched.column>*,.ui.grid>.stretched.column:not(.row)>*,.ui.grid>.stretched.row>.column>*,.ui.stretched.grid>.column>*,.ui.stretched.grid>.row>.column>*{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.ui.grid>.row>[class*="left aligned"].column.column,.ui.grid>[class*="left aligned"].column.column,.ui.grid>[class*="left aligned"].row>.column,.ui[class*="left aligned"].grid>.column,.ui[class*="left aligned"].grid>.row>.column{text-align:left;-ms-flex-item-align:inherit;align-self:inherit}.ui.grid>.row>[class*="center aligned"].column.column,.ui.grid>[class*="center aligned"].column.column,.ui.grid>[class*="center aligned"].row>.column,.ui[class*="center aligned"].grid>.column,.ui[class*="center aligned"].grid>.row>.column{text-align:center;-ms-flex-item-align:inherit;align-self:inherit}.ui[class*="center aligned"].grid{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.grid>.row>[class*="right aligned"].column.column,.ui.grid>[class*="right aligned"].column.column,.ui.grid>[class*="right aligned"].row>.column,.ui[class*="right aligned"].grid>.column,.ui[class*="right aligned"].grid>.row>.column{text-align:right;-ms-flex-item-align:inherit;align-self:inherit}.ui.grid>.justified.column.column,.ui.grid>.justified.row>.column,.ui.grid>.row>.justified.column.column,.ui.justified.grid>.column,.ui.justified.grid>.row>.column{text-align:justify;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.ui.grid>.primary.column,.ui.grid>.primary.row,.ui.grid>.row>.primary.column{background-color:#2185d0;color:#fff}.ui.grid>.row>.secondary.column,.ui.grid>.secondary.column,.ui.grid>.secondary.row{background-color:#1b1c1d;color:#fff}.ui.grid>.red.column,.ui.grid>.red.row,.ui.grid>.row>.red.column{background-color:#db2828;color:#fff}.ui.grid>.orange.column,.ui.grid>.orange.row,.ui.grid>.row>.orange.column{background-color:#f2711c;color:#fff}.ui.grid>.row>.yellow.column,.ui.grid>.yellow.column,.ui.grid>.yellow.row{background-color:#fbbd08;color:#fff}.ui.grid>.olive.column,.ui.grid>.olive.row,.ui.grid>.row>.olive.column{background-color:#b5cc18;color:#fff}.ui.grid>.green.column,.ui.grid>.green.row,.ui.grid>.row>.green.column{background-color:#21ba45;color:#fff}.ui.grid>.row>.teal.column,.ui.grid>.teal.column,.ui.grid>.teal.row{background-color:#00b5ad;color:#fff}.ui.grid>.blue.column,.ui.grid>.blue.row,.ui.grid>.row>.blue.column{background-color:#2185d0;color:#fff}.ui.grid>.row>.violet.column,.ui.grid>.violet.column,.ui.grid>.violet.row{background-color:#6435c9;color:#fff}.ui.grid>.purple.column,.ui.grid>.purple.row,.ui.grid>.row>.purple.column{background-color:#a333c8;color:#fff}.ui.grid>.pink.column,.ui.grid>.pink.row,.ui.grid>.row>.pink.column{background-color:#e03997;color:#fff}.ui.grid>.brown.column,.ui.grid>.brown.row,.ui.grid>.row>.brown.column{background-color:#a5673f;color:#fff}.ui.grid>.grey.column,.ui.grid>.grey.row,.ui.grid>.row>.grey.column{background-color:#767676;color:#fff}.ui.grid>.black.column,.ui.grid>.black.row,.ui.grid>.row>.black.column{background-color:#1b1c1d;color:#fff}.ui.grid>[class*="equal width"].row>.column,.ui[class*="equal width"].grid>.column:not(.row),.ui[class*="equal width"].grid>.row>.column{display:inline-block;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.ui.grid>[class*="equal width"].row>.wide.column,.ui[class*="equal width"].grid>.row>.wide.column,.ui[class*="equal width"].grid>.wide.column{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}@media only screen and (max-width:767.98px){.ui.grid>[class*="mobile reversed"].row,.ui[class*="mobile reversed"].grid,.ui[class*="mobile reversed"].grid>.row{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.ui.stackable[class*="mobile reversed"],.ui[class*="mobile vertically reversed"].grid{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"])>.column:first-child,.ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"])>.column:last-child,.ui[class*="mobile reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}.ui.grid[class*="vertically divided"][class*="mobile vertically reversed"]>.row:first-child:before{-webkit-box-shadow:0 -1px 0 0 rgba(34,36,38,.15);box-shadow:0 -1px 0 0 rgba(34,36,38,.15)}.ui.grid[class*="vertically divided"][class*="mobile vertically reversed"]>.row:last-child:before{-webkit-box-shadow:none;box-shadow:none}.ui[class*="mobile reversed"].celled.grid>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 #d4d4d5;box-shadow:-1px 0 0 0 #d4d4d5}.ui[class*="mobile reversed"].celled.grid>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.grid>[class*="tablet reversed"].row,.ui[class*="tablet reversed"].grid,.ui[class*="tablet reversed"].grid>.row{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.ui[class*="tablet vertically reversed"].grid{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"])>.column:first-child,.ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"])>.column:last-child,.ui[class*="tablet reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}.ui.grid[class*="vertically divided"][class*="tablet vertically reversed"]>.row:first-child:before{-webkit-box-shadow:0 -1px 0 0 rgba(34,36,38,.15);box-shadow:0 -1px 0 0 rgba(34,36,38,.15)}.ui.grid[class*="vertically divided"][class*="tablet vertically reversed"]>.row:last-child:before{-webkit-box-shadow:none;box-shadow:none}.ui[class*="tablet reversed"].celled.grid>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 #d4d4d5;box-shadow:-1px 0 0 0 #d4d4d5}.ui[class*="tablet reversed"].celled.grid>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}}@media only screen and (min-width:992px){.ui.grid>[class*="computer reversed"].row,.ui[class*="computer reversed"].grid,.ui[class*="computer reversed"].grid>.row{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.ui[class*="computer vertically reversed"].grid{-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse}.ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"])>.column:first-child,.ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 rgba(34,36,38,.15);box-shadow:-1px 0 0 0 rgba(34,36,38,.15)}.ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"])>.column:last-child,.ui[class*="computer reversed"].divided.grid:not([class*="vertically divided"])>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}.ui.grid[class*="vertically divided"][class*="computer vertically reversed"]>.row:first-child:before{-webkit-box-shadow:0 -1px 0 0 rgba(34,36,38,.15);box-shadow:0 -1px 0 0 rgba(34,36,38,.15)}.ui.grid[class*="vertically divided"][class*="computer vertically reversed"]>.row:last-child:before{-webkit-box-shadow:none;box-shadow:none}.ui[class*="computer reversed"].celled.grid>.row>.column:first-child{-webkit-box-shadow:-1px 0 0 0 #d4d4d5;box-shadow:-1px 0 0 0 #d4d4d5}.ui[class*="computer reversed"].celled.grid>.row>.column:last-child{-webkit-box-shadow:none;box-shadow:none}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.doubling.grid{width:auto}.ui.doubling.grid>.row,.ui.grid>.doubling.row{margin:0!important;padding:0!important}.ui.doubling.grid>.row>.column,.ui.grid>.doubling.row>.column{display:inline-block!important;padding-top:1rem!important;padding-bottom:1rem!important;-webkit-box-shadow:none!important;box-shadow:none!important;margin:0}.ui.grid>[class*="two column"].doubling.row.row>.column,.ui[class*="two column"].doubling.grid>.column:not(.row),.ui[class*="two column"].doubling.grid>.row>.column{width:100%!important}.ui.grid>[class*="three column"].doubling.row.row>.column,.ui[class*="three column"].doubling.grid>.column:not(.row),.ui[class*="three column"].doubling.grid>.row>.column{width:50%!important}.ui.grid>[class*="four column"].doubling.row.row>.column,.ui[class*="four column"].doubling.grid>.column:not(.row),.ui[class*="four column"].doubling.grid>.row>.column{width:50%!important}.ui.grid>[class*="five column"].doubling.row.row>.column,.ui[class*="five column"].doubling.grid>.column:not(.row),.ui[class*="five column"].doubling.grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="six column"].doubling.row.row>.column,.ui[class*="six column"].doubling.grid>.column:not(.row),.ui[class*="six column"].doubling.grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="seven column"].doubling.row.row>.column,.ui[class*="seven column"].doubling.grid>.column:not(.row),.ui[class*="seven column"].doubling.grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="eight column"].doubling.row.row>.column,.ui[class*="eight column"].doubling.grid>.column:not(.row),.ui[class*="eight column"].doubling.grid>.row>.column{width:25%!important}.ui.grid>[class*="nine column"].doubling.row.row>.column,.ui[class*="nine column"].doubling.grid>.column:not(.row),.ui[class*="nine column"].doubling.grid>.row>.column{width:25%!important}.ui.grid>[class*="ten column"].doubling.row.row>.column,.ui[class*="ten column"].doubling.grid>.column:not(.row),.ui[class*="ten column"].doubling.grid>.row>.column{width:20%!important}.ui.grid>[class*="eleven column"].doubling.row.row>.column,.ui[class*="eleven column"].doubling.grid>.column:not(.row),.ui[class*="eleven column"].doubling.grid>.row>.column{width:20%!important}.ui.grid>[class*="twelve column"].doubling.row.row>.column,.ui[class*="twelve column"].doubling.grid>.column:not(.row),.ui[class*="twelve column"].doubling.grid>.row>.column{width:16.66666667%!important}.ui.grid>[class*="thirteen column"].doubling.row.row>.column,.ui[class*="thirteen column"].doubling.grid>.column:not(.row),.ui[class*="thirteen column"].doubling.grid>.row>.column{width:16.66666667%!important}.ui.grid>[class*="fourteen column"].doubling.row.row>.column,.ui[class*="fourteen column"].doubling.grid>.column:not(.row),.ui[class*="fourteen column"].doubling.grid>.row>.column{width:14.28571429%!important}.ui.grid>[class*="fifteen column"].doubling.row.row>.column,.ui[class*="fifteen column"].doubling.grid>.column:not(.row),.ui[class*="fifteen column"].doubling.grid>.row>.column{width:14.28571429%!important}.ui.grid>[class*="sixteen column"].doubling.row.row>.column,.ui[class*="sixteen column"].doubling.grid>.column:not(.row),.ui[class*="sixteen column"].doubling.grid>.row>.column{width:12.5%!important}}@media only screen and (max-width:767.98px){.ui.doubling.grid>.row,.ui.grid>.doubling.row{margin:0!important;padding:0!important}.ui.doubling.grid>.row>.column,.ui.grid>.doubling.row>.column{padding-top:1rem!important;padding-bottom:1rem!important;margin:0!important;-webkit-box-shadow:none!important;box-shadow:none!important}.ui.grid>[class*="two column"].doubling:not(.stackable).row.row>.column,.ui[class*="two column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="two column"].doubling:not(.stackable).grid>.row>.column{width:100%!important}.ui.grid>[class*="three column"].doubling:not(.stackable).row.row>.column,.ui[class*="three column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="three column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="four column"].doubling:not(.stackable).row.row>.column,.ui[class*="four column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="four column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="five column"].doubling:not(.stackable).row.row>.column,.ui[class*="five column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="five column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="six column"].doubling:not(.stackable).row.row>.column,.ui[class*="six column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="six column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="seven column"].doubling:not(.stackable).row.row>.column,.ui[class*="seven column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="seven column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="eight column"].doubling:not(.stackable).row.row>.column,.ui[class*="eight column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="eight column"].doubling:not(.stackable).grid>.row>.column{width:50%!important}.ui.grid>[class*="nine column"].doubling:not(.stackable).row.row>.column,.ui[class*="nine column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="nine column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="ten column"].doubling:not(.stackable).row.row>.column,.ui[class*="ten column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="ten column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="eleven column"].doubling:not(.stackable).row.row>.column,.ui[class*="eleven column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="eleven column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="twelve column"].doubling:not(.stackable).row.row>.column,.ui[class*="twelve column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="twelve column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="thirteen column"].doubling:not(.stackable).row.row>.column,.ui[class*="thirteen column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="thirteen column"].doubling:not(.stackable).grid>.row>.column{width:33.33333333%!important}.ui.grid>[class*="fourteen column"].doubling:not(.stackable).row.row>.column,.ui[class*="fourteen column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="fourteen column"].doubling:not(.stackable).grid>.row>.column{width:25%!important}.ui.grid>[class*="fifteen column"].doubling:not(.stackable).row.row>.column,.ui[class*="fifteen column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="fifteen column"].doubling:not(.stackable).grid>.row>.column{width:25%!important}.ui.grid>[class*="sixteen column"].doubling:not(.stackable).row.row>.column,.ui[class*="sixteen column"].doubling:not(.stackable).grid>.column:not(.row),.ui[class*="sixteen column"].doubling:not(.stackable).grid>.row>.column{width:25%!important}}@media only screen and (max-width:767.98px){.ui.stackable.grid{width:auto;margin-left:0!important;margin-right:0!important}.ui.grid>.stackable.stackable.stackable.row>.column,.ui.stackable.grid>.column.grid>.column,.ui.stackable.grid>.column.row>.column,.ui.stackable.grid>.column:not(.row),.ui.stackable.grid>.row>.column,.ui.stackable.grid>.row>.wide.column,.ui.stackable.grid>.wide.column{width:100%!important;margin:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important;padding:1rem 1rem}.ui.stackable.grid:not(.vertically)>.row{margin:0;padding:0}.ui.container>.ui.stackable.grid>.column,.ui.container>.ui.stackable.grid>.row>.column{padding-left:0!important;padding-right:0!important}.ui.grid .ui.stackable.grid,.ui.segment:not(.vertical) .ui.stackable.page.grid{margin-left:-1rem!important;margin-right:-1rem!important}.ui.stackable.celled.grid>.column:not(.row):first-child,.ui.stackable.celled.grid>.row:first-child>.column:first-child,.ui.stackable.divided.grid>.column:not(.row):first-child,.ui.stackable.divided.grid>.row:first-child>.column:first-child{border-top:none!important}.ui.inverted.stackable.celled.grid>.column:not(.row),.ui.inverted.stackable.celled.grid>.row>.column,.ui.inverted.stackable.divided.grid>.column:not(.row),.ui.inverted.stackable.divided.grid>.row>.column{border-top:1px solid rgba(255,255,255,.1)}.ui.stackable.celled.grid>.column:not(.row),.ui.stackable.celled.grid>.row>.column,.ui.stackable.divided:not(.vertically).grid>.column:not(.row),.ui.stackable.divided:not(.vertically).grid>.row>.column{border-top:1px solid rgba(34,36,38,.15);-webkit-box-shadow:none!important;box-shadow:none!important;padding-top:2rem!important;padding-bottom:2rem!important}.ui.stackable.celled.grid>.row{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.stackable.divided:not(.vertically).grid>.column:not(.row),.ui.stackable.divided:not(.vertically).grid>.row>.column{padding-left:0!important;padding-right:0!important}}@media only screen and (max-width:767.98px){.ui.grid.grid.grid>.row>[class*="tablet only"].column:not(.mobile),.ui.grid.grid.grid>[class*="tablet only"].column:not(.mobile),.ui.grid.grid.grid>[class*="tablet only"].row:not(.mobile),.ui[class*="tablet only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="computer only"].column:not(.mobile),.ui.grid.grid.grid>[class*="computer only"].column:not(.mobile),.ui.grid.grid.grid>[class*="computer only"].row:not(.mobile),.ui[class*="computer only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].row:not(.mobile),.ui[class*="large screen only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].row:not(.mobile),.ui[class*="widescreen only"].grid.grid.grid:not(.mobile){display:none!important}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.grid.grid.grid>.row>[class*="mobile only"].column:not(.tablet),.ui.grid.grid.grid>[class*="mobile only"].column:not(.tablet),.ui.grid.grid.grid>[class*="mobile only"].row:not(.tablet),.ui[class*="mobile only"].grid.grid.grid:not(.tablet){display:none!important}.ui.grid.grid.grid>.row>[class*="computer only"].column:not(.tablet),.ui.grid.grid.grid>[class*="computer only"].column:not(.tablet),.ui.grid.grid.grid>[class*="computer only"].row:not(.tablet),.ui[class*="computer only"].grid.grid.grid:not(.tablet){display:none!important}.ui.grid.grid.grid>.row>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].row:not(.mobile),.ui[class*="large screen only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].row:not(.mobile),.ui[class*="widescreen only"].grid.grid.grid:not(.mobile){display:none!important}}@media only screen and (min-width:992px) and (max-width:1199.98px){.ui.grid.grid.grid>.row>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].row:not(.computer),.ui[class*="mobile only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].row:not(.computer),.ui[class*="tablet only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="large screen only"].row:not(.mobile),.ui[class*="large screen only"].grid.grid.grid:not(.mobile){display:none!important}.ui.grid.grid.grid>.row>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].row:not(.mobile),.ui[class*="widescreen only"].grid.grid.grid:not(.mobile){display:none!important}}@media only screen and (min-width:1200px) and (max-width:1919.98px){.ui.grid.grid.grid>.row>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].row:not(.computer),.ui[class*="mobile only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].row:not(.computer),.ui[class*="tablet only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].column:not(.mobile),.ui.grid.grid.grid>[class*="widescreen only"].row:not(.mobile),.ui[class*="widescreen only"].grid.grid.grid:not(.mobile){display:none!important}}@media only screen and (min-width:1920px){.ui.grid.grid.grid>.row>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].column:not(.computer),.ui.grid.grid.grid>[class*="mobile only"].row:not(.computer),.ui[class*="mobile only"].grid.grid.grid:not(.computer){display:none!important}.ui.grid.grid.grid>.row>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].column:not(.computer),.ui.grid.grid.grid>[class*="tablet only"].row:not(.computer),.ui[class*="tablet only"].grid.grid.grid:not(.computer){display:none!important}}.ui.ui.ui.compact.grid>.column:not(.row),.ui.ui.ui.compact.grid>.row>.column{padding-left:.5rem;padding-right:.5rem}.ui.ui.ui.compact.grid>*{padding-left:.5rem;padding-right:.5rem}.ui.ui.ui.compact.grid>.row{padding-top:.5rem;padding-bottom:.5rem}.ui.ui.ui.compact.grid>.column:not(.row){padding-top:.5rem;padding-bottom:.5rem}.ui.compact.relaxed.celled.grid>.column:not(.row),.ui.compact.relaxed.celled.grid>.row>.column{padding:.75em}.ui.compact[class*="very relaxed"].celled.grid>.column:not(.row),.ui.compact[class*="very relaxed"].celled.grid>.row>.column{padding:1em}.ui[class*="very compact"].grid>.column:not(.row),.ui[class*="very compact"].grid>.row>.column{padding-left:.25rem;padding-right:.5rem}.ui[class*="very compact"].grid>*{padding-left:.25rem;padding-right:.25rem}.ui[class*="very compact"].grid>.row{padding-top:.25rem;padding-bottom:.25rem}.ui[class*="very compact"].grid>.column:not(.row){padding-top:.25rem;padding-bottom:.25rem}.ui[class*="very compact"].relaxed.celled.grid>.column:not(.row),.ui[class*="very compact"].relaxed.celled.grid>.row>.column{padding:.375em}.ui[class*="very compact"][class*="very relaxed"].celled.grid>.column:not(.row),.ui[class*="very compact"][class*="very relaxed"].celled.grid>.row>.column{padding:.5em} + /* + * # Fomantic-UI - Input + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.input{position:relative;font-weight:400;font-style:normal;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;color:rgba(0,0,0,.87)}.ui.input>input{margin:0;max-width:100%;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;outline:0;-webkit-tap-highlight-color:rgba(255,255,255,0);text-align:left;line-height:1.21428571em;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;padding:.67857143em 1em;background:#fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);border-radius:.28571429rem;-webkit-transition:border-color .1s ease,-webkit-box-shadow .1s ease;transition:border-color .1s ease,-webkit-box-shadow .1s ease;transition:box-shadow .1s ease,border-color .1s ease;transition:box-shadow .1s ease,border-color .1s ease,-webkit-box-shadow .1s ease;-webkit-box-shadow:none;box-shadow:none}.ui.input>input::-webkit-input-placeholder{color:rgba(191,191,191,.87)}.ui.input>input::-moz-placeholder{color:rgba(191,191,191,.87)}.ui.input>input:-ms-input-placeholder{color:rgba(191,191,191,.87)}.ui.disabled.input,.ui.input:not(.disabled) input[disabled]{opacity:.45}.ui.disabled.input>input,.ui.input:not(.disabled) input[disabled]{pointer-events:none}.ui.input.down input,.ui.input>input:active{border-color:rgba(0,0,0,.3);background:#fafafa;color:rgba(0,0,0,.87);-webkit-box-shadow:none;box-shadow:none}.ui.loading.loading.input>i.icon:before{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;border-radius:500rem;border:.2em solid rgba(0,0,0,.1)}.ui.loading.loading.input>i.icon:after{position:absolute;content:'';top:50%;left:50%;margin:-.64285714em 0 0 -.64285714em;width:1.28571429em;height:1.28571429em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent}.ui.input.focus>input,.ui.input>input:focus{border-color:#85b7d9;background:#fff;color:rgba(0,0,0,.8);-webkit-box-shadow:none;box-shadow:none}.ui.input.focus>input::-webkit-input-placeholder,.ui.input>input:focus::-webkit-input-placeholder{color:rgba(115,115,115,.87)}.ui.input.focus>input::-moz-placeholder,.ui.input>input:focus::-moz-placeholder{color:rgba(115,115,115,.87)}.ui.input.focus>input:-ms-input-placeholder,.ui.input>input:focus:-ms-input-placeholder{color:rgba(115,115,115,.87)}.ui.input.error>input{background-color:#fff6f6;border-color:#e0b4b4;color:#9f3a38;-webkit-box-shadow:none;box-shadow:none}.ui.input.error>input::-webkit-input-placeholder{color:#e7bdbc}.ui.input.error>input::-moz-placeholder{color:#e7bdbc}.ui.input.error>input:-ms-input-placeholder{color:#e7bdbc}.ui.input.error>input:focus::-webkit-input-placeholder{color:#da9796}.ui.input.error>input:focus::-moz-placeholder{color:#da9796}.ui.input.error>input:focus:-ms-input-placeholder{color:#da9796}.ui.input.info>input{background-color:#f8ffff;border-color:#a9d5de;color:#276f86;-webkit-box-shadow:none;box-shadow:none}.ui.input.info>input::-webkit-input-placeholder{color:#98cfe1}.ui.input.info>input::-moz-placeholder{color:#98cfe1}.ui.input.info>input:-ms-input-placeholder{color:#98cfe1}.ui.input.info>input:focus::-webkit-input-placeholder{color:#70bdd6}.ui.input.info>input:focus::-moz-placeholder{color:#70bdd6}.ui.input.info>input:focus:-ms-input-placeholder{color:#70bdd6}.ui.input.success>input{background-color:#fcfff5;border-color:#a3c293;color:#2c662d;-webkit-box-shadow:none;box-shadow:none}.ui.input.success>input::-webkit-input-placeholder{color:#8fcf90}.ui.input.success>input::-moz-placeholder{color:#8fcf90}.ui.input.success>input:-ms-input-placeholder{color:#8fcf90}.ui.input.success>input:focus::-webkit-input-placeholder{color:#6cbf6d}.ui.input.success>input:focus::-moz-placeholder{color:#6cbf6d}.ui.input.success>input:focus:-ms-input-placeholder{color:#6cbf6d}.ui.input.warning>input{background-color:#fffaf3;border-color:#c9ba9b;color:#573a08;-webkit-box-shadow:none;box-shadow:none}.ui.input.warning>input::-webkit-input-placeholder{color:#edad3e}.ui.input.warning>input::-moz-placeholder{color:#edad3e}.ui.input.warning>input:-ms-input-placeholder{color:#edad3e}.ui.input.warning>input:focus::-webkit-input-placeholder{color:#e39715}.ui.input.warning>input:focus::-moz-placeholder{color:#e39715}.ui.input.warning>input:focus:-ms-input-placeholder{color:#e39715}.ui.transparent.input>input,.ui.transparent.input>textarea{border-color:transparent!important;background-color:transparent!important;padding:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-radius:0!important}.field .ui.transparent.input>textarea{padding:.67857143em 1em}:not(.field)>.ui.transparent.icon.input>i.icon{width:1.1em}:not(.field)>.ui.ui.ui.transparent.icon.input>input{padding-left:0;padding-right:2em}:not(.field)>.ui.ui.ui.transparent[class*="left icon"].input>input{padding-left:2em;padding-right:0}.ui.transparent.inverted.input{color:#fff}.ui.ui.transparent.inverted.input>input,.ui.ui.transparent.inverted.input>textarea{color:inherit}.ui.transparent.inverted.input>input::-webkit-input-placeholder{color:rgba(255,255,255,.5)}.ui.transparent.inverted.input>input::-moz-placeholder{color:rgba(255,255,255,.5)}.ui.transparent.inverted.input>input:-ms-input-placeholder{color:rgba(255,255,255,.5)}.ui.icon.input>i.icon{cursor:default;position:absolute;line-height:1;text-align:center;top:0;right:0;margin:0;height:100%;width:2.67142857em;opacity:.5;border-radius:0 .28571429rem .28571429rem 0;-webkit-transition:opacity .3s ease;transition:opacity .3s ease}.ui.icon.input>i.icon:not(.link){pointer-events:none}.ui.ui.ui.ui.icon.input>input,.ui.ui.ui.ui.icon.input>textarea{padding-right:2.67142857em}.ui.icon.input>i.icon:after,.ui.icon.input>i.icon:before{left:0;position:absolute;text-align:center;top:50%;width:100%;margin-top:-.5em}.ui.icon.input>i.link.icon{cursor:pointer}.ui.icon.input>i.circular.icon{top:.35em;right:.5em}.ui[class*="left icon"].input>i.icon{right:auto;left:1px;border-radius:.28571429rem 0 0 .28571429rem}.ui[class*="left icon"].input>i.circular.icon{right:auto;left:.5em}.ui.ui.ui.ui[class*="left icon"].input>input,.ui.ui.ui.ui[class*="left icon"].input>textarea{padding-left:2.67142857em;padding-right:1em}.ui.icon.input>input:focus~i.icon,.ui.icon.input>textarea:focus~i.icon{opacity:1}.ui.labeled.input>.label{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin:0;font-size:1em}.ui.labeled.input>.label:not(.corner){padding-top:.78571429em;padding-bottom:.78571429em}.ui.labeled.input:not([class*="corner labeled"]) .label:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.ui.labeled.input:not([class*="corner labeled"]) .label:first-child+input{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:transparent}.ui.labeled.input:not([class*="corner labeled"]) .label:first-child+input:focus{border-left-color:#85b7d9}.ui[class*="right labeled"].input>input{border-top-right-radius:0!important;border-bottom-right-radius:0!important;border-right-color:transparent!important}.ui[class*="right labeled"].input>input+.label{border-top-left-radius:0;border-bottom-left-radius:0}.ui[class*="right labeled"].input>input:focus{border-right-color:#85b7d9!important}.ui.labeled.input .corner.label{top:1px;right:1px;font-size:.64285714em;border-radius:0 .28571429rem 0 0}.ui[class*="corner labeled"]:not([class*="left corner labeled"]).labeled.input>input,.ui[class*="corner labeled"]:not([class*="left corner labeled"]).labeled.input>textarea{padding-right:2.5em!important}.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"])>input,.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"])>textarea{padding-right:3.25em!important}.ui[class*="corner labeled"].icon.input:not([class*="left corner labeled"])>.icon{margin-right:1.25em}.ui[class*="left corner labeled"].labeled.input>input,.ui[class*="left corner labeled"].labeled.input>textarea{padding-left:2.5em!important}.ui[class*="left corner labeled"].icon.input>input,.ui[class*="left corner labeled"].icon.input>textarea{padding-left:3.25em!important}.ui[class*="left corner labeled"].icon.input>.icon{margin-left:1.25em}.ui.icon.input>textarea~.icon{height:3em}:not(.field)>.ui.transparent.icon.input>textarea~.icon{height:1.3em}.ui.input>.ui.corner.label{top:1px;right:1px}.ui.input>.ui.left.corner.label{right:auto;left:1px}.ui.action.input.error>.ui.button,.ui.form>.field.error>.ui.action.input>.ui.button,.ui.form>.field.error>.ui.labeled.input:not([class*="corner labeled"])>.ui.label,.ui.labeled.input.error:not([class*="corner labeled"])>.ui.label{border-top:1px solid #e0b4b4;border-bottom:1px solid #e0b4b4}.ui.form>.field.error>.ui.labeled.input:not(.right):not([class*="corner labeled"])>.ui.label,.ui.form>.field.error>.ui.left.action.input>.ui.button,.ui.labeled.input.error:not(.right):not([class*="corner labeled"])>.ui.label,.ui.left.action.input.error>.ui.button{border-left:1px solid #e0b4b4}.ui.action.input.error:not(.left)>input+.ui.button,.ui.form>.field.error>.ui.action.input:not(.left)>input+.ui.button,.ui.form>.field.error>.ui.right.labeled.input:not([class*="corner labeled"])>input+.ui.label,.ui.right.labeled.input.error:not([class*="corner labeled"])>input+.ui.label{border-right:1px solid #e0b4b4}.ui.form>.field.error>.ui.right.labeled.input:not([class*="corner labeled"])>.ui.label:first-child,.ui.right.labeled.input.error:not([class*="corner labeled"])>.ui.label:first-child{border-left:1px solid #e0b4b4}.ui.action.input.info>.ui.button,.ui.form>.field.info>.ui.action.input>.ui.button,.ui.form>.field.info>.ui.labeled.input:not([class*="corner labeled"])>.ui.label,.ui.labeled.input.info:not([class*="corner labeled"])>.ui.label{border-top:1px solid #a9d5de;border-bottom:1px solid #a9d5de}.ui.form>.field.info>.ui.labeled.input:not(.right):not([class*="corner labeled"])>.ui.label,.ui.form>.field.info>.ui.left.action.input>.ui.button,.ui.labeled.input.info:not(.right):not([class*="corner labeled"])>.ui.label,.ui.left.action.input.info>.ui.button{border-left:1px solid #a9d5de}.ui.action.input.info:not(.left)>input+.ui.button,.ui.form>.field.info>.ui.action.input:not(.left)>input+.ui.button,.ui.form>.field.info>.ui.right.labeled.input:not([class*="corner labeled"])>input+.ui.label,.ui.right.labeled.input.info:not([class*="corner labeled"])>input+.ui.label{border-right:1px solid #a9d5de}.ui.form>.field.info>.ui.right.labeled.input:not([class*="corner labeled"])>.ui.label:first-child,.ui.right.labeled.input.info:not([class*="corner labeled"])>.ui.label:first-child{border-left:1px solid #a9d5de}.ui.action.input.success>.ui.button,.ui.form>.field.success>.ui.action.input>.ui.button,.ui.form>.field.success>.ui.labeled.input:not([class*="corner labeled"])>.ui.label,.ui.labeled.input.success:not([class*="corner labeled"])>.ui.label{border-top:1px solid #a3c293;border-bottom:1px solid #a3c293}.ui.form>.field.success>.ui.labeled.input:not(.right):not([class*="corner labeled"])>.ui.label,.ui.form>.field.success>.ui.left.action.input>.ui.button,.ui.labeled.input.success:not(.right):not([class*="corner labeled"])>.ui.label,.ui.left.action.input.success>.ui.button{border-left:1px solid #a3c293}.ui.action.input.success:not(.left)>input+.ui.button,.ui.form>.field.success>.ui.action.input:not(.left)>input+.ui.button,.ui.form>.field.success>.ui.right.labeled.input:not([class*="corner labeled"])>input+.ui.label,.ui.right.labeled.input.success:not([class*="corner labeled"])>input+.ui.label{border-right:1px solid #a3c293}.ui.form>.field.success>.ui.right.labeled.input:not([class*="corner labeled"])>.ui.label:first-child,.ui.right.labeled.input.success:not([class*="corner labeled"])>.ui.label:first-child{border-left:1px solid #a3c293}.ui.action.input.warning>.ui.button,.ui.form>.field.warning>.ui.action.input>.ui.button,.ui.form>.field.warning>.ui.labeled.input:not([class*="corner labeled"])>.ui.label,.ui.labeled.input.warning:not([class*="corner labeled"])>.ui.label{border-top:1px solid #c9ba9b;border-bottom:1px solid #c9ba9b}.ui.form>.field.warning>.ui.labeled.input:not(.right):not([class*="corner labeled"])>.ui.label,.ui.form>.field.warning>.ui.left.action.input>.ui.button,.ui.labeled.input.warning:not(.right):not([class*="corner labeled"])>.ui.label,.ui.left.action.input.warning>.ui.button{border-left:1px solid #c9ba9b}.ui.action.input.warning:not(.left)>input+.ui.button,.ui.form>.field.warning>.ui.action.input:not(.left)>input+.ui.button,.ui.form>.field.warning>.ui.right.labeled.input:not([class*="corner labeled"])>input+.ui.label,.ui.right.labeled.input.warning:not([class*="corner labeled"])>input+.ui.label{border-right:1px solid #c9ba9b}.ui.form>.field.warning>.ui.right.labeled.input:not([class*="corner labeled"])>.ui.label:first-child,.ui.right.labeled.input.warning:not([class*="corner labeled"])>.ui.label:first-child{border-left:1px solid #c9ba9b}.ui.action.input>.button,.ui.action.input>.buttons{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto}.ui.action.input>.button,.ui.action.input>.buttons>.button{padding-top:.78571429em;padding-bottom:.78571429em;margin:0}.ui[class*="left action"].input>input{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:transparent}.ui.action.input:not([class*="left action"])>input{border-top-right-radius:0;border-bottom-right-radius:0;border-right-color:transparent}.ui.action.input>.button:first-child,.ui.action.input>.buttons:first-child>.button,.ui.action.input>.dropdown:first-child{border-radius:.28571429rem 0 0 .28571429rem}.ui.action.input>.button:not(:first-child),.ui.action.input>.buttons:not(:first-child)>.button,.ui.action.input>.dropdown:not(:first-child){border-radius:0}.ui.action.input>.button:last-child,.ui.action.input>.buttons:last-child>.button,.ui.action.input>.dropdown:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.action.input:not([class*="left action"])>input:focus{border-right-color:#85b7d9}.ui.ui[class*="left action"].input>input:focus{border-left-color:#85b7d9}.ui.inverted.input>input{border:none}.ui.fluid.input{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.fluid.input>input{width:0!important}.ui.input{font-size:1em}.ui.mini.input{font-size:.78571429em}.ui.tiny.input{font-size:.85714286em}.ui.small.input{font-size:.92857143em}.ui.large.input{font-size:1.14285714em}.ui.big.input{font-size:1.28571429em}.ui.huge.input{font-size:1.42857143em}.ui.massive.input{font-size:1.71428571em} + /* + * # Fomantic-UI - Item + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.items>.item{display:-webkit-box;display:-ms-flexbox;display:flex;margin:1em 0;width:100%;min-height:0;background:0 0;padding:0;border:none;border-radius:0;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:-webkit-box-shadow .1s ease;transition:-webkit-box-shadow .1s ease;transition:box-shadow .1s ease;transition:box-shadow .1s ease,-webkit-box-shadow .1s ease;z-index:''}.ui.items>.item a{cursor:pointer}.ui.items{margin:1.5em 0}.ui.items:first-child{margin-top:0!important}.ui.items:last-child{margin-bottom:0!important}.ui.items>.item:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item:first-child{margin-top:0}.ui.items>.item:last-child{margin-bottom:0}.ui.items>.item>.image{position:relative;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;display:block;float:none;margin:0;padding:0;max-height:'';-ms-flex-item-align:start;align-self:start}.ui.items>.item>.image>img{display:block;width:100%;height:auto;border-radius:.125rem;border:none}.ui.items>.item>.image:only-child>img{border-radius:0}.ui.items>.item>.content{display:block;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;background:0 0;color:rgba(0,0,0,.87);margin:0;padding:0;-webkit-box-shadow:none;box-shadow:none;font-size:1em;border:none;border-radius:0}.ui.items>.item>.content:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item>.image+.content{min-width:0;width:auto;display:block;margin-left:0;-ms-flex-item-align:start;align-self:start;padding-left:1.5em}.ui.items>.item>.content>.header{display:inline-block;margin:-.21425em 0 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;font-weight:700;color:rgba(0,0,0,.85)}.ui.items>.item>.content>.header:not(.ui){font-size:1.28571429em}.ui.items>.item [class*="left floated"]{float:left}.ui.items>.item [class*="right floated"]{float:right}.ui.items>.item .content img{-ms-flex-item-align:center;align-self:center;width:''}.ui.items>.item .avatar img,.ui.items>.item img.avatar{width:'';height:'';border-radius:500rem}.ui.items>.item>.content>.description{margin-top:.6em;max-width:auto;font-size:1em;line-height:1.4285em;color:rgba(0,0,0,.87)}.ui.items>.item>.content p{margin:0 0 .5em}.ui.items>.item>.content p:last-child{margin-bottom:0}.ui.items>.item .meta{margin:.5em 0 .5em;font-size:1em;line-height:1em;color:rgba(0,0,0,.6)}.ui.items>.item .meta *{margin-right:.3em}.ui.items>.item .meta :last-child{margin-right:0}.ui.items>.item .meta [class*="right floated"]{margin-right:0;margin-left:.3em}.ui.items>.item>.content a:not(.ui){color:'';-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content a:not(.ui):hover{color:''}.ui.items>.item>.content>a.header{color:rgba(0,0,0,.85)}.ui.items>.item>.content>a.header:hover{color:#1e70bf}.ui.items>.item .meta>a:not(.ui){color:rgba(0,0,0,.4)}.ui.items>.item .meta>a:not(.ui):hover{color:rgba(0,0,0,.87)}.ui.items>.item>.content .favorite.icon{cursor:pointer;opacity:.75;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content .favorite.icon:hover{opacity:1;color:#ffb70a}.ui.items>.item>.content .active.favorite.icon{color:#ffe623}.ui.items>.item>.content .like.icon{cursor:pointer;opacity:.75;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.items>.item>.content .like.icon:hover{opacity:1;color:#ff2733}.ui.items>.item>.content .active.like.icon{color:#ff2733}.ui.items>.item .extra{display:block;position:relative;background:0 0;margin:.5rem 0 0;width:100%;padding:0 0 0;top:0;left:0;color:rgba(0,0,0,.4);-webkit-box-shadow:none;box-shadow:none;-webkit-transition:color .1s ease;transition:color .1s ease;border-top:none}.ui.items>.item .extra>*{margin:.25rem .5rem .25rem 0}.ui.items>.item .extra>[class*="right floated"]{margin:.25rem 0 .25rem .5rem}.ui.items>.item .extra:after{display:block;content:' ';height:0;clear:both;overflow:hidden;visibility:hidden}.ui.items>.item>.image:not(.ui){width:175px}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.items>.item{margin:1em 0}.ui.items>.item>.image:not(.ui){width:150px}.ui.items>.item>.image+.content{display:block;padding:0 0 0 1em}}@media only screen and (max-width:767.98px){.ui.items:not(.unstackable)>.item{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:2em 0}.ui.items:not(.unstackable)>.item>.image{display:block;margin-left:auto;margin-right:auto}.ui.items:not(.unstackable)>.item>.image,.ui.items:not(.unstackable)>.item>.image>img{max-width:100%!important;width:auto!important;max-height:250px!important}.ui.items:not(.unstackable)>.item>.image+.content{display:block;padding:1.5em 0 0}}.ui.items>.item>.image+[class*="top aligned"].content{-ms-flex-item-align:start;align-self:flex-start}.ui.items>.item>.image+[class*="middle aligned"].content{-ms-flex-item-align:center;align-self:center}.ui.items>.item>.image+[class*="bottom aligned"].content{-ms-flex-item-align:end;align-self:flex-end}.ui.relaxed.items>.item{margin:1.5em 0}.ui[class*="very relaxed"].items>.item{margin:2em 0}.ui.divided.items>.item{border-top:1px solid rgba(34,36,38,.15);margin:0;padding:1em 0}.ui.divided.items>.item:first-child{border-top:none;margin-top:0!important;padding-top:0!important}.ui.divided.items>.item:last-child{margin-bottom:0!important;padding-bottom:0!important}.ui.relaxed.divided.items>.item{margin:0;padding:1.5em 0}.ui[class*="very relaxed"].divided.items>.item{margin:0;padding:2em 0}.ui.items a.item:hover,.ui.link.items>.item:hover{cursor:pointer}.ui.items a.item:hover .content .header,.ui.link.items>.item:hover .content .header{color:#1e70bf}.ui.items>.item{font-size:1em}.ui.mini.items>.item{font-size:.78571429em}.ui.tiny.items>.item{font-size:.85714286em}.ui.small.items>.item{font-size:.92857143em}.ui.large.items>.item{font-size:1.14285714em}.ui.big.items>.item{font-size:1.28571429em}.ui.huge.items>.item{font-size:1.42857143em}.ui.massive.items>.item{font-size:1.71428571em}@media only screen and (max-width:767.98px){.ui.unstackable.items>.item>.image,.ui.unstackable.items>.item>.image>img{width:125px!important}}.ui.inverted.items>.item{background:0 0}.ui.inverted.items>.item>.content{background:0 0;color:rgba(255,255,255,.9)}.ui.inverted.items>.item .extra{background:0 0}.ui.inverted.items>.item>.content>.header{color:rgba(255,255,255,.9)}.ui.inverted.items>.item>.content>.description{color:rgba(255,255,255,.9)}.ui.inverted.items>.item .meta{color:rgba(255,255,255,.8)}.ui.inverted.items>.item>.content a:not(.ui){color:#57a4ef}.ui.inverted.items>.item>.content a:not(.ui):hover{color:#4183c4}.ui.inverted.items>.item>.content>a.header{color:rgba(255,255,255,.9)}.ui.inverted.items>.item>.content>a.header:hover{color:#fff}.ui.inverted.items>.item .meta>a:not(.ui){color:rgba(255,255,255,.7)}.ui.inverted.items>.item .meta>a:not(.ui):hover{color:rgba(255,255,255,.9)}.ui.inverted.items>.item>.content .favorite.icon:hover{color:#ffc63d}.ui.inverted.items>.item>.content .active.favorite.icon{color:#ffec56}.ui.inverted.items>.item>.content .like.icon:hover{color:#ff5a63}.ui.inverted.items>.item>.content .active.like.icon{color:#ff5a63}.ui.inverted.items>.item .extra{color:rgba(255,255,255,.7)}.ui.inverted.items a.item:hover .content .header,.ui.inverted.link.items>.item:hover .content .header{color:#fff}.ui.inverted.divided.items>.item{border-top:1px solid rgba(255,255,255,.1)}.ui.inverted.divided.items>.item:first-child{border-top:none} + /* + * # Fomantic-UI - Label + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.label{display:inline-block;line-height:1;vertical-align:baseline;margin:0 .14285714em;background-color:#e8e8e8;background-image:none;padding:.5833em .833em;color:rgba(0,0,0,.6);text-transform:none;font-weight:700;border:0 solid transparent;border-radius:.28571429rem;-webkit-transition:background .1s ease;transition:background .1s ease}.ui.label:first-child{margin-left:0}.ui.label:last-child{margin-right:0}a.ui.label{cursor:pointer}.ui.label>a{cursor:pointer;color:inherit;opacity:.5;-webkit-transition:.1s opacity ease;transition:.1s opacity ease}.ui.label>a:hover{opacity:1}.ui.label>img{width:auto!important;vertical-align:middle;height:2.1666em}.ui.label>.icon,.ui.left.icon.label>.icon{width:auto;margin:0 .75em 0 0}.ui.label>.detail{display:inline-block;vertical-align:top;font-weight:700;margin-left:1em;opacity:.8}.ui.label>.detail .icon{margin:0 .25em 0 0}.ui.label>.close.icon,.ui.label>.delete.icon{cursor:pointer;font-size:.92857143em;opacity:.5;-webkit-transition:background .1s ease;transition:background .1s ease}.ui.label>.close.icon:hover,.ui.label>.delete.icon:hover{opacity:1}.ui.label.left.icon>.close.icon,.ui.label.left.icon>.delete.icon{margin:0 .5em 0 0}.ui.label:not(.icon)>.close.icon,.ui.label:not(.icon)>.delete.icon{margin:0 0 0 .5em}.ui.icon.label>.icon{margin:0 auto}.ui.right.icon.label>.icon{margin:0 0 0 .75em}.ui.labels>.label{margin:0 .5em .5em 0}.ui.header>.ui.label{margin-top:-.29165em}.ui.attached.segment>.ui.top.left.attached.label,.ui.bottom.attached.segment>.ui.top.left.attached.label{border-top-left-radius:0}.ui.attached.segment>.ui.top.right.attached.label,.ui.bottom.attached.segment>.ui.top.right.attached.label{border-top-right-radius:0}.ui.top.attached.segment>.ui.bottom.left.attached.label{border-bottom-left-radius:0}.ui.top.attached.segment>.ui.bottom.right.attached.label{border-bottom-right-radius:0}.ui.top.attached.label+:not(.attached),.ui.top.attached.label~.ui.bottom.attached.label+:not(.attached){margin-top:2rem!important}.ui.bottom.attached.label~:last-child:not(.attached){margin-top:0;margin-bottom:2rem!important}.ui.image.label{width:auto!important;margin-top:0;margin-bottom:0;max-width:9999px;vertical-align:baseline;text-transform:none;background:#e8e8e8;padding:.5833em .833em .5833em .5em;border-radius:.28571429rem;-webkit-box-shadow:none;box-shadow:none}.ui.image.label img{display:inline-block;vertical-align:top;height:2.1666em;margin:-.5833em .5em -.5833em -.5em;border-radius:.28571429rem 0 0 .28571429rem}.ui.image.label .detail{background:rgba(0,0,0,.1);margin:-.5833em -.833em -.5833em .5em;padding:.5833em .833em;border-radius:0 .28571429rem .28571429rem 0}.ui.tag.label,.ui.tag.labels .label{margin-left:1em;position:relative;padding-left:1.5em;padding-right:1.5em;border-radius:0 .28571429rem .28571429rem 0;-webkit-transition:none;transition:none}.ui.tag.label:before,.ui.tag.labels .label:before{position:absolute;-webkit-transform:translateY(-50%) translateX(50%) rotate(-45deg);transform:translateY(-50%) translateX(50%) rotate(-45deg);top:50%;right:100%;content:'';background-color:inherit;background-image:none;width:1.56em;height:1.56em;-webkit-transition:none;transition:none}.ui.tag.label:after,.ui.tag.labels .label:after{position:absolute;content:'';top:50%;left:-.25em;margin-top:-.25em;background-color:#fff;width:.5em;height:.5em;-webkit-box-shadow:0 -1px 1px 0 rgba(0,0,0,.3);box-shadow:0 -1px 1px 0 rgba(0,0,0,.3);border-radius:500rem}.ui.basic.tag.label:before,.ui.basic.tag.labels .label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;right:calc(100% + 1px)}.ui.basic.tag.label:after,.ui.basic.tag.labels .label:after{-webkit-box-shadow:0 -1px 3px 0 rgba(0,0,0,.8);box-shadow:0 -1px 3px 0 rgba(0,0,0,.8)}.ui.corner.label{position:absolute;top:0;right:0;margin:0;padding:0;text-align:center;border-color:#e8e8e8;width:4em;height:4em;z-index:1;-webkit-transition:border-color .1s ease;transition:border-color .1s ease}.ui.corner.label{background-color:transparent!important}.ui.corner.label:after{position:absolute;content:"";right:0;top:0;z-index:-1;width:0;height:0;background-color:transparent;border-top:0 solid transparent;border-right:4em solid transparent;border-bottom:4em solid transparent;border-left:0 solid transparent;border-right-color:inherit;-webkit-transition:border-color .1s ease;transition:border-color .1s ease}.ui.corner.label .icon{cursor:inherit;position:absolute;top:.64285714em;left:auto;right:.57142857em;font-size:1.14285714em;margin:0}.ui.left.corner.label,.ui.left.corner.label:after{right:auto;left:0}.ui.left.corner.label:after{border-top:4em solid transparent;border-right:4em solid transparent;border-bottom:0 solid transparent;border-left:0 solid transparent;border-top-color:inherit}.ui.left.corner.label .icon{left:.57142857em;right:auto}.ui.segment>.ui.corner.label{top:-1px;right:-1px}.ui.segment>.ui.left.corner.label{right:auto;left:-1px}.ui.ribbon.label{position:relative;margin:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;border-radius:0 .28571429rem .28571429rem 0;border-color:rgba(0,0,0,.15)}.ui.ribbon.label:after{position:absolute;content:'';top:100%;left:0;background-color:transparent;border-style:solid;border-width:0 1.2em 1.2em 0;border-color:transparent;border-right-color:inherit;width:0;height:0}.ui.ribbon.label{left:calc(-1rem - 1.2em);margin-right:-1.2em;padding-left:calc(1rem + 1.2em);padding-right:1.2em}.ui[class*="right ribbon"].label{left:calc(100% + 1rem + 1.2em);padding-left:1.2em;padding-right:calc(1rem + 1.2em)}.ui.basic.ribbon.label{padding-top:calc(.5833em - 1px);padding-bottom:calc(.5833em - 1px)}.ui.basic.ribbon.label:not([class*="right ribbon"]){padding-left:calc(1rem + 1.2em - 1px);padding-right:calc(1.2em - 1px)}.ui.basic[class*="right ribbon"].label{padding-left:calc(1.2em - 1px);padding-right:calc(1rem + 1.2em - 1px)}.ui.basic.ribbon.label::after{top:calc(100% + 1px)}.ui.basic.ribbon.label:not([class*="right ribbon"])::after{left:-1px}.ui.basic[class*="right ribbon"].label::after{right:-1px}.ui[class*="right ribbon"].label{text-align:left;-webkit-transform:translateX(-100%);transform:translateX(-100%);border-radius:.28571429rem 0 0 .28571429rem}.ui[class*="right ribbon"].label:after{left:auto;right:0;border-style:solid;border-width:1.2em 1.2em 0 0;border-color:transparent;border-top-color:inherit}.ui.card .image>.ribbon.label,.ui.image>.ribbon.label{position:absolute;top:1rem}.ui.card .image>.ui.ribbon.label,.ui.image>.ui.ribbon.label{left:calc(.05rem - 1.2em)}.ui.card .image>.ui[class*="right ribbon"].label,.ui.image>.ui[class*="right ribbon"].label{left:calc(100% + -.05rem + 1.2em);padding-left:.833em}.ui.table td>.ui.ribbon.label{left:calc(-1em - 1.2em)}.ui.table td>.ui[class*="right ribbon"].label{left:calc(100% + 1em + 1.2em);padding-left:.833em}.ui.attached.label,.ui[class*="top attached"].label{width:100%;position:absolute;margin:0;top:0;left:0;padding:.75em 1em;border-radius:.21428571rem .21428571rem 0 0}.ui[class*="bottom attached"].label{top:auto;bottom:0;border-radius:0 0 .21428571rem .21428571rem}.ui[class*="top left attached"].label{width:auto;margin-top:0!important;border-radius:.21428571rem 0 .28571429rem 0}.ui[class*="top right attached"].label{width:auto;left:auto;right:0;border-radius:0 .21428571rem 0 .28571429rem}.ui[class*="bottom left attached"].label{width:auto;top:auto;bottom:0;border-radius:0 .28571429rem 0 .21428571rem}.ui[class*="bottom right attached"].label{top:auto;bottom:0;left:auto;right:0;width:auto;border-radius:.28571429rem 0 .21428571rem 0}.ui.label.disabled{opacity:.5}.ui.labels a.label:hover,a.ui.label:hover{background-color:#e0e0e0;border-color:#e0e0e0;background-image:none;color:rgba(0,0,0,.8)}.ui.labels a.label:hover:before,a.ui.label:hover:before{color:rgba(0,0,0,.8)}.ui.active.label{background-color:#d0d0d0;border-color:#d0d0d0;background-image:none;color:rgba(0,0,0,.95)}.ui.active.label:before{background-color:#d0d0d0;background-image:none;color:rgba(0,0,0,.95)}.ui.labels a.active.label:hover,a.ui.active.label:hover{background-color:#c8c8c8;border-color:#c8c8c8;background-image:none;color:rgba(0,0,0,.95)}.ui.labels a.active.label:hover:before,a.ui.active.label:hover:before{background-color:#c8c8c8;background-image:none;color:rgba(0,0,0,.95)}.ui.label.visible:not(.dropdown),.ui.labels.visible .label{display:inline-block!important}.ui.label.hidden,.ui.labels.hidden .label{display:none!important}.ui.basic.label,.ui.basic.labels .label{background:none #fff;border:1px solid rgba(34,36,38,.15);color:rgba(0,0,0,.87);-webkit-box-shadow:none;box-shadow:none}.ui.basic.labels a.label:hover,a.ui.basic.label:hover{text-decoration:none;background:none #fff;color:#1e70bf;-webkit-box-shadow:none;box-shadow:none}.ui.basic.pointing.label:before{border-color:inherit}.ui.fluid.labels>.label,.ui.label.fluid{width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.ui.inverted.label,.ui.inverted.labels .label{color:rgba(255,255,255,.9);background-color:#b5b5b5}.ui.inverted.corner.label{border-color:#b5b5b5}.ui.inverted.corner.label:hover{border-color:#e8e8e8;-webkit-transition:none;transition:none}.ui.inverted.basic.label,.ui.inverted.basic.label:hover,.ui.inverted.basic.labels .label{border-color:rgba(255,255,255,.5);background:#1b1c1d}.ui.inverted.basic.label:hover{color:#4183c4}.ui.primary.labels .label,.ui.ui.ui.primary.label{background-color:#2185d0;border-color:#2185d0;color:rgba(255,255,255,.9)}.ui.primary.labels a.label:hover,a.ui.ui.ui.primary.label:hover{background-color:#1678c2;border-color:#1678c2;color:#fff}.ui.ui.ui.primary.ribbon.label{border-color:#1a69a4}.ui.basic.labels .primary.label,.ui.ui.ui.basic.primary.label{background:none #fff;border-color:#2185d0;color:#2185d0}.ui.basic.labels a.primary.label:hover,a.ui.ui.ui.basic.primary.label:hover{background:none #fff;border-color:#1678c2;color:#1678c2}.ui.inverted.labels .primary.label,.ui.ui.ui.inverted.primary.label{background-color:#54c8ff;border-color:#54c8ff;color:#1b1c1d}.ui.inverted.labels a.primary.label:hover,a.ui.ui.ui.inverted.primary.label:hover{background-color:#21b8ff;border-color:#21b8ff;color:#1b1c1d}.ui.ui.ui.inverted.primary.ribbon.label{border-color:#21b8ff}.ui.inverted.basic.labels .primary.label,.ui.ui.ui.inverted.basic.primary.label{background-color:#1b1c1d;border-color:#54c8ff;color:#54c8ff}.ui.inverted.basic.labels a.primary.label:hover,a.ui.ui.ui.inverted.basic.primary.label:hover{border-color:#21b8ff;background-color:#1b1c1d;color:#21b8ff}.ui.inverted.basic.tag.labels .primary.label,.ui.ui.ui.inverted.primary.basic.tag.label{border:1px solid #54c8ff}.ui.inverted.basic.tag.labels .primary.label:before,.ui.ui.ui.inverted.primary.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.secondary.labels .label,.ui.ui.ui.secondary.label{background-color:#1b1c1d;border-color:#1b1c1d;color:rgba(255,255,255,.9)}.ui.secondary.labels a.label:hover,a.ui.ui.ui.secondary.label:hover{background-color:#27292a;border-color:#27292a;color:#fff}.ui.ui.ui.secondary.ribbon.label{border-color:#020203}.ui.basic.labels .secondary.label,.ui.ui.ui.basic.secondary.label{background:none #fff;border-color:#1b1c1d;color:#1b1c1d}.ui.basic.labels a.secondary.label:hover,a.ui.ui.ui.basic.secondary.label:hover{background:none #fff;border-color:#27292a;color:#27292a}.ui.inverted.labels .secondary.label,.ui.ui.ui.inverted.secondary.label{background-color:#545454;border-color:#545454;color:#1b1c1d}.ui.inverted.labels a.secondary.label:hover,a.ui.ui.ui.inverted.secondary.label:hover{background-color:#6e6e6e;border-color:#6e6e6e;color:#1b1c1d}.ui.ui.ui.inverted.secondary.ribbon.label{border-color:#3b3b3b}.ui.inverted.basic.labels .secondary.label,.ui.ui.ui.inverted.basic.secondary.label{background-color:#1b1c1d;border-color:#545454;color:#545454}.ui.inverted.basic.labels a.secondary.label:hover,a.ui.ui.ui.inverted.basic.secondary.label:hover{border-color:#6e6e6e;background-color:#1b1c1d;color:#6e6e6e}.ui.inverted.basic.tag.labels .secondary.label,.ui.ui.ui.inverted.secondary.basic.tag.label{border:1px solid #545454}.ui.inverted.basic.tag.labels .secondary.label:before,.ui.ui.ui.inverted.secondary.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.red.labels .label,.ui.ui.ui.red.label{background-color:#db2828;border-color:#db2828;color:#fff}.ui.red.labels a.label:hover,a.ui.ui.ui.red.label:hover{background-color:#d01919;border-color:#d01919;color:#fff}.ui.ui.ui.red.ribbon.label{border-color:#b21e1e}.ui.basic.labels .red.label,.ui.ui.ui.basic.red.label{background:none #fff;border-color:#db2828;color:#db2828}.ui.basic.labels a.red.label:hover,a.ui.ui.ui.basic.red.label:hover{background:none #fff;border-color:#d01919;color:#d01919}.ui.inverted.labels .red.label,.ui.ui.ui.inverted.red.label{background-color:#ff695e;border-color:#ff695e;color:#1b1c1d}.ui.inverted.labels a.red.label:hover,a.ui.ui.ui.inverted.red.label:hover{background-color:#ff392b;border-color:#ff392b;color:#1b1c1d}.ui.ui.ui.inverted.red.ribbon.label{border-color:#ff392b}.ui.inverted.basic.labels .red.label,.ui.ui.ui.inverted.basic.red.label{background-color:#1b1c1d;border-color:#ff695e;color:#ff695e}.ui.inverted.basic.labels a.red.label:hover,a.ui.ui.ui.inverted.basic.red.label:hover{border-color:#ff392b;background-color:#1b1c1d;color:#ff392b}.ui.inverted.basic.tag.labels .red.label,.ui.ui.ui.inverted.red.basic.tag.label{border:1px solid #ff695e}.ui.inverted.basic.tag.labels .red.label:before,.ui.ui.ui.inverted.red.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.orange.labels .label,.ui.ui.ui.orange.label{background-color:#f2711c;border-color:#f2711c;color:#fff}.ui.orange.labels a.label:hover,a.ui.ui.ui.orange.label:hover{background-color:#f26202;border-color:#f26202;color:#fff}.ui.ui.ui.orange.ribbon.label{border-color:#cf590c}.ui.basic.labels .orange.label,.ui.ui.ui.basic.orange.label{background:none #fff;border-color:#f2711c;color:#f2711c}.ui.basic.labels a.orange.label:hover,a.ui.ui.ui.basic.orange.label:hover{background:none #fff;border-color:#f26202;color:#f26202}.ui.inverted.labels .orange.label,.ui.ui.ui.inverted.orange.label{background-color:#ff851b;border-color:#ff851b;color:#1b1c1d}.ui.inverted.labels a.orange.label:hover,a.ui.ui.ui.inverted.orange.label:hover{background-color:#e76b00;border-color:#e76b00;color:#1b1c1d}.ui.ui.ui.inverted.orange.ribbon.label{border-color:#e76b00}.ui.inverted.basic.labels .orange.label,.ui.ui.ui.inverted.basic.orange.label{background-color:#1b1c1d;border-color:#ff851b;color:#ff851b}.ui.inverted.basic.labels a.orange.label:hover,a.ui.ui.ui.inverted.basic.orange.label:hover{border-color:#e76b00;background-color:#1b1c1d;color:#e76b00}.ui.inverted.basic.tag.labels .orange.label,.ui.ui.ui.inverted.orange.basic.tag.label{border:1px solid #ff851b}.ui.inverted.basic.tag.labels .orange.label:before,.ui.ui.ui.inverted.orange.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.ui.ui.yellow.label,.ui.yellow.labels .label{background-color:#fbbd08;border-color:#fbbd08;color:#fff}.ui.yellow.labels a.label:hover,a.ui.ui.ui.yellow.label:hover{background-color:#eaae00;border-color:#eaae00;color:#fff}.ui.ui.ui.yellow.ribbon.label{border-color:#cd9903}.ui.basic.labels .yellow.label,.ui.ui.ui.basic.yellow.label{background:none #fff;border-color:#fbbd08;color:#fbbd08}.ui.basic.labels a.yellow.label:hover,a.ui.ui.ui.basic.yellow.label:hover{background:none #fff;border-color:#eaae00;color:#eaae00}.ui.inverted.labels .yellow.label,.ui.ui.ui.inverted.yellow.label{background-color:#ffe21f;border-color:#ffe21f;color:#1b1c1d}.ui.inverted.labels a.yellow.label:hover,a.ui.ui.ui.inverted.yellow.label:hover{background-color:#ebcd00;border-color:#ebcd00;color:#1b1c1d}.ui.ui.ui.inverted.yellow.ribbon.label{border-color:#ebcd00}.ui.inverted.basic.labels .yellow.label,.ui.ui.ui.inverted.basic.yellow.label{background-color:#1b1c1d;border-color:#ffe21f;color:#ffe21f}.ui.inverted.basic.labels a.yellow.label:hover,a.ui.ui.ui.inverted.basic.yellow.label:hover{border-color:#ebcd00;background-color:#1b1c1d;color:#ebcd00}.ui.inverted.basic.tag.labels .yellow.label,.ui.ui.ui.inverted.yellow.basic.tag.label{border:1px solid #ffe21f}.ui.inverted.basic.tag.labels .yellow.label:before,.ui.ui.ui.inverted.yellow.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.olive.labels .label,.ui.ui.ui.olive.label{background-color:#b5cc18;border-color:#b5cc18;color:#fff}.ui.olive.labels a.label:hover,a.ui.ui.ui.olive.label:hover{background-color:#a7bd0d;border-color:#a7bd0d;color:#fff}.ui.ui.ui.olive.ribbon.label{border-color:#8d9e13}.ui.basic.labels .olive.label,.ui.ui.ui.basic.olive.label{background:none #fff;border-color:#b5cc18;color:#b5cc18}.ui.basic.labels a.olive.label:hover,a.ui.ui.ui.basic.olive.label:hover{background:none #fff;border-color:#a7bd0d;color:#a7bd0d}.ui.inverted.labels .olive.label,.ui.ui.ui.inverted.olive.label{background-color:#d9e778;border-color:#d9e778;color:#1b1c1d}.ui.inverted.labels a.olive.label:hover,a.ui.ui.ui.inverted.olive.label:hover{background-color:#d2e745;border-color:#d2e745;color:#1b1c1d}.ui.ui.ui.inverted.olive.ribbon.label{border-color:#cddf4d}.ui.inverted.basic.labels .olive.label,.ui.ui.ui.inverted.basic.olive.label{background-color:#1b1c1d;border-color:#d9e778;color:#d9e778}.ui.inverted.basic.labels a.olive.label:hover,a.ui.ui.ui.inverted.basic.olive.label:hover{border-color:#d2e745;background-color:#1b1c1d;color:#d2e745}.ui.inverted.basic.tag.labels .olive.label,.ui.ui.ui.inverted.olive.basic.tag.label{border:1px solid #d9e778}.ui.inverted.basic.tag.labels .olive.label:before,.ui.ui.ui.inverted.olive.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.green.labels .label,.ui.ui.ui.green.label{background-color:#21ba45;border-color:#21ba45;color:#fff}.ui.green.labels a.label:hover,a.ui.ui.ui.green.label:hover{background-color:#16ab39;border-color:#16ab39;color:#fff}.ui.ui.ui.green.ribbon.label{border-color:#198f35}.ui.basic.labels .green.label,.ui.ui.ui.basic.green.label{background:none #fff;border-color:#21ba45;color:#21ba45}.ui.basic.labels a.green.label:hover,a.ui.ui.ui.basic.green.label:hover{background:none #fff;border-color:#16ab39;color:#16ab39}.ui.inverted.labels .green.label,.ui.ui.ui.inverted.green.label{background-color:#2ecc40;border-color:#2ecc40;color:#1b1c1d}.ui.inverted.labels a.green.label:hover,a.ui.ui.ui.inverted.green.label:hover{background-color:#1ea92e;border-color:#1ea92e;color:#1b1c1d}.ui.ui.ui.inverted.green.ribbon.label{border-color:#25a233}.ui.inverted.basic.labels .green.label,.ui.ui.ui.inverted.basic.green.label{background-color:#1b1c1d;border-color:#2ecc40;color:#2ecc40}.ui.inverted.basic.labels a.green.label:hover,a.ui.ui.ui.inverted.basic.green.label:hover{border-color:#1ea92e;background-color:#1b1c1d;color:#1ea92e}.ui.inverted.basic.tag.labels .green.label,.ui.ui.ui.inverted.green.basic.tag.label{border:1px solid #2ecc40}.ui.inverted.basic.tag.labels .green.label:before,.ui.ui.ui.inverted.green.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.teal.labels .label,.ui.ui.ui.teal.label{background-color:#00b5ad;border-color:#00b5ad;color:#fff}.ui.teal.labels a.label:hover,a.ui.ui.ui.teal.label:hover{background-color:#009c95;border-color:#009c95;color:#fff}.ui.ui.ui.teal.ribbon.label{border-color:#00827c}.ui.basic.labels .teal.label,.ui.ui.ui.basic.teal.label{background:none #fff;border-color:#00b5ad;color:#00b5ad}.ui.basic.labels a.teal.label:hover,a.ui.ui.ui.basic.teal.label:hover{background:none #fff;border-color:#009c95;color:#009c95}.ui.inverted.labels .teal.label,.ui.ui.ui.inverted.teal.label{background-color:#6dffff;border-color:#6dffff;color:#1b1c1d}.ui.inverted.labels a.teal.label:hover,a.ui.ui.ui.inverted.teal.label:hover{background-color:#3affff;border-color:#3affff;color:#1b1c1d}.ui.ui.ui.inverted.teal.ribbon.label{border-color:#3affff}.ui.inverted.basic.labels .teal.label,.ui.ui.ui.inverted.basic.teal.label{background-color:#1b1c1d;border-color:#6dffff;color:#6dffff}.ui.inverted.basic.labels a.teal.label:hover,a.ui.ui.ui.inverted.basic.teal.label:hover{border-color:#3affff;background-color:#1b1c1d;color:#3affff}.ui.inverted.basic.tag.labels .teal.label,.ui.ui.ui.inverted.teal.basic.tag.label{border:1px solid #6dffff}.ui.inverted.basic.tag.labels .teal.label:before,.ui.ui.ui.inverted.teal.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.blue.labels .label,.ui.ui.ui.blue.label{background-color:#2185d0;border-color:#2185d0;color:#fff}.ui.blue.labels a.label:hover,a.ui.ui.ui.blue.label:hover{background-color:#1678c2;border-color:#1678c2;color:#fff}.ui.ui.ui.blue.ribbon.label{border-color:#1a69a4}.ui.basic.labels .blue.label,.ui.ui.ui.basic.blue.label{background:none #fff;border-color:#2185d0;color:#2185d0}.ui.basic.labels a.blue.label:hover,a.ui.ui.ui.basic.blue.label:hover{background:none #fff;border-color:#1678c2;color:#1678c2}.ui.inverted.labels .blue.label,.ui.ui.ui.inverted.blue.label{background-color:#54c8ff;border-color:#54c8ff;color:#1b1c1d}.ui.inverted.labels a.blue.label:hover,a.ui.ui.ui.inverted.blue.label:hover{background-color:#21b8ff;border-color:#21b8ff;color:#1b1c1d}.ui.ui.ui.inverted.blue.ribbon.label{border-color:#21b8ff}.ui.inverted.basic.labels .blue.label,.ui.ui.ui.inverted.basic.blue.label{background-color:#1b1c1d;border-color:#54c8ff;color:#54c8ff}.ui.inverted.basic.labels a.blue.label:hover,a.ui.ui.ui.inverted.basic.blue.label:hover{border-color:#21b8ff;background-color:#1b1c1d;color:#21b8ff}.ui.inverted.basic.tag.labels .blue.label,.ui.ui.ui.inverted.blue.basic.tag.label{border:1px solid #54c8ff}.ui.inverted.basic.tag.labels .blue.label:before,.ui.ui.ui.inverted.blue.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.ui.ui.violet.label,.ui.violet.labels .label{background-color:#6435c9;border-color:#6435c9;color:#fff}.ui.violet.labels a.label:hover,a.ui.ui.ui.violet.label:hover{background-color:#5829bb;border-color:#5829bb;color:#fff}.ui.ui.ui.violet.ribbon.label{border-color:#502aa1}.ui.basic.labels .violet.label,.ui.ui.ui.basic.violet.label{background:none #fff;border-color:#6435c9;color:#6435c9}.ui.basic.labels a.violet.label:hover,a.ui.ui.ui.basic.violet.label:hover{background:none #fff;border-color:#5829bb;color:#5829bb}.ui.inverted.labels .violet.label,.ui.ui.ui.inverted.violet.label{background-color:#a291fb;border-color:#a291fb;color:#1b1c1d}.ui.inverted.labels a.violet.label:hover,a.ui.ui.ui.inverted.violet.label:hover{background-color:#745aff;border-color:#745aff;color:#1b1c1d}.ui.ui.ui.inverted.violet.ribbon.label{border-color:#7860f9}.ui.inverted.basic.labels .violet.label,.ui.ui.ui.inverted.basic.violet.label{background-color:#1b1c1d;border-color:#a291fb;color:#a291fb}.ui.inverted.basic.labels a.violet.label:hover,a.ui.ui.ui.inverted.basic.violet.label:hover{border-color:#745aff;background-color:#1b1c1d;color:#745aff}.ui.inverted.basic.tag.labels .violet.label,.ui.ui.ui.inverted.violet.basic.tag.label{border:1px solid #a291fb}.ui.inverted.basic.tag.labels .violet.label:before,.ui.ui.ui.inverted.violet.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.purple.labels .label,.ui.ui.ui.purple.label{background-color:#a333c8;border-color:#a333c8;color:#fff}.ui.purple.labels a.label:hover,a.ui.ui.ui.purple.label:hover{background-color:#9627ba;border-color:#9627ba;color:#fff}.ui.ui.ui.purple.ribbon.label{border-color:#82299f}.ui.basic.labels .purple.label,.ui.ui.ui.basic.purple.label{background:none #fff;border-color:#a333c8;color:#a333c8}.ui.basic.labels a.purple.label:hover,a.ui.ui.ui.basic.purple.label:hover{background:none #fff;border-color:#9627ba;color:#9627ba}.ui.inverted.labels .purple.label,.ui.ui.ui.inverted.purple.label{background-color:#dc73ff;border-color:#dc73ff;color:#1b1c1d}.ui.inverted.labels a.purple.label:hover,a.ui.ui.ui.inverted.purple.label:hover{background-color:#cf40ff;border-color:#cf40ff;color:#1b1c1d}.ui.ui.ui.inverted.purple.ribbon.label{border-color:#cf40ff}.ui.inverted.basic.labels .purple.label,.ui.ui.ui.inverted.basic.purple.label{background-color:#1b1c1d;border-color:#dc73ff;color:#dc73ff}.ui.inverted.basic.labels a.purple.label:hover,a.ui.ui.ui.inverted.basic.purple.label:hover{border-color:#cf40ff;background-color:#1b1c1d;color:#cf40ff}.ui.inverted.basic.tag.labels .purple.label,.ui.ui.ui.inverted.purple.basic.tag.label{border:1px solid #dc73ff}.ui.inverted.basic.tag.labels .purple.label:before,.ui.ui.ui.inverted.purple.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.pink.labels .label,.ui.ui.ui.pink.label{background-color:#e03997;border-color:#e03997;color:#fff}.ui.pink.labels a.label:hover,a.ui.ui.ui.pink.label:hover{background-color:#e61a8d;border-color:#e61a8d;color:#fff}.ui.ui.ui.pink.ribbon.label{border-color:#c71f7e}.ui.basic.labels .pink.label,.ui.ui.ui.basic.pink.label{background:none #fff;border-color:#e03997;color:#e03997}.ui.basic.labels a.pink.label:hover,a.ui.ui.ui.basic.pink.label:hover{background:none #fff;border-color:#e61a8d;color:#e61a8d}.ui.inverted.labels .pink.label,.ui.ui.ui.inverted.pink.label{background-color:#ff8edf;border-color:#ff8edf;color:#1b1c1d}.ui.inverted.labels a.pink.label:hover,a.ui.ui.ui.inverted.pink.label:hover{background-color:#ff5bd1;border-color:#ff5bd1;color:#1b1c1d}.ui.ui.ui.inverted.pink.ribbon.label{border-color:#ff5bd1}.ui.inverted.basic.labels .pink.label,.ui.ui.ui.inverted.basic.pink.label{background-color:#1b1c1d;border-color:#ff8edf;color:#ff8edf}.ui.inverted.basic.labels a.pink.label:hover,a.ui.ui.ui.inverted.basic.pink.label:hover{border-color:#ff5bd1;background-color:#1b1c1d;color:#ff5bd1}.ui.inverted.basic.tag.labels .pink.label,.ui.ui.ui.inverted.pink.basic.tag.label{border:1px solid #ff8edf}.ui.inverted.basic.tag.labels .pink.label:before,.ui.ui.ui.inverted.pink.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.brown.labels .label,.ui.ui.ui.brown.label{background-color:#a5673f;border-color:#a5673f;color:#fff}.ui.brown.labels a.label:hover,a.ui.ui.ui.brown.label:hover{background-color:#975b33;border-color:#975b33;color:#fff}.ui.ui.ui.brown.ribbon.label{border-color:#805031}.ui.basic.labels .brown.label,.ui.ui.ui.basic.brown.label{background:none #fff;border-color:#a5673f;color:#a5673f}.ui.basic.labels a.brown.label:hover,a.ui.ui.ui.basic.brown.label:hover{background:none #fff;border-color:#975b33;color:#975b33}.ui.inverted.labels .brown.label,.ui.ui.ui.inverted.brown.label{background-color:#d67c1c;border-color:#d67c1c;color:#1b1c1d}.ui.inverted.labels a.brown.label:hover,a.ui.ui.ui.inverted.brown.label:hover{background-color:#b0620f;border-color:#b0620f;color:#1b1c1d}.ui.ui.ui.inverted.brown.ribbon.label{border-color:#a96216}.ui.inverted.basic.labels .brown.label,.ui.ui.ui.inverted.basic.brown.label{background-color:#1b1c1d;border-color:#d67c1c;color:#d67c1c}.ui.inverted.basic.labels a.brown.label:hover,a.ui.ui.ui.inverted.basic.brown.label:hover{border-color:#b0620f;background-color:#1b1c1d;color:#b0620f}.ui.inverted.basic.tag.labels .brown.label,.ui.ui.ui.inverted.brown.basic.tag.label{border:1px solid #d67c1c}.ui.inverted.basic.tag.labels .brown.label:before,.ui.ui.ui.inverted.brown.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.grey.labels .label,.ui.ui.ui.grey.label{background-color:#767676;border-color:#767676;color:#fff}.ui.grey.labels a.label:hover,a.ui.ui.ui.grey.label:hover{background-color:#838383;border-color:#838383;color:#fff}.ui.ui.ui.grey.ribbon.label{border-color:#5d5d5d}.ui.basic.labels .grey.label,.ui.ui.ui.basic.grey.label{background:none #fff;border-color:#767676;color:#767676}.ui.basic.labels a.grey.label:hover,a.ui.ui.ui.basic.grey.label:hover{background:none #fff;border-color:#838383;color:#838383}.ui.inverted.labels .grey.label,.ui.ui.ui.inverted.grey.label{background-color:#dcddde;border-color:#dcddde;color:#1b1c1d}.ui.inverted.labels a.grey.label:hover,a.ui.ui.ui.inverted.grey.label:hover{background-color:#c2c4c5;border-color:#c2c4c5;color:#fff}.ui.ui.ui.inverted.grey.ribbon.label{border-color:#e9eaea}.ui.inverted.basic.labels .grey.label,.ui.ui.ui.inverted.basic.grey.label{background-color:#1b1c1d;border-color:#dcddde;color:rgba(255,255,255,.9)}.ui.inverted.basic.labels a.grey.label:hover,a.ui.ui.ui.inverted.basic.grey.label:hover{border-color:#c2c4c5;background-color:#1b1c1d}.ui.inverted.basic.tag.labels .grey.label,.ui.ui.ui.inverted.grey.basic.tag.label{border:1px solid #dcddde}.ui.inverted.basic.tag.labels .grey.label:before,.ui.ui.ui.inverted.grey.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.black.labels .label,.ui.ui.ui.black.label{background-color:#1b1c1d;border-color:#1b1c1d;color:#fff}.ui.black.labels a.label:hover,a.ui.ui.ui.black.label:hover{background-color:#27292a;border-color:#27292a;color:#fff}.ui.ui.ui.black.ribbon.label{border-color:#020203}.ui.basic.labels .black.label,.ui.ui.ui.basic.black.label{background:none #fff;border-color:#1b1c1d;color:#1b1c1d}.ui.basic.labels a.black.label:hover,a.ui.ui.ui.basic.black.label:hover{background:none #fff;border-color:#27292a;color:#27292a}.ui.inverted.labels .black.label,.ui.ui.ui.inverted.black.label{background-color:#545454;border-color:#545454;color:#1b1c1d}.ui.inverted.labels a.black.label:hover,a.ui.ui.ui.inverted.black.label:hover{background-color:#000;border-color:#000;color:#fff}.ui.ui.ui.inverted.black.ribbon.label{border-color:#616161}.ui.inverted.basic.labels .black.label,.ui.ui.ui.inverted.basic.black.label{background-color:#1b1c1d;border-color:#545454;color:rgba(255,255,255,.9)}.ui.inverted.basic.labels a.black.label:hover,a.ui.ui.ui.inverted.basic.black.label:hover{border-color:#000;background-color:#1b1c1d}.ui.inverted.basic.tag.labels .black.label,.ui.ui.ui.inverted.black.basic.tag.label{border:1px solid #545454}.ui.inverted.basic.tag.labels .black.label:before,.ui.ui.ui.inverted.black.basic.tag.label:before{border-color:inherit;border-width:1px 0 0 1px;border-style:inherit;background-color:#1b1c1d;right:calc(100% + 1px)}.ui.horizontal.label,.ui.horizontal.labels .label{margin:0 .5em 0 0;padding:.4em .833em;min-width:3em;text-align:center}.ui.circular.label,.ui.circular.labels .label{min-width:2em;min-height:2em;padding:.5em!important;line-height:1em;text-align:center;border-radius:500rem}.ui.empty.circular.label,.ui.empty.circular.labels .label{min-width:0;min-height:0;overflow:hidden;width:.5em;height:.5em;vertical-align:baseline}.ui.pointing.label{position:relative}.ui.attached.pointing.label{position:absolute}.ui.pointing.label:before{background-color:inherit;background-image:inherit;border-width:0;border-style:solid;border-color:inherit}.ui.pointing.label:before{position:absolute;content:'';-webkit-transform:rotate(45deg);transform:rotate(45deg);background-image:none;z-index:2;width:.6666em;height:.6666em;-webkit-transition:none;transition:none}.ui.pointing.label,.ui[class*="pointing above"].label{margin-top:1em}.ui.pointing.label:before,.ui[class*="pointing above"].label:before{border-width:1px 0 0 1px;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);top:0;left:50%}.ui[class*="bottom pointing"].label,.ui[class*="pointing below"].label{margin-top:0;margin-bottom:1em}.ui[class*="bottom pointing"].label:before,.ui[class*="pointing below"].label:before{border-width:0 1px 1px 0;top:auto;right:auto;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);top:100%;left:50%}.ui[class*="left pointing"].label{margin-top:0;margin-left:.6666em}.ui[class*="left pointing"].label:before{border-width:0 0 1px 1px;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);bottom:auto;right:auto;top:50%;left:0}.ui[class*="right pointing"].label{margin-top:0;margin-right:.6666em}.ui[class*="right pointing"].label:before{border-width:1px 1px 0 0;-webkit-transform:translateX(50%) translateY(-50%) rotate(45deg);transform:translateX(50%) translateY(-50%) rotate(45deg);top:50%;right:0;bottom:auto;left:auto}.ui.basic.pointing.label:before,.ui.basic[class*="pointing above"].label:before{margin-top:-1px}.ui.basic[class*="bottom pointing"].label:before,.ui.basic[class*="pointing below"].label:before{bottom:auto;top:100%;margin-top:1px}.ui.basic[class*="left pointing"].label:before{top:50%;left:-1px}.ui.basic[class*="right pointing"].label:before{top:50%;right:-1px}.ui.floating.label{position:absolute;z-index:100;top:-1em;right:0;white-space:nowrap;-webkit-transform:translateX(50%);transform:translateX(50%)}.ui.right.aligned.floating.label{-webkit-transform:translateX(1.2em);transform:translateX(1.2em)}.ui.left.floating.label{left:0;right:auto;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.ui.left.aligned.floating.label{-webkit-transform:translateX(-1.2em);transform:translateX(-1.2em)}.ui.bottom.floating.label{top:auto;bottom:-1em}.ui.label,.ui.labels .label{font-size:.85714286rem}.ui.mini.label,.ui.mini.labels .label{font-size:.64285714rem}.ui.tiny.label,.ui.tiny.labels .label{font-size:.71428571rem}.ui.small.label,.ui.small.labels .label{font-size:.78571429rem}.ui.large.label,.ui.large.labels .label{font-size:1rem}.ui.big.label,.ui.big.labels .label{font-size:1.28571429rem}.ui.huge.label,.ui.huge.labels .label{font-size:1.42857143rem}.ui.massive.label,.ui.massive.labels .label{font-size:1.71428571rem}.ui.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin:1rem 0;font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;background:#fff;font-weight:400;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);border-radius:.28571429rem;min-height:2.85714286em}.ui.menu:after{content:'';display:block;height:0;clear:both;visibility:hidden}.ui.menu:first-child{margin-top:0}.ui.menu:last-child{margin-bottom:0}.ui.menu .menu{margin:0}.ui.menu:not(.vertical)>.menu{display:-webkit-box;display:-ms-flexbox;display:flex}.ui.menu:not(.vertical) .item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ui.menu .item{position:relative;vertical-align:middle;line-height:1;text-decoration:none;-webkit-tap-highlight-color:transparent;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:0 0;padding:.92857143em 1.14285714em;text-transform:none;color:rgba(0,0,0,.87);font-weight:400;-webkit-transition:background .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background .1s ease,color .1s ease,-webkit-box-shadow .1s ease;transition:background .1s ease,box-shadow .1s ease,color .1s ease;transition:background .1s ease,box-shadow .1s ease,color .1s ease,-webkit-box-shadow .1s ease}.ui.menu>.item:first-child{border-radius:.28571429rem 0 0 .28571429rem}.ui.menu .item:before{position:absolute;content:'';top:0;right:0;height:100%;width:1px;background:rgba(34,36,38,.1)}.ui.menu .item>a:not(.ui),.ui.menu .item>p:only-child,.ui.menu .text.item>*{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;line-height:1.3}.ui.menu .item>p:first-child{margin-top:0}.ui.menu .item>p:last-child{margin-bottom:0}.ui.menu .item>i.icon{opacity:.9;float:none;margin:0 .35714286em 0 0}.ui.menu:not(.vertical) .item>.button{position:relative;top:0;margin:-.5em 0;padding-bottom:.78571429em;padding-top:.78571429em;font-size:1em}.ui.menu>.container,.ui.menu>.grid{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:inherit;-ms-flex-align:inherit;align-items:inherit;-webkit-box-orient:inherit;-webkit-box-direction:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.menu .item>.input{width:100%}.ui.menu:not(.vertical) .item>.input{position:relative;top:0;margin:-.5em 0}.ui.menu .item>.input input{font-size:1em;padding-top:.57142857em;padding-bottom:.57142857em}.ui.menu .header.item,.ui.vertical.menu .header.item{margin:0;background:'';text-transform:normal;font-weight:700}.ui.vertical.menu .item>.header:not(.ui){margin:0 0 .5em;font-size:1em;font-weight:700}.ui.menu .item>i.dropdown.icon{padding:0;float:right;margin:0 0 0 1em}.ui.menu .dropdown.item .menu{min-width:calc(100% - 1px);border-radius:0 0 .28571429rem .28571429rem;background:#fff;margin:0 0 0;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.08);box-shadow:0 1px 3px 0 rgba(0,0,0,.08);-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.ui.menu .ui.dropdown .menu>.item{margin:0;text-align:left;font-size:1em!important;padding:.78571429em 1.14285714em!important;background:0 0!important;color:rgba(0,0,0,.87)!important;text-transform:none!important;font-weight:400!important;-webkit-box-shadow:none!important;box-shadow:none!important;-webkit-transition:none!important;transition:none!important}.ui.menu .ui.dropdown .menu>.item:hover{background:rgba(0,0,0,.05)!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown .menu>.selected.item{background:rgba(0,0,0,.05)!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown .menu>.active.item{background:rgba(0,0,0,.03)!important;font-weight:700!important;color:rgba(0,0,0,.95)!important}.ui.menu .ui.dropdown.item .menu .item:not(.filtered){display:block}.ui.menu .ui.dropdown .menu>.item .icon:not(.dropdown){display:inline-block;font-size:1em!important;float:none;margin:0 .75em 0 0!important}.ui.secondary.menu .dropdown.item>.menu,.ui.text.menu .dropdown.item>.menu{border-radius:.28571429rem;margin-top:.35714286em}.ui.menu .pointing.dropdown.item .menu{margin-top:.75em}.ui.inverted.menu .search.dropdown.item>.search,.ui.inverted.menu .search.dropdown.item>.text{color:rgba(255,255,255,.9)}.ui.vertical.menu .dropdown.item>.icon{float:right;content:"\f0da";margin-left:1em}.ui.vertical.menu .dropdown.item .menu{left:100%;min-width:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;margin:0;-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,.08);box-shadow:0 1px 3px 0 rgba(0,0,0,.08);border-radius:0 .28571429rem .28571429rem .28571429rem}.ui.vertical.menu .dropdown.item.upward .menu{bottom:0}.ui.vertical.menu .dropdown.item:not(.upward) .menu{top:0}.ui.vertical.menu .active.dropdown.item{border-top-right-radius:0;border-bottom-right-radius:0}.ui.vertical.menu .dropdown.active.item{-webkit-box-shadow:none;box-shadow:none}.ui.item.menu .dropdown .menu .item{width:100%}.ui.menu .item>.label:not(.floating){margin-left:1em;padding:.3em .78571429em}.ui.vertical.menu .item>.label{margin-top:-.15em;margin-bottom:-.15em;padding:.3em .78571429em}.ui.menu .item>.floating.label{padding:.3em .78571429em}.ui.menu .item>.label{background:#999;color:#fff}.ui.menu .item>.image.label img{margin:-.2833em .8em -.2833em -.8em;height:1.5666em}.ui.menu .item>img:not(.ui){display:inline-block;vertical-align:middle;margin:-.3em 0;width:2.5em}.ui.vertical.menu .item>img:not(.ui):only-child{display:block;max-width:100%;width:auto}.ui.menu .list .item:before{background:0 0!important}.ui.vertical.sidebar.menu>.item:first-child:before{display:block!important}.ui.vertical.sidebar.menu>.item::before{top:auto;bottom:0}@media only screen and (max-width:767.98px){.ui.menu>.ui.container{width:100%!important;margin-left:0!important;margin-right:0!important}}@media only screen and (min-width:768px){.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.item:not(.right):not(.borderless):first-child{border-left:1px solid rgba(34,36,38,.1)}.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.right.item:not(.borderless):last-child,.ui.menu:not(.secondary):not(.text):not(.tabular):not(.borderless)>.container>.right.menu>.item:not(.borderless):last-child{border-right:1px solid rgba(34,36,38,.1)}}.ui.link.menu .item:hover,.ui.menu .dropdown.item:hover,.ui.menu .link.item:hover,.ui.menu a.item:hover{cursor:pointer;background:rgba(0,0,0,.03);color:rgba(0,0,0,.95)}.ui.link.menu .item:active,.ui.menu .link.item:active,.ui.menu a.item:active{background:rgba(0,0,0,.03);color:rgba(0,0,0,.95)}.ui.menu .active.item{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);font-weight:400;-webkit-box-shadow:none;box-shadow:none}.ui.menu .active.item>i.icon{opacity:1}.ui.menu .active.item:hover,.ui.vertical.menu .active.item:hover{background-color:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.ui.menu .item.disabled{cursor:default;background-color:transparent;color:rgba(40,40,40,.3);pointer-events:none}.ui.menu:not(.vertical) .left.item,.ui.menu:not(.vertical) .left.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-right:auto!important}.ui.menu:not(.vertical) .right.item,.ui.menu:not(.vertical) .right.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-left:auto!important}.ui.menu:not(.vertical) :not(.dropdown)>.left.menu,.ui.menu:not(.vertical) :not(.dropdown)>.right.menu{display:inherit}.ui.menu:not(.vertical) .center.item,.ui.menu:not(.vertical) .center.menu{display:-webkit-box;display:-ms-flexbox;display:flex;margin-left:auto!important;margin-right:auto!important}.ui.menu .right.item::before,.ui.menu .right.menu>.item::before{right:auto;left:0}.ui.menu .center.item:last-child::before,.ui.menu .center.menu>.item:last-child::before{display:none}.ui.vertical.menu{display:block;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;background:#fff;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.ui.vertical.menu .item{display:block;background:0 0;border-top:none;border-right:none}.ui.vertical.menu>.item:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.vertical.menu>.item:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.vertical.menu .item>.label{float:right;text-align:center}.ui.vertical.menu .item>i.icon,.ui.vertical.menu .item>i.icons{width:1.18em;float:right;margin:0 0 0 .5em}.ui.vertical.menu .item>.label+i.icon{float:none;margin:0 .5em 0 0}.ui.vertical.menu .item:before{position:absolute;content:'';top:0;left:0;width:100%;height:1px;background:rgba(34,36,38,.1)}.ui.vertical.menu .item:first-child:before{display:none!important}.ui.vertical.menu .item>.menu{margin:.5em -1.14285714em 0}.ui.vertical.menu .menu .item{background:0 0;padding:.5em 1.33333333em;font-size:.85714286em;color:rgba(0,0,0,.5)}.ui.vertical.menu .item .menu .link.item:hover,.ui.vertical.menu .item .menu a.item:hover{color:rgba(0,0,0,.85)}.ui.vertical.menu .menu .item:before{display:none}.ui.vertical.menu .active.item{background:rgba(0,0,0,.05);border-radius:0;-webkit-box-shadow:none;box-shadow:none}.ui.vertical.menu>.active.item:first-child{border-radius:.28571429rem .28571429rem 0 0}.ui.vertical.menu>.active.item:last-child{border-radius:0 0 .28571429rem .28571429rem}.ui.vertical.menu>.active.item:only-child{border-radius:.28571429rem}.ui.vertical.menu .active.item .menu .active.item{border-left:none}.ui.vertical.menu .item .menu .active.item{background-color:transparent;font-weight:700;color:rgba(0,0,0,.95)}.ui.tabular.menu{border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border:none;background:none transparent;border-bottom:1px solid #d4d4d5}.ui.tabular.fluid.menu{width:calc(100% + 2px)!important}.ui.tabular.menu .item{background:0 0;border-bottom:none;border-left:1px solid transparent;border-right:1px solid transparent;border-top:2px solid transparent;padding:.92857143em 1.42857143em;color:rgba(0,0,0,.87)}.ui.tabular.menu .item:before{display:none}.ui.tabular.menu .item:hover{background-color:transparent;color:rgba(0,0,0,.8)}.ui.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-top-width:1px;border-color:#d4d4d5;font-weight:700;margin-bottom:-1px;-webkit-box-shadow:none;box-shadow:none;border-radius:.28571429rem .28571429rem 0 0!important}.ui.tabular.menu+.attached:not(.top).segment,.ui.tabular.menu+.attached:not(.top).segment+.attached:not(.top).segment{border-top:none;margin-left:0;margin-top:0;margin-right:0;width:100%}.top.attached.segment+.ui.bottom.tabular.menu{position:relative;width:calc(100% + 2px);left:-1px}.ui.bottom.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-top:1px solid #d4d4d5}.ui.bottom.tabular.menu .item{background:0 0;border-left:1px solid transparent;border-right:1px solid transparent;border-bottom:1px solid transparent;border-top:none}.ui.bottom.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:-1px 0 0 0;border-radius:0 0 .28571429rem .28571429rem!important}.ui.vertical.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-right:1px solid #d4d4d5}.ui.vertical.tabular.menu .item{background:0 0;border-left:1px solid transparent;border-bottom:1px solid transparent;border-top:1px solid transparent;border-right:none}.ui.vertical.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:0 -1px 0 0;border-radius:.28571429rem 0 0 .28571429rem!important}.ui.vertical.right.tabular.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none!important;box-shadow:none!important;border-bottom:none;border-right:none;border-left:1px solid #d4d4d5}.ui.vertical.right.tabular.menu .item{background:0 0;border-right:1px solid transparent;border-bottom:1px solid transparent;border-top:1px solid transparent;border-left:none}.ui.vertical.right.tabular.menu .active.item{background:none #fff;color:rgba(0,0,0,.95);border-color:#d4d4d5;margin:0 0 0 -1px;border-radius:0 .28571429rem .28571429rem 0!important}.ui.tabular.menu .active.dropdown.item{margin-bottom:0;border-left:1px solid transparent;border-right:1px solid transparent;border-top:2px solid transparent;border-bottom:none}.ui.pagination.menu{margin:0;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.ui.pagination.menu .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.compact.menu .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.pagination.menu .item:last-child:before{display:none}.ui.pagination.menu .item{min-width:3em;text-align:center}.ui.pagination.menu .icon.item i.icon{vertical-align:top}.ui.pagination.menu .active.item{border-top:none;padding-top:.92857143em;background-color:rgba(0,0,0,.05);color:rgba(0,0,0,.95);-webkit-box-shadow:none;box-shadow:none}.ui.secondary.menu{background:0 0;margin-left:-.35714286em;margin-right:-.35714286em;border-radius:0;border:none;-webkit-box-shadow:none;box-shadow:none}.ui.secondary.menu .item{-ms-flex-item-align:center;align-self:center;-webkit-box-shadow:none;box-shadow:none;border:none;padding:.78571429em .92857143em;margin:0 .35714286em;background:0 0;-webkit-transition:color .1s ease;transition:color .1s ease;border-radius:.28571429rem}.ui.secondary.menu .item:before{display:none!important}.ui.secondary.menu .header.item{border-radius:0;border-right:none;background:none transparent}.ui.secondary.menu .item>img:not(.ui){margin:0}.ui.secondary.menu .dropdown.item:hover,.ui.secondary.menu .link.item:hover,.ui.secondary.menu a.item:hover{background:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.secondary.menu .active.item{-webkit-box-shadow:none;box-shadow:none;background:rgba(0,0,0,.05);color:rgba(0,0,0,.95);border-radius:.28571429rem}.ui.secondary.menu .active.item:hover{-webkit-box-shadow:none;box-shadow:none;background:rgba(0,0,0,.05);color:rgba(0,0,0,.95)}.ui.secondary.inverted.menu .link.item:not(.disabled),.ui.secondary.inverted.menu a.item:not(.disabled){color:rgba(255,255,255,.7)}.ui.secondary.inverted.menu .dropdown.item:hover,.ui.secondary.inverted.menu .link.item:hover,.ui.secondary.inverted.menu a.item:hover{background:rgba(255,255,255,.08);color:#fff}.ui.secondary.inverted.menu .active.item{background:rgba(255,255,255,.15);color:#fff}.ui.secondary.item.menu{margin-left:0;margin-right:0}.ui.secondary.item.menu .item:last-child{margin-right:0}.ui.secondary.attached.menu{-webkit-box-shadow:none;box-shadow:none}.ui.vertical.secondary.menu .item:not(.dropdown)>.menu{margin:0 -.92857143em}.ui.vertical.secondary.menu .item:not(.dropdown)>.menu>.item{margin:0;padding:.5em 1.33333333em}.ui.secondary.vertical.menu>.item{border:none;margin:0 0 .35714286em;border-radius:.28571429rem!important}.ui.secondary.vertical.menu>.header.item{border-radius:0}.ui.vertical.secondary.menu .item>.menu .item{background-color:transparent}.ui.secondary.inverted.menu{background-color:transparent}.ui.secondary.pointing.menu{margin-left:0;margin-right:0;border-bottom:2px solid rgba(34,36,38,.15)}.ui.secondary.pointing.menu .item{border-bottom-color:transparent;border-bottom-style:solid;border-radius:0;-ms-flex-item-align:end;align-self:flex-end;margin:0 0 -2px;padding:.85714286em 1.14285714em;border-bottom-width:2px;-webkit-transition:color .1s ease;transition:color .1s ease}.ui.secondary.pointing.menu .ui.dropdown .menu .item{border-bottom-width:0}.ui.secondary.pointing.menu .item>.label:not(.floating){margin-top:-.3em;margin-bottom:-.3em}.ui.secondary.pointing.menu .item>.circular.label{margin-top:-.5em;margin-bottom:-.5em}.ui.secondary.pointing.menu .header.item{color:rgba(0,0,0,.85)!important}.ui.secondary.pointing.menu .text.item{-webkit-box-shadow:none!important;box-shadow:none!important}.ui.secondary.pointing.menu .item:after{display:none}.ui.secondary.pointing.menu .dropdown.item:hover,.ui.secondary.pointing.menu .link.item:hover,.ui.secondary.pointing.menu a.item:hover{background-color:transparent;color:rgba(0,0,0,.87)}.ui.secondary.pointing.menu .dropdown.item:active,.ui.secondary.pointing.menu .link.item:active,.ui.secondary.pointing.menu a.item:active{background-color:transparent;border-color:rgba(34,36,38,.15)}.ui.secondary.pointing.menu .active.item{background-color:transparent;-webkit-box-shadow:none;box-shadow:none;border-color:currentColor;font-weight:700;color:rgba(0,0,0,.95)}.ui.secondary.pointing.menu .active.item:hover{border-color:currentColor;color:rgba(0,0,0,.95)}.ui.secondary.pointing.menu .active.dropdown.item{border-color:transparent}.ui.secondary.vertical.pointing.menu{border-bottom-width:0;border-right-width:2px;border-right-style:solid;border-right-color:rgba(34,36,38,.15)}.ui.secondary.vertical.pointing.menu .item{border-bottom:none;border-right-style:solid;border-right-color:transparent;border-radius:0!important;margin:0 -2px 0 0;border-right-width:2px}.ui.secondary.vertical.pointing.menu .active.item{border-color:currentColor}.ui.secondary.inverted.pointing.menu{border-color:rgba(255,255,255,.1)}.ui.secondary.inverted.pointing.menu .item:not(.disabled){color:rgba(255,255,255,.9)}.ui.secondary.inverted.pointing.menu .header.item{color:#fff!important}.ui.secondary.inverted.pointing.menu .link.item:hover,.ui.secondary.inverted.pointing.menu a.item:hover{color:#fff}.ui.ui.secondary.inverted.pointing.menu .active.item{border-color:#fff;color:#fff;background-color:transparent}.ui.text.menu{background:none transparent;border-radius:0;-webkit-box-shadow:none;box-shadow:none;border:none;margin:1em -.5em}.ui.text.menu .item{border-radius:0;-webkit-box-shadow:none;box-shadow:none;-ms-flex-item-align:center;align-self:center;margin:0 0;padding:.35714286em .5em;font-weight:400;color:rgba(0,0,0,.6);-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.ui.text.menu .item:before,.ui.text.menu .menu .item:before{display:none!important}.ui.text.menu .header.item{background-color:transparent;opacity:1;color:rgba(0,0,0,.85);font-size:.92857143em;text-transform:uppercase;font-weight:700}.ui.text.menu .item>img:not(.ui){margin:0}.ui.text.item.menu .item{margin:0}.ui.vertical.text.menu{margin:1em 0}.ui.vertical.text.menu:first-child{margin-top:0}.ui.vertical.text.menu:last-child{margin-bottom:0}.ui.vertical.text.menu .item{margin:.57142857em 0;padding-left:0;padding-right:0}.ui.vertical.text.menu .item>i.icon{float:none;margin:0 .35714286em 0 0}.ui.vertical.text.menu .header.item{margin:.57142857em 0 .71428571em}.ui.vertical.text.menu .item:not(.dropdown)>.menu{margin:0}.ui.vertical.text.menu .item:not(.dropdown)>.menu>.item{margin:0;padding:.5em 0}.ui.text.menu .item:hover{opacity:1;background-color:transparent}.ui.text.menu .active.item{background-color:transparent;border:none;-webkit-box-shadow:none;box-shadow:none;font-weight:400;color:rgba(0,0,0,.95)}.ui.text.menu .active.item:hover{background-color:transparent}.ui.text.pointing.menu .active.item:after{-webkit-box-shadow:none;box-shadow:none}.ui.text.attached.menu{-webkit-box-shadow:none;box-shadow:none}.ui.inverted.text.menu,.ui.inverted.text.menu .active.item,.ui.inverted.text.menu .item,.ui.inverted.text.menu .item:hover{background-color:transparent}.ui.fluid.text.menu{margin-left:0;margin-right:0}.ui.vertical.icon.menu{display:inline-block;width:auto}.ui.icon.menu .item{height:auto;text-align:center;color:#1b1c1d}.ui.icon.menu .item>.icon:not(.dropdown){margin:0;opacity:1}.ui.icon.menu .icon:before{opacity:1}.ui.menu .icon.item>.icon{width:auto;margin:0 auto}.ui.vertical.icon.menu .item>.icon:not(.dropdown){display:block;opacity:1;margin:0 auto;float:none}.ui.inverted.icon.menu .item{color:#fff}.ui.labeled.icon.menu{text-align:center}.ui.labeled.icon.menu .item{min-width:6em;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.labeled.icon.menu>.item>.icon:not(.dropdown){height:1em;display:block;font-size:1.71428571em!important;margin:0 auto .5rem!important}.ui.fluid.labeled.icon.menu>.item{min-width:0}@media only screen and (max-width:767.98px){.ui.stackable.menu{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ui.stackable.menu .item{width:100%!important}.ui.stackable.menu .item:before{position:absolute;content:'';top:auto;bottom:0;left:0;width:100%;height:1px;background:rgba(34,36,38,.1)}.ui.stackable.menu .left.item,.ui.stackable.menu .left.menu{margin-right:0!important}.ui.stackable.menu .right.item,.ui.stackable.menu .right.menu{margin-left:0!important}.ui.stackable.menu .center.item,.ui.stackable.menu .center.menu{margin-left:0!important;margin-right:0!important}.ui.stackable.menu .center.menu,.ui.stackable.menu .left.menu,.ui.stackable.menu .right.menu{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.ui.ui.menu .primary.active.item,.ui.ui.primary.menu .active.item,.ui.ui.primary.menu .active.item:hover{color:#2185d0}.ui.ui.menu .red.active.item,.ui.ui.red.menu .active.item,.ui.ui.red.menu .active.item:hover{color:#db2828}.ui.ui.menu .orange.active.item,.ui.ui.orange.menu .active.item,.ui.ui.orange.menu .active.item:hover{color:#f2711c}.ui.ui.menu .yellow.active.item,.ui.ui.yellow.menu .active.item,.ui.ui.yellow.menu .active.item:hover{color:#fbbd08}.ui.ui.menu .olive.active.item,.ui.ui.olive.menu .active.item,.ui.ui.olive.menu .active.item:hover{color:#b5cc18}.ui.ui.green.menu .active.item,.ui.ui.green.menu .active.item:hover,.ui.ui.menu .green.active.item{color:#21ba45}.ui.ui.menu .teal.active.item,.ui.ui.teal.menu .active.item,.ui.ui.teal.menu .active.item:hover{color:#00b5ad}.ui.ui.blue.menu .active.item,.ui.ui.blue.menu .active.item:hover,.ui.ui.menu .blue.active.item{color:#2185d0}.ui.ui.menu .violet.active.item,.ui.ui.violet.menu .active.item,.ui.ui.violet.menu .active.item:hover{color:#6435c9}.ui.ui.menu .purple.active.item,.ui.ui.purple.menu .active.item,.ui.ui.purple.menu .active.item:hover{color:#a333c8}.ui.ui.menu .pink.active.item,.ui.ui.pink.menu .active.item,.ui.ui.pink.menu .active.item:hover{color:#e03997}.ui.ui.brown.menu .active.item,.ui.ui.brown.menu .active.item:hover,.ui.ui.menu .brown.active.item{color:#a5673f}.ui.ui.grey.menu .active.item,.ui.ui.grey.menu .active.item:hover,.ui.ui.menu .grey.active.item{color:#767676}.ui.ui.black.menu .active.item,.ui.ui.black.menu .active.item:hover,.ui.ui.menu .black.active.item{color:#1b1c1d}.ui.inverted.menu{border:0 solid transparent;background:#1b1c1d;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.menu .item,.ui.inverted.menu .item>a:not(.ui){background:0 0;color:rgba(255,255,255,.9)}.ui.inverted.menu .item.menu{background:0 0}.ui.inverted.menu .item:before{background:rgba(255,255,255,.08)}.ui.vertical.inverted.menu .item:before{background:rgba(255,255,255,.08)}.ui.vertical.inverted.menu .menu .item,.ui.vertical.inverted.menu .menu .item a:not(.ui){color:rgba(255,255,255,.5)}.ui.inverted.menu .header.item{margin:0;background:0 0;-webkit-box-shadow:none;box-shadow:none}.ui.ui.inverted.menu .item.disabled{color:rgba(225,225,225,.3)}.ui.inverted.menu .dropdown.item:hover,.ui.inverted.menu .link.item:hover,.ui.inverted.menu a.item:hover,.ui.link.inverted.menu .item:hover{background:rgba(255,255,255,.08);color:#fff}.ui.vertical.inverted.menu .item .menu .link.item:hover,.ui.vertical.inverted.menu .item .menu a.item:hover{background:0 0;color:#fff}.ui.inverted.menu .link.item:active,.ui.inverted.menu a.item:active{background:rgba(255,255,255,.08);color:#fff}.ui.inverted.menu .active.item{background:#3d3e3f;color:#fff!important}.ui.inverted.vertical.menu .item .menu .active.item{background:0 0;color:#fff}.ui.inverted.pointing.menu .active.item:after{background:#3d3e3f;margin:0!important;-webkit-box-shadow:none!important;box-shadow:none!important;border:none!important}.ui.inverted.menu .active.item:hover{background:#3d3e3f;color:#fff!important}.ui.inverted.pointing.menu .active.item:hover:after{background:#3d3e3f}.ui.floated.menu{float:left;margin:0 .5rem 0 0}.ui.floated.menu .item:last-child:before{display:none}.ui.right.floated.menu{float:right;margin:0 0 0 .5rem}.ui.ui.inverted.menu .primary.active.item,.ui.ui.inverted.primary.menu{background-color:#2185d0}.ui.inverted.primary.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.primary.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.primary.menu .active.item{background-color:#1678c2}.ui.ui.inverted.menu .red.active.item,.ui.ui.inverted.red.menu{background-color:#db2828}.ui.inverted.red.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.red.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.red.menu .active.item{background-color:#d01919}.ui.ui.inverted.menu .orange.active.item,.ui.ui.inverted.orange.menu{background-color:#f2711c}.ui.inverted.orange.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.orange.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.orange.menu .active.item{background-color:#f26202}.ui.ui.inverted.menu .yellow.active.item,.ui.ui.inverted.yellow.menu{background-color:#fbbd08}.ui.inverted.yellow.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.yellow.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.yellow.menu .active.item{background-color:#eaae00}.ui.ui.inverted.menu .olive.active.item,.ui.ui.inverted.olive.menu{background-color:#b5cc18}.ui.inverted.olive.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.olive.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.olive.menu .active.item{background-color:#a7bd0d}.ui.ui.inverted.green.menu,.ui.ui.inverted.menu .green.active.item{background-color:#21ba45}.ui.inverted.green.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.green.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.green.menu .active.item{background-color:#16ab39}.ui.ui.inverted.menu .teal.active.item,.ui.ui.inverted.teal.menu{background-color:#00b5ad}.ui.inverted.teal.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.teal.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.teal.menu .active.item{background-color:#009c95}.ui.ui.inverted.blue.menu,.ui.ui.inverted.menu .blue.active.item{background-color:#2185d0}.ui.inverted.blue.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.blue.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.blue.menu .active.item{background-color:#1678c2}.ui.ui.inverted.menu .violet.active.item,.ui.ui.inverted.violet.menu{background-color:#6435c9}.ui.inverted.violet.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.violet.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.violet.menu .active.item{background-color:#5829bb}.ui.ui.inverted.menu .purple.active.item,.ui.ui.inverted.purple.menu{background-color:#a333c8}.ui.inverted.purple.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.purple.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.purple.menu .active.item{background-color:#9627ba}.ui.ui.inverted.menu .pink.active.item,.ui.ui.inverted.pink.menu{background-color:#e03997}.ui.inverted.pink.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.pink.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.pink.menu .active.item{background-color:#e61a8d}.ui.ui.inverted.brown.menu,.ui.ui.inverted.menu .brown.active.item{background-color:#a5673f}.ui.inverted.brown.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.brown.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.brown.menu .active.item{background-color:#975b33}.ui.ui.inverted.grey.menu,.ui.ui.inverted.menu .grey.active.item{background-color:#767676}.ui.inverted.grey.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.grey.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.grey.menu .active.item{background-color:#838383}.ui.ui.inverted.black.menu,.ui.ui.inverted.menu .black.active.item{background-color:#1b1c1d}.ui.inverted.black.menu .item:before{background-color:rgba(34,36,38,.1)}.ui.ui.inverted.black.menu .active.item{background-color:rgba(0,0,0,.1)}.ui.inverted.pointing.black.menu .active.item{background-color:#27292a}.ui.ui.ui.inverted.pointing.menu .active.item:after{background-color:inherit}.ui.fitted.menu .item,.ui.fitted.menu .item .menu .item,.ui.menu .fitted.item{padding:0}.ui.horizontally.fitted.menu .item,.ui.horizontally.fitted.menu .item .menu .item,.ui.menu .horizontally.fitted.item{padding-top:.92857143em;padding-bottom:.92857143em}.ui.menu .vertically.fitted.item,.ui.vertically.fitted.menu .item,.ui.vertically.fitted.menu .item .menu .item{padding-left:1.14285714em;padding-right:1.14285714em}.ui.borderless.menu .item .menu .item:before,.ui.borderless.menu .item:before,.ui.menu .borderless.item:before{background:0 0!important}.ui.compact.menu{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin:0;vertical-align:middle}.ui.compact.vertical.menu{display:-ms-inline-flexbox!important;display:inline-block}.ui.compact.menu:not(.secondary) .item:last-child{border-radius:0 .28571429rem .28571429rem 0}.ui.compact.menu .item:last-child:before{display:none}.ui.compact.vertical.menu{width:auto!important}.ui.compact.vertical.menu .item:last-child::before{display:block}.ui.menu.fluid,.ui.vertical.menu.fluid{width:100%!important}.ui.item.menu,.ui.item.menu .item{width:100%;padding-left:0!important;padding-right:0!important;margin-left:0!important;margin-right:0!important;text-align:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ui.attached.item.menu:not(.tabular){margin:0 -1px!important}.ui.item.menu .item:last-child:before{display:none}.ui.menu.two.item .item{width:50%}.ui.menu.three.item .item{width:33.333%}.ui.menu.four.item .item{width:25%}.ui.menu.five.item .item{width:20%}.ui.menu.six.item .item{width:16.666%}.ui.menu.seven.item .item{width:14.285%}.ui.menu.eight.item .item{width:12.5%}.ui.menu.nine.item .item{width:11.11%}.ui.menu.ten.item .item{width:10%}.ui.menu.eleven.item .item{width:9.09%}.ui.menu.twelve.item .item{width:8.333%}.ui.menu.fixed{position:fixed;z-index:101;margin:0;width:100%}.ui.menu.fixed,.ui.menu.fixed .item:first-child,.ui.menu.fixed .item:last-child{border-radius:0!important}.ui.fixed.menu,.ui[class*="top fixed"].menu{top:0;left:0;right:auto;bottom:auto}.ui[class*="top fixed"].menu{border-top:none;border-left:none;border-right:none}.ui[class*="right fixed"].menu{border-top:none;border-bottom:none;border-right:none;top:0;right:0;left:auto;bottom:auto;width:auto;height:100%}.ui[class*="bottom fixed"].menu{border-bottom:none;border-left:none;border-right:none;bottom:0;left:0;top:auto;right:auto}.ui[class*="left fixed"].menu{border-top:none;border-bottom:none;border-left:none;top:0;left:0;right:auto;bottom:auto;width:auto;height:100%}.ui.fixed.menu+.ui.grid{padding-top:2.75rem}.ui.pointing.menu .item:after{visibility:hidden;position:absolute;content:'';top:100%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);background:0 0;margin:.5px 0 0;width:.57142857em;height:.57142857em;border:none;border-bottom:1px solid #d4d4d5;border-right:1px solid #d4d4d5;z-index:2;-webkit-transition:background .1s ease;transition:background .1s ease}.ui.vertical.pointing.menu .item:after{position:absolute;top:50%;right:0;bottom:auto;left:auto;-webkit-transform:translateX(50%) translateY(-50%) rotate(45deg);transform:translateX(50%) translateY(-50%) rotate(45deg);margin:0 -.5px 0 0;border:none;border-top:1px solid #d4d4d5;border-right:1px solid #d4d4d5}.ui.pointing.menu .ui.dropdown .menu .item:after,.ui.vertical.pointing.menu .ui.dropdown .menu .item:after{display:none}.ui.pointing.menu .active.item:after{visibility:visible}.ui.pointing.menu .active.dropdown.item:after{visibility:hidden}.ui.pointing.menu .active.item .menu .active.item:after,.ui.pointing.menu .dropdown.active.item:after{display:none}.ui.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.pointing.menu .active.item:after{background-color:#f2f2f2}.ui.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .active.item:hover:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .active.item:after{background-color:#f2f2f2}.ui.vertical.pointing.menu .menu .active.item:after{background-color:#fff}.ui.inverted.pointing.menu .primary.active.item:after{background-color:#2185d0}.ui.inverted.pointing.menu .secondary.active.item:after{background-color:#1b1c1d}.ui.inverted.pointing.menu .red.active.item:after{background-color:#db2828}.ui.inverted.pointing.menu .orange.active.item:after{background-color:#f2711c}.ui.inverted.pointing.menu .yellow.active.item:after{background-color:#fbbd08}.ui.inverted.pointing.menu .olive.active.item:after{background-color:#b5cc18}.ui.inverted.pointing.menu .green.active.item:after{background-color:#21ba45}.ui.inverted.pointing.menu .teal.active.item:after{background-color:#00b5ad}.ui.inverted.pointing.menu .blue.active.item:after{background-color:#2185d0}.ui.inverted.pointing.menu .violet.active.item:after{background-color:#6435c9}.ui.inverted.pointing.menu .purple.active.item:after{background-color:#a333c8}.ui.inverted.pointing.menu .pink.active.item:after{background-color:#e03997}.ui.inverted.pointing.menu .brown.active.item:after{background-color:#a5673f}.ui.inverted.pointing.menu .grey.active.item:after{background-color:#767676}.ui.inverted.pointing.menu .black.active.item:after{background-color:#1b1c1d}.ui.attached.menu{top:0;bottom:0;border-radius:0;margin:0 -1px;width:calc(100% + 2px);max-width:calc(100% + 2px);-webkit-box-shadow:none;box-shadow:none}.ui.attached+.ui.attached.menu:not(.top){border-top:none}.ui[class*="top attached"].menu{bottom:0;margin-bottom:0;top:0;margin-top:1rem;border-radius:.28571429rem .28571429rem 0 0}.ui.menu[class*="top attached"]:first-child{margin-top:0}.ui[class*="bottom attached"].menu{bottom:0;margin-top:0;top:0;margin-bottom:1rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;border-radius:0 0 .28571429rem .28571429rem}.ui[class*="bottom attached"].menu:last-child{margin-bottom:0}.ui.top.attached.menu>.item:first-child{border-radius:.28571429rem 0 0 0}.ui.bottom.attached.menu>.item:first-child{border-radius:0 0 0 .28571429rem}.ui.attached.menu:not(.tabular){border:1px solid #d4d4d5}.ui.attached.inverted.menu{border:none}.ui.attached.tabular.menu{margin-left:0;margin-right:0;width:100%}.ui.menu{font-size:1rem}.ui.vertical.menu{width:15rem}.ui.mini.menu,.ui.mini.menu .dropdown,.ui.mini.menu .dropdown .menu>.item{font-size:.78571429rem}.ui.mini.vertical.menu:not(.icon){width:9rem}.ui.tiny.menu,.ui.tiny.menu .dropdown,.ui.tiny.menu .dropdown .menu>.item{font-size:.85714286rem}.ui.tiny.vertical.menu:not(.icon){width:11rem}.ui.small.menu,.ui.small.menu .dropdown,.ui.small.menu .dropdown .menu>.item{font-size:.92857143rem}.ui.small.vertical.menu:not(.icon){width:13rem}.ui.large.menu,.ui.large.menu .dropdown,.ui.large.menu .dropdown .menu>.item{font-size:1.07142857rem}.ui.large.vertical.menu:not(.icon){width:18rem}.ui.big.menu,.ui.big.menu .dropdown,.ui.big.menu .dropdown .menu>.item{font-size:1.14285714rem}.ui.big.vertical.menu:not(.icon){width:20rem}.ui.huge.menu,.ui.huge.menu .dropdown,.ui.huge.menu .dropdown .menu>.item{font-size:1.21428571rem}.ui.huge.vertical.menu:not(.icon){width:22rem}.ui.massive.menu,.ui.massive.menu .dropdown,.ui.massive.menu .dropdown .menu>.item{font-size:1.28571429rem}.ui.massive.vertical.menu:not(.icon){width:25rem}.ui.menu .ui.inverted.inverted.dropdown.item .menu{background:#1b1c1d;-webkit-box-shadow:none;box-shadow:none}.ui.menu .ui.inverted.dropdown .menu>.item{color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.active.item{background:0 0!important;color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.item:hover{background:rgba(255,255,255,.08)!important;color:rgba(255,255,255,.8)!important}.ui.menu .ui.inverted.dropdown .menu>.selected.item{background:rgba(255,255,255,.15)!important;color:rgba(255,255,255,.8)!important}.ui.vertical.menu .inverted.dropdown.item .menu{-webkit-box-shadow:none;box-shadow:none} + /* + * # Fomantic-UI - Segment + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + .ui.segment{position:relative;background:#fff;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);margin:1rem 0;padding:1em 1em;border-radius:.28571429rem;border:1px solid rgba(34,36,38,.15)}.ui.segment:first-child{margin-top:0}.ui.segment:last-child{margin-bottom:0}.ui.vertical.segment{margin:0;padding-left:0;padding-right:0;background:none transparent;border-radius:0;-webkit-box-shadow:none;box-shadow:none;border:none;border-bottom:1px solid rgba(34,36,38,.15)}.ui.vertical.segment:last-child{border-bottom:none}.ui.inverted.segment>.ui.header,.ui.inverted.segment>.ui.header>.sub.header{color:#fff}.ui[class*="bottom attached"].segment>[class*="top attached"].label{border-top-left-radius:0;border-top-right-radius:0}.ui[class*="top attached"].segment>[class*="bottom attached"].label{border-bottom-left-radius:0;border-bottom-right-radius:0}.ui.attached.segment:not(.top):not(.bottom)>[class*="top attached"].label{border-top-left-radius:0;border-top-right-radius:0}.ui.attached.segment:not(.top):not(.bottom)>[class*="bottom attached"].label{border-bottom-left-radius:0;border-bottom-right-radius:0}.ui.grid>.row>.ui.segment.column,.ui.grid>.ui.segment.column,.ui.page.grid.segment{padding-top:2em;padding-bottom:2em}.ui.grid.segment{margin:1rem 0;border-radius:.28571429rem}.ui.basic.table.segment{background:#fff;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15)}.ui[class*="very basic"].table.segment{padding:1em 1em}.ui.segment.tab:last-child{margin-bottom:1rem}.ui.placeholder.segment{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;max-width:initial;-webkit-animation:none;animation:none;overflow:visible;padding:1em 1em;min-height:18rem;background:#f9fafb;border-color:rgba(34,36,38,.15);-webkit-box-shadow:0 2px 25px 0 rgba(34,36,38,.05) inset;box-shadow:0 2px 25px 0 rgba(34,36,38,.05) inset}.ui.placeholder.segment .button,.ui.placeholder.segment textarea{display:block}.ui.placeholder.segment .button,.ui.placeholder.segment .field,.ui.placeholder.segment textarea,.ui.placeholder.segment>.ui.input{max-width:15rem;margin-left:auto;margin-right:auto}.ui.placeholder.segment .column .button,.ui.placeholder.segment .column .field,.ui.placeholder.segment .column textarea,.ui.placeholder.segment .column>.ui.input{max-width:15rem;margin-left:auto;margin-right:auto}.ui.placeholder.segment>.inline{-ms-flex-item-align:center;align-self:center}.ui.placeholder.segment>.inline>.button{display:inline-block;width:auto;margin:0 .35714286rem 0 0}.ui.placeholder.segment>.inline>.button:last-child{margin-right:0}.ui.piled.segment,.ui.piled.segments{margin:3em 0;-webkit-box-shadow:'';box-shadow:'';z-index:auto}.ui.piled.segment:first-child{margin-top:0}.ui.piled.segment:last-child{margin-bottom:0}.ui.piled.segment:after,.ui.piled.segment:before,.ui.piled.segments:after,.ui.piled.segments:before{background-color:#fff;visibility:visible;content:'';display:block;height:100%;left:0;position:absolute;width:100%;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:'';box-shadow:''}.ui.piled.segment:before,.ui.piled.segments:before{-webkit-transform:rotate(-1.2deg);transform:rotate(-1.2deg);top:0;z-index:-2}.ui.piled.segment:after,.ui.piled.segments:after{-webkit-transform:rotate(1.2deg);transform:rotate(1.2deg);top:0;z-index:-1}.ui[class*="top attached"].piled.segment{margin-top:3em;margin-bottom:0}.ui.piled.segment[class*="top attached"]:first-child{margin-top:0}.ui.piled.segment[class*="bottom attached"]{margin-top:0;margin-bottom:3em}.ui.piled.segment[class*="bottom attached"]:last-child{margin-bottom:0}.ui.stacked.segment{padding-bottom:1.4em}.ui.stacked.segment:after,.ui.stacked.segment:before,.ui.stacked.segments:after,.ui.stacked.segments:before{content:'';position:absolute;bottom:-3px;left:0;border-top:1px solid rgba(34,36,38,.15);background:rgba(0,0,0,.03);width:100%;height:6px;visibility:visible}.ui.stacked.segment:before,.ui.stacked.segments:before{display:none}.ui.tall.stacked.segment:before,.ui.tall.stacked.segments:before{display:block;bottom:0}.ui.stacked.inverted.segment:after,.ui.stacked.inverted.segment:before,.ui.stacked.inverted.segments:after,.ui.stacked.inverted.segments:before{background-color:rgba(0,0,0,.03);border-top:1px solid rgba(34,36,38,.35)}.ui.padded.segment{padding:1.5em}.ui[class*="very padded"].segment{padding:3em}.ui.padded.segment.vertical.segment,.ui[class*="very padded"].vertical.segment{padding-left:0;padding-right:0}.ui.compact.segment{display:table}.ui.compact.segments{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}.ui.compact.segments .segment,.ui.segments .compact.segment{display:block;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto}.ui.circular.segment{display:table-cell;padding:2em;text-align:center;vertical-align:middle;border-radius:500em}.ui.raised.raised.segment,.ui.raised.raised.segments{-webkit-box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15);box-shadow:0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.15)}.ui.segments{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;position:relative;margin:1rem 0;border:1px solid rgba(34,36,38,.15);-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);border-radius:.28571429rem}.ui.segments:first-child{margin-top:0}.ui.segments:last-child{margin-bottom:0}.ui.segments>.segment{top:0;bottom:0;border-radius:0;margin:0;width:auto;-webkit-box-shadow:none;box-shadow:none;border:none;border-top:1px solid rgba(34,36,38,.15)}.ui.segments:not(.horizontal)>.segment:first-child{top:0;bottom:0;border-top:none;margin-top:0;margin-bottom:0;border-radius:.28571429rem .28571429rem 0 0}.ui.segments:not(.horizontal)>.segment:last-child{top:0;bottom:0;margin-top:0;margin-bottom:0;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;border-radius:0 0 .28571429rem .28571429rem}.ui.segments:not(.horizontal)>.segment:only-child{border-radius:.28571429rem}.ui.segments>.ui.segments{border-top:1px solid rgba(34,36,38,.15);margin:1rem 1rem}.ui.segments>.segments:first-child{border-top:none}.ui.segments>.segment+.segments:not(.horizontal){margin-top:0}.ui.horizontal.segments{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;background-color:transparent;padding:0;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15);box-shadow:0 1px 2px 0 rgba(34,36,38,.15);margin:1rem 0;border-radius:.28571429rem;border:1px solid rgba(34,36,38,.15)}.ui.stackable.horizontal.segments{-ms-flex-wrap:wrap;flex-wrap:wrap}.ui.segments>.horizontal.segments{margin:0;background-color:transparent;border-radius:0;border:none;-webkit-box-shadow:none;box-shadow:none;border-top:1px solid rgba(34,36,38,.15)}.ui.horizontal.segments:not(.compact)>.segment:not(.compact){-webkit-box-flex:1;flex:1 1 auto;-ms-flex:1 1 0}.ui.horizontal.segments>.segment{margin:0;min-width:0;border-radius:0;border:none;-webkit-box-shadow:none;box-shadow:none;border-left:1px solid rgba(34,36,38,.15)}.ui.segments>.horizontal.segments:first-child{border-top:none}.ui.horizontal.segments:not(.stackable)>.segment:first-child{border-left:none}.ui.disabled.segment{opacity:.45;color:rgba(40,40,40,.3)}.ui.loading.segment{position:relative;cursor:default;pointer-events:none;text-shadow:none!important;-webkit-transition:all 0s linear;transition:all 0s linear}.ui.loading.segment:before{position:absolute;content:'';top:0;left:0;background:rgba(255,255,255,.8);width:100%;height:100%;border-radius:.28571429rem;z-index:100}.ui.loading.segment:after{position:absolute;content:'';top:50%;left:50%;margin:-1.5em 0 0 -1.5em;width:3em;height:3em;-webkit-animation:loader .6s infinite linear;animation:loader .6s infinite linear;border:.2em solid #767676;border-radius:500rem;-webkit-box-shadow:0 0 0 1px transparent;box-shadow:0 0 0 1px transparent;visibility:visible;z-index:101}.ui.basic.segment,.ui.basic.segments,.ui.segments .ui.basic.segment{background:none transparent;-webkit-box-shadow:none;box-shadow:none;border:none;border-radius:0}.ui.clearing.segment:after{content:"";display:block;clear:both}.ui.red.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #db2828}.ui.inverted.red.segment.segment.segment.segment.segment{background-color:#db2828;color:#fff}.ui.orange.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #f2711c}.ui.inverted.orange.segment.segment.segment.segment.segment{background-color:#f2711c;color:#fff}.ui.yellow.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #fbbd08}.ui.inverted.yellow.segment.segment.segment.segment.segment{background-color:#fbbd08;color:#fff}.ui.olive.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #b5cc18}.ui.inverted.olive.segment.segment.segment.segment.segment{background-color:#b5cc18;color:#fff}.ui.green.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #21ba45}.ui.inverted.green.segment.segment.segment.segment.segment{background-color:#21ba45;color:#fff}.ui.teal.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #00b5ad}.ui.inverted.teal.segment.segment.segment.segment.segment{background-color:#00b5ad;color:#fff}.ui.blue.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #2185d0}.ui.inverted.blue.segment.segment.segment.segment.segment{background-color:#2185d0;color:#fff}.ui.violet.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #6435c9}.ui.inverted.violet.segment.segment.segment.segment.segment{background-color:#6435c9;color:#fff}.ui.purple.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #a333c8}.ui.inverted.purple.segment.segment.segment.segment.segment{background-color:#a333c8;color:#fff}.ui.pink.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #e03997}.ui.inverted.pink.segment.segment.segment.segment.segment{background-color:#e03997;color:#fff}.ui.brown.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #a5673f}.ui.inverted.brown.segment.segment.segment.segment.segment{background-color:#a5673f;color:#fff}.ui.grey.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #767676}.ui.inverted.grey.segment.segment.segment.segment.segment{background-color:#767676;color:#fff}.ui.black.segment.segment.segment.segment.segment:not(.inverted){border-top:2px solid #1b1c1d}.ui.inverted.black.segment.segment.segment.segment.segment{background-color:#1b1c1d;color:#fff}.ui[class*="left aligned"].segment{text-align:left}.ui[class*="right aligned"].segment{text-align:right}.ui[class*="center aligned"].segment{text-align:center}.ui.floated.segment,.ui[class*="left floated"].segment{float:left;margin-right:1em}.ui[class*="right floated"].segment{float:right;margin-left:1em}.ui.inverted.segment{border:none;-webkit-box-shadow:none;box-shadow:none}.ui.inverted.segment,.ui.primary.inverted.segment{background:#1b1c1d;color:rgba(255,255,255,.9)}.ui.inverted.segment .segment{color:rgba(0,0,0,.87)}.ui.inverted.segment .inverted.segment{color:rgba(255,255,255,.9)}.ui.inverted.attached.segment{border-color:#555}.ui.inverted.loading.segment{color:#fff}.ui.inverted.loading.segment:before{background:rgba(0,0,0,.85)}.ui.secondary.segment{background:#f3f4f5;color:rgba(0,0,0,.6)}.ui.secondary.inverted.segment{background:#4c4f52 -webkit-gradient(linear,left top,left bottom,color-stop(0,rgba(255,255,255,.2)),to(rgba(255,255,255,.2)));background:#4c4f52 -webkit-linear-gradient(rgba(255,255,255,.2) 0,rgba(255,255,255,.2) 100%);background:#4c4f52 linear-gradient(rgba(255,255,255,.2) 0,rgba(255,255,255,.2) 100%);color:rgba(255,255,255,.8)}.ui.tertiary.segment{background:#dcddde;color:rgba(0,0,0,.6)}.ui.tertiary.inverted.segment{background:#717579 -webkit-gradient(linear,left top,left bottom,color-stop(0,rgba(255,255,255,.35)),to(rgba(255,255,255,.35)));background:#717579 -webkit-linear-gradient(rgba(255,255,255,.35) 0,rgba(255,255,255,.35) 100%);background:#717579 linear-gradient(rgba(255,255,255,.35) 0,rgba(255,255,255,.35) 100%);color:rgba(255,255,255,.8)}.ui.attached.segment{top:0;bottom:0;border-radius:0;margin:0 -1px;width:calc(100% + 2px);max-width:calc(100% + 2px);-webkit-box-shadow:none;box-shadow:none;border:1px solid #d4d4d5}.ui.attached:not(.message)+.ui.attached.segment:not(.top){border-top:none}.ui[class*="top attached"].segment{bottom:0;margin-bottom:0;top:0;margin-top:1rem;border-radius:.28571429rem .28571429rem 0 0}.ui.segment[class*="top attached"]:first-child{margin-top:0}.ui.segment[class*="bottom attached"]{bottom:0;margin-top:0;top:0;margin-bottom:1rem;-webkit-box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;box-shadow:0 1px 2px 0 rgba(34,36,38,.15),none;border-radius:0 0 .28571429rem .28571429rem}.ui.segment[class*="bottom attached"]:last-child{margin-bottom:1rem}.ui.fitted.segment:not(.horizontally){padding-top:0;padding-bottom:0}.ui.fitted.segment:not(.vertically){padding-left:0;padding-right:0}.ui.segment,.ui.segments .segment{font-size:1rem}.ui.mini.segment,.ui.mini.segments .segment{font-size:.78571429rem}.ui.tiny.segment,.ui.tiny.segments .segment{font-size:.85714286rem}.ui.small.segment,.ui.small.segments .segment{font-size:.92857143rem}.ui.large.segment,.ui.large.segments .segment{font-size:1.14285714rem}.ui.big.segment,.ui.big.segments .segment{font-size:1.28571429rem}.ui.huge.segment,.ui.huge.segments .segment{font-size:1.42857143rem}.ui.massive.segment,.ui.massive.segments .segment{font-size:1.71428571rem} + /* + * # Fomantic-UI - Text + * http://github.com/fomantic/Fomantic-UI/ + * + * + * Released under the MIT license + * https://github.com/fomantic/Fomantic-UI/blob/master/LICENSE.md + * + */ + span.ui.primary.text{color:#2185d0}span.ui.inverted.primary.text{color:#54c8ff}span.ui.secondary.text{color:#1b1c1d}span.ui.inverted.secondary.text{color:#545454}span.ui.red.text{color:#db2828}span.ui.inverted.red.text{color:#ff695e}span.ui.orange.text{color:#f2711c}span.ui.inverted.orange.text{color:#ff851b}span.ui.yellow.text{color:#fbbd08}span.ui.inverted.yellow.text{color:#ffe21f}span.ui.olive.text{color:#b5cc18}span.ui.inverted.olive.text{color:#d9e778}span.ui.green.text{color:#21ba45}span.ui.inverted.green.text{color:#2ecc40}span.ui.teal.text{color:#00b5ad}span.ui.inverted.teal.text{color:#6dffff}span.ui.blue.text{color:#2185d0}span.ui.inverted.blue.text{color:#54c8ff}span.ui.violet.text{color:#6435c9}span.ui.inverted.violet.text{color:#a291fb}span.ui.purple.text{color:#a333c8}span.ui.inverted.purple.text{color:#dc73ff}span.ui.pink.text{color:#e03997}span.ui.inverted.pink.text{color:#ff8edf}span.ui.brown.text{color:#a5673f}span.ui.inverted.brown.text{color:#d67c1c}span.ui.grey.text{color:#767676}span.ui.inverted.grey.text{color:#dcddde}span.ui.black.text{color:#1b1c1d}span.ui.inverted.black.text{color:#545454}span.ui.disabled.text{opacity:.45}span.ui.medium.text{font-size:1em}span.ui.mini.text{font-size:.4em}span.ui.tiny.text{font-size:.5em}span.ui.small.text{font-size:.75em}span.ui.large.text{font-size:1.5em}span.ui.big.text{font-size:2em}span.ui.huge.text{font-size:4em}span.ui.massive.text{font-size:8em} diff --git a/code/modules/vchat/css/ss13styles.css b/code/modules/vchat/css/ss13styles.css new file mode 100644 index 0000000000..1e60cb435e --- /dev/null +++ b/code/modules/vchat/css/ss13styles.css @@ -0,0 +1,225 @@ +/* VChat Styles */ +body.inverted { + background-color: #111111; +} + +#topmenu { + overflow-x: auto; + overflow-y: hidden; +} + +#contentbox { + margin-top: 3.3rem; /* Make room for the fixed top menu */ + overflow-wrap: break-word; + word-wrap: break-word; /* IE, blah */ +} + +.menu, .item { + padding-top: 0.2em !important; + padding-bottom: 0.2em !important; +} + +.ui.menu .item > .label:not(.floating) { + margin-left: 0; + padding-bottom: 0; + padding-top: 0; + text-align: center; + font-size: 10px; +} + +.item { + cursor: pointer; +} + +.inputbox { + width: 5em !important; + padding: 0.1em !important; +} + +.icon-left-open-outline:hover, .icon-right-open-outline:hover, .icon-cancel-circled-outline:hover { + color: red; +} + +.blinkwarn { + color: #FF0000; + animation: blink-animation 1s steps(5, start) infinite; +} + +@keyframes blink-animation { + to { + visibility: hidden; + } +} + +/* SS13 Styles */ +#messagebox {font-family: Verdana, sans-serif;} + +#messagebox h1, #messagebox h2, #messagebox h3, #messagebox h4, #messagebox h5, #messagebox h6 {color: #0000ff; font-family: Georgia, Verdana, sans-serif;} + +#messagebox em {font-style: normal; font-weight: bold;} +#messagebox a {text-decoration: underline;} + +.motd {color: #638500;font-family: Verdana, sans-serif;} +.motd h1, .motd h2, .motd h3, .motd h4, .motd h5, .motd h6 + {color: #638500;text-decoration: underline;} +.motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover + {color: #638500;} + +.prefix {font-weight: bold;} +.log_message {color: #386AFF; font-weight: bold;} +/*.inverted .message {color: #386AFF; font-weight: bold;}*/ + +/* OOC */ +.ooc {font-weight: bold;} +.ooc img.text_tag {width: 32px; height: 10px;} + +.ooc .everyone {color: #002eb8;} +.inverted .ooc .everyone {color: #004ed8;} /* Dark mode */ +.looc {color: #3A9696;} +.ooc .elevated {color: #2e78d9;} +.ooc .moderator {color: #184880;} +.ooc .developer {color: #1b521f;} +.ooc .admin {color: #b82e00;} +.ooc .event_manager {color: #660033;} +.ooc .aooc {color: #960018;} + +/* Admin: Private Messages */ +.pm .howto {color: #ff0000; font-weight: bold; font-size: 200%;} +.pm .in {color: #ff0000;} +.pm .out {color: #ff0000;} +.pm .other {color: #0000ff;} + +/* Admin: Channels */ +.mod_channel {color: #735638; font-weight: bold;} +.mod_channel .admin {color: #b82e00; font-weight: bold;} +.admin_channel {color: #9611D4; font-weight: bold;} +.event_channel {color: #cc3399; font-weight: bold;} + +/* Radio: Misc */ +.deadsay {color: #530FAD;} +.inverted .deadsay {color: #732FCD;} /* Dark mode */ +.radio {color: #008000;} +.deptradio {color: #ff00ff;} /* when all other department colors fail */ +.newscaster {color: #750000;} + +/* Radio Channels */ +.comradio {color: #193A7A;} +.inverted .comradio {color: #395A9A;} /* Dark mode */ +.syndradio {color: #6D3F40;} +.centradio {color: #5C5C8A;} +.airadio {color: #FF00FF;} +.entradio {color: #339966;} + +.secradio {color: #A30000;} +.engradio {color: #A66300;} +.medradio {color: #008160;} +.sciradio {color: #993399;} +.supradio {color: #5F4519;} +.srvradio {color: #6eaa2c;} +.expradio {color: #555555;} + +/* Miscellaneous */ +.name {font-weight: bold;} +.say {} +.emote {} +.alert {color: #ff0000;} +h1.alert, h2.alert {color: #000000;} +.nif {} /* VOREStation Add */ + +/* Game Messages */ + +.attack {color: #ff0000;} +.moderate {color: #CC0000;} +.disarm {color: #990000;} +.passive {color: #660000;} + +.critical {color: #ff0000; font-weight: bold; font-size: 150%;} +.danger {color: #ff0000; font-weight: bold;} +.warning {color: #ff0000; font-style: italic;} +.rose {color: #ff5050;} +.info {color: #0000CC;} +.inverted .info {color: #6060c9;} /* Dark mode */ +.notice {color: #000099;} +.inverted .notice {color: #6060c9;} /* Dark mode */ +.alium {color: #00ff00;} +.cult {color: #800080; font-weight: bold; font-style: italic;} + +.reflex_shoot {color: #000099; font-style: italic;} + +/* Languages */ + +.alien {color: #543354;} +.tajaran {color: #803B56;} +.tajaran_signlang {color: #941C1C;} +.akhani {color: #AC398C;} +.skrell {color: #00B0B3;} +.skrellfar {color: #70FCFF;} +.soghun {color: #228B22;} +.solcom {color: #22228B;} +.changeling {color: #800080;} +.sergal {color: #0077FF;} +.birdsongc {color: #CC9900;} +.vulpkanin {color: #B97A57;} +.enochian {color: #848A33; letter-spacing:-1pt; word-spacing:4pt; font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;} +.daemon {color: #5E339E; letter-spacing:-1pt; word-spacing:0pt; font-family: "Courier New", Courier, monospace;} +.bug {color: #9e9e39;} +.vox {color: #AA00AA;} +.zaddat {color: #941C1C;} +.rough {font-family: "Trebuchet MS", cursive, sans-serif;} +.say_quote {font-family: Georgia, Verdana, sans-serif;} +.terminus {font-family: "Times New Roman", Times, serif, sans-serif} +.interface {color: #330033;} +/* YW Edit start */ +.psionic {color: #993399;} +.wingdings {font-family: Wingdings, Webdings} +/* YW Edit End */ + +/*BIG IMG.icon {width: 32px; height: 32px;}*/ +img.icon {vertical-align: middle; max-height: 1em;} +img.icon.bigicon {max-height: 32px;} + +/* Zoom levels - Replaced with arbitrary font sizes +.zoom_normal { + font-size: 0.9em; + line-height: 1.35; +} +.zoom_more { + font-size: 1.2em; + line-height: 1.4; +} +.zoom_less { + font-size: 0.8em; + line-height: 1.2; +} +*/ + +/* Debug Logs */ +.debug_error {color:#FF0000; font-weight:bold} +.debug_warning {color:#FF0000;} +.debug_info {} +.debug_debug {color:#0000FF;} +.debug_trace {color:#888888;} + +/* Log animations */ +.msgsanim-enter-active { + transition: opacity 0.5s, transform 0.5s; +} +.msgsanim-enter { + opacity: 0; + transform: translateX(15px); +} + +/* Dog mode */ +.woof { + background-color: rgba(255,255,255,0) !important; + background-image: url('dog.gif') !important; + background-size: cover !important; + + transition: background-color 15s; +} + +.woof div, .woof span { + background-color: rgba(0,0,0,0) !important; + + transition: background-color 15s; +} diff --git a/code/modules/vchat/css/vchat-font-embedded.css b/code/modules/vchat/css/vchat-font-embedded.css new file mode 100644 index 0000000000..b32c6015cd --- /dev/null +++ b/code/modules/vchat/css/vchat-font-embedded.css @@ -0,0 +1,61 @@ +@font-face { + font-family: 'vchat'; + src: url('../font/vchat.eot?83566288'); + src: url('../font/vchat.eot?83566288#iefix') format('embedded-opentype'), + url('../font/vchat.svg?83566288#vchat') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'vchat'; + src: url('data:application/octet-stream;base64,d09GRgABAAAAABC8AA8AAAAAGsgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADsAAABUIIslek9TLzIAAAGUAAAAQwAAAFY+IEjXY21hcAAAAdgAAABnAAABxOlCnH9jdnQgAAACQAAAABMAAAAgBtX/BGZwZ20AAAJUAAAFkAAAC3CKkZBZZ2FzcAAAB+QAAAAIAAAACAAAABBnbHlmAAAH7AAABcYAAAc4Lw5OdGhlYWQAAA20AAAAMwAAADYX4wdTaGhlYQAADegAAAAfAAAAJAc5A1dobXR4AAAOCAAAACAAAAAgGg7/+mxvY2EAAA4oAAAAEgAAABIJOAc8bWF4cAAADjwAAAAgAAAAIAE/DAZuYW1lAAAOXAAAAXsAAAKpxQA+9HBvc3QAAA/YAAAAZwAAAKXxwKkScHJlcAAAEEAAAAB6AAAAhuVBK7x4nGNgZGBg4GIwYLBjYHJx8wlh4MtJLMljkGJgYYAAkDwymzEnMz2RgQPGA8qxgGkOIGaDiAIAJjsFSAB4nGNgZHZinMDAysDAVMW0h4GBoQdCMz5gMGRkAooysDIzYAUBaa4pDA4vGF6wMQf9z2KIYg5imAYUZgTJAQDU/gt1AHic7ZHBCYBADATnNN6BWIpYkC8fFp8udBNThoFZ2CXksQEWYBa7MGgXjZhTact8Zs3cOHLHIvf+PFJC5S110q7pYmfIdv7ZUu9yIxr7yA4L9YYX8QsvolUv4k9eqF/1/8F4AW2zFTUAeJxjYEADEhDIHPQ/C4QBEmwD3QB4nK1WaXfTRhQdeUmchCwlCy1qYcTEabBGJmzBgAlBsmMgXZytlaCLFDvpvvGJ3+Bf82Tac+g3flrvGy8kkLTncJqTo3fnzdXM22USWpLYC+uRlJsvxdTWJo3sPAnphk3LUXwoO3shZYrJ3wVREK2W2rcdh0REIlC1rrBEEPseWZpkfOhRRsu2pFdNyi096S5b40G9Vd9+GjrKsTuhpGYzdGg9siVVGFWiSKY9UtKmZaj6K0krvL/CzFfNUMKITiJpvBnG0EjeG2e0ymg1tuMoimyy3ChSJJrhQRR5lNUS5+SKCQzKB82Q8sqnEeXD/Iis2KOcVrBLttP8vi95p3c5P7Ffb1G25EAfyI7s4Ox0JV+EW1th3LST7ShUEXbXd0Js2exU/2aP8ppGA7crMr3QjGCpfIUQKz+hzP4hWS2cT/mSR6NaspETQetlTuxLPoHW44gpcc0YWdDd0QkR1P2SMwz2mD4e/PHeKZYLEwJ4HMt6RyWcCBMpYXM0SdowcmAlZYsqqfWumDjldVrEW8J+7drRl85o41B3YjxbDx1bOVHJ8WhSp5lMndpJzaMpDaKUdCZ4zK8DKD+iSV5tYzWJlUfTOGbGhEQiAi3cS1NBLDuxpCkEzaMZvbkbprl2LVqkyQP13KP39OZWuLnTU9oO9LNGf1anYjrYC9PpaeQv8Wna5SJF6frpGX5M4kHWAjKRLTbDlIMHb/0O0svXlhyF1wbY7u3zK6h91kTwpAH7G9AeT9UpCUyFmFWIVkBirWtZlsnVrBapyNR3Q5pWvqzTBIpyHBfHvoxx/V8zM5aYEr7fidOzIy49c+1LCNMcfJt1PZrXqcVyAXFmeU6nWZbv6zTH8gOd5lme1+kIS1unoyw/1GmB5Uc6HWN5QQuadN/BkIsw5AIOkDCEpQNDWF6CISwVDGG5CENYFmEIyyUYwvJjGMJyGYawvKxl1dRTSePamVgGbEJgYo4eucxF5WoquVRCu2hUakOeEm6VVBTPqn9loF488oY5sBZIl8iaXzHOlY9G5fjWFS1vGjtXwLHqbx+O9jnxUtaLhT8F/9XWVCW9Ys3Dk6vwG4aebCeqNql4dE2Xz1U9uv5fVFRYC/QbSIVYKMqybHBnIoSPOp2GaqCVQ8xszDy063XLmp/D/TcxQhZQ/fg3FBoL3INOWUlZ7eCs1dfbstw7g3I4EyxJMTfz+lb4IiOz0n6RWcqej3wecAWMSmXYagOtFbzZJzEPmd4kzwRxW1E2SNrYzgSJDRzzgHnznQQmYeqqDeRO4YYN+AVhbsF5J1yieqMsh+5F7PMopPxbp+JE9qhojMCz2Rthr+9Cym9xDCQ0+aV+DFQVoakYNRXQNFJuqAZfxtm6bULGDvQjKnbDsqziw8cW95WSbRmEfKSI1aOjn9Zeok6q3H5mFJfvnb4FwSA1MX9733RxkMq7WskyR20DU7calVPXmkPjVYfq5lH1vePsEzlrmm66Jx56X9Oq28HFXCyw9m0O0lImF9T1YYUNosvFpVDqZTRJ77gHGBYY0O9Qio3/q/rYfJ4rVYXRcSTfTtS30edgDPwP2H9H9QPQ92Pocg0uz/eaE59u9OFsma6iF+un6Dcwa625WboG3NB0A+IhR62OuMoNfKcGcXqkuRzpIeBj3RXiAcAmgMXgE921jOZTAKP5jDk+wOfMYdBkDoMt5jDYZs4awA5zGOwyh8Eecxh8wZx1gC+ZwyBkDoOIOQyeMCcAeMocBl8xh8HXzGHwDXPuA3zLHAYxcxgkzGGwr+nWMMwtXtBdoLZBVaADU09Y3MPiUFNlyP6OF4b9vUHM/sEgpv6o6faQ+hMvDPVng5j6i0FM/VXTnSH1N14Y6u8GMfUPg5j6TL8Yy2UGv4x8lwoHlF1sPufvifcP28VAuQABAAH//wAPeJx9Vd1vG1UWP+fe+fbHeMYz49ilk6mdjFNn7Kbjj/EKN7G3oWnThOxWprjBzVqrlQpSK5BgtaUqJUDYtAkVL6u2i/K6AoGEUB53tTywD/Ba1BqWl/aFFx5W/AF0zB0XVqvVaqWR7sfvnDvn/s75nQsUYPQp/ZCMIA0NOAMvt1+CeIzG4vQSEBCBiIMUAgg8CANVIryMnMJzg6ROlATG4kpsoCFFpF02UOwBW6w0A9NAWHv69KkTi532sSeDM80z/tHy7Iw7dWjSzk0YDbOuJWUB0phO88Ys+papGYJ4KO8WtVrjSbbOoN8ItELerTWCas0tMqheyAsZs+pbhlAsaIYVYDQVC6YhMD+GVrCRqdZrDd8STZro9PudR+921rHfvjvbQmxaNt5VUinlrmOGX3itu6bz09q28L7pOObztmfjBcsmeeOCU3KQ3O63w9+MTyBSpz9fevQuM72YMlJo2jhfcswLuq5j+Cd2EjZt7wl8wbJt6wWb+T4/PhAAuNEPoxfp53QVNDgNXTgP2+2tJUtLUSIoVOWIpBxzkjwnSwmBxORqKR6TRIGL8UsiUlCAKpcE5GIY4/BSEhnnkqxIA5AhxsmxAfsB6cWRkPhJQOR7wPMJ/tTZZ1ZX0vr6uWfOnz3/61+tdFe7Tx0P6nPlGTc3oZ9OL0+kVN6axYJYFDNiJshUg0xQDIoi9a1JDBpVfxItQcWMjSoKhfwRFJM4i3mXTcZLt7GAxQou4Ng0mMdqOi8Ylj+PbtG3kaVjHsncXvmTtbffXvukvOftr25tre57haacXlaTLXtVV7JBkFX0VbuVVJc1uTm9N8XAkxG4piu5CNTWIvBkWm5OPe21Wr0WaZc662Tj2oDrkW/3q9c3X9/29/f97dc3r1cx5xVOmVx9e/JEtlHLnnCuV6m5XPC8wrIV7Z7M1upsd7vOmacKXvgqO26+O19b31pnH2MReKaDD+hFaoLEMjUNR6AJF9q/MxFhhtW0g7xEl4ADmXDyIIZEQUACA0CgEtJBHCXgRYkfgCgIYhdEUeiBIAorR+cUGaFem2sebVa8qfzkwVw2mZA1ReM5kFBKRPWfT6I1jxX8N3tYW0CXlfKURYRivhjR7k67bDSsjB9M+41aBZkPjn4m5Pizl8nVD6+Sy2Fv7vfpIVaG5Ms/11uvHT22uIFr1fybWj5rGehfvHPn6ztkjyz2Nze4s9e7weWPr1z5+PKj+4tH/tBo3b5Hvg6H35D01dJv38tX31JredTfuPPNLfLecI/VMuPoK/I++RbikAUXqrDUXuQRY4JMCUUyiEoWaJcNFHoKJ1GgsDJTPJBLJhAq5WJ1pnrIybkHXCOdyCazogBxjMcZA5nHymfKZX2AMp1rho2sAdRc/J9InSF7kTI3N7DfYfqLpOtg77+27MP9Dt6LOkHodfrkL7YVemN94z3LfvTc/wFZ74PR38k1EoIKB6HULmYSQnTJpSjjhGU8siBdIAR6bAIrE1kjz/ETs9NGlDEZWcgG05A1jRZL1xGmq5qbF8m1jYVOObyJL3t/PFe+dascWnhY9Wz14UPV9tTn2htYxlcYeu6tCP1HOIy2Hz6ITICMRqNPf4rJhl+0G0+YCZ5j0bCgeBxnAMYJgIh/DiP2Nda9c9mUrdkxBVRUhcdsRwp2a0HDz8gY0cqEHxWXH8jo7i1sEPbzSsXb8vCV8J1yxGbqwYPUZCmFh8Ov1F/234zwNyqeF95kOPmnM6M9eJgqTapYYteJ9ERZrNv0IxqM9XQYTrSPTztZyjEGeWQxU46wxsbsOKCsbggiYXVDSPSEEFyJRHPwQMb8T6lI46cCdFaHOqT9Rsp1hJSVzliPe1SkkehWjaiHWSLe/wx1En4XbobfEdQ/uz0chl8Oh3/bjSe1mHRjR4xpyfjuru5qOzd0V6fV8F/hlfB7DifwGuZ++GukocgDL91IOOLurugkbuxorr6zw1zgRy8nWgkAAHicY2BkYGAA4jclBQ7x/DZfGbiZXwBFGG7lP7wJo///+Z/P/ILZF8jlYGACiQIAm8gPXAB4nGNgZGBgDvqfBSRf/P/z/w/zCwagCArgAAC2HgeVAAPoAAADqgAAA8P//ANrAAACpgAAAn4AAAJ+//8Dqv//AAAAAACwAYQCFgKMAtYDLAOcAAAAAQAAAAgAXgAFAAAAAAACACYANgBzAAAAmAtwAAAAAHicdZLNTgIxFIVPBTRCdKGJGzd3o5GYDCOJG1YaIixcmLBg42oc5o8MU9IpJLyA7+AD+Fo+i4fSKC6cpu13zr29vU0GwBm+oLD77jl3rNCi2vEBjjDw3KD/6LlJHnluoYNnz4dUU89t3OLVcwfneGcF1TymmuPDs0JbNTwf4FSdeG7Qv/TcJF95buFC9Twf0n/y3MZUvXju4Fp9DvVyY4ost3Iz7Eo/7IfythFNq6iiUqKVzbWp5UFSXdmkLHUQ68U6ziM7SbJVGRnHbpkmpi50JXdB6PQ4qRIT2WS2rVivs761qaRGL2Tka8nS6HkS2yC3djno9fbvwBAaS2xgUCBDDgvBDd0u9z5CNwVvzBBm7rIKVIhQ0omw4oncRWrqB86UqqKbMKMkB4i5LrDmnvOExYSxjCdLKrPn/9KUGduKhasluGOVcC8+ZrxyOZG7afbTY82sjF1bjtR1Y9ztwh/kb1/Cd29jczox/cC93tIdoMfxzzu+AR8YeKsAeJxtyE0KgCAQQOGZ/jMKatEtPJSMkwmDhtX9W4SLoLd7HxTwpuC/DgssscIaG2yxwx7UFsVy0sbagaLT8b7EB55ONon2vONh7pPzzcLbpePBIcuSvNu/tJIJxKLJJxK2mQEemSgqTwB4nGPw3sFwIihiIyNjX+QGxp0cDBwMyQUbGVidNjEwMmiBGJu5mBg5ICw+BjCLzWkX0wGgNCeQze60i8EBwmZmcNmowtgRGLHBoSNiI3OKy0Y1EG8XRwMDI4tDR3JIBEhJJBBs5mFi5NHawfi/dQNL70YmBhcADHYj9AAA') format('woff'), + url('data:application/octet-stream;base64,AAEAAAAPAIAAAwBwR1NVQiCLJXoAAAD8AAAAVE9TLzI+IEjXAAABUAAAAFZjbWFw6UKcfwAAAagAAAHEY3Z0IAbV/wQAAA6wAAAAIGZwZ22KkZBZAAAO0AAAC3BnYXNwAAAAEAAADqgAAAAIZ2x5Zi8OTnQAAANsAAAHOGhlYWQX4wdTAAAKpAAAADZoaGVhBzkDVwAACtwAAAAkaG10eBoO//oAAAsAAAAAIGxvY2EJOAc8AAALIAAAABJtYXhwAT8MBgAACzQAAAAgbmFtZcUAPvQAAAtUAAACqXBvc3TxwKkSAAAOAAAAAKVwcmVw5UErvAAAGkAAAACGAAEAAAAKADAAPgACREZMVAAObGF0bgAaAAQAAAAAAAAAAQAAAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAEDQgGQAAUAAAJ6ArwAAACMAnoCvAAAAeAAMQECAAACAAUDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBmRWQAQOgA6AYDUv9qAFoDUgCWAAAAAQAAAAAAAAAAAAUAAAADAAAALAAAAAQAAAFgAAEAAAAAAFoAAwABAAAALAADAAoAAAFgAAQALgAAAAQABAABAADoBv//AADoAP//AAAAAQAEAAAAAQACAAMABAAFAAYABwAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAZAAAAAAAAAAHAADoAAAA6AAAAAABAADoAQAA6AEAAAACAADoAgAA6AIAAAADAADoAwAA6AMAAAAEAADoBAAA6AQAAAAFAADoBQAA6AUAAAAGAADoBgAA6AYAAAAHAAMAAP++A6oC/wATADYAUgBzQHAADAsDCwwDbQACAAcAAgdgEAEAAAYFAAZgDwgCBQkBBAoFBGAOEgIKDQELDAoLYBEBAwEBA1QRAQMDAVgAAQMBTDg3FRQBAE9OS0lGREFAPTs3UjhSMjEtKygmJCEfHhoYFDYVNREOCQYAEwETEwUUKwEyFhURFAYHISImJxE0NjsBMhYXATI2NxEjIiY0NjczNCYnISImNSMiBhcVMzIWFAYnIxEUFjcBMhYUBgcjFRQGIiYnNSMiLgE2FzM1NDYyFgcVAw1BXFxB/Y9BWgFcQNErPAEBOBYeAdEKEBAK0SAV/sgsPNEVIAHRChAQCtEeFgHVFSAgFWgeLB4BZxYeAiIUZyAqIAECl1xA/l9BWgFcQAIIQVw+Kv2PHhYBbBAUEAEVHgE+KiAVZxISEgH+lBUgAQE4HiwcAWkWHh4WaR4qIAFoFSAgFWgAAAAE//z/bwPHA00AEQBLAFQAXQCHQIRHFhEQAwIGCgMPBAIICj0gDgUECQgNBgILCTMqDAsIBwYECwVHBwEDAAoAAwptBgEECwELBAFtDgEKDQEICQoIYAAJAAsECQtgAAAAAlgMAQICDEgAAQEFWAAFBQ0FSVZVTUwTElpZVV1WXVFQTFRNVEVDNzUwLSgmGhgSSxNKGBAPBRYrASMHJwcXBxc3FzM3FzcnNycHAzIWHwE3NjMyHwEWBg8BFx4BDwEGIyIvAQcOASsBIiYvAQcGIyIvASY2PwEnLgE/ATYzMh8BNz4BMxMiBhQWMj4BJicyHgEGIiY+AQIwnC2wT4WFT7AtnCyyTYSETbIsIzgJE0oPDjweTRIKGTc3GQoSTR48Dg9KEQk4JZwkOAkTSA8OPB5PEgoaNzcZChFPHjwOD0gTCTgkTiw8PFg8AkAqQVoCXn5gBFgC5bIziIB/hzKysjKHf4CIMwEaLCNJFQQ1hx9GGTY0GUYgiDMDFUojLCwjShYENYcfSBk0NRlGIIc1BBVJIyz+eTxYPD5UPjRahFpahFoAAAAABQAA/6gDbAMVAAgAEQAlAC8AOABnQGQVAQEAKAEDASABBQgDRwAEAAkCBAlgCwECCgEAAQIAYAABAAMIAQNgDAEIAAUHCAVgAAcGBgdUAAcHBlgABgcGTDEwCgkBADU0MDgxOC4sJCIfHRoZDg0JEQoRBQQACAEIDQUUKwEiDgEWPgEuAScyHgEGIiY+AQE0PwEmNTQ2JBYCBiciJwcGIyImJSYnBwYUFhcyNyUyNjQuAQ4BFgH/QVoCXn5gBFhDV3gCfKp8Anj+WDB1E9YBLtYC0pk1PH0xPUReAU8zIoIRIhkWFAEybJiY2JgCnAJEXIBeBFaIVDd4rnp6rnj91UQvdzY8l9QC2P7W2gITfCpimiIzgw80IgESgZjalgKa1pwAAAQAAP/XAqcC5QAMABkAJgAzAEdARAUBAQsGCQMCAwECYAcBAwAAA1QHAQMDAFgKBAgDAAMATCgnGxoODQEALi0nMygzISAaJhsmFBMNGQ4ZBwYADAEMDAUUKxciJicRNDYyFgcRFAYDIgYXERQeATY3ETQmASImJxE0NjIWBxEUBgMiBhcRFB4BNjURNCacQVoBXIBeAVxBFSABHiweASABWEFaAVyAXgFcQRUgAR4sHh4pXEEB1EFcXEH+LEFcAqUeFv4sFh4CIhQB1BYe/VtcQQHUQVxcQf4sQVwCpR4W/iwWHgIiFAHUFh4AAgAA/70CfgL+AA8AHQAqQCcXDQYDAgMBRwABAAMCAQNgAAIAAAJUAAICAFgAAAIATBgZFCIEBRgrJRQGJyInCQE2MhYUDwEXFiUBFjI2NC8BNzY0JiIHAn5eP0Et/o0BcyyGWS2Wli3+FgEpDyweD+DgDx4sD1tAXgEtAXQBcyxZgy2Wli3D/tYPHiwP4N8PLB4PAAL///++An4C/gAPAB4AOUA2HBUNBQQDAgFHAAEFAQIDAQJgAAMAAANUAAMDAFgEAQADAEwREAEAGhkQHhEeCwoADwEPBgUUKxciJjY/AScmNDc2MhcJAQYDIgYWHwEHBhQWMjcJASacP14CLZaWLi4shCwBdP6MLUEVIAEQ398QHyoQASn+1w9CXIItlpYugS4sLP6N/owtAtkgKBHf4BAqHw8BKgEpDwAAAAAD////hwOrAzcACAARACkARkBDJSAZAwQDAUcFAQQDAgMEAm0GAQAAAwQAA2AHAQIBAQJUBwECAgFYAAECAUwKCQEAHRsXFQ4NCREKEQUEAAgBCAgFFCsBMgASAAQAEgATMjYQJiAGEBYTFxYGIyIvAQcGIyImPwEnJjYfATc2FgcB1cIBEgL+6v6A/uoCARLCl9bW/tLW1ruLDA4RCwiJigcLEQ4Mi4sSJhGKiRImEgMz/u7+ev7wBAEYAX4BGvy61gEu1tb+0tYBbYkNIAeLiwcgDYmKESYSiooSJhEAAAEAAAABAADsdHBAXw889QALA+gAAAAA2m/h2QAAAADab+HZ//z/bwPoA00AAAAIAAIAAAAAAAAAAQAAA1L/agAAA+j//P/8A+gAAQAAAAAAAAAAAAAAAAAAAAgD6AAAA6oAAAPD//wDawAAAqYAAAJ+AAACfv//A6r//wAAAAAAsAGEAhYCjALWAywDnAAAAAEAAAAIAF4ABQAAAAAAAgAmADYAcwAAAJgLcAAAAAAAAAASAN4AAQAAAAAAAAA1AAAAAQAAAAAAAQAFADUAAQAAAAAAAgAHADoAAQAAAAAAAwAFAEEAAQAAAAAABAAFAEYAAQAAAAAABQALAEsAAQAAAAAABgAFAFYAAQAAAAAACgArAFsAAQAAAAAACwATAIYAAwABBAkAAABqAJkAAwABBAkAAQAKAQMAAwABBAkAAgAOAQ0AAwABBAkAAwAKARsAAwABBAkABAAKASUAAwABBAkABQAWAS8AAwABBAkABgAKAUUAAwABBAkACgBWAU8AAwABBAkACwAmAaVDb3B5cmlnaHQgKEMpIDIwMjAgYnkgb3JpZ2luYWwgYXV0aG9ycyBAIGZvbnRlbGxvLmNvbXZjaGF0UmVndWxhcnZjaGF0dmNoYXRWZXJzaW9uIDEuMHZjaGF0R2VuZXJhdGVkIGJ5IHN2ZzJ0dGYgZnJvbSBGb250ZWxsbyBwcm9qZWN0Lmh0dHA6Ly9mb250ZWxsby5jb20AQwBvAHAAeQByAGkAZwBoAHQAIAAoAEMAKQAgADIAMAAyADAAIABiAHkAIABvAHIAaQBnAGkAbgBhAGwAIABhAHUAdABoAG8AcgBzACAAQAAgAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAHYAYwBoAGEAdABSAGUAZwB1AGwAYQByAHYAYwBoAGEAdAB2AGMAaABhAHQAVgBlAHIAcwBpAG8AbgAgADEALgAwAHYAYwBoAGEAdABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAQIBAwEEAQUBBgEHAQgBCQAKZm9sZGVyLWFkZAtjb2ctb3V0bGluZQ5zZWFyY2gtb3V0bGluZQ1wYXVzZS1vdXRsaW5lEWxlZnQtb3Blbi1vdXRsaW5lEnJpZ2h0LW9wZW4tb3V0bGluZRZjYW5jZWwtY2lyY2xlZC1vdXRsaW5lAAAAAAAAAQAB//8ADwAAAAAAAAAAAAAAAAAAAAAAGAAYABgAGANS/2oDUv9qsAAsILAAVVhFWSAgS7gADlFLsAZTWliwNBuwKFlgZiCKVViwAiVhuQgACABjYyNiGyEhsABZsABDI0SyAAEAQ2BCLbABLLAgYGYtsAIsIGQgsMBQsAQmWrIoAQpDRWNFUltYISMhG4pYILBQUFghsEBZGyCwOFBYIbA4WVkgsQEKQ0VjRWFksChQWCGxAQpDRWNFILAwUFghsDBZGyCwwFBYIGYgiophILAKUFhgGyCwIFBYIbAKYBsgsDZQWCGwNmAbYFlZWRuwAStZWSOwAFBYZVlZLbADLCBFILAEJWFkILAFQ1BYsAUjQrAGI0IbISFZsAFgLbAELCMhIyEgZLEFYkIgsAYjQrEBCkNFY7EBCkOwAWBFY7ADKiEgsAZDIIogirABK7EwBSWwBCZRWGBQG2FSWVgjWSEgsEBTWLABKxshsEBZI7AAUFhlWS2wBSywB0MrsgACAENgQi2wBiywByNCIyCwACNCYbACYmawAWOwAWCwBSotsAcsICBFILALQ2O4BABiILAAUFiwQGBZZrABY2BEsAFgLbAILLIHCwBDRUIqIbIAAQBDYEItsAkssABDI0SyAAEAQ2BCLbAKLCAgRSCwASsjsABDsAQlYCBFiiNhIGQgsCBQWCGwABuwMFBYsCAbsEBZWSOwAFBYZVmwAyUjYUREsAFgLbALLCAgRSCwASsjsABDsAQlYCBFiiNhIGSwJFBYsAAbsEBZI7AAUFhlWbADJSNhRESwAWAtsAwsILAAI0KyCwoDRVghGyMhWSohLbANLLECAkWwZGFELbAOLLABYCAgsAxDSrAAUFggsAwjQlmwDUNKsABSWCCwDSNCWS2wDywgsBBiZrABYyC4BABjiiNhsA5DYCCKYCCwDiNCIy2wECxLVFixBGREWSSwDWUjeC2wESxLUVhLU1ixBGREWRshWSSwE2UjeC2wEiyxAA9DVVixDw9DsAFhQrAPK1mwAEOwAiVCsQwCJUKxDQIlQrABFiMgsAMlUFixAQBDYLAEJUKKiiCKI2GwDiohI7ABYSCKI2GwDiohG7EBAENgsAIlQrACJWGwDiohWbAMQ0ewDUNHYLACYiCwAFBYsEBgWWawAWMgsAtDY7gEAGIgsABQWLBAYFlmsAFjYLEAABMjRLABQ7AAPrIBAQFDYEItsBMsALEAAkVUWLAPI0IgRbALI0KwCiOwAWBCIGCwAWG1EBABAA4AQkKKYLESBiuwcisbIlktsBQssQATKy2wFSyxARMrLbAWLLECEystsBcssQMTKy2wGCyxBBMrLbAZLLEFEystsBossQYTKy2wGyyxBxMrLbAcLLEIEystsB0ssQkTKy2wHiwAsA0rsQACRVRYsA8jQiBFsAsjQrAKI7ABYEIgYLABYbUQEAEADgBCQopgsRIGK7ByKxsiWS2wHyyxAB4rLbAgLLEBHistsCEssQIeKy2wIiyxAx4rLbAjLLEEHistsCQssQUeKy2wJSyxBh4rLbAmLLEHHistsCcssQgeKy2wKCyxCR4rLbApLCA8sAFgLbAqLCBgsBBgIEMjsAFgQ7ACJWGwAWCwKSohLbArLLAqK7AqKi2wLCwgIEcgILALQ2O4BABiILAAUFiwQGBZZrABY2AjYTgjIIpVWCBHICCwC0NjuAQAYiCwAFBYsEBgWWawAWNgI2E4GyFZLbAtLACxAAJFVFiwARawLCqwARUwGyJZLbAuLACwDSuxAAJFVFiwARawLCqwARUwGyJZLbAvLCA1sAFgLbAwLACwAUVjuAQAYiCwAFBYsEBgWWawAWOwASuwC0NjuAQAYiCwAFBYsEBgWWawAWOwASuwABa0AAAAAABEPiM4sS8BFSotsDEsIDwgRyCwC0NjuAQAYiCwAFBYsEBgWWawAWNgsABDYTgtsDIsLhc8LbAzLCA8IEcgsAtDY7gEAGIgsABQWLBAYFlmsAFjYLAAQ2GwAUNjOC2wNCyxAgAWJSAuIEewACNCsAIlSYqKRyNHI2EgWGIbIVmwASNCsjMBARUUKi2wNSywABawBCWwBCVHI0cjYbAJQytlii4jICA8ijgtsDYssAAWsAQlsAQlIC5HI0cjYSCwBCNCsAlDKyCwYFBYILBAUVizAiADIBuzAiYDGllCQiMgsAhDIIojRyNHI2EjRmCwBEOwAmIgsABQWLBAYFlmsAFjYCCwASsgiophILACQ2BkI7ADQ2FkUFiwAkNhG7ADQ2BZsAMlsAJiILAAUFiwQGBZZrABY2EjICCwBCYjRmE4GyOwCENGsAIlsAhDRyNHI2FgILAEQ7ACYiCwAFBYsEBgWWawAWNgIyCwASsjsARDYLABK7AFJWGwBSWwAmIgsABQWLBAYFlmsAFjsAQmYSCwBCVgZCOwAyVgZFBYIRsjIVkjICCwBCYjRmE4WS2wNyywABYgICCwBSYgLkcjRyNhIzw4LbA4LLAAFiCwCCNCICAgRiNHsAErI2E4LbA5LLAAFrADJbACJUcjRyNhsABUWC4gPCMhG7ACJbACJUcjRyNhILAFJbAEJUcjRyNhsAYlsAUlSbACJWG5CAAIAGNjIyBYYhshWWO4BABiILAAUFiwQGBZZrABY2AjLiMgIDyKOCMhWS2wOiywABYgsAhDIC5HI0cjYSBgsCBgZrACYiCwAFBYsEBgWWawAWMjICA8ijgtsDssIyAuRrACJUZSWCA8WS6xKwEUKy2wPCwjIC5GsAIlRlBYIDxZLrErARQrLbA9LCMgLkawAiVGUlggPFkjIC5GsAIlRlBYIDxZLrErARQrLbA+LLA1KyMgLkawAiVGUlggPFkusSsBFCstsD8ssDYriiAgPLAEI0KKOCMgLkawAiVGUlggPFkusSsBFCuwBEMusCsrLbBALLAAFrAEJbAEJiAuRyNHI2GwCUMrIyA8IC4jOLErARQrLbBBLLEIBCVCsAAWsAQlsAQlIC5HI0cjYSCwBCNCsAlDKyCwYFBYILBAUVizAiADIBuzAiYDGllCQiMgR7AEQ7ACYiCwAFBYsEBgWWawAWNgILABKyCKimEgsAJDYGQjsANDYWRQWLACQ2EbsANDYFmwAyWwAmIgsABQWLBAYFlmsAFjYbACJUZhOCMgPCM4GyEgIEYjR7ABKyNhOCFZsSsBFCstsEIssDUrLrErARQrLbBDLLA2KyEjICA8sAQjQiM4sSsBFCuwBEMusCsrLbBELLAAFSBHsAAjQrIAAQEVFBMusDEqLbBFLLAAFSBHsAAjQrIAAQEVFBMusDEqLbBGLLEAARQTsDIqLbBHLLA0Ki2wSCywABZFIyAuIEaKI2E4sSsBFCstsEkssAgjQrBIKy2wSiyyAABBKy2wSyyyAAFBKy2wTCyyAQBBKy2wTSyyAQFBKy2wTiyyAABCKy2wTyyyAAFCKy2wUCyyAQBCKy2wUSyyAQFCKy2wUiyyAAA+Ky2wUyyyAAE+Ky2wVCyyAQA+Ky2wVSyyAQE+Ky2wViyyAABAKy2wVyyyAAFAKy2wWCyyAQBAKy2wWSyyAQFAKy2wWiyyAABDKy2wWyyyAAFDKy2wXCyyAQBDKy2wXSyyAQFDKy2wXiyyAAA/Ky2wXyyyAAE/Ky2wYCyyAQA/Ky2wYSyyAQE/Ky2wYiywNysusSsBFCstsGMssDcrsDsrLbBkLLA3K7A8Ky2wZSywABawNyuwPSstsGYssDgrLrErARQrLbBnLLA4K7A7Ky2waCywOCuwPCstsGkssDgrsD0rLbBqLLA5Ky6xKwEUKy2wayywOSuwOystsGwssDkrsDwrLbBtLLA5K7A9Ky2wbiywOisusSsBFCstsG8ssDorsDsrLbBwLLA6K7A8Ky2wcSywOiuwPSstsHIsswkEAgNFWCEbIyFZQiuwCGWwAyRQeLABFTAtAEu4AMhSWLEBAY5ZsAG5CAAIAGNwsQAFQrIAAQAqsQAFQrMKAgEIKrEABUKzDgABCCqxAAZCugLAAAEACSqxAAdCugBAAAEACSqxAwBEsSQBiFFYsECIWLEDZESxJgGIUVi6CIAAAQRAiGNUWLEDAERZWVlZswwCAQwquAH/hbAEjbECAEQAAA==') format('truetype'); +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'vchat'; + src: url('../font/vchat.svg?83566288#vchat') format('svg'); + } +} +*/ + + [class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "vchat"; + font-style: normal; + font-weight: normal; + speak: none; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* you can be more comfortable with increased icons size */ + font-size: 150%; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} +.icon-folder-add:before { content: '\e800'; } /* '' */ +.icon-cog-outline:before { content: '\e801'; } /* '' */ +.icon-search-outline:before { content: '\e802'; } /* '' */ +.icon-pause-outline:before { content: '\e803'; } /* '' */ +.icon-left-open-outline:before { content: '\e804'; } /* '' */ +.icon-right-open-outline:before { content: '\e805'; } /* '' */ +.icon-cancel-circled-outline:before { content: '\e806'; } /* '' */ \ No newline at end of file diff --git a/code/modules/vchat/html/vchat.html b/code/modules/vchat/html/vchat.html new file mode 100644 index 0000000000..a3612d78e4 --- /dev/null +++ b/code/modules/vchat/html/vchat.html @@ -0,0 +1,191 @@ + + + + VChat + + + + + + + + + + + + + + + +
    +

    You probably shouldn't see this page. This generally means chat is very broken.

    +

    You can wait a few seconds to see if it loads, or try OOC > Reload VChat.

    +
    + + + + + diff --git a/code/modules/vchat/js/polyfills.js b/code/modules/vchat/js/polyfills.js new file mode 100644 index 0000000000..0d1223f7b6 --- /dev/null +++ b/code/modules/vchat/js/polyfills.js @@ -0,0 +1,78 @@ +// https://tc39.github.io/ecma262/#sec-array.prototype.find +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). + // d. If testResult is true, return kValue. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; + }, + configurable: true, + writable: true + }); +} + +if (!Date.now) { + Date.now = function now() { + return new Date().getTime(); + }; +} + +//IE ugh +function storageAvailable(type) { + var storage; + try { + storage = window[type]; + var x = '__storage_test__'; + storage.setItem(x, x); + storage.removeItem(x); + return true; + } + catch(e) { + return e instanceof DOMException && ( + // everything except Firefox + e.code === 22 || + // Firefox + e.code === 1014 || + // test name field too, because code might not be present + // everything except Firefox + e.name === 'QuotaExceededError' || + // Firefox + e.name === 'NS_ERROR_DOM_QUOTA_REACHED') && + // acknowledge QuotaExceededError only if there's something already stored + (storage && storage.length !== 0); + } +} \ No newline at end of file diff --git a/code/modules/vchat/js/vchat.js b/code/modules/vchat/js/vchat.js new file mode 100644 index 0000000000..4a25f1bec2 --- /dev/null +++ b/code/modules/vchat/js/vchat.js @@ -0,0 +1,934 @@ +//The 'V' is for 'VORE' but you can pretend it's for Vue.js if you really want. + +(function(){ + var oldLog = console.log; + console.log = function (message) { + send_debug(message); + oldLog.apply(console, arguments); + }; + var oldError = console.error; + console.error = function (message) { + send_debug(message); + oldError.apply(console, arguments); + } + window.onerror = function (message, url, line, col, error) { + var stacktrace = ""; + if(error && error.stack) { + stacktrace = error.stack; + } + send_debug(message+" ("+url+"@"+line+":"+col+") "+error+"|UA: "+navigator.userAgent+"|Stack: "+stacktrace); + return true; + } +})(); + + +//Options for vchat +var vchat_opts = { + msBeforeDropped: 30000, //No ping for this long, and the server must be gone + cookiePrefix: "vst-", //If you're another server, you can change this if you want. + alwaysShow: ["vc_adminpm", "vc_system"], //Categories to always display on every tab. YW Edit: removes vc_looc adds: vc_adminpm + vchatTabsVer: 1.0 //Version of vchat tabs save 'file' +}; + +var DARKMODE_COLORS = { + buttonBgColor: "#40628a", + buttonTextColor: "#FFFFFF", + windowBgColor: "#272727", + highlightColor: "#009900", + tabTextColor: "#FFFFFF", + tabBackgroundColor: "#272727" +}; + +var LIGHTMODE_COLORS = { + buttonBgColor: "none", + buttonTextColor: "#000000", + windowBgColor: "none", + highlightColor: "#007700", + tabTextColor: "#000000", + tabBackgroundColor: "none" +}; + + +/*********** +* +* Setup Methods +* +************/ + +var set_storage = set_cookie; +var get_storage = get_cookie; +var domparser = new DOMParser(); + +//Upgrade to LS +if (storageAvailable('localStorage')) { + set_storage = set_localstorage; + get_storage = get_localstorage; +} + +//State-tracking variables +var vchat_state = { + ready: false, + + //Userinfo as reported by byond + byond_ip: null, + byond_cid: null, + byond_ckey: null, + + //Ping status + lastPingReceived: 0, + latency_sent: 0, + + //Last ID + lastId: 0 +} + +function start_vchat() { + //Instantiate Vue.js + start_vue(); + + //Inform byond we're done + vchat_state.ready = true; + push_Topic('done_loading'); + + //I'll do my own winsets + doWinset("htmloutput", {"is-visible": true}); + doWinset("oldoutput", {"is-visible": false}); + doWinset("chatloadlabel", {"is-visible": false}); + + //Commence the pingening + setInterval(check_ping, vchat_opts.msBeforeDropped); + + //For fun + send_debug("VChat Loaded!"); + //throw new Error("VChat Loaded!"); + +} + +//Loads vue for chat usage +var vueapp; +function start_vue() { + vueapp = new Vue({ + el: '#app', + data: { + messages: [], //List o messages from byond + shown_messages: [], //Used on filtered tabs, but not "Main" because it has 0len categories list, which bypasses filtering for speed + unshown_messages: 0, //How many messages in archive would be shown but aren't + archived_messages: [], //Too old to show + tabs: [ //Our tabs + {name: "Main", categories: [], immutable: true, active: true} + ], + unread_messages: {}, //Message categories that haven't been looked at since we got one of them + editing: false, //If we're in settings edit mode + paused: false, //Autoscrolling + latency: 0, //Not necessarily network latency, since the game server has to align the responses into ticks + reconnecting: false, //If we've lost our connection + ext_styles: "", //Styles for chat downloaded files + is_admin: false, + + //Settings + inverted: false, //Dark mode + crushing: 3, //Combine similar messages + animated: false, //Small CSS animations for new messages + fontsize: 0.9, //Font size nudging + lineheight: 130, + showingnum: 200, //How many messages to show + + //The table to map game css classes to our vchat categories + type_table: [ + { + matches: ".say, .emote", + becomes: "vc_localchat", + pretty: "Local Chat", + tooltip: "In-character local messages (say, emote, etc)", + required: false, + admin: false + }, + { + matches: ".alert, .syndradio, .centradio, .airadio, .entradio, .comradio, .secradio, .engradio, .medradio, .sciradio, .supradio, .srvradio, .expradio, .radio, .deptradio, .newscaster", + becomes: "vc_radio", + pretty: "Radio Comms", + tooltip: "All departments of radio messages", + required: false, + admin: false + }, + { + matches: ".notice:not(.pm), .adminnotice, .info, .sinister, .cult", + becomes: "vc_info", + pretty: "Notices", + tooltip: "Non-urgent messages from the game and items", + required: false, + admin: false + }, + { + matches: ".critical, .danger, .userdanger, .warning:not(.pm), .italics", + becomes: "vc_warnings", + pretty: "Warnings", + tooltip: "Urgent messages from the game and items", + required: false, + admin: false + }, + { + matches: ".deadsay", + becomes: "vc_deadchat", + pretty: "Deadchat", + tooltip: "All of deadchat", + required: false, + admin: false + }, + { + matches: ".ooc:not(.looc)", + becomes: "vc_globalooc", + pretty: "Global OOC", + tooltip: "The bluewall of global OOC messages", + required: false, + admin: false + }, + //VOREStation Add Start + { + matches: ".nif", + becomes: "vc_nif", + pretty: "NIF Messages", + tooltip: "Messages from the NIF itself and people inside", + required: false, + admin: false + }, + //VOREStation Add End + { + matches: ".pm", + becomes: "vc_adminpm", + pretty: "Admin PMs", + tooltip: "Messages to/from admins ('adminhelps'), always enabled", + required: true, //YW Edit + admin: false + }, + { + matches: ".admin_channel", + becomes: "vc_adminchat", + pretty: "Admin Chat", + tooltip: "ASAY messages", + required: false, + admin: true + }, + { + matches: ".mod_channel", + becomes: "vc_modchat", + pretty: "Mod Chat", + tooltip: "MSAY messages", + required: false, + admin: true + }, + { + matches: ".event_channel", + becomes: "vc_eventchat", + pretty: "Event Chat", + tooltip: "ESAY messages", + required: false, + admin: true + }, + { + matches: ".ooc.looc, .ooc .looc", //Dumb game + becomes: "vc_looc", + pretty: "Local OOC", + tooltip: "Local OOC messages", + required: false //YW Edit + }, + { + matches: ".boldannounce", + becomes: "vc_system", + pretty: "System Messages", + tooltip: "Messages from your client, always enabled", + required: true + } + ], + }, + mounted: function() { + //Load our settings + this.load_settings(); + + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'ss13styles.css'); + xhr.onreadystatechange = (function() { + this.ext_styles = xhr.responseText; + }).bind(this); + xhr.send(); + }, + updated: function() { + if(!this.editing && !this.paused) { + window.scrollTo(0,document.getElementById("messagebox").scrollHeight); + } + }, + watch: { + reconnecting: function(newSetting, oldSetting) { + if(newSetting == true && oldSetting == false) { + this.internal_message("Your client has lost connection to the server, or there is severe lag. Your client will reconnect if possible."); + } else if (newSetting == false && oldSetting == true) { + this.internal_message("Your client has reconnected to the server."); + } + }, + //Save the inverted setting to LS + inverted: function (newSetting) { + set_storage("darkmode",newSetting); + if(newSetting) { //Special treatment for which is outside Vue's scope and has custom css + document.body.classList.add("inverted"); + switch_ui_mode(DARKMODE_COLORS); + } else { + document.body.classList.remove("inverted"); + switch_ui_mode(LIGHTMODE_COLORS); + } + }, + crushing: function (newSetting) { + set_storage("crushing",newSetting); + }, + animated: function (newSetting) { + set_storage("animated",newSetting); + }, + fontsize: function (newSetting, oldSetting) { + if(isNaN(newSetting)) { //Numbers only + this.fontsize = oldSetting; + return; + } + if(newSetting < 0.2) { + this.fontsize = 0.2; + } else if(newSetting > 5) { + this.fontsize = 5; + } + set_storage("fontsize",newSetting); + }, + lineheight: function (newSetting, oldSetting) { + if(!isFinite(newSetting)) { //Integers only + this.lineheight = oldSetting; + return; + } + if(newSetting < 100) { + this.lineheight = 100; + } else if(newSetting > 200) { + this.lineheight = 200; + } + set_storage("lineheight",newSetting); + }, + showingnum: function (newSetting, oldSetting) { + if(!isFinite(newSetting)) { //Integers only + this.showingnum = oldSetting; + return; + } + + newSetting = Math.floor(newSetting); + if(newSetting < 50) { + this.showingnum = 50; + } else if(newSetting > 2000) { + this.showingnum = 2000; + } + + set_storage("showingnum",this.showingnum); + this.attempt_archive(); + }, + current_categories: function(newSetting, oldSetting) { + if(newSetting.length) { + this.apply_filter(newSetting); + } + } + }, + computed: { + //Which tab is active? + active_tab: function() { + //Had to polyfill this stupid .find since IE doesn't have EC6 + let tab = this.tabs.find( function(tab) { + return tab.active; + }); + return tab; + }, + //What color does the latency pip get? + ping_classes: function() { + if(!this.latency) { + return this.reconnecting ? "red" : "green"; //Standard + } + + if (this.latency == "?") { return "grey"; } //Waiting for latency test reply + else if(this.latency < 0 ) {return "red"; } + else if(this.latency <= 200) { return "green"; } + else if(this.latency <= 400) { return "yellow"; } + else { return "grey"; } + }, + current_categories: function() { + if(this.active_tab == this.tabs[0]) { + return []; //Everything, no filtering, special case for speed. + } else { + return this.active_tab.categories.concat(vchat_opts.alwaysShow); + } + } + }, + methods: { + //Load the chat settings + load_settings: function() { + this.inverted = get_storage("darkmode", false); + this.crushing = get_storage("crushing", 3); + this.animated = get_storage("animated", false); + this.fontsize = get_storage("fontsize", 0.9); + this.lineheight = get_storage("lineheight", 130); + this.showingnum = get_storage("showingnum", 200); + + if(isNaN(this.crushing)){this.crushing = 3;} //This used to be a bool (03-02-2020) + if(isNaN(this.fontsize)){this.fontsize = 0.9;} //This used to be a string (03-02-2020) + + this.load_tabs(); + }, + load_tabs: function() { + var loadstring = get_storage("tabs") + if(!loadstring) + return; + var loadfile = JSON.parse(loadstring); + //Malformed somehow. + if(!loadfile.version || !loadfile.tabs) { + this.internal_message("There was a problem loading your tabs. Any new ones you make will be saved, however."); + return; + } + //Version is old? Sorry. + if(!loadfile.version == vchat_opts.vchatTabsVer) { + this.internal_message("Your saved tabs are for an older version of VChat and must be recreated, sorry."); + return; + } + + this.tabs.push.apply(this.tabs, loadfile.tabs); + }, + save_tabs: function() { + var savefile = { + version: vchat_opts.vchatTabsVer, + tabs: [] + } + + //The tabs contain a bunch of vue stuff that gets funky when you try to serialize it with stringify, so we 'purify' it + this.tabs.forEach(function(tab){ + if(tab.immutable) + return; + + var name = tab.name; + + var categories = []; + tab.categories.forEach(function(category){categories.push(category);}); + + var cleantab = {name: name, categories: categories, immutable: false, active: false} + + savefile.tabs.push(cleantab); + }); + + var savestring = JSON.stringify(savefile); + set_storage("tabs", savestring); + }, + //Change to another tab + switchtab: function(tab) { + if(tab == this.active_tab) return; + this.active_tab.active = false; + tab.active = true; + + tab.categories.forEach( function(cls) { + this.unread_messages[cls] = 0; + }, this); + + this.apply_filter(this.current_categories); + }, + //Toggle edit mode + editmode: function() { + this.editing = !this.editing; + this.save_tabs(); + }, + //Toggle autoscroll + pause: function() { + this.paused = !this.paused; + }, + //Create a new tab (stupid lack of classes in ES5...) + newtab: function() { + this.tabs.push({ + name: "New Tab", + categories: [], + immutable: false, + active: false + }); + this.switchtab(this.tabs[this.tabs.length - 1]); + }, + //Rename an existing tab + renametab: function() { + if(this.active_tab.immutable) { + return; + } + var tabtorename = this.active_tab; + var newname = window.prompt("Type the desired tab name:", tabtorename.name); + if(newname === null || newname === "" || tabtorename === null) { + return; + } + tabtorename.name = newname; + }, + //Delete the currently active tab + deltab: function(tab) { + if(!tab) { + tab = this.active_tab; + } + if(tab.immutable) { + return; + } + this.switchtab(this.tabs[0]); + this.tabs.splice(this.tabs.indexOf(tab), 1); + }, + movetab: function(tab, shift) { + if(!tab || tab.immutable) { + return; + } + var at = this.tabs.indexOf(tab); + var to = at + shift; + this.tabs.splice(to, 0, this.tabs.splice(at, 1)[0]); + }, + tab_unread_count: function(tab) { + var unreads = 0; + var thisum = this.unread_messages; + tab.categories.find( function(cls){ + if(thisum[cls]) { + unreads += thisum[cls]; + } + }); + return unreads; + }, + tab_unread_categories: function(tab) { + var unreads = false; + var thisum = this.unread_messages; + tab.categories.find( function(cls){ + if(thisum[cls]) { + unreads = true; + return true; + } + }); + + return { red: unreads, grey: !unreads}; + }, + attempt_archive: function() { + var wiggle = 20; //Wiggle room to prevent hysterisis effects. Slice off 20 at a time. + //Pushing out old messages + if(this.messages.length > this.showingnum) {//Time to slice off old messages + var too_old = this.messages.splice(0,wiggle); //We do a few at a time to avoid doing it too often + Array.prototype.push.apply(this.archived_messages, too_old); //ES6 adds spread operator. I'd use it if I could. + }/* + //Pulling back old messages + } else if(this.messages.length < (this.showingnum - wiggle)) { //Sigh, repopulate old messages + var too_new = this.archived_messages.splice(this.messages.length - (this.showingnum - wiggle)); + Array.prototype.shift.apply(this.messages, too_new); + } + */ + }, + apply_filter: function(cat_array) { + //Clean up the array + this.shown_messages.splice(0); + this.unshown_messages = 0; + + //For each message, try to find it's category in the categories we're showing + this.messages.forEach( function(msg){ + if(cat_array.indexOf(msg.category) > -1) { //Returns the position in the array, and -1 for not found + this.shown_messages.push(msg); + } + }, this); + + //For each message, try to find it's category in the categories we're showing + this.archived_messages.forEach( function(msg){ + if(cat_array.indexOf(msg.category) > -1) { //Returns the position in the array, and -1 for not found + this.unshown_messages++; + } + }, this); + }, + //Push a new message into our array + add_message: function(message) { + //IE doesn't support the 'class' syntactic sugar so we're left making our own object. + let newmessage = { + time: message.time, + category: "error", + content: message.message, + repeats: 1 + }; + + //Get a category + newmessage.category = this.get_category(newmessage.content); + + //Try to crush it with one of the last few + if(this.crushing) { + let crushwith = this.messages.slice(-(this.crushing)); + for (let i = crushwith.length - 1; i >= 0; i--) { + let oldmessage = crushwith[i]; + if(oldmessage.content == newmessage.content) { + newmessage.repeats += oldmessage.repeats; + this.messages.splice(this.messages.indexOf(oldmessage), 1); + } + } + } + + //Unread indicator and insertion into current tab shown messages if sensible + if(this.current_categories.length && (this.current_categories.indexOf(newmessage.category) < 0)) { //Not in the current categories + if (isNaN(this.unread_messages[newmessage.category])) { + this.unread_messages[newmessage.category] = 0; + } + this.unread_messages[newmessage.category] += 1; + } else if(this.current_categories.length) { //Is in the current categories + this.shown_messages.push(newmessage); + } + + //Append to vue's messages + newmessage.id = ++vchat_state.lastId; + this.attempt_archive(); + this.messages.push(newmessage); + }, + //Push an internally generated message into our array + internal_message: function(message) { + let newmessage = { + time: this.messages.length ? this.messages.slice(-1).time+1 : 0, + category: "vc_system", + content: "[VChat Internal] " + message + "" + }; + newmessage.id = ++vchat_state.lastId; + this.messages.push(newmessage); + }, + on_mouseup: function(event) { + // Focus map window on mouseup so hotkeys work. Exception for if they highlighted text or clicked an input. + let ele = event.target; + let textSelected = ('getSelection' in window) && window.getSelection().isCollapsed === false; + if (!textSelected && !(ele && (ele.tagName === 'INPUT' || ele.tagName === 'TEXTAREA'))) { + focusMapWindow(); + // Okay focusing map window appears to prevent click event from being fired. So lets do it ourselves. + event.preventDefault(); + event.target.click(); + } + }, + click_message: function(event) { + let ele = event.target; + if(ele.tagName === "A") { + event.stopPropagation(); + event.preventDefault ? event.preventDefault() : (event.returnValue = false); //The second one is the weird IE method. + + var href = ele.getAttribute('href'); // Gets actual href without transformation into fully qualified URL + + if (href[0] == '?' || (href.length >= 8 && href.substring(0,8) == "byond://")) { + window.location = href; //Internal byond link + } else { //It's an external link + window.location = "byond://?action=openLink&link="+encodeURIComponent(href); + } + } + }, + //Derive a vchat category based on css classes + get_category: function(message) { + if(!vchat_state.ready) { + push_Topic('not_ready'); + return; + } + + let doc = domparser.parseFromString(message, 'text/html'); + let evaluating = doc.querySelector('span'); + + let category = "nomatch"; //What we use if the classes aren't anything we know. + if(!evaluating) return category; + this.type_table.find( function(type) { + if(evaluating.msMatchesSelector(type.matches)) { + category = type.becomes; + return true; + } + }); + + return category; + }, + save_chatlog: function() { + var textToSave = ""; + + var messagesToSave = this.archived_messages.concat(this.messages); + + messagesToSave.forEach( function(message) { + textToSave += message.content; + if(message.repeats > 1) { + textToSave += "(x"+message.repeats+")"; + } + textToSave += "
    \n"; + }); + textToSave += ""; + + var fileprefix = "log"; + var extension =".html"; + + var now = new Date(); + var hours = String(now.getHours()); + if(hours.length < 2) { + hours = "0" + hours; + } + var minutes = String(now.getMinutes()); + if(minutes.length < 2) { + minutes = "0" + minutes; + } + var dayofmonth = String(now.getDate()); + if(dayofmonth.length < 2) { + dayofmonth = "0" + dayofmonth; + } + var month = String(now.getMonth()+1); //0-11 + if(month.length < 2) { + month = "0" + month; + } + var year = String(now.getFullYear()); + var datesegment = " "+year+"-"+month+"-"+dayofmonth+" ("+hours+" "+minutes+")"; + + var filename = fileprefix+datesegment+extension; + + //Unlikely to work unfortunately, not supported in any version of IE, only Edge + var hiddenElement = document.createElement('a'); + if (hiddenElement.download !== undefined) { + hiddenElement.href = 'data:attachment/text,' + encodeURI(textToSave); //Has a problem in byond 512 due to weird unicode handling + hiddenElement.target = '_blank'; + hiddenElement.download = filename; + hiddenElement.click(); + //Probably what will end up getting used + } else { + var blob = new Blob([textToSave], {type: 'text/html;charset=utf8;'}); + saved = window.navigator.msSaveOrOpenBlob(blob, filename); + } + }, + do_latency_test: function() { + send_latency_check(); + }, + blur_this: function(event) { + event.target.blur(); + } + } + }); +} + +/*********** +* +* Actual Methods +* +************/ +function check_ping() { + var time_ago = Date.now() - vchat_state.lastPingReceived; + if(time_ago > vchat_opts.msBeforeDropped) + vueapp.reconnecting = true; +} + +//Send a 'ping' to byond +function send_latency_check() { + if(vchat_state.latency_sent) + return; + + vchat_state.latency_sent = Date.now(); + vueapp.latency = "?"; + push_Topic("ping"); + setTimeout(function() { + if(vchat_state.latency_ms == "?") { + vchat_state.latency_ms = 999; + } + }, 1000); // 1 second to reply otherwise we mark it as bad + setTimeout(function() { + vchat_state.latency_sent = 0; + vueapp.latency = 0; + }, 5000); //5 seconds to display ping time overall +} + +function get_latency_check() { + if(!vchat_state.latency_sent) { + return; //Too late + } + + vueapp.latency = Date.now() - vchat_state.latency_sent; +} + +//We accept double-url-encoded JSON strings because Byond is garbage and UTF-8 encoded url_encode() text has crazy garbage in it. +function byondDecode(message) { + + //Byond encodes spaces as pluses?! This is 1998 I guess. + message = message.replace(/\+/g, "%20"); + try { + message = decodeURIComponent(message); + } catch (err) { + message = unescape(message); + } + return JSON.parse(message); +} + +//This is the function byond actually communicates with using byond's client << output() method. +function putmessage(messages) { + messages = byondDecode(messages); + if (Array.isArray(messages)) { + messages.forEach(function(message) { + vueapp.add_message(message); + }); + } else if (typeof messages === 'object') { + vueapp.add_message(messages); + } +} + +//Send an internal message generated in the javascript +function system_message(message) { + vueapp.internal_message(message); +} + +//This is the other direction of communication, to push a Topic message back +function push_Topic(topic_uri) { + window.location = '?_src_=chat&proc=' + topic_uri; //Yes that's really how it works. +} + +//Tells byond client to focus the main map window. +function focusMapWindow() { + window.location = 'byond://winset?mapwindow.map.focus=true'; +} + +//Debug event +function send_debug(message) { + push_Topic("debug¶m[message]="+encodeURIComponent(message)); +} + +//A side-channel to send events over that aren't just chat messages, if necessary. +function get_event(event) { + if(!vchat_state.ready) { + push_Topic("not_ready"); + return; + } + + var parsed_event = {evttype: 'internal_error', event: event}; + parsed_event = byondDecode(event); + + switch(parsed_event.evttype) { + //We didn't parse it very well + case 'internal_error': + system_message("Event parse error: " + event); + break; + + //They provided byond data. + case 'byond_player': + send_client_data(); + vueapp.is_admin = (parsed_event.admin === 'true'); + vchat_state.byond_ip = parsed_event.address; + vchat_state.byond_cid = parsed_event.cid; + vchat_state.byond_ckey = parsed_event.ckey; + set_storage("ip",vchat_state.byond_ip); + set_storage("cid",vchat_state.byond_cid); + set_storage("ckey",vchat_state.byond_ckey); + break; + + //Just a ping. + case 'keepalive': + vchat_state.lastPingReceived = Date.now(); + vueapp.reconnecting = false; + break; + + //Response to a latency test. + case 'pong': + get_latency_check(); + break; + + //The server doesn't know if we're loaded or not (we bail above if we're not, so we must be). + case 'availability': + push_Topic("done_loading"); + break; + + default: + system_message("Didn't know what to do with event: " + event); + } +} + +//Send information retrieved from storage +function send_client_data() { + let client_data = { + ip: get_storage("ip"), + cid: get_storage("cid"), + ckey: get_storage("ckey") + }; + push_Topic("ident¶m[clientdata]="+JSON.stringify(client_data)); +} + +//Newer localstorage methods +function set_localstorage(key, value) { + let localstorage = window.localStorage; + localstorage.setItem(vchat_opts.cookiePrefix+key,value); +} + +function get_localstorage(key, deffo) { + let localstorage = window.localStorage; + let value = localstorage.getItem(vchat_opts.cookiePrefix+key); + + //localstorage only stores strings. + if(value === "null" || value === null) { + value = deffo; + //Coerce bools back into their native forms + } else if(value === "true") { + value = true; + } else if(value === "false") { + value = false; + //Coerce numbers back into numerical form + } else if(!isNaN(value)) { + value = +value; + } + return value; +} + +//Older cookie methods +function set_cookie(key, value) { + let now = new Date(); + now.setFullYear(now.getFullYear() + 1); + let then = now.toUTCString(); + document.cookie = vchat_opts.cookiePrefix+key+"="+value+";expires="+then+";path=/"; +} + +function get_cookie(key, deffo) { + var candidates = {cookie: null, localstorage: null, indexeddb: null}; + let cookie_array = document.cookie.split(';'); + let cookie_object = {}; + cookie_array.forEach( function(element) { + let clean = element.replace(vchat_opts.cookiePrefix,"").trim(); //Strip the prefix, trim whitespace + let equals = clean.search("="); //Find the equals + let left = decodeURIComponent(clean.substring(0,equals)); //From start to one char before equals + let right = decodeURIComponent(clean.substring(equals+1)); //From one char after equals to end + //cookies only stores strings. + if(right == "null" || right === null) { + right = deffo; + } else if(right === "true") { + right = true; + } else if(right === "false") { + right = false; + } else if(!isNaN(right)) { + right = +right; + } + cookie_object[left] = right; //Stick into object + }); + candidates.cookie = cookie_object[key]; //Return value of that key in our object (or undefined) +} + +// Button Controls that need background-color and text-color set. +var SKIN_BUTTONS = [ + /* Rpane */ "rpane.textb", "rpane.infob", "rpane.wikib", "rpane.forumb", "rpane.rulesb", "rpane.github", "rpane.mapb", "rpane.changelog", + /* Mainwindow */ "mainwindow.saybutton", "mainwindow.mebutton", "mainwindow.hotkey_toggle" + +]; +// Windows or controls that need background-color set. +var SKIN_ELEMENTS = [ + /* Mainwindow */ "mainwindow", "mainwindow.mainvsplit", "mainwindow.tooltip", + /* Rpane */ "rpane", "rpane.rpanewindow", "rpane.mediapanel", +]; + +function switch_ui_mode(options) { + doWinset(SKIN_BUTTONS.reduce(function(params, ctl) {params[ctl + ".background-color"] = options.buttonBgColor; return params;}, {})); + doWinset(SKIN_BUTTONS.reduce(function(params, ctl) {params[ctl + ".text-color"] = options.buttonTextColor; return params;}, {})); + doWinset(SKIN_ELEMENTS.reduce(function(params, ctl) {params[ctl + ".background-color"] = options.windowBgColor; return params;}, {})); + doWinset("infowindow", { + "background-color": options.tabBackgroundColor, + "text-color": options.tabTextColor + }); + doWinset("infowindow.info", { + "background-color": options.tabBackgroundColor, + "text-color": options.tabTextColor, + "highlight-color": options.highlightColor, + "tab-text-color": options.tabTextColor, + "tab-background-color": options.tabBackgroundColor + }); +} + +function doWinset(control_id, params) { + if (typeof params === 'undefined') { + params = control_id; // Handle single-argument use case. + control_id = null; + } + var url = "byond://winset?"; + if (control_id) { + url += ("id=" + control_id + "&"); + } + url += Object.keys(params).map(function(ctl) { + return ctl + "=" + encodeURIComponent(params[ctl]); + }).join("&"); + window.location = url; +} diff --git a/code/modules/vchat/js/vue.min.js b/code/modules/vchat/js/vue.min.js new file mode 100644 index 0000000000..e22cf13003 --- /dev/null +++ b/code/modules/vchat/js/vue.min.js @@ -0,0 +1,11965 @@ +/*! + * Vue.js v2.6.11 + * (c) 2014-2019 Evan You + * Released under the MIT License. + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.Vue = factory()); +}(this, function () { 'use strict'; + + /* */ + + var emptyObject = Object.freeze({}); + + // These helpers produce better VM code in JS engines due to their + // explicitness and function inlining. + function isUndef (v) { + return v === undefined || v === null + } + + function isDef (v) { + return v !== undefined && v !== null + } + + function isTrue (v) { + return v === true + } + + function isFalse (v) { + return v === false + } + + /** + * Check if value is primitive. + */ + function isPrimitive (value) { + return ( + typeof value === 'string' || + typeof value === 'number' || + // $flow-disable-line + typeof value === 'symbol' || + typeof value === 'boolean' + ) + } + + /** + * Quick object check - this is primarily used to tell + * Objects from primitive values when we know the value + * is a JSON-compliant type. + */ + function isObject (obj) { + return obj !== null && typeof obj === 'object' + } + + /** + * Get the raw type string of a value, e.g., [object Object]. + */ + var _toString = Object.prototype.toString; + + function toRawType (value) { + return _toString.call(value).slice(8, -1) + } + + /** + * Strict object type check. Only returns true + * for plain JavaScript objects. + */ + function isPlainObject (obj) { + return _toString.call(obj) === '[object Object]' + } + + function isRegExp (v) { + return _toString.call(v) === '[object RegExp]' + } + + /** + * Check if val is a valid array index. + */ + function isValidArrayIndex (val) { + var n = parseFloat(String(val)); + return n >= 0 && Math.floor(n) === n && isFinite(val) + } + + function isPromise (val) { + return ( + isDef(val) && + typeof val.then === 'function' && + typeof val.catch === 'function' + ) + } + + /** + * Convert a value to a string that is actually rendered. + */ + function toString (val) { + return val == null + ? '' + : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) + ? JSON.stringify(val, null, 2) + : String(val) + } + + /** + * Convert an input value to a number for persistence. + * If the conversion fails, return original string. + */ + function toNumber (val) { + var n = parseFloat(val); + return isNaN(n) ? val : n + } + + /** + * Make a map and return a function for checking if a key + * is in that map. + */ + function makeMap ( + str, + expectsLowerCase + ) { + var map = Object.create(null); + var list = str.split(','); + for (var i = 0; i < list.length; i++) { + map[list[i]] = true; + } + return expectsLowerCase + ? function (val) { return map[val.toLowerCase()]; } + : function (val) { return map[val]; } + } + + /** + * Check if a tag is a built-in tag. + */ + var isBuiltInTag = makeMap('slot,component', true); + + /** + * Check if an attribute is a reserved attribute. + */ + var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); + + /** + * Remove an item from an array. + */ + function remove (arr, item) { + if (arr.length) { + var index = arr.indexOf(item); + if (index > -1) { + return arr.splice(index, 1) + } + } + } + + /** + * Check whether an object has the property. + */ + var hasOwnProperty = Object.prototype.hasOwnProperty; + function hasOwn (obj, key) { + return hasOwnProperty.call(obj, key) + } + + /** + * Create a cached version of a pure function. + */ + function cached (fn) { + var cache = Object.create(null); + return (function cachedFn (str) { + var hit = cache[str]; + return hit || (cache[str] = fn(str)) + }) + } + + /** + * Camelize a hyphen-delimited string. + */ + var camelizeRE = /-(\w)/g; + var camelize = cached(function (str) { + return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) + }); + + /** + * Capitalize a string. + */ + var capitalize = cached(function (str) { + return str.charAt(0).toUpperCase() + str.slice(1) + }); + + /** + * Hyphenate a camelCase string. + */ + var hyphenateRE = /\B([A-Z])/g; + var hyphenate = cached(function (str) { + return str.replace(hyphenateRE, '-$1').toLowerCase() + }); + + /** + * Simple bind polyfill for environments that do not support it, + * e.g., PhantomJS 1.x. Technically, we don't need this anymore + * since native bind is now performant enough in most browsers. + * But removing it would mean breaking code that was able to run in + * PhantomJS 1.x, so this must be kept for backward compatibility. + */ + + /* istanbul ignore next */ + function polyfillBind (fn, ctx) { + function boundFn (a) { + var l = arguments.length; + return l + ? l > 1 + ? fn.apply(ctx, arguments) + : fn.call(ctx, a) + : fn.call(ctx) + } + + boundFn._length = fn.length; + return boundFn + } + + function nativeBind (fn, ctx) { + return fn.bind(ctx) + } + + var bind = Function.prototype.bind + ? nativeBind + : polyfillBind; + + /** + * Convert an Array-like object to a real Array. + */ + function toArray (list, start) { + start = start || 0; + var i = list.length - start; + var ret = new Array(i); + while (i--) { + ret[i] = list[i + start]; + } + return ret + } + + /** + * Mix properties into target object. + */ + function extend (to, _from) { + for (var key in _from) { + to[key] = _from[key]; + } + return to + } + + /** + * Merge an Array of Objects into a single Object. + */ + function toObject (arr) { + var res = {}; + for (var i = 0; i < arr.length; i++) { + if (arr[i]) { + extend(res, arr[i]); + } + } + return res + } + + /* eslint-disable no-unused-vars */ + + /** + * Perform no operation. + * Stubbing args to make Flow happy without leaving useless transpiled code + * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/). + */ + function noop (a, b, c) {} + + /** + * Always return false. + */ + var no = function (a, b, c) { return false; }; + + /* eslint-enable no-unused-vars */ + + /** + * Return the same value. + */ + var identity = function (_) { return _; }; + + /** + * Generate a string containing static keys from compiler modules. + */ + function genStaticKeys (modules) { + return modules.reduce(function (keys, m) { + return keys.concat(m.staticKeys || []) + }, []).join(',') + } + + /** + * Check if two values are loosely equal - that is, + * if they are plain objects, do they have the same shape? + */ + function looseEqual (a, b) { + if (a === b) { return true } + var isObjectA = isObject(a); + var isObjectB = isObject(b); + if (isObjectA && isObjectB) { + try { + var isArrayA = Array.isArray(a); + var isArrayB = Array.isArray(b); + if (isArrayA && isArrayB) { + return a.length === b.length && a.every(function (e, i) { + return looseEqual(e, b[i]) + }) + } else if (a instanceof Date && b instanceof Date) { + return a.getTime() === b.getTime() + } else if (!isArrayA && !isArrayB) { + var keysA = Object.keys(a); + var keysB = Object.keys(b); + return keysA.length === keysB.length && keysA.every(function (key) { + return looseEqual(a[key], b[key]) + }) + } else { + /* istanbul ignore next */ + return false + } + } catch (e) { + /* istanbul ignore next */ + return false + } + } else if (!isObjectA && !isObjectB) { + return String(a) === String(b) + } else { + return false + } + } + + /** + * Return the first index at which a loosely equal value can be + * found in the array (if value is a plain object, the array must + * contain an object of the same shape), or -1 if it is not present. + */ + function looseIndexOf (arr, val) { + for (var i = 0; i < arr.length; i++) { + if (looseEqual(arr[i], val)) { return i } + } + return -1 + } + + /** + * Ensure a function is called only once. + */ + function once (fn) { + var called = false; + return function () { + if (!called) { + called = true; + fn.apply(this, arguments); + } + } + } + + var SSR_ATTR = 'data-server-rendered'; + + var ASSET_TYPES = [ + 'component', + 'directive', + 'filter' + ]; + + var LIFECYCLE_HOOKS = [ + 'beforeCreate', + 'created', + 'beforeMount', + 'mounted', + 'beforeUpdate', + 'updated', + 'beforeDestroy', + 'destroyed', + 'activated', + 'deactivated', + 'errorCaptured', + 'serverPrefetch' + ]; + + /* */ + + + + var config = ({ + /** + * Option merge strategies (used in core/util/options) + */ + // $flow-disable-line + optionMergeStrategies: Object.create(null), + + /** + * Whether to suppress warnings. + */ + silent: false, + + /** + * Show production mode tip message on boot? + */ + productionTip: "development" !== 'production', + + /** + * Whether to enable devtools + */ + devtools: "development" !== 'production', + + /** + * Whether to record perf + */ + performance: false, + + /** + * Error handler for watcher errors + */ + errorHandler: null, + + /** + * Warn handler for watcher warns + */ + warnHandler: null, + + /** + * Ignore certain custom elements + */ + ignoredElements: [], + + /** + * Custom user key aliases for v-on + */ + // $flow-disable-line + keyCodes: Object.create(null), + + /** + * Check if a tag is reserved so that it cannot be registered as a + * component. This is platform-dependent and may be overwritten. + */ + isReservedTag: no, + + /** + * Check if an attribute is reserved so that it cannot be used as a component + * prop. This is platform-dependent and may be overwritten. + */ + isReservedAttr: no, + + /** + * Check if a tag is an unknown element. + * Platform-dependent. + */ + isUnknownElement: no, + + /** + * Get the namespace of an element + */ + getTagNamespace: noop, + + /** + * Parse the real tag name for the specific platform. + */ + parsePlatformTagName: identity, + + /** + * Check if an attribute must be bound using property, e.g. value + * Platform-dependent. + */ + mustUseProp: no, + + /** + * Perform updates asynchronously. Intended to be used by Vue Test Utils + * This will significantly reduce performance if set to false. + */ + async: true, + + /** + * Exposed for legacy reasons + */ + _lifecycleHooks: LIFECYCLE_HOOKS + }); + + /* */ + + /** + * unicode letters used for parsing html tags, component names and property paths. + * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname + * skipping \u10000-\uEFFFF due to it freezing up PhantomJS + */ + var unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/; + + /** + * Check if a string starts with $ or _ + */ + function isReserved (str) { + var c = (str + '').charCodeAt(0); + return c === 0x24 || c === 0x5F + } + + /** + * Define a property. + */ + function def (obj, key, val, enumerable) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }); + } + + /** + * Parse simple path. + */ + var bailRE = new RegExp(("[^" + (unicodeRegExp.source) + ".$_\\d]")); + function parsePath (path) { + if (bailRE.test(path)) { + return + } + var segments = path.split('.'); + return function (obj) { + for (var i = 0; i < segments.length; i++) { + if (!obj) { return } + obj = obj[segments[i]]; + } + return obj + } + } + + /* */ + + // can we use __proto__? + var hasProto = '__proto__' in {}; + + // Browser environment sniffing + var inBrowser = typeof window !== 'undefined'; + var inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; + var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); + var UA = inBrowser && window.navigator.userAgent.toLowerCase(); + var isIE = UA && /msie|trident/.test(UA); + var isIE9 = UA && UA.indexOf('msie 9.0') > 0; + var isEdge = UA && UA.indexOf('edge/') > 0; + var isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android'); + var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); + var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; + var isPhantomJS = UA && /phantomjs/.test(UA); + var isFF = UA && UA.match(/firefox\/(\d+)/); + + // Firefox has a "watch" function on Object.prototype... + var nativeWatch = ({}).watch; + + var supportsPassive = false; + if (inBrowser) { + try { + var opts = {}; + Object.defineProperty(opts, 'passive', ({ + get: function get () { + /* istanbul ignore next */ + supportsPassive = true; + } + })); // https://github.com/facebook/flow/issues/285 + window.addEventListener('test-passive', null, opts); + } catch (e) {} + } + + // this needs to be lazy-evaled because vue may be required before + // vue-server-renderer can set VUE_ENV + var _isServer; + var isServerRendering = function () { + if (_isServer === undefined) { + /* istanbul ignore if */ + if (!inBrowser && !inWeex && typeof global !== 'undefined') { + // detect presence of vue-server-renderer and avoid + // Webpack shimming the process + _isServer = global['process'] && global['process'].env.VUE_ENV === 'server'; + } else { + _isServer = false; + } + } + return _isServer + }; + + // detect devtools + var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; + + /* istanbul ignore next */ + function isNative (Ctor) { + return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) + } + + var hasSymbol = + typeof Symbol !== 'undefined' && isNative(Symbol) && + typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); + + var _Set; + /* istanbul ignore if */ // $flow-disable-line + if (typeof Set !== 'undefined' && isNative(Set)) { + // use native Set when available. + _Set = Set; + } else { + // a non-standard Set polyfill that only works with primitive keys. + _Set = /*@__PURE__*/(function () { + function Set () { + this.set = Object.create(null); + } + Set.prototype.has = function has (key) { + return this.set[key] === true + }; + Set.prototype.add = function add (key) { + this.set[key] = true; + }; + Set.prototype.clear = function clear () { + this.set = Object.create(null); + }; + + return Set; + }()); + } + + /* */ + + var warn = noop; + var tip = noop; + var generateComponentTrace = (noop); // work around flow check + var formatComponentName = (noop); + + { + var hasConsole = typeof console !== 'undefined'; + var classifyRE = /(?:^|[-_])(\w)/g; + var classify = function (str) { return str + .replace(classifyRE, function (c) { return c.toUpperCase(); }) + .replace(/[-_]/g, ''); }; + + warn = function (msg, vm) { + var trace = vm ? generateComponentTrace(vm) : ''; + + if (config.warnHandler) { + config.warnHandler.call(null, msg, vm, trace); + } else if (hasConsole && (!config.silent)) { + console.error(("[Vue warn]: " + msg + trace)); + } + }; + + tip = function (msg, vm) { + if (hasConsole && (!config.silent)) { + console.warn("[Vue tip]: " + msg + ( + vm ? generateComponentTrace(vm) : '' + )); + } + }; + + formatComponentName = function (vm, includeFile) { + if (vm.$root === vm) { + return '' + } + var options = typeof vm === 'function' && vm.cid != null + ? vm.options + : vm._isVue + ? vm.$options || vm.constructor.options + : vm; + var name = options.name || options._componentTag; + var file = options.__file; + if (!name && file) { + var match = file.match(/([^/\\]+)\.vue$/); + name = match && match[1]; + } + + return ( + (name ? ("<" + (classify(name)) + ">") : "") + + (file && includeFile !== false ? (" at " + file) : '') + ) + }; + + var repeat = function (str, n) { + var res = ''; + while (n) { + if (n % 2 === 1) { res += str; } + if (n > 1) { str += str; } + n >>= 1; + } + return res + }; + + generateComponentTrace = function (vm) { + if (vm._isVue && vm.$parent) { + var tree = []; + var currentRecursiveSequence = 0; + while (vm) { + if (tree.length > 0) { + var last = tree[tree.length - 1]; + if (last.constructor === vm.constructor) { + currentRecursiveSequence++; + vm = vm.$parent; + continue + } else if (currentRecursiveSequence > 0) { + tree[tree.length - 1] = [last, currentRecursiveSequence]; + currentRecursiveSequence = 0; + } + } + tree.push(vm); + vm = vm.$parent; + } + return '\n\nfound in\n\n' + tree + .map(function (vm, i) { return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) + ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") + : formatComponentName(vm))); }) + .join('\n') + } else { + return ("\n\n(found in " + (formatComponentName(vm)) + ")") + } + }; + } + + /* */ + + var uid = 0; + + /** + * A dep is an observable that can have multiple + * directives subscribing to it. + */ + var Dep = function Dep () { + this.id = uid++; + this.subs = []; + }; + + Dep.prototype.addSub = function addSub (sub) { + this.subs.push(sub); + }; + + Dep.prototype.removeSub = function removeSub (sub) { + remove(this.subs, sub); + }; + + Dep.prototype.depend = function depend () { + if (Dep.target) { + Dep.target.addDep(this); + } + }; + + Dep.prototype.notify = function notify () { + // stabilize the subscriber list first + var subs = this.subs.slice(); + if (!config.async) { + // subs aren't sorted in scheduler if not running async + // we need to sort them now to make sure they fire in correct + // order + subs.sort(function (a, b) { return a.id - b.id; }); + } + for (var i = 0, l = subs.length; i < l; i++) { + subs[i].update(); + } + }; + + // The current target watcher being evaluated. + // This is globally unique because only one watcher + // can be evaluated at a time. + Dep.target = null; + var targetStack = []; + + function pushTarget (target) { + targetStack.push(target); + Dep.target = target; + } + + function popTarget () { + targetStack.pop(); + Dep.target = targetStack[targetStack.length - 1]; + } + + /* */ + + var VNode = function VNode ( + tag, + data, + children, + text, + elm, + context, + componentOptions, + asyncFactory + ) { + this.tag = tag; + this.data = data; + this.children = children; + this.text = text; + this.elm = elm; + this.ns = undefined; + this.context = context; + this.fnContext = undefined; + this.fnOptions = undefined; + this.fnScopeId = undefined; + this.key = data && data.key; + this.componentOptions = componentOptions; + this.componentInstance = undefined; + this.parent = undefined; + this.raw = false; + this.isStatic = false; + this.isRootInsert = true; + this.isComment = false; + this.isCloned = false; + this.isOnce = false; + this.asyncFactory = asyncFactory; + this.asyncMeta = undefined; + this.isAsyncPlaceholder = false; + }; + + var prototypeAccessors = { child: { configurable: true } }; + + // DEPRECATED: alias for componentInstance for backwards compat. + /* istanbul ignore next */ + prototypeAccessors.child.get = function () { + return this.componentInstance + }; + + Object.defineProperties( VNode.prototype, prototypeAccessors ); + + var createEmptyVNode = function (text) { + if ( text === void 0 ) text = ''; + + var node = new VNode(); + node.text = text; + node.isComment = true; + return node + }; + + function createTextVNode (val) { + return new VNode(undefined, undefined, undefined, String(val)) + } + + // optimized shallow clone + // used for static nodes and slot nodes because they may be reused across + // multiple renders, cloning them avoids errors when DOM manipulations rely + // on their elm reference. + function cloneVNode (vnode) { + var cloned = new VNode( + vnode.tag, + vnode.data, + // #7975 + // clone children array to avoid mutating original in case of cloning + // a child. + vnode.children && vnode.children.slice(), + vnode.text, + vnode.elm, + vnode.context, + vnode.componentOptions, + vnode.asyncFactory + ); + cloned.ns = vnode.ns; + cloned.isStatic = vnode.isStatic; + cloned.key = vnode.key; + cloned.isComment = vnode.isComment; + cloned.fnContext = vnode.fnContext; + cloned.fnOptions = vnode.fnOptions; + cloned.fnScopeId = vnode.fnScopeId; + cloned.asyncMeta = vnode.asyncMeta; + cloned.isCloned = true; + return cloned + } + + /* + * not type checking this file because flow doesn't play well with + * dynamically accessing methods on Array prototype + */ + + var arrayProto = Array.prototype; + var arrayMethods = Object.create(arrayProto); + + var methodsToPatch = [ + 'push', + 'pop', + 'shift', + 'unshift', + 'splice', + 'sort', + 'reverse' + ]; + + /** + * Intercept mutating methods and emit events + */ + methodsToPatch.forEach(function (method) { + // cache original method + var original = arrayProto[method]; + def(arrayMethods, method, function mutator () { + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + + var result = original.apply(this, args); + var ob = this.__ob__; + var inserted; + switch (method) { + case 'push': + case 'unshift': + inserted = args; + break + case 'splice': + inserted = args.slice(2); + break + } + if (inserted) { ob.observeArray(inserted); } + // notify change + ob.dep.notify(); + return result + }); + }); + + /* */ + + var arrayKeys = Object.getOwnPropertyNames(arrayMethods); + + /** + * In some cases we may want to disable observation inside a component's + * update computation. + */ + var shouldObserve = true; + + function toggleObserving (value) { + shouldObserve = value; + } + + /** + * Observer class that is attached to each observed + * object. Once attached, the observer converts the target + * object's property keys into getter/setters that + * collect dependencies and dispatch updates. + */ + var Observer = function Observer (value) { + this.value = value; + this.dep = new Dep(); + this.vmCount = 0; + def(value, '__ob__', this); + if (Array.isArray(value)) { + if (hasProto) { + protoAugment(value, arrayMethods); + } else { + copyAugment(value, arrayMethods, arrayKeys); + } + this.observeArray(value); + } else { + this.walk(value); + } + }; + + /** + * Walk through all properties and convert them into + * getter/setters. This method should only be called when + * value type is Object. + */ + Observer.prototype.walk = function walk (obj) { + var keys = Object.keys(obj); + for (var i = 0; i < keys.length; i++) { + defineReactive$$1(obj, keys[i]); + } + }; + + /** + * Observe a list of Array items. + */ + Observer.prototype.observeArray = function observeArray (items) { + for (var i = 0, l = items.length; i < l; i++) { + observe(items[i]); + } + }; + + // helpers + + /** + * Augment a target Object or Array by intercepting + * the prototype chain using __proto__ + */ + function protoAugment (target, src) { + /* eslint-disable no-proto */ + target.__proto__ = src; + /* eslint-enable no-proto */ + } + + /** + * Augment a target Object or Array by defining + * hidden properties. + */ + /* istanbul ignore next */ + function copyAugment (target, src, keys) { + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + def(target, key, src[key]); + } + } + + /** + * Attempt to create an observer instance for a value, + * returns the new observer if successfully observed, + * or the existing observer if the value already has one. + */ + function observe (value, asRootData) { + if (!isObject(value) || value instanceof VNode) { + return + } + var ob; + if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if ( + shouldObserve && + !isServerRendering() && + (Array.isArray(value) || isPlainObject(value)) && + Object.isExtensible(value) && + !value._isVue + ) { + ob = new Observer(value); + } + if (asRootData && ob) { + ob.vmCount++; + } + return ob + } + + /** + * Define a reactive property on an Object. + */ + function defineReactive$$1 ( + obj, + key, + val, + customSetter, + shallow + ) { + var dep = new Dep(); + + var property = Object.getOwnPropertyDescriptor(obj, key); + if (property && property.configurable === false) { + return + } + + // cater for pre-defined getter/setters + var getter = property && property.get; + var setter = property && property.set; + if ((!getter || setter) && arguments.length === 2) { + val = obj[key]; + } + + var childOb = !shallow && observe(val); + Object.defineProperty(obj, key, { + enumerable: true, + configurable: true, + get: function reactiveGetter () { + var value = getter ? getter.call(obj) : val; + if (Dep.target) { + dep.depend(); + if (childOb) { + childOb.dep.depend(); + if (Array.isArray(value)) { + dependArray(value); + } + } + } + return value + }, + set: function reactiveSetter (newVal) { + var value = getter ? getter.call(obj) : val; + /* eslint-disable no-self-compare */ + if (newVal === value || (newVal !== newVal && value !== value)) { + return + } + /* eslint-enable no-self-compare */ + if (customSetter) { + customSetter(); + } + // #7981: for accessor properties without setter + if (getter && !setter) { return } + if (setter) { + setter.call(obj, newVal); + } else { + val = newVal; + } + childOb = !shallow && observe(newVal); + dep.notify(); + } + }); + } + + /** + * Set a property on an object. Adds the new property and + * triggers change notification if the property doesn't + * already exist. + */ + function set (target, key, val) { + if (isUndef(target) || isPrimitive(target) + ) { + warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target)))); + } + if (Array.isArray(target) && isValidArrayIndex(key)) { + target.length = Math.max(target.length, key); + target.splice(key, 1, val); + return val + } + if (key in target && !(key in Object.prototype)) { + target[key] = val; + return val + } + var ob = (target).__ob__; + if (target._isVue || (ob && ob.vmCount)) { + warn( + 'Avoid adding reactive properties to a Vue instance or its root $data ' + + 'at runtime - declare it upfront in the data option.' + ); + return val + } + if (!ob) { + target[key] = val; + return val + } + defineReactive$$1(ob.value, key, val); + ob.dep.notify(); + return val + } + + /** + * Delete a property and trigger change if necessary. + */ + function del (target, key) { + if (isUndef(target) || isPrimitive(target) + ) { + warn(("Cannot delete reactive property on undefined, null, or primitive value: " + ((target)))); + } + if (Array.isArray(target) && isValidArrayIndex(key)) { + target.splice(key, 1); + return + } + var ob = (target).__ob__; + if (target._isVue || (ob && ob.vmCount)) { + warn( + 'Avoid deleting properties on a Vue instance or its root $data ' + + '- just set it to null.' + ); + return + } + if (!hasOwn(target, key)) { + return + } + delete target[key]; + if (!ob) { + return + } + ob.dep.notify(); + } + + /** + * Collect dependencies on array elements when the array is touched, since + * we cannot intercept array element access like property getters. + */ + function dependArray (value) { + for (var e = (void 0), i = 0, l = value.length; i < l; i++) { + e = value[i]; + e && e.__ob__ && e.__ob__.dep.depend(); + if (Array.isArray(e)) { + dependArray(e); + } + } + } + + /* */ + + /** + * Option overwriting strategies are functions that handle + * how to merge a parent option value and a child option + * value into the final value. + */ + var strats = config.optionMergeStrategies; + + /** + * Options with restrictions + */ + { + strats.el = strats.propsData = function (parent, child, vm, key) { + if (!vm) { + warn( + "option \"" + key + "\" can only be used during instance " + + 'creation with the `new` keyword.' + ); + } + return defaultStrat(parent, child) + }; + } + + /** + * Helper that recursively merges two data objects together. + */ + function mergeData (to, from) { + if (!from) { return to } + var key, toVal, fromVal; + + var keys = hasSymbol + ? Reflect.ownKeys(from) + : Object.keys(from); + + for (var i = 0; i < keys.length; i++) { + key = keys[i]; + // in case the object is already observed... + if (key === '__ob__') { continue } + toVal = to[key]; + fromVal = from[key]; + if (!hasOwn(to, key)) { + set(to, key, fromVal); + } else if ( + toVal !== fromVal && + isPlainObject(toVal) && + isPlainObject(fromVal) + ) { + mergeData(toVal, fromVal); + } + } + return to + } + + /** + * Data + */ + function mergeDataOrFn ( + parentVal, + childVal, + vm + ) { + if (!vm) { + // in a Vue.extend merge, both should be functions + if (!childVal) { + return parentVal + } + if (!parentVal) { + return childVal + } + // when parentVal & childVal are both present, + // we need to return a function that returns the + // merged result of both functions... no need to + // check if parentVal is a function here because + // it has to be a function to pass previous merges. + return function mergedDataFn () { + return mergeData( + typeof childVal === 'function' ? childVal.call(this, this) : childVal, + typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal + ) + } + } else { + return function mergedInstanceDataFn () { + // instance merge + var instanceData = typeof childVal === 'function' + ? childVal.call(vm, vm) + : childVal; + var defaultData = typeof parentVal === 'function' + ? parentVal.call(vm, vm) + : parentVal; + if (instanceData) { + return mergeData(instanceData, defaultData) + } else { + return defaultData + } + } + } + } + + strats.data = function ( + parentVal, + childVal, + vm + ) { + if (!vm) { + if (childVal && typeof childVal !== 'function') { + warn( + 'The "data" option should be a function ' + + 'that returns a per-instance value in component ' + + 'definitions.', + vm + ); + + return parentVal + } + return mergeDataOrFn(parentVal, childVal) + } + + return mergeDataOrFn(parentVal, childVal, vm) + }; + + /** + * Hooks and props are merged as arrays. + */ + function mergeHook ( + parentVal, + childVal + ) { + var res = childVal + ? parentVal + ? parentVal.concat(childVal) + : Array.isArray(childVal) + ? childVal + : [childVal] + : parentVal; + return res + ? dedupeHooks(res) + : res + } + + function dedupeHooks (hooks) { + var res = []; + for (var i = 0; i < hooks.length; i++) { + if (res.indexOf(hooks[i]) === -1) { + res.push(hooks[i]); + } + } + return res + } + + LIFECYCLE_HOOKS.forEach(function (hook) { + strats[hook] = mergeHook; + }); + + /** + * Assets + * + * When a vm is present (instance creation), we need to do + * a three-way merge between constructor options, instance + * options and parent options. + */ + function mergeAssets ( + parentVal, + childVal, + vm, + key + ) { + var res = Object.create(parentVal || null); + if (childVal) { + assertObjectType(key, childVal, vm); + return extend(res, childVal) + } else { + return res + } + } + + ASSET_TYPES.forEach(function (type) { + strats[type + 's'] = mergeAssets; + }); + + /** + * Watchers. + * + * Watchers hashes should not overwrite one + * another, so we merge them as arrays. + */ + strats.watch = function ( + parentVal, + childVal, + vm, + key + ) { + // work around Firefox's Object.prototype.watch... + if (parentVal === nativeWatch) { parentVal = undefined; } + if (childVal === nativeWatch) { childVal = undefined; } + /* istanbul ignore if */ + if (!childVal) { return Object.create(parentVal || null) } + { + assertObjectType(key, childVal, vm); + } + if (!parentVal) { return childVal } + var ret = {}; + extend(ret, parentVal); + for (var key$1 in childVal) { + var parent = ret[key$1]; + var child = childVal[key$1]; + if (parent && !Array.isArray(parent)) { + parent = [parent]; + } + ret[key$1] = parent + ? parent.concat(child) + : Array.isArray(child) ? child : [child]; + } + return ret + }; + + /** + * Other object hashes. + */ + strats.props = + strats.methods = + strats.inject = + strats.computed = function ( + parentVal, + childVal, + vm, + key + ) { + if (childVal && "development" !== 'production') { + assertObjectType(key, childVal, vm); + } + if (!parentVal) { return childVal } + var ret = Object.create(null); + extend(ret, parentVal); + if (childVal) { extend(ret, childVal); } + return ret + }; + strats.provide = mergeDataOrFn; + + /** + * Default strategy. + */ + var defaultStrat = function (parentVal, childVal) { + return childVal === undefined + ? parentVal + : childVal + }; + + /** + * Validate component names + */ + function checkComponents (options) { + for (var key in options.components) { + validateComponentName(key); + } + } + + function validateComponentName (name) { + if (!new RegExp(("^[a-zA-Z][\\-\\.0-9_" + (unicodeRegExp.source) + "]*$")).test(name)) { + warn( + 'Invalid component name: "' + name + '". Component names ' + + 'should conform to valid custom element name in html5 specification.' + ); + } + if (isBuiltInTag(name) || config.isReservedTag(name)) { + warn( + 'Do not use built-in or reserved HTML elements as component ' + + 'id: ' + name + ); + } + } + + /** + * Ensure all props option syntax are normalized into the + * Object-based format. + */ + function normalizeProps (options, vm) { + var props = options.props; + if (!props) { return } + var res = {}; + var i, val, name; + if (Array.isArray(props)) { + i = props.length; + while (i--) { + val = props[i]; + if (typeof val === 'string') { + name = camelize(val); + res[name] = { type: null }; + } else { + warn('props must be strings when using array syntax.'); + } + } + } else if (isPlainObject(props)) { + for (var key in props) { + val = props[key]; + name = camelize(key); + res[name] = isPlainObject(val) + ? val + : { type: val }; + } + } else { + warn( + "Invalid value for option \"props\": expected an Array or an Object, " + + "but got " + (toRawType(props)) + ".", + vm + ); + } + options.props = res; + } + + /** + * Normalize all injections into Object-based format + */ + function normalizeInject (options, vm) { + var inject = options.inject; + if (!inject) { return } + var normalized = options.inject = {}; + if (Array.isArray(inject)) { + for (var i = 0; i < inject.length; i++) { + normalized[inject[i]] = { from: inject[i] }; + } + } else if (isPlainObject(inject)) { + for (var key in inject) { + var val = inject[key]; + normalized[key] = isPlainObject(val) + ? extend({ from: key }, val) + : { from: val }; + } + } else { + warn( + "Invalid value for option \"inject\": expected an Array or an Object, " + + "but got " + (toRawType(inject)) + ".", + vm + ); + } + } + + /** + * Normalize raw function directives into object format. + */ + function normalizeDirectives (options) { + var dirs = options.directives; + if (dirs) { + for (var key in dirs) { + var def$$1 = dirs[key]; + if (typeof def$$1 === 'function') { + dirs[key] = { bind: def$$1, update: def$$1 }; + } + } + } + } + + function assertObjectType (name, value, vm) { + if (!isPlainObject(value)) { + warn( + "Invalid value for option \"" + name + "\": expected an Object, " + + "but got " + (toRawType(value)) + ".", + vm + ); + } + } + + /** + * Merge two option objects into a new one. + * Core utility used in both instantiation and inheritance. + */ + function mergeOptions ( + parent, + child, + vm + ) { + { + checkComponents(child); + } + + if (typeof child === 'function') { + child = child.options; + } + + normalizeProps(child, vm); + normalizeInject(child, vm); + normalizeDirectives(child); + + // Apply extends and mixins on the child options, + // but only if it is a raw options object that isn't + // the result of another mergeOptions call. + // Only merged options has the _base property. + if (!child._base) { + if (child.extends) { + parent = mergeOptions(parent, child.extends, vm); + } + if (child.mixins) { + for (var i = 0, l = child.mixins.length; i < l; i++) { + parent = mergeOptions(parent, child.mixins[i], vm); + } + } + } + + var options = {}; + var key; + for (key in parent) { + mergeField(key); + } + for (key in child) { + if (!hasOwn(parent, key)) { + mergeField(key); + } + } + function mergeField (key) { + var strat = strats[key] || defaultStrat; + options[key] = strat(parent[key], child[key], vm, key); + } + return options + } + + /** + * Resolve an asset. + * This function is used because child instances need access + * to assets defined in its ancestor chain. + */ + function resolveAsset ( + options, + type, + id, + warnMissing + ) { + /* istanbul ignore if */ + if (typeof id !== 'string') { + return + } + var assets = options[type]; + // check local registration variations first + if (hasOwn(assets, id)) { return assets[id] } + var camelizedId = camelize(id); + if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } + var PascalCaseId = capitalize(camelizedId); + if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } + // fallback to prototype chain + var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; + if (warnMissing && !res) { + warn( + 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, + options + ); + } + return res + } + + /* */ + + + + function validateProp ( + key, + propOptions, + propsData, + vm + ) { + var prop = propOptions[key]; + var absent = !hasOwn(propsData, key); + var value = propsData[key]; + // boolean casting + var booleanIndex = getTypeIndex(Boolean, prop.type); + if (booleanIndex > -1) { + if (absent && !hasOwn(prop, 'default')) { + value = false; + } else if (value === '' || value === hyphenate(key)) { + // only cast empty string / same name to boolean if + // boolean has higher priority + var stringIndex = getTypeIndex(String, prop.type); + if (stringIndex < 0 || booleanIndex < stringIndex) { + value = true; + } + } + } + // check default value + if (value === undefined) { + value = getPropDefaultValue(vm, prop, key); + // since the default value is a fresh copy, + // make sure to observe it. + var prevShouldObserve = shouldObserve; + toggleObserving(true); + observe(value); + toggleObserving(prevShouldObserve); + } + { + assertProp(prop, key, value, vm, absent); + } + return value + } + + /** + * Get the default value of a prop. + */ + function getPropDefaultValue (vm, prop, key) { + // no default, return undefined + if (!hasOwn(prop, 'default')) { + return undefined + } + var def = prop.default; + // warn against non-factory defaults for Object & Array + if (isObject(def)) { + warn( + 'Invalid default value for prop "' + key + '": ' + + 'Props with type Object/Array must use a factory function ' + + 'to return the default value.', + vm + ); + } + // the raw prop value was also undefined from previous render, + // return previous default value to avoid unnecessary watcher trigger + if (vm && vm.$options.propsData && + vm.$options.propsData[key] === undefined && + vm._props[key] !== undefined + ) { + return vm._props[key] + } + // call factory function for non-Function types + // a value is Function if its prototype is function even across different execution context + return typeof def === 'function' && getType(prop.type) !== 'Function' + ? def.call(vm) + : def + } + + /** + * Assert whether a prop is valid. + */ + function assertProp ( + prop, + name, + value, + vm, + absent + ) { + if (prop.required && absent) { + warn( + 'Missing required prop: "' + name + '"', + vm + ); + return + } + if (value == null && !prop.required) { + return + } + var type = prop.type; + var valid = !type || type === true; + var expectedTypes = []; + if (type) { + if (!Array.isArray(type)) { + type = [type]; + } + for (var i = 0; i < type.length && !valid; i++) { + var assertedType = assertType(value, type[i]); + expectedTypes.push(assertedType.expectedType || ''); + valid = assertedType.valid; + } + } + + if (!valid) { + warn( + getInvalidTypeMessage(name, value, expectedTypes), + vm + ); + return + } + var validator = prop.validator; + if (validator) { + if (!validator(value)) { + warn( + 'Invalid prop: custom validator check failed for prop "' + name + '".', + vm + ); + } + } + } + + var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; + + function assertType (value, type) { + var valid; + var expectedType = getType(type); + if (simpleCheckRE.test(expectedType)) { + var t = typeof value; + valid = t === expectedType.toLowerCase(); + // for primitive wrapper objects + if (!valid && t === 'object') { + valid = value instanceof type; + } + } else if (expectedType === 'Object') { + valid = isPlainObject(value); + } else if (expectedType === 'Array') { + valid = Array.isArray(value); + } else { + valid = value instanceof type; + } + return { + valid: valid, + expectedType: expectedType + } + } + + /** + * Use function string name to check built-in types, + * because a simple equality check will fail when running + * across different vms / iframes. + */ + function getType (fn) { + var match = fn && fn.toString().match(/^\s*function (\w+)/); + return match ? match[1] : '' + } + + function isSameType (a, b) { + return getType(a) === getType(b) + } + + function getTypeIndex (type, expectedTypes) { + if (!Array.isArray(expectedTypes)) { + return isSameType(expectedTypes, type) ? 0 : -1 + } + for (var i = 0, len = expectedTypes.length; i < len; i++) { + if (isSameType(expectedTypes[i], type)) { + return i + } + } + return -1 + } + + function getInvalidTypeMessage (name, value, expectedTypes) { + var message = "Invalid prop: type check failed for prop \"" + name + "\"." + + " Expected " + (expectedTypes.map(capitalize).join(', ')); + var expectedType = expectedTypes[0]; + var receivedType = toRawType(value); + var expectedValue = styleValue(value, expectedType); + var receivedValue = styleValue(value, receivedType); + // check if we need to specify expected value + if (expectedTypes.length === 1 && + isExplicable(expectedType) && + !isBoolean(expectedType, receivedType)) { + message += " with value " + expectedValue; + } + message += ", got " + receivedType + " "; + // check if we need to specify received value + if (isExplicable(receivedType)) { + message += "with value " + receivedValue + "."; + } + return message + } + + function styleValue (value, type) { + if (type === 'String') { + return ("\"" + value + "\"") + } else if (type === 'Number') { + return ("" + (Number(value))) + } else { + return ("" + value) + } + } + + function isExplicable (value) { + var explicitTypes = ['string', 'number', 'boolean']; + return explicitTypes.some(function (elem) { return value.toLowerCase() === elem; }) + } + + function isBoolean () { + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + + return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; }) + } + + /* */ + + function handleError (err, vm, info) { + // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. + // See: https://github.com/vuejs/vuex/issues/1505 + pushTarget(); + try { + if (vm) { + var cur = vm; + while ((cur = cur.$parent)) { + var hooks = cur.$options.errorCaptured; + if (hooks) { + for (var i = 0; i < hooks.length; i++) { + try { + var capture = hooks[i].call(cur, err, vm, info) === false; + if (capture) { return } + } catch (e) { + globalHandleError(e, cur, 'errorCaptured hook'); + } + } + } + } + } + globalHandleError(err, vm, info); + } finally { + popTarget(); + } + } + + function invokeWithErrorHandling ( + handler, + context, + args, + vm, + info + ) { + var res; + try { + res = args ? handler.apply(context, args) : handler.call(context); + if (res && !res._isVue && isPromise(res) && !res._handled) { + res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); }); + // issue #9511 + // avoid catch triggering multiple times when nested calls + res._handled = true; + } + } catch (e) { + handleError(e, vm, info); + } + return res + } + + function globalHandleError (err, vm, info) { + if (config.errorHandler) { + try { + return config.errorHandler.call(null, err, vm, info) + } catch (e) { + // if the user intentionally throws the original error in the handler, + // do not log it twice + if (e !== err) { + logError(e, null, 'config.errorHandler'); + } + } + } + logError(err, vm, info); + } + + function logError (err, vm, info) { + { + warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); + } + /* istanbul ignore else */ + if ((inBrowser || inWeex) && typeof console !== 'undefined') { + console.error(err); + } else { + throw err + } + } + + /* */ + + var isUsingMicroTask = false; + + var callbacks = []; + var pending = false; + + function flushCallbacks () { + pending = false; + var copies = callbacks.slice(0); + callbacks.length = 0; + for (var i = 0; i < copies.length; i++) { + copies[i](); + } + } + + // Here we have async deferring wrappers using microtasks. + // In 2.5 we used (macro) tasks (in combination with microtasks). + // However, it has subtle problems when state is changed right before repaint + // (e.g. #6813, out-in transitions). + // Also, using (macro) tasks in event handler would cause some weird behaviors + // that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109). + // So we now use microtasks everywhere, again. + // A major drawback of this tradeoff is that there are some scenarios + // where microtasks have too high a priority and fire in between supposedly + // sequential events (e.g. #4521, #6690, which have workarounds) + // or even between bubbling of the same event (#6566). + var timerFunc; + + // The nextTick behavior leverages the microtask queue, which can be accessed + // via either native Promise.then or MutationObserver. + // MutationObserver has wider support, however it is seriously bugged in + // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It + // completely stops working after triggering a few times... so, if native + // Promise is available, we will use it: + /* istanbul ignore next, $flow-disable-line */ + if (typeof Promise !== 'undefined' && isNative(Promise)) { + var p = Promise.resolve(); + timerFunc = function () { + p.then(flushCallbacks); + // In problematic UIWebViews, Promise.then doesn't completely break, but + // it can get stuck in a weird state where callbacks are pushed into the + // microtask queue but the queue isn't being flushed, until the browser + // needs to do some other work, e.g. handle a timer. Therefore we can + // "force" the microtask queue to be flushed by adding an empty timer. + if (isIOS) { setTimeout(noop); } + }; + isUsingMicroTask = true; + } else if (!isIE && typeof MutationObserver !== 'undefined' && ( + isNative(MutationObserver) || + // PhantomJS and iOS 7.x + MutationObserver.toString() === '[object MutationObserverConstructor]' + )) { + // Use MutationObserver where native Promise is not available, + // e.g. PhantomJS, iOS7, Android 4.4 + // (#6466 MutationObserver is unreliable in IE11) + var counter = 1; + var observer = new MutationObserver(flushCallbacks); + var textNode = document.createTextNode(String(counter)); + observer.observe(textNode, { + characterData: true + }); + timerFunc = function () { + counter = (counter + 1) % 2; + textNode.data = String(counter); + }; + isUsingMicroTask = true; + } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { + // Fallback to setImmediate. + // Technically it leverages the (macro) task queue, + // but it is still a better choice than setTimeout. + timerFunc = function () { + setImmediate(flushCallbacks); + }; + } else { + // Fallback to setTimeout. + timerFunc = function () { + setTimeout(flushCallbacks, 0); + }; + } + + function nextTick (cb, ctx) { + var _resolve; + callbacks.push(function () { + if (cb) { + try { + cb.call(ctx); + } catch (e) { + handleError(e, ctx, 'nextTick'); + } + } else if (_resolve) { + _resolve(ctx); + } + }); + if (!pending) { + pending = true; + timerFunc(); + } + // $flow-disable-line + if (!cb && typeof Promise !== 'undefined') { + return new Promise(function (resolve) { + _resolve = resolve; + }) + } + } + + /* */ + + var mark; + var measure; + + { + var perf = inBrowser && window.performance; + /* istanbul ignore if */ + if ( + perf && + perf.mark && + perf.measure && + perf.clearMarks && + perf.clearMeasures + ) { + mark = function (tag) { return perf.mark(tag); }; + measure = function (name, startTag, endTag) { + perf.measure(name, startTag, endTag); + perf.clearMarks(startTag); + perf.clearMarks(endTag); + // perf.clearMeasures(name) + }; + } + } + + /* not type checking this file because flow doesn't play well with Proxy */ + + var initProxy; + + { + var allowedGlobals = makeMap( + 'Infinity,undefined,NaN,isFinite,isNaN,' + + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + + 'require' // for Webpack/Browserify + ); + + var warnNonPresent = function (target, key) { + warn( + "Property or method \"" + key + "\" is not defined on the instance but " + + 'referenced during render. Make sure that this property is reactive, ' + + 'either in the data option, or for class-based components, by ' + + 'initializing the property. ' + + 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', + target + ); + }; + + var warnReservedPrefix = function (target, key) { + warn( + "Property \"" + key + "\" must be accessed with \"$data." + key + "\" because " + + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + + 'prevent conflicts with Vue internals. ' + + 'See: https://vuejs.org/v2/api/#data', + target + ); + }; + + var hasProxy = + typeof Proxy !== 'undefined' && isNative(Proxy); + + if (hasProxy) { + var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); + config.keyCodes = new Proxy(config.keyCodes, { + set: function set (target, key, value) { + if (isBuiltInModifier(key)) { + warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); + return false + } else { + target[key] = value; + return true + } + } + }); + } + + var hasHandler = { + has: function has (target, key) { + var has = key in target; + var isAllowed = allowedGlobals(key) || + (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)); + if (!has && !isAllowed) { + if (key in target.$data) { warnReservedPrefix(target, key); } + else { warnNonPresent(target, key); } + } + return has || !isAllowed + } + }; + + var getHandler = { + get: function get (target, key) { + if (typeof key === 'string' && !(key in target)) { + if (key in target.$data) { warnReservedPrefix(target, key); } + else { warnNonPresent(target, key); } + } + return target[key] + } + }; + + initProxy = function initProxy (vm) { + if (hasProxy) { + // determine which proxy handler to use + var options = vm.$options; + var handlers = options.render && options.render._withStripped + ? getHandler + : hasHandler; + vm._renderProxy = new Proxy(vm, handlers); + } else { + vm._renderProxy = vm; + } + }; + } + + /* */ + + var seenObjects = new _Set(); + + /** + * Recursively traverse an object to evoke all converted + * getters, so that every nested property inside the object + * is collected as a "deep" dependency. + */ + function traverse (val) { + _traverse(val, seenObjects); + seenObjects.clear(); + } + + function _traverse (val, seen) { + var i, keys; + var isA = Array.isArray(val); + if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { + return + } + if (val.__ob__) { + var depId = val.__ob__.dep.id; + if (seen.has(depId)) { + return + } + seen.add(depId); + } + if (isA) { + i = val.length; + while (i--) { _traverse(val[i], seen); } + } else { + keys = Object.keys(val); + i = keys.length; + while (i--) { _traverse(val[keys[i]], seen); } + } + } + + /* */ + + var normalizeEvent = cached(function (name) { + var passive = name.charAt(0) === '&'; + name = passive ? name.slice(1) : name; + var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first + name = once$$1 ? name.slice(1) : name; + var capture = name.charAt(0) === '!'; + name = capture ? name.slice(1) : name; + return { + name: name, + once: once$$1, + capture: capture, + passive: passive + } + }); + + function createFnInvoker (fns, vm) { + function invoker () { + var arguments$1 = arguments; + + var fns = invoker.fns; + if (Array.isArray(fns)) { + var cloned = fns.slice(); + for (var i = 0; i < cloned.length; i++) { + invokeWithErrorHandling(cloned[i], null, arguments$1, vm, "v-on handler"); + } + } else { + // return handler return value for single handlers + return invokeWithErrorHandling(fns, null, arguments, vm, "v-on handler") + } + } + invoker.fns = fns; + return invoker + } + + function updateListeners ( + on, + oldOn, + add, + remove$$1, + createOnceHandler, + vm + ) { + var name, def$$1, cur, old, event; + for (name in on) { + def$$1 = cur = on[name]; + old = oldOn[name]; + event = normalizeEvent(name); + if (isUndef(cur)) { + warn( + "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), + vm + ); + } else if (isUndef(old)) { + if (isUndef(cur.fns)) { + cur = on[name] = createFnInvoker(cur, vm); + } + if (isTrue(event.once)) { + cur = on[name] = createOnceHandler(event.name, cur, event.capture); + } + add(event.name, cur, event.capture, event.passive, event.params); + } else if (cur !== old) { + old.fns = cur; + on[name] = old; + } + } + for (name in oldOn) { + if (isUndef(on[name])) { + event = normalizeEvent(name); + remove$$1(event.name, oldOn[name], event.capture); + } + } + } + + /* */ + + function mergeVNodeHook (def, hookKey, hook) { + if (def instanceof VNode) { + def = def.data.hook || (def.data.hook = {}); + } + var invoker; + var oldHook = def[hookKey]; + + function wrappedHook () { + hook.apply(this, arguments); + // important: remove merged hook to ensure it's called only once + // and prevent memory leak + remove(invoker.fns, wrappedHook); + } + + if (isUndef(oldHook)) { + // no existing hook + invoker = createFnInvoker([wrappedHook]); + } else { + /* istanbul ignore if */ + if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { + // already a merged invoker + invoker = oldHook; + invoker.fns.push(wrappedHook); + } else { + // existing plain hook + invoker = createFnInvoker([oldHook, wrappedHook]); + } + } + + invoker.merged = true; + def[hookKey] = invoker; + } + + /* */ + + function extractPropsFromVNodeData ( + data, + Ctor, + tag + ) { + // we are only extracting raw values here. + // validation and default values are handled in the child + // component itself. + var propOptions = Ctor.options.props; + if (isUndef(propOptions)) { + return + } + var res = {}; + var attrs = data.attrs; + var props = data.props; + if (isDef(attrs) || isDef(props)) { + for (var key in propOptions) { + var altKey = hyphenate(key); + { + var keyInLowerCase = key.toLowerCase(); + if ( + key !== keyInLowerCase && + attrs && hasOwn(attrs, keyInLowerCase) + ) { + tip( + "Prop \"" + keyInLowerCase + "\" is passed to component " + + (formatComponentName(tag || Ctor)) + ", but the declared prop name is" + + " \"" + key + "\". " + + "Note that HTML attributes are case-insensitive and camelCased " + + "props need to use their kebab-case equivalents when using in-DOM " + + "templates. You should probably use \"" + altKey + "\" instead of \"" + key + "\"." + ); + } + } + checkProp(res, props, key, altKey, true) || + checkProp(res, attrs, key, altKey, false); + } + } + return res + } + + function checkProp ( + res, + hash, + key, + altKey, + preserve + ) { + if (isDef(hash)) { + if (hasOwn(hash, key)) { + res[key] = hash[key]; + if (!preserve) { + delete hash[key]; + } + return true + } else if (hasOwn(hash, altKey)) { + res[key] = hash[altKey]; + if (!preserve) { + delete hash[altKey]; + } + return true + } + } + return false + } + + /* */ + + // The template compiler attempts to minimize the need for normalization by + // statically analyzing the template at compile time. + // + // For plain HTML markup, normalization can be completely skipped because the + // generated render function is guaranteed to return Array. There are + // two cases where extra normalization is needed: + + // 1. When the children contains components - because a functional component + // may return an Array instead of a single root. In this case, just a simple + // normalization is needed - if any child is an Array, we flatten the whole + // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep + // because functional components already normalize their own children. + function simpleNormalizeChildren (children) { + for (var i = 0; i < children.length; i++) { + if (Array.isArray(children[i])) { + return Array.prototype.concat.apply([], children) + } + } + return children + } + + // 2. When the children contains constructs that always generated nested Arrays, + // e.g.