diff --git a/.dockerignore b/.dockerignore index 400701794cf0e..f7cf8d129fc46 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,30 +1,27 @@ -.dockerignore -.editorconfig -.travis.yml -GPLv3.txt -LICENSE -README.md -TGS3.json -.github -.gitignore -.gitattributes -.git/hooks -.git/info -.git/modules -.git/objects -.git/refs -.vs* -cfg -data -SQL -tgui/node_modules -tgstation.dmb -tgstation.int -tgstation.rsc -tgstation.lk -tgstation.dyn.rsc -libmariadb.dll -rust_g.dll -BSQL.dll -appveyor.yml -Dockerfile +.dockerignore +.editorconfig +GPLv3.txt +LICENSE +README.md +TGS3.json +.github +.gitignore +.gitattributes +.git/hooks +.git/info +.git/modules +.git/objects +.git/refs +.vs* +cfg +data +SQL +tgui/node_modules +tgstation.dmb +tgstation.int +tgstation.rsc +tgstation.lk +tgstation.dyn.rsc +*.dll +Dockerfile +tools/bootstrap/.cache diff --git a/.editorconfig b/.editorconfig index 95e40c0cd3c47..be7033ee79311 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,6 @@ indent_style = space [/tgui/**/*.{js,styl,ract,json,html}] indent_style = space indent_size = 2 + +[Dockerfile] +indent_style = space diff --git a/.gitattributes b/.gitattributes index cbfcb9e2c88dc..655894289ad9a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,15 +1,50 @@ #Normalize files * text=auto -*.sh text eol=lf + +## Enforce text mode and LF line breaks +*.bat text eol=lf +*.cjs text eol=lf +*.css text eol=lf *.dm text eol=lf *.dme text eol=lf +*.dmf text eol=lf +*.htm text eol=lf +*.html text eol=lf +*.js text eol=lf +*.json text eol=lf +*.jsx text eol=lf +*.md text eol=lf +*.ps1 text eol=lf +*.py text eol=lf +*.scss text eol=lf +*.sh text eol=lf +*.sql text eol=lf +*.svg text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.txt text eol=lf +*.yaml text eol=lf +*.yml text eol=lf + +## Enforce binary mode +*.bmp binary +*.dll binary +*.dmb binary +*.exe binary +*.gif binary +*.jpg binary +*.png binary +*.so binary # merger hooks, run tools/hooks/install.bat or install.sh to set up *.dmm merge=dmm *.dmi merge=dmi # exlude maps from contribution count -*.dmm linguist-generated=true +*.dmm linguist-generated=true + +## Force tab indents on dm files +*.dm whitespace=indent-with-non-tab # force changelog merging to use union html/changelog.html merge=union diff --git a/.github/.github/autolabeler.yml b/.github/.github/autolabeler.yml deleted file mode 100644 index 75979e3f7af1d..0000000000000 --- a/.github/.github/autolabeler.yml +++ /dev/null @@ -1,6 +0,0 @@ -code: ["*.dm"] -mapping: ["*.dmm"] -config: ["config/*"] -tooling: ["tools/*"] -sql: ["*.sql"] -dme: ["*.dme"] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1a82a97422d2d..b2b1baeba782e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,14 +8,11 @@ .dockerignore @crossedfall .github @crossedfall -.travis.yml @crossedfall -appveyor.yml @crossedfall +/code/_compile_options.dm @crossedfall /code/modules/tgs @crossedfall -/config @crossedfall dependencies.sh @crossedfall Dockerfile @crossedfall /SQL @crossedfall -TGS3.json @crossedfall /tools @crossedfall @@ -27,11 +24,6 @@ TGS3.json @crossedfall # powerfulbacon /code/game/machinery/shuttle @powerfulbacon -/code/modules/shuttle @powerfulbacon - -# zeskorion - -/code/__DEFINES/diseases.dm @zeskorion -/code/controllers/subsystem/disease.dm @zeskorion -/code/datums/diseases @zeskorion -/code/modules/antagonists/disease @zeskorion +/code/modules/shuttle/super_cruise @powerfulbacon +/code/modules/shuttle/shuttle_creation @powerfulbacon +/_maps/map_files/CorgStation @powerfulbacon diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2d276fd2760f8..11745569b3290 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -97,7 +97,7 @@ Do not add any of the following in a Pull Request or risk getting the PR closed: - Any content that violates GitHub Terms of Service. - Racial or homophobic slurs of any kind. - National Socialist Party of Germany content, National Socialist Party of Germany related content, or National Socialist Party of Germany references -- Code adding, removing, or updating the availability of alien races/species/human mutants without prior approval. Pull requests attempting to add or remove features from said races/species/mutants require prior approval as well. +- Code adding, removing, or updating the availability of alien races/species/human mutants without prior Maintainer approval. Pull requests attempting to add or remove features from said races/species/mutants require prior approval as well. Just because something isn't on this list doesn't mean that it's acceptable. Use common sense above all else. diff --git a/.github/comment-agent.yml b/.github/comment-agent.yml new file mode 100644 index 0000000000000..e147d3bf0c496 --- /dev/null +++ b/.github/comment-agent.yml @@ -0,0 +1,15 @@ +# A mapping of keyword aliases to event type. Form of match:event type. +# Required. +aliasMappings: + "?rebuild tgui": "rebuild-tgui" + "?rebuild-tgui": "rebuild-tgui" + +# Determines if alias matching is case sensitive. +# Optional. Defaults to true. +caseSensitive: false + +# A mapping of event types to user group permissions. +# Form of event type:group name or combined names (Explained lower in the README). +# Optional, defaults to MEMBER for each. +permissionMappings: + "rebuild-tgui": [MEMBER, PRAUTHOR] diff --git a/.github/file_labeler.yml b/.github/file_labeler.yml new file mode 100644 index 0000000000000..a9afd6e3c5905 --- /dev/null +++ b/.github/file_labeler.yml @@ -0,0 +1,26 @@ +Administration: +- code/modules/admin/**/* + +Config Update: +- config/**/* + +GitHub: +- .github/**/* + +mapping: +- _maps/**/* + +sound: +- sound/**/* + +sprites: +- icons/**/*.dmi + +sql: +- SQL/**/*.sql + +tools: +- tools/**/* + +TGUI-Changes: +- tgui/**/* diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml index 0e705866dd0ee..0155701ccca6b 100644 --- a/.github/workflows/compile_changelogs.yml +++ b/.github/workflows/compile_changelogs.yml @@ -2,12 +2,12 @@ name: Compile changelogs on: schedule: - - cron: "*/15 * * * *" + - cron: "0 * * * *" + workflow_dispatch: jobs: CompileCL: runs-on: ubuntu-latest - if: github.repository == 'BeeStation/BeeStation-Hornet' steps: - name: Checkout uses: actions/checkout@v1 @@ -16,11 +16,11 @@ jobs: - name: Python setup uses: actions/setup-python@v1 with: - python-version: '3.x' + python-version: "3.8" - name: Install depends run: | python -m pip install --upgrade pip - pip install pyyaml bs4 + pip install -r tools/changelog/requirements.txt - name: Compile CL run: python tools/changelog/ss13_genchangelog.py html/changelog.html html/changelogs - name: Commit diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml new file mode 100644 index 0000000000000..8c75f1d0ad475 --- /dev/null +++ b/.github/workflows/continuous_integration.yml @@ -0,0 +1,112 @@ +name: Run tests + +on: + workflow_dispatch: + push: + paths-ignore: + - 'html/changelogs/**' + - 'html/changelog.html' + branches: + - master + pull_request: + branches: + - master + +jobs: + run_linters: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup cache + id: cache-spacemandmm + uses: actions/cache@v2 + with: + path: ~/dreamchecker + key: ${{ runner.os }}-spacemandmm-cache-${{ hashFiles('dependencies.sh') }} + - name: Install SpacemanDMM + if: steps.cache-spacemandmm.outputs.cache-hit != 'true' + run: bash tools/ci/install_spaceman_dmm.sh dreamchecker + - name: Install Tools + run: | + pip3 install setuptools + bash tools/ci/install_node.sh + bash tools/ci/install_auxmos.sh + pip3 install -r tools/requirements.txt + - name: Run Linters + run: | + bash tools/ci/check_filedirs.sh beestation.dme + bash tools/ci/check_changelogs.sh + find . -name "*.php" -print0 | xargs -0 -n1 php -l + find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python3 ./tools/json_verifier.py + tgui/bin/tgui --lint + tgui/bin/tgui --test + bash tools/ci/check_grep.sh + tools/bootstrap/python -m dmi.test + tools/bootstrap/python -m mapmerge2.dmm_test + ~/dreamchecker + compile_all_maps: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup cache + id: cache-byond + uses: actions/cache@v2 + with: + path: ~/BYOND + key: ${{ runner.os }}-byond-cache-${{ hashFiles('Dockerfile') }} + - name: Install BYOND + if: steps.cache-byond.outputs.cache-hit != 'true' + run: bash tools/ci/install_byond.sh + - name: Compile All Maps + run: | + source $HOME/BYOND/byond/bin/byondsetup + python3 tools/ci/template_dm_generator.py + tools/build/build dm -DCIBUILDING -DCITESTING -DALL_MAPS + run_all_tests: + runs-on: ubuntu-latest + services: + mysql: + image: mysql:latest + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: + - uses: actions/checkout@v2 + - name: Setup cache + id: cache-byond + uses: actions/cache@v2 + with: + path: ~/BYOND + key: ${{ runner.os }}-byond-cache-${{ hashFiles('Dockerfile') }} + - name: Install BYOND + if: steps.cache-byond.outputs.cache-hit != 'true' + run: bash tools/ci/install_byond.sh + - name: Setup database + run: | + sudo systemctl start mysql + mysql -u root -proot -e 'CREATE DATABASE bee_ci;' + mysql -u root -proot bee_ci < SQL/beestation_schema.sql + - name: Install rust-g + run: | + sudo dpkg --add-architecture i386 + sudo apt update || true + sudo apt install libssl1.1:i386 + bash tools/ci/install_rust_g.sh + - name: Install auxmos + run: | + bash tools/ci/install_auxmos.sh + - name: Compile and run tests + run: | + source $HOME/BYOND/byond/bin/byondsetup + tools/build/build -DCIBUILDING + bash tools/ci/run_server.sh + test_windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Compile + run: pwsh tools/ci/build.ps1 + env: + DM_EXE: "C:\\byond\\bin\\dm.exe" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 645bdf081fd3c..3fef279444ff3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,17 +1,17 @@ name: Docker Image CI on: + workflow_dispatch: schedule: - cron: 0 0 * * * jobs: build: runs-on: ubuntu-latest - if: github.repository == 'BeeStation/BeeStation-Hornet' steps: - uses: actions/checkout@v2 - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@master + uses: elgohr/Publish-Docker-Github-Action@v5 with: name: beestation/beestation username: ${{ secrets.DOCKER_USERNAME }} diff --git a/.github/workflows/extra_pr_labels.yml b/.github/workflows/extra_pr_labels.yml new file mode 100644 index 0000000000000..f70ad1b27aa16 --- /dev/null +++ b/.github/workflows/extra_pr_labels.yml @@ -0,0 +1,25 @@ +name: Add Extra PR labels + +on: + push: + branches: + - master + pull_request_target: + +jobs: + Label: + runs-on: ubuntu-latest + steps: + - name: Check for conflicting PRs + uses: eps1lon/actions-label-merge-conflict@513a24fc7dca40990863be2935e059e650728400 + with: + dirtyLabel: "Merge Conflict" + repoToken: "${{ secrets.GITHUB_TOKEN }}" + commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request." + - name: Apply labels based on changed files + if: github.event_name != 'push' + uses: actions/labeler@main + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + sync-labels: true + configuration-path: .github/file_labeler.yml diff --git a/.github/workflows/make_changelogs.yml b/.github/workflows/make_changelogs.yml index 62335b326ad51..e199e9a799874 100644 --- a/.github/workflows/make_changelogs.yml +++ b/.github/workflows/make_changelogs.yml @@ -2,12 +2,13 @@ name: Make changelogs on: push: - branches: [master] + branches: + - master jobs: MakeCL: runs-on: ubuntu-latest - if: github.repository == 'BeeStation/BeeStation-Hornet' && !contains(github.event.head_commit.message, '[ci skip]') + if: "!contains(github.event.head_commit.message, '[ci skip]')" steps: - name: Checkout uses: actions/checkout@v1 @@ -16,11 +17,11 @@ jobs: - name: Python setup uses: actions/setup-python@v1 with: - python-version: '3.x' + python-version: "3.8" - name: Install depends run: | python -m pip install --upgrade pip - pip install ruamel.yaml PyGithub + pip install -r tools/changelog/requirements.txt - name: Make CL env: #GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} Use this instead if you have unprotected branches diff --git a/.github/workflows/stale_issues.yml b/.github/workflows/stale_issues.yml index 78ee27a047b75..f4430cd337149 100644 --- a/.github/workflows/stale_issues.yml +++ b/.github/workflows/stale_issues.yml @@ -7,12 +7,11 @@ on: jobs: stale: runs-on: ubuntu-latest - if: github.repository == 'BeeStation/BeeStation-Hornet' steps: - uses: actions/stale@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: "This issue has been inactive for long enough to be automatically marked as stale. If this was a bug report and hasn't been addressed yet, and is still a probelm, please don't hesitate to notify a maintainer." + stale-issue-message: "This issue has been inactive for long enough to be automatically marked as stale. If this was a bug report and hasn't been addressed yet, and is still a problem, please don't hesitate to notify a maintainer." stale-issue-label: 'Stale' exempt-issue-label: 'Triaged' days-before-stale: 30 diff --git a/.github/workflows/stale_prs.yml b/.github/workflows/stale_prs.yml index 0db9127623a77..6524f6ddcc5a9 100644 --- a/.github/workflows/stale_prs.yml +++ b/.github/workflows/stale_prs.yml @@ -7,7 +7,6 @@ on: jobs: stale: runs-on: ubuntu-latest - if: github.repository == 'BeeStation/BeeStation-Hornet' steps: - uses: actions/stale@v1 with: diff --git a/.github/workflows/tgui_recompile.yml b/.github/workflows/tgui_recompile.yml new file mode 100644 index 0000000000000..bbdcd9d325840 --- /dev/null +++ b/.github/workflows/tgui_recompile.yml @@ -0,0 +1,90 @@ +name: Rebuild TGUI + +on: + repository_dispatch: + types: [rebuild-tgui] + push: + branches: + - 'master' + paths: + - 'tgui/**.js' + - 'tgui/**.scss' + +# Config +env: + COMMIT_NAME: 'ss13-beebot' + COMMIT_EMAIL: '56381746+ss13-beebot@users.noreply.github.com' + DEFAULT_BRANCH: 'master' + TOKEN: '${{ secrets.CL_TOKEN }}' + +jobs: + ondemand_rebuild: + if: ${{ github.event_name == 'repository_dispatch' }} + name: On-Demand Rebuild + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + repository: ${{ github.event.client_payload.pr_head_full_repo_name }} + ref: ${{ github.event.client_payload.pr_head_ref }} + token: ${{ env.TOKEN }} + + - name: Setup Author + run: | + git config --local user.email "${{ env.COMMIT_EMAIL }}" + git config --local user.name "${{ env.COMMIT_NAME }}" + - name: Setup Node + uses: actions/setup-node@v1 + with: + node-version: '>=12.13' + + # This only runs if the PR has a merge conflict. Serves to attempt resolving merge conflicts rather than just rebuilding. + # Uses a smart little git hack to make a merge commit for just a limited set of files. + - name: Conflict Resolution + if: ${{ github.event.client_payload.mergeable == false }} + run: | + git remote add base https://github.com/${{ github.repository }}.git + git fetch base ${{ env.DEFAULT_BRANCH }} + git merge --squash -s ours --no-commit base/${{ env.DEFAULT_BRANCH }} + git checkout HEAD . + git clean -fxd + git checkout base/${{ env.DEFAULT_BRANCH }} tgui/public + git commit -m "TGUI Reset" -a || true + + - name: Build TGUI + run: bin/tgui + working-directory: ./tgui + + - name: Commit and Push Build + run: | + git commit -m "TGUI Rebuild" -a || true + git push + + auto_rebuild: + if: ${{ github.event_name == 'push' }} + name: Automatic Rebuild + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 25 + token: ${{ env.TOKEN }} + + - name: Setup Node + uses: actions/setup-node@v1 + with: + node-version: '>=12.13' + + - name: Build TGUI + run: bin/tgui + working-directory: ./tgui + + - name: Commit and Push Build + run: | + git config --local user.email "${{ env.COMMIT_EMAIL }}" + git config --local user.name "${{ env.COMMIT_NAME }}" + git pull origin master + git commit -m "Automatic TGUI Rebuild [ci skip]" -a || true + git push diff --git a/.github/workflows/update_tgs_dmapi.yml b/.github/workflows/update_tgs_dmapi.yml index 83c196a22fea5..af3f2951d57c4 100644 --- a/.github/workflows/update_tgs_dmapi.yml +++ b/.github/workflows/update_tgs_dmapi.yml @@ -40,7 +40,7 @@ jobs: destination_branch: "master" pr_title: "Automatic TGS DMAPI Update" pr_body: "This pull request updates the TGS DMAPI to the latest version. Please note any breaking or unimplemented changes before merging." - pr_label: "Tools" + pr_label: "tools" pr_allow_empty: false #github_token: ${{ secrets.GITHUB_TOKEN }} Use this instead if you have unprotected branches github_token: ${{ secrets.CL_TOKEN }} diff --git a/.gitignore b/.gitignore index 9b583ce0eb159..1768f9e0ba261 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ #Ignore byond config folder. /cfg/**/* +# Ignore compiled linux libs in the root folder, e.g. librust_g.so +/*.so + #Ignore compiled files and other files generated during compilation. *.mdme *.dmb @@ -49,27 +52,6 @@ __pycache__/ *.py[cod] *$py.class -# C extensions -#*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. @@ -77,8 +59,7 @@ var/ *.spec # Installer logs -pip-log.txt -pip-delete-this-directory.txt +pip-*.txt # Unit test / coverage reports htmlcov/ @@ -88,7 +69,6 @@ htmlcov/ .cache nosetests.xml coverage.xml -*,cover .hypothesis/ # Translations @@ -97,10 +77,6 @@ coverage.xml # Django stuff: *.log -local_settings.py - -# Flask instance folder -instance/ # Scrapy stuff: .scrapy @@ -108,9 +84,6 @@ instance/ # Sphinx documentation docs/_build/ -# PyBuilder -target/ - # IPython Notebook .ipynb_checkpoints @@ -123,10 +96,6 @@ celerybeat-schedule # dotenv .env -# virtualenv -venv/ -ENV/ - # IntelliJ IDEA / PyCharm (with plugin) .idea @@ -149,12 +118,6 @@ Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ -# Windows Installer files -#*.cab -#*.msi -#*.msm -#*.msp - # Windows shortcuts *.lnk @@ -195,10 +158,10 @@ Temporary Items #Visual studio stuff *.vscode/* -!/.vscode/extensions.json -!/.vscode/settings.json -tools/MapAtmosFixer/MapAtmosFixer/obj/* -tools/MapAtmosFixer/MapAtmosFixer/bin/* +/tools/MapAtmosFixer/MapAtmosFixer/obj/* +/tools/MapAtmosFixer/MapAtmosFixer/bin/* +/tools/CreditsTool/bin/* +/tools/CreditsTool/obj/* #GitHub Atom .atom-build.json @@ -216,3 +179,6 @@ tools/MapAtmosFixer/MapAtmosFixer/bin/* !/config/jukebox_music/sounds/exclude /config/title_music/sounds/* !/config/title_music/sounds/exclude + +# Common build tooling, C B T +!/tools/build diff --git a/.tgs4.yml b/.tgs4.yml new file mode 100644 index 0000000000000..6c44ca4ebe796 --- /dev/null +++ b/.tgs4.yml @@ -0,0 +1,4 @@ +linux_scripts: + PreCompile.sh: tools/tgs4_scripts/PreCompile.sh +windows_scripts: + PreCompile.bat: tools/tgs4_scripts/PreCompile.bat diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b63ac697b416c..0000000000000 --- a/.travis.yml +++ /dev/null @@ -1,88 +0,0 @@ -language: generic -os: linux -dist: bionic - -branches: - only: - - master - -matrix: - include: - - name: "Run Linters" - addons: - apt: - packages: - - python3 - - python3-pip - - python3-setuptools - install: - - tools/travis/install_build_tools.sh - - tools/travis/install_spaceman_dmm.sh dreamchecker - script: - - tools/travis/check_filedirs.sh beestation.dme - - tools/travis/check_changelogs.sh - - find . -name "*.php" -print0 | xargs -0 -n1 php -l - - find . -name "*.json" -not -path "./tgui/node_modules/*" -print0 | xargs -0 python3 ./tools/json_verifier.py - - tools/travis/build_tgui.sh - - tools/travis/check_grep.sh - - ~/dreamchecker - - - name: "Compile All Maps" - addons: - apt: - packages: - - libstdc++6:i386 - cache: - directories: - - $HOME/BYOND - install: - - tools/travis/install_byond.sh - - source $HOME/BYOND/byond/bin/byondsetup - before_script: - - tools/travis/template_dm_generator.py - script: - - tools/travis/dm.sh -DTRAVISBUILDING -DTRAVISTESTING -DALL_MAPS beestation.dme - - - name: "Compile and Run Tests" - addons: - mariadb: '10.2' - apt: - sources: - - sourceline: "ppa:ubuntu-toolchain-r/test" - packages: - - libstdc++6:i386 - - gcc-multilib - - g++-7 - - g++-7-multilib - - libssl1.1:i386 - - zlib1g:i386 - cache: - directories: - - $HOME/BYOND - install: - - tools/travis/install_byond.sh - - source $HOME/BYOND/byond/bin/byondsetup - - tools/travis/install_rust_g.sh - before_script: - - mysql -u root -e 'CREATE DATABASE bee_travis;' - - mysql -u root bee_travis < SQL/beestation_schema.sql - script: - - tools/travis/dm.sh -DTRAVISBUILDING beestation.dme || travis_terminate 1 - - tools/travis/run_server.sh - -# - name: "Generate Documentation" -# # Only run for non-PR commits to the real master branch. -# if: branch = master AND head_branch IS blank -# install: -# - tools/travis/install_spaceman_dmm.sh dmdoc -# before_script: -# # Travis checks out a hash, try to get back on a branch. -# - git checkout -qf $(git name-rev --name-only HEAD) || true -# script: -# - ~/dmdoc -# - touch dmdoc/.nojekyll -# deploy: -# provider: pages -# skip_cleanup: true -# local_dir: dmdoc -# github_token: $DMDOC_GITHUB_TOKEN diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0abac7a5338e5..8f47c8079670c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,8 +1,9 @@ -{ - "recommendations": [ - "gbasood.byond-dm-language-support", - "platymuus.dm-langclient", - "EditorConfig.EditorConfig", - "dbaeumer.vscode-eslint" - ] -} +{ + "recommendations": [ + "gbasood.byond-dm-language-support", + "platymuus.dm-langclient", + "EditorConfig.EditorConfig", + "arcanis.vscode-zipfs", + "dbaeumer.vscode-eslint" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000..543058728f561 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "byond", + "request": "launch", + "name": "Launch DreamSeeker", + "preLaunchTask": "Build All", + "dmb": "${workspaceFolder}/${command:CurrentDMB}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 4f62a16105604..052769d3cc124 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,28 @@ { "dreammaker.extoolsDLL": "byond-extools.dll", + "eslint.nodePath": "./tgui/.yarn/sdks", + "eslint.workingDirectories": [ + "./tgui" + ], + "typescript.tsdk": "./tgui/.yarn/sdks/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true, + "search.exclude": { + "**/.yarn": true, + "**/.pnp.*": true + }, + "workbench.editorAssociations": { + "*.dmi": "imagePreview.previewEditor" + }, + "files.eol": "\n", + "gitlens.advanced.blame.customArguments": ["-w"], + "tgstationTestExplorer.project.resultsType": "json", + "[javascript]": { + "editor.rulers": [80] + }, + "[typescript]": { + "editor.rulers": [80] + }, + "[scss]": { + "editor.rulers": [80] + } } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000000..56c1756ff6afb --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,55 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "process", + "command": "tools/build/build", + "windows": { + "command": ".\\tools\\build\\build.bat" + }, + "options": { + "env": { + "DM_EXE": "${config:dreammaker.byondPath}" + } + }, + "problemMatcher": [ + "$dreammaker", + "$tsc", + "$eslint-stylish" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "dependsOn": "dm: reparse", + "label": "Build All" + }, + { + "type": "dreammaker", + "dme": "beestation.dme", + "problemMatcher": [ + "$dreammaker" + ], + "group": "build", + "label": "dm: build - beestation.dme" + }, + { + "type": "shell", + "command": "tgui/bin/tgui", + "windows": { + "command": ".\\tgui\\bin\\tgui.bat" + }, + "problemMatcher": [ + "$tsc", + "$eslint-stylish" + ], + "group": "build", + "label": "tgui: build" + }, + { + "command": "${command:dreammaker.reparse}", + "group": "build", + "label": "dm: reparse" + } + ] +} diff --git a/BUILD.bat b/BUILD.bat new file mode 100644 index 0000000000000..68eaef0c2d351 --- /dev/null +++ b/BUILD.bat @@ -0,0 +1,3 @@ +@echo off +call "%~dp0\tools\build\build.bat" %* +pause diff --git a/Dockerfile b/Dockerfile index b416fcaa1fda1..878ae2cc3b8de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,72 +1,66 @@ -FROM beestation/byond:513.1536 as base -ONBUILD ENV BYOND_MAJOR=513 -ONBUILD ENV BYOND_MINOR=1536 - -FROM base as build_base - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - git \ - dos2unix\ - ca-certificates - -FROM build_base as rust_g - -WORKDIR /rust_g - -RUN apt-get install -y --no-install-recommends \ - libssl-dev \ - pkg-config \ - curl \ - gcc-multilib \ - && curl https://sh.rustup.rs -sSf | sh -s -- -y --default-host i686-unknown-linux-gnu \ - && git init \ - && git remote add origin https://github.com/BeeStation/rust-g - -COPY dependencies.sh . - -RUN dos2unix dependencies.sh \ - && /bin/bash -c "source dependencies.sh \ - && git fetch --depth 1 origin \$RUST_G_VERSION" \ - && git checkout FETCH_HEAD \ - && ~/.cargo/bin/cargo build --release --all-features \ - && apt-get --purge remove -y dos2unix - -FROM base as dm_base - -WORKDIR /beestation - -FROM dm_base as build - -COPY . . - -RUN apt-get update \ - && apt-get install -y --no-install-recommends dos2unix \ - && rm -rf /var/lib/apt/lists/* \ - && DreamMaker -max_errors 0 beestation.dme && dos2unix tools/deploy.sh && tools/deploy.sh /deploy - -FROM dm_base - -EXPOSE 1337 - -RUN apt-get update \ - && apt-get install -y --no-install-recommends software-properties-common \ - && add-apt-repository ppa:ubuntu-toolchain-r/test \ - && apt-get update \ - && apt-get upgrade -y \ - && apt-get dist-upgrade -y \ - && apt-get install -y --no-install-recommends \ - mariadb-client \ - libssl1.0.0 \ - && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /root/.byond/bin - -COPY --from=rust_g /rust_g/target/release/librust_g.so /root/.byond/bin/rust_g -COPY --from=build /deploy ./ - -#extools fexists memes -RUN ln -s /beestation/libbyond-extools.so /root/.byond/bin/libbyond-extools.so - -VOLUME [ "/beestation/config", "/beestation/data" ] - -ENTRYPOINT [ "DreamDaemon", "beestation.dmb", "-port", "1337", "-trusted", "-close", "-verbose" ] +# syntax=docker/dockerfile:1 +FROM beestation/byond:514.1568 as base + +# Install the tools needed to compile our rust dependencies +FROM base as rust-build +ENV PKG_CONFIG_ALLOW_CROSS=1 \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH +WORKDIR /build +COPY dependencies.sh . +RUN dpkg --add-architecture i386 \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + curl ca-certificates gcc-multilib \ + g++-multilib libc6-i386 zlib1g-dev:i386 \ + libssl-dev:i386 pkg-config:i386 git \ + && /bin/bash -c "source dependencies.sh \ + && curl https://sh.rustup.rs | sh -s -- -y -t i686-unknown-linux-gnu --no-modify-path --profile minimal --default-toolchain \$RUST_VERSION" \ + && rm -rf /var/lib/apt/lists/* + +# Build rust-g +FROM rust-build as rustg +RUN git init \ + && git remote add origin https://github.com/BeeStation/rust-g \ + && /bin/bash -c "source dependencies.sh \ + && git fetch --depth 1 origin \$RUST_G_VERSION" \ + && git checkout FETCH_HEAD \ + && cargo build --release --all-features --target i686-unknown-linux-gnu + +# Build auxmos +FROM rust-build as auxmos +RUN git init \ + && git remote add origin https://github.com/BeeStation/auxmos \ + && /bin/bash -c "source dependencies.sh \ + && git fetch --depth 1 origin \$AUXMOS_VERSION" \ + && git checkout FETCH_HEAD \ + && cargo rustc --target=i686-unknown-linux-gnu --release --features=trit_fire_hook,plasma_fire_hook,generic_fire_hook + +# Install nodejs which is required to deploy BeeStation +FROM base as node +COPY dependencies.sh . +RUN apt-get update \ + && apt-get install curl -y \ + && /bin/bash -c "source dependencies.sh \ + && curl -fsSL https://deb.nodesource.com/setup_\$NODE_VERSION.x | bash -" \ + && apt-get install -y nodejs + +# Build TGUI, tgfonts, and the dmb +FROM node as dm-build +ENV TG_BOOTSTRAP_NODE_LINUX=1 +WORKDIR /dm-build +COPY . . +# Required to satisfy our compile_options +COPY --from=auxmos /build/target/i686-unknown-linux-gnu/release/libauxmos.so /dm-build/auxtools/libauxmos.so +RUN tools/build/build \ + && tools/deploy.sh /deploy \ + && apt-get autoremove curl -y \ + && rm -rf /var/lib/apt/lists/* + +FROM base +WORKDIR /beestation +COPY --from=dm-build /deploy ./ +COPY --from=rustg /build/target/i686-unknown-linux-gnu/release/librust_g.so /root/.byond/bin/rust_g +VOLUME [ "/beestation/config", "/beestation/data" ] +ENTRYPOINT [ "DreamDaemon", "beestation.dmb", "-port", "1337", "-trusted", "-close", "-verbose" ] +EXPOSE 1337 diff --git a/MIT.txt b/MIT.txt deleted file mode 100644 index 59be2d32ebea1..0000000000000 --- a/MIT.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 MCHSL - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 0a542cd1efc0a..b97d685903fd1 100644 --- a/README.md +++ b/README.md @@ -1,176 +1,171 @@ -
" - if(i<=leftcolumn.len) - dat += leftcolumn[i] - dat += " | " - if(i<=rightcolumn.len) - dat += rightcolumn[i] - dat += " |
[A.name] | " - temp_html += "Delete | " - if(LAZYLEN(stored_mutations) < max_storage) - temp_html += "Import | " - else - temp_html += "Import | " - temp_html += "
[HM.name] | " - if(diskette) - temp_html += "Export | " - else - temp_html += "Export | " - temp_html += "Delete | " - if(combine == HM.type) - temp_html += "Combine | Combine | " - temp_html += "
[CM.name] |
" - temp_html += " |
| | "
- temp_html += "
" - temp_html += " |
Crime | -Details | -Author | -Time Added | -Del | -
---|---|---|---|---|
[c.crimeName] | " - dat += "[c.crimeDetails] | " - dat += "[c.author] | " - dat += "[c.time] | " - dat += "\[X\] | " - dat += "
Time Added | Del | ||||||
---|---|---|---|---|---|---|---|
[c.crimeName] | " - dat += "[c.crimeDetails] | " + if(!c.crimeDetails) + dat += "\[+\] | " + else + dat += "[c.crimeDetails] | " dat += "[c.author] | " dat += "[c.time] | " - dat += "\[X\] | " + dat += "\[X\] | " dat += "
Crime | -Details | -Author | -Time Added | -
---|---|---|---|
[c.crimeName] | " - P.info += "[c.crimeDetails] | " - P.info += "[c.author] | " - P.info += "[c.time] | " - P.info += "
Crime | @@ -441,7 +588,7 @@ What a mess.*/Author | Time Added |
---|---|---|
[c.crimeName] | " P.info += "[c.crimeDetails] | " P.info += "[c.author] | " @@ -449,7 +596,6 @@ What a mess.*/ P.info += "
Crime | @@ -75,23 +79,7 @@Author | Time Added | |
---|---|---|---|
[c.crimeName] | -[c.crimeDetails] | -[c.author] | -[c.time] | -
Crime | -Details | -Author | -Time Added | -||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[c.crimeName] | [c.crimeDetails] | [c.author] | @@ -113,8 +101,11 @@ var/mob/M = usr switch(href_list["choice"]) if("Login") + if(iscyborg(M)) //cyborgs cannot be set to arrest + return var/obj/item/card/id/scan = M.get_idcard(TRUE) - authenticated = scan.registered_name + if(scan) + authenticated = scan.registered_name if(authenticated) for(var/datum/data/record/R in GLOB.data_core.security) if(R.fields["name"] == authenticated) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 5de5c8df558e3..3fafde5cc6275 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -16,6 +16,7 @@ GLOBAL_LIST_EMPTY(cryopod_computers) icon_state = "cellconsole_1" // circuit = /obj/item/circuitboard/cryopodcontrol density = FALSE + layer = ABOVE_WINDOW_LAYER interaction_flags_machine = INTERACT_MACHINE_OFFLINE req_one_access = list(ACCESS_HEADS, ACCESS_ARMORY) //Heads of staff or the warden can go here to claim recover items from their department that people went were cryodormed with. var/mode = null @@ -150,7 +151,7 @@ GLOBAL_LIST_EMPTY(cryopod_computers) var/time_till_despawn = 5 * 600 // This is reduced to 30 seconds if a player manually enters cryo var/despawn_world_time = null // Used to keep track of the safe period. - var/obj/machinery/computer/cryopod/control_computer + var/datum/weakref/control_computer_weakref var/last_no_computer_message = 0 // These items are preserved when the process() despawn proc occurs. @@ -189,23 +190,28 @@ GLOBAL_LIST_EMPTY(cryopod_computers) update_icon() find_control_computer() -/obj/machinery/cryopod/proc/find_control_computer(urgent = 0) - for(var/M in GLOB.cryopod_computers) - var/obj/machinery/computer/cryopod/C = M - if(get_area(C) == get_area(src)) - control_computer = C +// This is not a good situation +/obj/machinery/cryopod/Destroy() + control_computer_weakref = null + return ..() + +/obj/machinery/cryopod/proc/find_control_computer(urgent = FALSE) + for(var/cryo_console as anything in GLOB.cryopod_computers) + var/obj/machinery/computer/cryopod/console = cryo_console + if(get_area(console) == get_area(src)) + control_computer_weakref = WEAKREF(console) break // Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged - if(!control_computer && urgent && last_no_computer_message + 5*60*10 < world.time) + if(!control_computer_weakref && urgent && last_no_computer_message + 5*60*10 < world.time) log_admin("Cryopod in [get_area(src)] could not find control computer!") message_admins("Cryopod in [get_area(src)] could not find control computer!") last_no_computer_message = world.time - return control_computer != null + return control_computer_weakref != null /obj/machinery/cryopod/close_machine(mob/user) - if(!control_computer) + if(!control_computer_weakref) find_control_computer(TRUE) if((isnull(user) || istype(user)) && state_open && !panel_open) ..(user) @@ -246,7 +252,7 @@ GLOBAL_LIST_EMPTY(cryopod_computers) return if(!mob_occupant.client && mob_occupant.stat < 2) //Occupant is living and has no client. - if(!control_computer) + if(!control_computer_weakref) find_control_computer(urgent = TRUE)//better hope you found it this time despawn_occupant() @@ -254,45 +260,42 @@ GLOBAL_LIST_EMPTY(cryopod_computers) /obj/machinery/cryopod/proc/handle_objectives() var/mob/living/mob_occupant = occupant //Update any existing objectives involving this mob. - for(var/datum/objective/O in GLOB.objectives) + for(var/datum/objective/O as() in GLOB.objectives) + if(O.target != mob_occupant.mind) + continue // We don't want revs to get objectives that aren't for heads of staff. Letting // them win or lose based on cryo is silly so we remove the objective. - if(istype(O,/datum/objective/mutiny) && O.target == mob_occupant.mind) + if(istype(O,/datum/objective/mutiny)) O.team.objectives -= O - qdel(O) - for(var/datum/mind/M in O.team.members) + for(var/datum/mind/M as() in O.team.members) to_chat(M.current, "
- [left_part] - | -- [list_queue()] - | -
[get_region_accesses_name(i)]: | " - accesses += "
"
- for(var/A in get_region_accesses(i))
- if(A in conf_access)
- accesses += "[replacetext(get_access_desc(A), " ", " ")] "
- else
- accesses += "[replacetext(get_access_desc(A), " ", " ")] "
- accesses += " " - accesses += " | "
- accesses += "
According to \the [src], you are now in \"[html_encode(A.name)]\".
" . += "" @@ -140,12 +140,9 @@ legend = FALSE -/obj/item/areaeditor/proc/get_area() - var/turf/T = get_turf(usr) - var/area/A = T.loc - return A - -/obj/item/areaeditor/proc/get_area_type(area/A = get_area()) +/obj/item/areaeditor/proc/get_area_type(area/A) + if (!A) + A = get_area(usr) if(A.outdoors) return AREA_SPACE var/list/SPECIALS = list( @@ -183,7 +180,7 @@ return "" /obj/item/areaeditor/proc/edit_area() - var/area/A = get_area() + var/area/A = get_area(usr) var/prevname = "[A.name]" var/str = stripped_input(usr,"New area name:", "Area Creation", "", MAX_NAME_LEN) if(!str || !length(str) || str==prevname) //cancel diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index f3dc980355d3c..3d9d39c4c20e2 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -63,6 +63,8 @@ return ..() /obj/item/bodybag/bluespace/proc/CanReachReact(atom/movable/source, list/next) + SIGNAL_HANDLER + return COMPONENT_BLOCK_REACH /obj/item/bodybag/bluespace/deploy_bodybag(mob/user, atom/location) diff --git a/code/game/objects/items/broom.dm b/code/game/objects/items/broom.dm new file mode 100644 index 0000000000000..f8913c878b9ce --- /dev/null +++ b/code/game/objects/items/broom.dm @@ -0,0 +1,85 @@ +/obj/item/pushbroom + name = "broom" + desc = "This is my BROOMSTICK! It can be used manually or braced with two hands to sweep items as you move. It has a telescopic handle for compact storage." + icon = 'icons/obj/janitor.dmi' + icon_state = "broom0" + lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi' + force = 8 + throwforce = 10 + throw_speed = 3 + throw_range = 7 + w_class = WEIGHT_CLASS_NORMAL + attack_verb = list("swept", "brushed off", "bludgeoned", "whacked") + resistance_flags = FLAMMABLE + +/obj/item/pushbroom/Initialize() + . = ..() + RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield) + RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield) + +/obj/item/pushbroom/ComponentInitialize() + . = ..() + AddComponent(/datum/component/two_handed, force_unwielded=8, force_wielded=12, icon_wielded="broom1") + +/obj/item/pushbroom/update_icon_state() + icon_state = "broom0" + ..() + +/// triggered on wield of two handed item +/obj/item/pushbroom/proc/on_wield(obj/item/source, mob/user) + SIGNAL_HANDLER + + to_chat(user, "You brace the [src] against the ground in a firm sweeping stance.") + RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/sweep) + +/// triggered on unwield of two handed item +/obj/item/pushbroom/proc/on_unwield(obj/item/source, mob/user) + SIGNAL_HANDLER + + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + +/obj/item/pushbroom/afterattack(atom/A, mob/user, proximity) + . = ..() + if(!proximity) + return + if(ISWIELDED(src)) + sweep(user, A, FALSE) + else + to_chat(user, "You need to wield \the [src] in both hands to sweep!") + +/obj/item/pushbroom/proc/sweep(mob/user, atom/A, moving = TRUE) + SIGNAL_HANDLER + + var/turf/target + if (!moving) + if (isturf(A)) + target = A + else + target = get_turf(A) + else + target = get_turf(user) + if (locate(/obj/structure/table) in target.contents) + return + var/i = 0 + var/turf/target_turf = get_step(target, user.dir) + var/obj/machinery/disposal/bin/target_bin = locate(/obj/machinery/disposal/bin) in target_turf.contents + for(var/obj/item/garbage in target.contents) + if(!garbage.anchored) + if (target_bin) + garbage.forceMove(target_bin) + else + garbage.Move(target_turf, user.dir) + i++ + if(i > 19) + break + if(i > 0) + if (target_bin) + target_bin.update_icon() + to_chat(user, "You sweep the pile of garbage into [target_bin].") + playsound(loc, 'sound/weapons/thudswoosh.ogg', 30, TRUE, -1) + +/obj/item/pushbroom/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) //bless you whoever fixes this copypasta + J.put_in_cart(src, user) + J.mybroom=src + J.update_icon() diff --git a/code/game/objects/items/candle.dm b/code/game/objects/items/candle.dm index 5a2b457ff21f1..5dd0b75e16ea0 100644 --- a/code/game/objects/items/candle.dm +++ b/code/game/objects/items/candle.dm @@ -9,7 +9,8 @@ w_class = WEIGHT_CLASS_TINY light_color = LIGHT_COLOR_FIRE heat = 1000 - var/wax = 1000 + /// How many seconds it burns for + var/wax = 2000 var/lit = FALSE var/infinite = FALSE var/start_lit = FALSE @@ -20,7 +21,7 @@ light() /obj/item/candle/update_icon() - icon_state = "candle[(wax > 400) ? ((wax > 750) ? 1 : 2) : 3][lit ? "_lit" : ""]" + icon_state = "candle[(wax > 800) ? ((wax > 1500) ? 1 : 2) : 3][lit ? "_lit" : ""]" /obj/item/candle/attackby(obj/item/W, mob/user, params) var/msg = W.ignition_effect(src, user) @@ -58,12 +59,12 @@ put_out_candle() return ..() -/obj/item/candle/process() +/obj/item/candle/process(delta_time) if(!lit) return PROCESS_KILL if(!infinite) - wax-- - if(!wax) + wax -= delta_time + if(wax <= 0) new /obj/item/trash/candle(loc) qdel(src) update_icon() diff --git a/code/game/objects/items/cardboard_cutouts.dm b/code/game/objects/items/cardboard_cutouts.dm index 7de1e0b876f49..44dad907fd9be 100644 --- a/code/game/objects/items/cardboard_cutouts.dm +++ b/code/game/objects/items/cardboard_cutouts.dm @@ -185,3 +185,63 @@ /obj/item/cardboard_cutout/adaptive //Purchased by Syndicate agents, these cutouts are indistinguishable from normal cutouts but aren't discolored when their appearance is changed deceptive = TRUE + +// --- CHESS PIECES --- + +// WHITE + +/obj/item/cardboard_cutout/adaptive/chess + desc = "A large cardboard cutout resembling a chess piece." + +/obj/item/cardboard_cutout/adaptive/chess/king + name = "White King" + icon_state = "cutout_ian"; + +/obj/item/cardboard_cutout/adaptive/chess/queen + name = "White Queen" + icon_state = "cutout_clown"; + +/obj/item/cardboard_cutout/adaptive/chess/rook + name = "White Rook" + icon_state = "cutout_deathsquad"; + +/obj/item/cardboard_cutout/adaptive/chess/knight + name = "White Knight" + icon_state = "cutout_lusty"; + +/obj/item/cardboard_cutout/adaptive/chess/bishop + name = "White Bishop" + icon_state = "cutout_ntsec"; + +/obj/item/cardboard_cutout/adaptive/chess/pawn + name = "White Pawn" + icon_state = "cutout_greytide"; + +// BLACK + +/obj/item/cardboard_cutout/adaptive/chess/black + color = "#9999BB"; + +/obj/item/cardboard_cutout/adaptive/chess/black/king + name = "Black King" + icon_state = "cutout_wizard"; + +/obj/item/cardboard_cutout/adaptive/chess/black/queen + name = "Black Queen" + icon_state = "cutout_traitor"; + +/obj/item/cardboard_cutout/adaptive/chess/black/rook + name = "Black Rook" + icon_state = "cutout_cultist"; + +/obj/item/cardboard_cutout/adaptive/chess/black/knight + name = "Black Knight" + icon_state = "cutout_fukken_xeno"; + +/obj/item/cardboard_cutout/adaptive/chess/black/bishop + name = "Black Bishop" + icon_state = "cutout_fluke"; + +/obj/item/cardboard_cutout/adaptive/chess/black/pawn + name = "Black Pawn" + icon_state = "cutout_shadowling"; \ No newline at end of file diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 98d30612f75b5..0204aa4bb614d 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -111,7 +111,7 @@ lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' slot_flags = ITEM_SLOT_ID - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100, "stamina" = 0) resistance_flags = FIRE_PROOF | ACID_PROOF var/mining_points = 0 //For redeeming at mining equipment vendors var/list/access = list() @@ -466,6 +466,19 @@ update_label("John Doe", "Clowny") assignment = "Syndicate Officer" access = list(ACCESS_SYNDICATE) +/obj/item/card/id/syndicate/debug + name = "\improper Debug ID" + desc = "A shimmering ID card with the ability to open anything." + icon_state = "centcom" + registered_name = "Central Command" + assignment = "Admiral" + anyone = TRUE + +/obj/item/card/id/syndicate/debug/Initialize() + access = get_every_access() + registered_account = SSeconomy.get_dep_account(ACCOUNT_CAR) + . = ..() + /obj/item/card/id/captains_spare name = "captain's spare ID" desc = "The spare ID of the High Lord himself." @@ -548,6 +561,15 @@ update_label("John Doe", "Clowny") access = get_all_accesses() . = ..() +/obj/item/card/id/ert/kudzu + registered_name = "Weed Whacker" + assignment = "Weed Whacker" + icon_state = "ert" + +/obj/item/card/id/ert/kudzu/Initialize() + access = get_all_accesses() + . = ..() + /obj/item/card/id/prisoner name = "prisoner ID card" desc = "You are a number, you are not a free man." @@ -643,11 +665,12 @@ update_label("John Doe", "Clowny") ///Department Budget Cards/// /obj/item/card/id/departmental_budget - name = "departmental card (FUCK)" + name = "departmental card (budget)" desc = "Provides access to the departmental budget." icon_state = "budget" var/department_ID = ACCOUNT_CIV var/department_name = ACCOUNT_CIV_NAME + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF /obj/item/card/id/departmental_budget/Initialize() . = ..() @@ -746,6 +769,9 @@ update_label("John Doe", "Clowny") /obj/item/card/id/job/miner icon_state = "miner" +/obj/item/card/id/job/exploration + icon_state = "exploration" + /obj/item/card/id/job/cargo icon_state = "cargo" @@ -757,3 +783,26 @@ update_label("John Doe", "Clowny") /obj/item/card/id/job/lawyer icon_state = "lawyer" + +/obj/item/card/id/pass + name = "promotion pass" + desc = "A card that, when swiped on your ID card, will grant you all the access. Should not substitute your actual ID card." + icon_state = "data_1" + registered_name = "Unregistered ID" + assignment = "Access Pass" + +/obj/item/card/id/pass/afterattack(atom/target, mob/user, proximity) + . = ..() + if (!proximity) + return . + var/obj/item/card/id/idcard = target + if(istype(idcard)) + for(var/give_access in access) + idcard.access |= give_access + if(assignment!=initial(assignment)) + idcard.assignment = assignment + if(name!=initial(name)) + idcard.name = name + to_chat(user, "You upgrade your [idcard] with the [name].") + log_id("[key_name(user)] added access to '[idcard]' using [src] at [AREACOORD(user)].") + qdel(src) diff --git a/code/game/objects/items/chainsaw.dm b/code/game/objects/items/chainsaw.dm new file mode 100644 index 0000000000000..f060eb90e16c2 --- /dev/null +++ b/code/game/objects/items/chainsaw.dm @@ -0,0 +1,140 @@ + +// CHAINSAW +/obj/item/chainsaw + name = "chainsaw" + desc = "A versatile power tool. Useful for limbing trees and delimbing humans." + icon_state = "chainsaw_off" + lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi' + flags_1 = CONDUCT_1 + force = 13 + block_power = 20 + block_upgrade_walk = 2 + block_flags = BLOCKING_ACTIVE | BLOCKING_NASTY + attack_weight = 2 + var/force_on = 24 + w_class = WEIGHT_CLASS_HUGE + throwforce = 13 + throw_speed = 2 + throw_range = 4 + materials = list(/datum/material/iron=13000) + attack_verb = list("sawed", "tore", "cut", "chopped", "diced") + hitsound = "swing_hit" + sharpness = IS_SHARP + actions_types = list(/datum/action/item_action/startchainsaw) + var/on = FALSE + tool_behaviour = TOOL_SAW + toolspeed = 0.5 + +/obj/item/chainsaw/Initialize() + . = ..() + +/obj/item/chainsaw/ComponentInitialize() + . = ..() + AddComponent(/datum/component/butchering, 30, 100, 0, 'sound/weapons/chainsawhit.ogg', TRUE) + AddComponent(/datum/component/two_handed, require_twohands=TRUE, block_power_unwielded=block_power, block_power_wielded=block_power) + +/obj/item/chainsaw/suicide_act(mob/living/carbon/user) + if(on) + user.visible_message("[user] begins to tear [user.p_their()] head off with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(src, 'sound/weapons/chainsawhit.ogg', 100, TRUE) + var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD) + if(myhead) + myhead.dismember() + else + user.visible_message("[user] smashes [src] into [user.p_their()] neck, destroying [user.p_their()] esophagus! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(src, 'sound/weapons/genhit1.ogg', 100, TRUE) + return(BRUTELOSS) + +/obj/item/chainsaw/attack_self(mob/user) + on = !on + to_chat(user, "As you pull the starting cord dangling from [src], [on ? "it begins to whirr." : "the chain stops moving."]") + force = on ? force_on : initial(force) + throwforce = on ? force_on : initial(force) + icon_state = "chainsaw_[on ? "on" : "off"]" + var/datum/component/butchering/butchering = src.GetComponent(/datum/component/butchering) + butchering.butchering_enabled = on + + if(on) + hitsound = 'sound/weapons/chainsawhit.ogg' + else + hitsound = "swing_hit" + + if(src == user.get_active_held_item()) //update inhands + user.update_inv_hands() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +// DOOMGUY CHAINSAW +/obj/item/chainsaw/doomslayer + name = "THE GREAT COMMUNICATOR" + desc = "VRRRRRRR!!!" + armour_penetration = 100 + force_on = 30 + +/obj/item/chainsaw/doomslayer/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(attack_type == PROJECTILE_ATTACK) + owner.visible_message("Ranged attacks just make [owner] angrier!") + playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE) + return 1 + return 0 + +// ENERGY CHAINSAW +/obj/item/chainsaw/energy + name = "energy chainsaw" + desc = "Become Leatherspace." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "echainsaw_off" + lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi' + force_on = 40 + w_class = WEIGHT_CLASS_HUGE + attack_verb = list("sawed", "shred", "rended", "gutted", "eviscerated") + actions_types = list(/datum/action/item_action/startchainsaw) + block_power = 50 + armour_penetration = 50 + light_color = "#ff0000" + var/onsound + var/offsound + onsound = 'sound/weapons/echainsawon.ogg' + offsound = 'sound/weapons/echainsawoff.ogg' + on = FALSE + var/brightness_on = 3 + +/obj/item/chainsaw/energy/attack_self(mob/user) + on = !on + to_chat(user, "As you pull the starting cord dangling from [src], [on ? "it begins to whirr intimidatingly." : "the plasma microblades stop moving."]") + force = on ? force_on : initial(force) + playsound(user, on ? onsound : offsound , 50, 1) + set_light(on ? brightness_on : 0) + throwforce = on ? force_on : initial(force) + icon_state = "echainsaw_[on ? "on" : "off"]" + + if(hitsound == "swing_hit") + hitsound = pick('sound/weapons/echainsawhit1.ogg','sound/weapons/echainsawhit2.ogg') + else + hitsound = "swing_hit" + + if(src == user.get_active_held_item()) + user.update_inv_hands() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +// DOOMGUY ENERGY CHAINSAW +/obj/item/chainsaw/energy/doom + name = "super energy chainsaw" + desc = "The chainsaw you want when you need to kill every damn thing in the room." + force_on = 60 + w_class = WEIGHT_CLASS_NORMAL + block_power = 75 + block_level = 1 + attack_weight = 3 //fear him + armour_penetration = 75 + var/knockdown = 1 + brightness_on = 6 + +/obj/item/chainsaw/energy/doom/attack(mob/living/target) + ..() + target.Knockdown(4) \ No newline at end of file diff --git a/code/game/objects/items/charter.dm b/code/game/objects/items/charter.dm index 337092108e52f..ecbb9cafa15d7 100644 --- a/code/game/objects/items/charter.dm +++ b/code/game/objects/items/charter.dm @@ -100,7 +100,7 @@ /obj/item/station_charter/flag - name = "nanotrasen banner" + name = "\improper Nanotrasen banner" icon = 'icons/obj/banner.dmi' name_type = "planet" icon_state = "banner" diff --git a/code/game/objects/items/chromosome.dm b/code/game/objects/items/chromosome.dm index 1340e7f35b44c..8330a8e202349 100644 --- a/code/game/objects/items/chromosome.dm +++ b/code/game/objects/items/chromosome.dm @@ -53,32 +53,32 @@ /obj/item/chromosome/stabilizer name = "stabilizer chromosome" - desc = "A chromosome that adjusts to the body to reduce genetic damage by 20%." + desc = "A chromosome that reduces mutation instability by 20%." icon_state = "stabilizer" stabilizer_coeff = 0.8 weight = 1 /obj/item/chromosome/synchronizer name = "synchronizer chromosome" - desc = "A chromosome that gives the mind more controle over the mutation, reducing knockback and downsides by 50%." + desc = "A chromosome that reduces mutation knockback and downsides by 50%." icon_state = "synchronizer" synchronizer_coeff = 0.5 /obj/item/chromosome/power name = "power chromosome" - desc = "A power chromosome for boosting certain mutation's power by 50%." + desc = "A chromosome that increases mutation power by 50%." icon_state = "power" power_coeff = 1.5 /obj/item/chromosome/energy name = "energetic chromosome" - desc = "A chromosome that reduces cooldown on action based mutations by 50%." + desc = "A chromosome that reduces action based mutation cooldowns by by 50%." icon_state = "energy" energy_coeff = 0.5 /obj/item/chromosome/reinforcer name = "reinforcement chromosome" - desc = "Renders the mutation immune to mutadone." + desc = "A chromosome that renders mutations immune to mutadone." icon_state = "reinforcer" weight = 3 diff --git a/code/game/objects/items/chrono_eraser.dm b/code/game/objects/items/chrono_eraser.dm index 04caab6593d6a..154215e098086 100644 --- a/code/game/objects/items/chrono_eraser.dm +++ b/code/game/objects/items/chrono_eraser.dm @@ -38,7 +38,7 @@ user.put_in_hands(PA) /obj/item/chrono_eraser/item_action_slot_check(slot, mob/user) - if(slot == SLOT_BACK) + if(slot == ITEM_SLOT_BACK) return 1 /obj/item/gun/energy/chrono_gun @@ -133,6 +133,10 @@ if(istype(C)) gun = C.gun +/obj/item/projectile/energy/chrono_beam/Destroy() + gun = null + return ..() + /obj/item/projectile/energy/chrono_beam/on_hit(atom/target) if(target && gun && isliving(target)) var/obj/structure/chrono_field/F = new(target.loc, target, gun) @@ -151,7 +155,9 @@ gun = loc . = ..() - +/obj/item/ammo_casing/energy/chrono_beam/Destroy() + gun = null + return ..() @@ -167,7 +173,7 @@ interaction_flags_atom = NONE var/mob/living/captured = null var/obj/item/gun/energy/chrono_gun/gun = null - var/tickstokill = 15 + var/timetokill = 30 var/mutable_appearance/mob_underlay var/preloaded = 0 var/RPpos = null @@ -198,7 +204,7 @@ return ..() /obj/structure/chrono_field/update_icon() - var/ttk_frame = 1 - (tickstokill / initial(tickstokill)) + var/ttk_frame = 1 - (timetokill / initial(timetokill)) ttk_frame = CLAMP(CEILING(ttk_frame * CHRONO_FRAME_COUNT, 1), 1, CHRONO_FRAME_COUNT) if(ttk_frame != RPpos) RPpos = ttk_frame @@ -206,15 +212,15 @@ underlays = list() //hack: BYOND refuses to update the underlay to match the icon_state otherwise underlays += mob_underlay -/obj/structure/chrono_field/process() +/obj/structure/chrono_field/process(delta_time) if(captured) - if(tickstokill > initial(tickstokill)) + if(timetokill > initial(timetokill)) for(var/atom/movable/AM in contents) AM.forceMove(drop_location()) qdel(src) - else if(tickstokill <= 0) + else if(timetokill <= 0) to_chat(captured, "As the last essence of your being is erased from time, you are taken back to your most enjoyable memory. You feel happy...") - var/mob/dead/observer/ghost = captured.ghostize(1) + var/mob/dead/observer/ghost = captured.ghostize(TRUE,SENTIENCE_ERASE) if(captured.mind) if(ghost) ghost.mind = null @@ -229,12 +235,12 @@ update_icon() if(gun) if(gun.field_check(src)) - tickstokill-- + timetokill -= delta_time else gun = null return .() else - tickstokill++ + timetokill += delta_time else qdel(src) @@ -248,12 +254,18 @@ return BULLET_ACT_HIT /obj/structure/chrono_field/assume_air() - return 0 + return null + +/obj/effect/chrono_field/assume_air_moles() + return null + +/obj/effect/chrono_field/assume_air_ratio() + return null /obj/structure/chrono_field/return_air() //we always have nominal air and temperature var/datum/gas_mixture/GM = new - GM.set_moles(/datum/gas/oxygen, MOLES_O2STANDARD) - GM.set_moles(/datum/gas/nitrogen, MOLES_N2STANDARD) + GM.set_moles(GAS_O2, MOLES_O2STANDARD) + GM.set_moles(GAS_N2, MOLES_N2STANDARD) GM.set_temperature(T20C) return GM diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index 62b63fdaa4fca..1e29de9537a4f 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -22,14 +22,15 @@ CIGARETTE PACKETS ARE IN FANCY.DM icon_state = "match_unlit" var/lit = FALSE var/burnt = FALSE - var/smoketime = 5 // 10 seconds + /// How long the match lasts in seconds + var/smoketime = 10 w_class = WEIGHT_CLASS_TINY heat = 1000 grind_results = list(/datum/reagent/phosphorus = 2) -/obj/item/match/process() - smoketime-- - if(smoketime < 1) +/obj/item/match/process(delta_time) + smoketime -= delta_time + if(smoketime <= 0) matchburnout() else open_flame(heat) @@ -90,7 +91,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM ..() /obj/item/proc/help_light_cig(mob/living/M) - var/mask_item = M.get_item_by_slot(SLOT_WEAR_MASK) + var/mask_item = M.get_item_by_slot(ITEM_SLOT_MASK) if(istype(mask_item, /obj/item/clothing/mask/cigarette)) return mask_item @@ -100,7 +101,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/match/firebrand name = "firebrand" desc = "An unlit firebrand. It makes you wonder why it's not just called a stick." - smoketime = 20 //40 seconds + smoketime = 40 grind_results = list(/datum/reagent/carbon = 2) /obj/item/match/firebrand/Initialize() @@ -120,7 +121,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM body_parts_covered = null grind_results = list() heat = 1000 - var/dragtime = 100 + var/dragtime = 10 var/nextdragtime = 0 var/lit = FALSE var/starts_lit = FALSE @@ -128,7 +129,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM var/icon_off = "cigoff" var/type_butt = /obj/item/cigbutt var/lastHolder = null - var/smoketime = 180 // 1 is 2 seconds, so a single cigarette will last 6 minutes. + /// How long the cigarette lasts in seconds + var/smoketime = 360 var/chem_volume = 30 var/smoke_all = FALSE /// Should we smoke all of the chems in the cig before it runs out. Splits each puff to take a portion of the overall chems so by the end you'll always have consumed all of the chems inside. var/list/list_reagents = list(/datum/reagent/drug/nicotine = 15) @@ -144,7 +146,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM reagents.add_reagent_list(list_reagents) if(starts_lit) light() - AddComponent(/datum/component/knockoff,90,list(BODY_ZONE_PRECISE_MOUTH),list(SLOT_WEAR_MASK))//90% to knock off when wearing a mask + AddComponent(/datum/component/knockoff,90,list(BODY_ZONE_PRECISE_MOUTH),list(ITEM_SLOT_MASK))//90% to knock off when wearing a mask /obj/item/clothing/mask/cigarette/Destroy() STOP_PROCESSING(SSobj, src) @@ -247,7 +249,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM * of chems to give them each time so they'll have smoked it all by the end */ if (smoke_all) - to_smoke = reagents.total_volume/((smoketime * 2) / (dragtime / 10)) + to_smoke = reagents.total_volume / (smoketime / dragtime) reagents.reaction(C, INGEST, fraction) if(!reagents.trans_to(C, to_smoke)) @@ -255,13 +257,13 @@ CIGARETTE PACKETS ARE IN FANCY.DM return reagents.remove_any(to_smoke) -/obj/item/clothing/mask/cigarette/process() +/obj/item/clothing/mask/cigarette/process(delta_time) var/turf/location = get_turf(src) var/mob/living/M = loc if(isliving(loc)) M.IgniteMob() - smoketime-- - if(smoketime < 1) + smoketime -= delta_time + if(smoketime <= 0) new type_butt(location) if(ismob(loc)) to_chat(M, "Your [name] goes out.") @@ -270,7 +272,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM return open_flame() if((reagents && reagents.total_volume) && (nextdragtime <= world.time)) - nextdragtime = world.time + dragtime + nextdragtime = world.time + dragtime SECONDS handle_reagents() /obj/item/clothing/mask/cigarette/attack_self(mob/user) @@ -326,14 +328,14 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/clothing/mask/cigarette/carp desc = "A Carp Classic brand cigarette." - + /obj/item/clothing/mask/cigarette/plasma - list_reagents = list(/datum/reagent/toxin/plasma = 5) + list_reagents = list(/datum/reagent/toxin/plasma = 5) /obj/item/clothing/mask/cigarette/syndicate desc = "An unknown brand cigarette." chem_volume = 60 - smoketime = 60 + smoketime = 2 * 60 smoke_all = TRUE list_reagents = list(/datum/reagent/drug/nicotine = 10, /datum/reagent/medicine/omnizine = 15) @@ -356,7 +358,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM type_butt = /obj/item/cigbutt/roach throw_speed = 0.5 item_state = "spliffoff" - smoketime = 120 // four minutes + smoketime = 4 * 60 chem_volume = 50 list_reagents = null @@ -401,7 +403,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM type_butt = /obj/item/cigbutt/cigarbutt throw_speed = 0.5 item_state = "cigaroff" - smoketime = 300 // 11 minutes + smoketime = 11 * 60 chem_volume = 40 list_reagents = list(/datum/reagent/drug/nicotine = 25) @@ -411,7 +413,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM icon_state = "cigar2off" icon_on = "cigar2on" icon_off = "cigar2off" - smoketime = 600 // 20 minutes + smoketime = 20 * 60 chem_volume = 80 list_reagents =list(/datum/reagent/drug/nicotine = 40) @@ -421,7 +423,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM icon_state = "cigar2off" icon_on = "cigar2on" icon_off = "cigar2off" - smoketime = 900 // 30 minutes + smoketime = 30 * 60 chem_volume = 50 list_reagents =list(/datum/reagent/drug/nicotine = 15) @@ -462,10 +464,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM STOP_PROCESSING(SSobj, src) . = ..() -/obj/item/clothing/mask/cigarette/pipe/process() +/obj/item/clothing/mask/cigarette/pipe/process(delta_time) var/turf/location = get_turf(src) - smoketime-- - if(smoketime < 1) + smoketime -= delta_time + if(smoketime <= 0) new /obj/effect/decal/cleanable/ash(location) if(ismob(loc)) var/mob/living/M = loc @@ -489,8 +491,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(!packeditem) if(G.dry == 1) to_chat(user, "You stuff [O] into [src].") - smoketime = 400 - packeditem = 1 + smoketime = 13 * 60 + packeditem = TRUE name = "[O.name]-packed [initial(name)]" if(O.reagents) O.reagents.trans_to(src, O.reagents.total_volume, transfered_by = user) @@ -775,8 +777,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM w_class = WEIGHT_CLASS_TINY var/chem_volume = 100 var/vapetime = 0 //this so it won't puff out clouds every tick - var/screw = 0 // kinky - var/super = 0 //for the fattest vapes dude. + /// How often we take a drag in seconds + var/vapedelay = 8 + var/screw = FALSE // kinky + var/super = FALSE //for the fattest vapes dude. /obj/item/clothing/mask/vape/suicide_act(mob/user) user.visible_message("[user] is puffin hard on dat vape, [user.p_they()] trying to join the vape life on a whole notha plane!")//it doesn't give you cancer, it is cancer @@ -852,7 +856,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM reagents.clear_reagents() /obj/item/clothing/mask/vape/equipped(mob/user, slot) - if(slot == SLOT_WEAR_MASK) + if(slot == ITEM_SLOT_MASK) if(!screw) to_chat(user, "You start puffing on the vape.") DISABLE_BITFIELD(reagents.flags, NO_REACT) @@ -861,8 +865,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM to_chat(user, "You need to close the cap first!") /obj/item/clothing/mask/vape/dropped(mob/user) - var/mob/living/carbon/C = user - if(C.get_item_by_slot(SLOT_WEAR_MASK) == src) + if(user.get_item_by_slot(ITEM_SLOT_MASK) == src) ENABLE_BITFIELD(reagents.flags, NO_REACT) STOP_PROCESSING(SSobj, src) @@ -888,13 +891,13 @@ CIGARETTE PACKETS ARE IN FANCY.DM return reagents.remove_any(REAGENTS_METABOLISM) -/obj/item/clothing/mask/vape/process() +/obj/item/clothing/mask/vape/process(delta_time) var/mob/living/M = loc if(isliving(loc)) M.IgniteMob() - vapetime++ + vapetime += delta_time if(!reagents.total_volume) if(ismob(loc)) @@ -904,17 +907,17 @@ CIGARETTE PACKETS ARE IN FANCY.DM return //open flame removed because vapes are a closed system, they wont light anything on fire - if(super && vapetime > 3)//Time to start puffing those fat vapes, yo. + if(super && vapetime >= vapedelay)//Time to start puffing those fat vapes, yo. var/datum/effect_system/smoke_spread/chem/smoke_machine/s = new s.set_up(reagents, 1, 24, loc) s.start() - vapetime = 0 + vapetime -= vapedelay - if((obj_flags & EMAGGED) && vapetime > 3) + if((obj_flags & EMAGGED) && vapetime >= vapedelay) var/datum/effect_system/smoke_spread/chem/smoke_machine/s = new s.set_up(reagents, 4, 24, loc) s.start() - vapetime = 0 + vapetime -= vapedelay if(prob(5))//small chance for the vape to break and deal damage if it's emagged playsound(get_turf(src), 'sound/effects/pop_expl.ogg', 50, 0) M.apply_damage(20, BURN, BODY_ZONE_HEAD) diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm index e4c7c58a64ca6..a7282e9e2928e 100644 --- a/code/game/objects/items/circuitboards/computer_circuitboards.dm +++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm @@ -2,32 +2,32 @@ /obj/item/circuitboard/computer/aiupload - name = "AI Upload (Computer Board)" + name = "AI upload (Computer Board)" icon_state = "command" build_path = /obj/machinery/computer/upload/ai /obj/item/circuitboard/computer/borgupload - name = "Cyborg Upload (Computer Board)" + name = "cyborg upload (Computer Board)" icon_state = "command" build_path = /obj/machinery/computer/upload/borg /obj/item/circuitboard/computer/bsa_control - name = "Bluespace Artillery Controls (Computer Board)" + name = "bluespace artillery controls (Computer Board)" icon_state = "command" build_path = /obj/machinery/computer/bsa_control /obj/item/circuitboard/computer/card - name = "ID Console (Computer Board)" + name = "ID console (Computer Board)" icon_state = "command" build_path = /obj/machinery/computer/card /obj/item/circuitboard/computer/card/centcom - name = "CentCom ID Console (Computer Board)" + name = "CentCom ID console (Computer Board)" icon_state = "command" build_path = /obj/machinery/computer/card/centcom /obj/item/circuitboard/computer/card/minor - name = "Department Management Console (Computer Board)" + name = "department management console (Computer Board)" icon_state = "command" build_path = /obj/machinery/computer/card/minor var/target_dept = 1 @@ -45,10 +45,24 @@ . += "Currently set to \"[dept_list[target_dept]]\"." /obj/item/circuitboard/computer/communications - name = "Communications (Computer Board)" + name = "communications console (Computer Board)" icon_state = "command" + desc = "Can be modified using a screwdriver." build_path = /obj/machinery/computer/communications var/lastTimeUsed = 0 + var/insecure = FALSE // Forbids shuttles that are set as illegal. + +/obj/item/circuitboard/computer/communications/attackby(obj/item/I, mob/user, params) + if(I.tool_behaviour == TOOL_SCREWDRIVER) + insecure = !insecure + if(insecure) + desc = "Tampering has removed some safety features from this circuit board. A screwdriver can undo this." + to_chat(user, "You disable the shuttle safety features of the board.") + else + desc = "Can be modified using a screwdriver." + to_chat(user, "You re-enable the shuttle safety features of the board.") + else + return ..() //obj/item/circuitboard/computer/shield // name = "Shield Control (Computer Board)" @@ -59,117 +73,117 @@ /obj/item/circuitboard/computer/apc_control - name = "\improper Power Flow Control Console (Computer Board)" + name = "power flow control console (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/apc_control /obj/item/circuitboard/computer/atmos_alert - name = "Atmospheric Alert (Computer Board)" + name = "atmospheric alert console (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/atmos_alert /obj/item/circuitboard/computer/atmos_control - name = "Atmospheric Monitor (Computer Board)" + name = "atmospheric monitor console (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/atmos_control /obj/item/circuitboard/computer/atmos_control/tank - name = "Tank Control (Computer Board)" + name = "tank control console (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank /obj/item/circuitboard/computer/atmos_control/tank/oxygen_tank - name = "Oxygen Supply Control (Computer Board)" + name = "oxygen supply control (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank/oxygen_tank /obj/item/circuitboard/computer/atmos_control/tank/toxin_tank - name = "Plasma Supply Control (Computer Board)" + name = "plasma supply control (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank/toxin_tank /obj/item/circuitboard/computer/atmos_control/tank/air_tank - name = "Mixed Air Supply Control (Computer Board)" + name = "mixed air supply control (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank/air_tank /obj/item/circuitboard/computer/atmos_control/tank/mix_tank - name = "Gas Mix Supply Control (Computer Board)" + name = "gas mix supply control (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank/mix_tank /obj/item/circuitboard/computer/atmos_control/tank/nitrous_tank - name = "Nitrous Oxide Supply Control (Computer Board)" + name = "nitrous oxide supply control (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank/nitrous_tank /obj/item/circuitboard/computer/atmos_control/tank/nitrogen_tank - name = "Nitrogen Supply Control (Computer Board)" + name = "nitrogen supply control (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank/nitrogen_tank /obj/item/circuitboard/computer/atmos_control/tank/carbon_tank - name = "Carbon Dioxide Supply Control (Computer Board)" + name = "carbon dioxide supply control (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank/carbon_tank /obj/item/circuitboard/computer/atmos_control/tank/incinerator - name = "Incinerator Air Control (Computer Board)" + name = "incinerator air control (Computer Board)" build_path = /obj/machinery/computer/atmos_control/tank/incinerator /obj/item/circuitboard/computer/auxillary_base - name = "Auxillary Base Management Console (Computer Board)" + name = "auxillary base management console (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/auxillary_base /obj/item/circuitboard/computer/base_construction - name = "circuit board (Aux Mining Base Construction Console)" + name = "aux mining base construction console (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/camera_advanced/base_construction /obj/item/circuitboard/computer/comm_monitor - name = "Telecommunications Monitor (Computer Board)" + name = "telecommunications monitor (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/telecomms/monitor /obj/item/circuitboard/computer/comm_server - name = "Telecommunications Server Monitor (Computer Board)" + name = "telecommunications server monitor (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/telecomms/server /obj/item/circuitboard/computer/message_monitor - name = "Message Monitor (Computer Board)" + name = "message monitor (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/message_monitor /obj/item/circuitboard/computer/powermonitor - name = "Power Monitor (Computer Board)" //name fixed 250810 + name = "power monitor (Computer Board)" //name fixed 250810 icon_state = "engineering" build_path = /obj/machinery/computer/monitor /obj/item/circuitboard/computer/powermonitor/secret - name = "Outdated Power Monitor (Computer Board)" //Variant used on ruins to prevent them from showing up on PDA's. + name = "outdated power monitor (Computer Board)" //Variant used on ruins to prevent them from showing up on PDA's. build_path = /obj/machinery/computer/monitor/secret /obj/item/circuitboard/computer/sat_control - name = "Satellite Network Control (Computer Board)" + name = "satellite network control (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/sat_control /obj/item/circuitboard/computer/solar_control - name = "Solar Control (Computer Board)" //name fixed 250810 + name = "solar control (Computer Board)" //name fixed 250810 icon_state = "engineering" build_path = /obj/machinery/power/solar_control /obj/item/circuitboard/computer/stationalert - name = "Station Alerts (Computer Board)" + name = "station alerts console (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/station_alert /obj/item/circuitboard/computer/teleporter - name = "Teleporter (Computer Board)" + name = "teleporter console (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/teleporter /obj/item/circuitboard/computer/turbine_computer - name = "Turbine Computer (Computer Board)" + name = "turbine computer (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/turbine_computer /obj/item/circuitboard/computer/turbine_control - name = "Turbine control (Computer Board)" + name = "turbine control (Computer Board)" icon_state = "engineering" build_path = /obj/machinery/computer/turbine_computer @@ -178,7 +192,7 @@ /obj/item/circuitboard/computer/advanced_camera - name = "Advanced Camera Console (Computer Board)" + name = "advanced camera console (Computer Board)" icon_state = "generic" build_path = /obj/machinery/computer/camera_advanced/syndie @@ -188,7 +202,7 @@ build_path = /obj/machinery/computer/arcade/amputation /obj/item/circuitboard/computer/arcade/battle - name = "Arcade Battle (Computer Board)" + name = "arcade battle (Computer Board)" icon_state = "generic" build_path = /obj/machinery/computer/arcade/battle @@ -198,12 +212,12 @@ build_path = /obj/machinery/computer/arcade/orion_trail /obj/item/circuitboard/computer/holodeck// Not going to let people get this, but it's just here for future - name = "Holodeck Control (Computer Board)" + name = "holodeck control (Computer Board)" icon_state = "generic" build_path = /obj/machinery/computer/holodeck /obj/item/circuitboard/computer/libraryconsole - name = "Library Visitor Console (Computer Board)" + name = "library visitor console (Computer Board)" icon_state = "generic" build_path = /obj/machinery/computer/libraryconsole @@ -221,9 +235,9 @@ return ..() /obj/item/circuitboard/computer/monastery_shuttle - name = "Monastery Shuttle (Computer Board)" + name = "monastery shuttle console (Computer Board)" icon_state = "generic" - build_path = /obj/machinery/computer/shuttle/monastery_shuttle + build_path = /obj/machinery/computer/shuttle_flight/monastery_shuttle /obj/item/circuitboard/computer/olddoor name = "DoorMex (Computer Board)" @@ -231,12 +245,12 @@ build_path = /obj/machinery/computer/pod/old /obj/item/circuitboard/computer/pod - name = "Massdriver control (Computer Board)" + name = "mass driver launch control (Computer Board)" icon_state = "generic" build_path = /obj/machinery/computer/pod /obj/item/circuitboard/computer/slot_machine - name = "Slot Machine (Computer Board)" + name = "slot machine (Computer Board)" icon_state = "generic" build_path = /obj/machinery/computer/slot_machine @@ -246,9 +260,9 @@ build_path = /obj/machinery/computer/pod/old/swf /obj/item/circuitboard/computer/syndicate_shuttle - name = "Syndicate Shuttle (Computer Board)" + name = "syndicate shuttle console (Computer Board)" icon_state = "generic" - build_path = /obj/machinery/computer/shuttle/syndicate + build_path = /obj/machinery/computer/shuttle_flight/syndicate var/challenge = FALSE var/moved = FALSE @@ -266,48 +280,43 @@ build_path = /obj/machinery/computer/pod/old/syndicate /obj/item/circuitboard/computer/white_ship - name = "White Ship (Computer Board)" + name = "white ship control (Computer Board)" icon_state = "generic" - build_path = /obj/machinery/computer/shuttle/white_ship + build_path = /obj/machinery/computer/shuttle_flight/white_ship /obj/item/circuitboard/computer/white_ship/pod - name = "Salvage Pod (Computer Board)" - build_path = /obj/machinery/computer/shuttle/white_ship/pod + name = "salvage pod control (Computer Board)" + build_path = /obj/machinery/computer/shuttle_flight/white_ship/pod /obj/item/circuitboard/computer/white_ship/pod/recall - name = "Salvage Pod Recall (Computer Board)" - build_path = /obj/machinery/computer/shuttle/white_ship/pod/recall + name = "salvage pod recall control (Computer Board)" + build_path = /obj/machinery/computer/shuttle_flight/white_ship/pod/recall /obj/item/circuitboard/computer/shuttle/flight_control - name = "Shuttle Flight Control (Computer Board)" + name = "shuttle flight control (Computer Board)" icon_state = "generic" - build_path = /obj/machinery/computer/custom_shuttle - -/obj/item/circuitboard/computer/shuttle/docker - name = "Shuttle Navigation Computer (Computer Board)" - icon_state = "generic" - build_path = /obj/machinery/computer/camera_advanced/shuttle_docker/custom - + build_path = /obj/machinery/computer/shuttle_flight/custom_shuttle //Medical /obj/item/circuitboard/computer/cloning - name = "Cloning (Computer Board)" + name = "cloning console (Computer Board)" icon_state = "medical" build_path = /obj/machinery/computer/cloning + var/list/records = list() /obj/item/circuitboard/computer/crew - name = "Crew Monitoring Console (Computer Board)" + name = "crew monitoring console (Computer Board)" icon_state = "medical" build_path = /obj/machinery/computer/crew /obj/item/circuitboard/computer/med_data - name = "Medical Records Console (Computer Board)" + name = "medical records console (Computer Board)" icon_state = "medical" build_path = /obj/machinery/computer/med_data /obj/item/circuitboard/computer/operating - name = "Operating Computer (Computer Board)" + name = "operating computer (Computer Board)" icon_state = "medical" build_path = /obj/machinery/computer/operating @@ -316,13 +325,13 @@ icon_state = "medical" build_path = /obj/machinery/computer/pandemic -/obj/item/circuitboard/computer/prototype_cloning - name = "Prototype Cloning (Computer Board)" +/obj/item/circuitboard/computer/cloning/prototype + name = "prototype cloning console (Computer Board)" icon_state = "medical" - build_path = /obj/machinery/computer/prototype_cloning + build_path = /obj/machinery/computer/cloning/prototype /obj/item/circuitboard/computer/scan_consolenew - name = "DNA Machine (Computer Board)" + name = "DNA machine (Computer Board)" icon_state = "medical" build_path = /obj/machinery/computer/scan_consolenew @@ -331,42 +340,42 @@ /obj/item/circuitboard/computer/aifixer - name = "AI Integrity Restorer (Computer Board)" + name = "AI integrity restorer console (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/aifixer /obj/item/circuitboard/computer/launchpad_console - name = "Launchpad Control Console (Computer Board)" + name = "launchpad control console (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/launchpad /obj/item/circuitboard/computer/mech_bay_power_console - name = "Mech Bay Power Control Console (Computer Board)" + name = "mech bay power control console (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/mech_bay_power_console /obj/item/circuitboard/computer/mecha_control - name = "Exosuit Control Console (Computer Board)" + name = "exosuit control console (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/mecha /obj/item/circuitboard/computer/nanite_chamber_control - name = "Nanite Chamber Control (Computer Board)" + name = "nanite chamber control (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/nanite_chamber_control /obj/item/circuitboard/computer/nanite_cloud_controller - name = "Nanite Cloud Control (Computer Board)" + name = "nanite cloud control (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/nanite_cloud_controller /obj/item/circuitboard/computer/rdconsole - name = "R&D Console (Computer Board)" + name = "R&D console (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/rdconsole/core /obj/item/circuitboard/computer/rdconsole/production - name = "R&D Console Production Only (Computer Board)" + name = "R&D console - production only (Computer Board)" build_path = /obj/machinery/computer/rdconsole/production /obj/item/circuitboard/computer/rdconsole/attackby(obj/item/I, mob/user, params) @@ -383,22 +392,22 @@ return ..() /obj/item/circuitboard/computer/rdservercontrol - name = "R&D Server Control (Computer Board)" + name = "R&D server control (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/rdservercontrol /obj/item/circuitboard/computer/research - name = "Research Monitor (Computer Board)" + name = "research monitor (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/security/research /obj/item/circuitboard/computer/robotics - name = "Robotics Control (Computer Board)" + name = "robotics control (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/robotics /obj/item/circuitboard/computer/xenobiology - name = "circuit board (Xenobiology Console)" + name = "xenobiology console (Computer Board)" icon_state = "science" build_path = /obj/machinery/computer/camera_advanced/xenobio @@ -407,36 +416,36 @@ /obj/item/circuitboard/computer/labor_shuttle - name = "Labor Shuttle (Computer Board)" + name = "labor shuttle console (Computer Board)" icon_state = "security" - build_path = /obj/machinery/computer/shuttle/labor + build_path = /obj/machinery/computer/shuttle_flight/labor /obj/item/circuitboard/computer/labor_shuttle/one_way - name = "Prisoner Shuttle Console (Computer Board)" - build_path = /obj/machinery/computer/shuttle/labor/one_way + name = "prisoner shuttle console (Computer Board)" + build_path = /obj/machinery/computer/shuttle_flight/labor/one_way /obj/item/circuitboard/computer/gulag_teleporter_console - name = "Labor Camp teleporter console (Computer Board)" + name = "labor camp teleporter console (Computer Board)" icon_state = "security" build_path = /obj/machinery/computer/prisoner/gulag_teleporter_computer /obj/item/circuitboard/computer/prisoner - name = "Prisoner Management Console (Computer Board)" + name = "prisoner management console (Computer Board)" icon_state = "security" build_path = /obj/machinery/computer/prisoner /obj/item/circuitboard/computer/secure_data - name = "Security Records Console (Computer Board)" + name = "security records console (Computer Board)" icon_state = "security" build_path = /obj/machinery/computer/secure_data /obj/item/circuitboard/computer/security - name = "Security Cameras (Computer Board)" + name = "security camera console (Computer Board)" icon_state = "security" build_path = /obj/machinery/computer/security /obj/item/circuitboard/computer/warrant - name = "Security Warrant Viewer (Computer Board)" + name = "security warrant console (Computer Board)" icon_state = "security" build_path = /obj/machinery/computer/warrant @@ -445,14 +454,18 @@ //Supply +/obj/item/circuitboard/computer/objective + name = "Nanotrasen objective console (Computer Board)" + icon_state = "supply" + build_path = /obj/machinery/computer/objective /obj/item/circuitboard/computer/bounty - name = "Nanotrasen Bounty Console (Computer Board)" + name = "Nanotrasen bounty console (Computer Board)" icon_state = "supply" build_path = /obj/machinery/computer/bounty /obj/item/circuitboard/computer/cargo - name = "Supply Console (Computer Board)" + name = "supply console (Computer Board)" icon_state = "supply" build_path = /obj/machinery/computer/cargo var/contraband = FALSE @@ -471,7 +484,7 @@ to_chat(user, "You adjust [src]'s routing and receiver spectrum, unlocking special supplies and contraband.") /obj/item/circuitboard/computer/cargo/express - name = "Express Supply Console (Computer Board)" + name = "express supply console (Computer Board)" icon_state = "supply" build_path = /obj/machinery/computer/cargo/express @@ -487,29 +500,33 @@ obj_flags |= EMAGGED /obj/item/circuitboard/computer/cargo/request - name = "Supply Request Console (Computer Board)" + name = "supply request console (Computer Board)" icon_state = "supply" build_path = /obj/machinery/computer/cargo/request /obj/item/circuitboard/computer/ferry - name = "Transport Ferry (Computer Board)" + name = "transport ferry control (Computer Board)" icon_state = "supply" - build_path = /obj/machinery/computer/shuttle/ferry + build_path = /obj/machinery/computer/shuttle_flight/ferry /obj/item/circuitboard/computer/ferry/request - name = "Transport Ferry Console (Computer Board)" - build_path = /obj/machinery/computer/shuttle/ferry/request + name = "transport ferry console (Computer Board)" + build_path = /obj/machinery/computer/shuttle_flight/ferry/request /obj/item/circuitboard/computer/mining - name = "Outpost Status Display (Computer Board)" + name = "outpost status display (Computer Board)" icon_state = "supply" build_path = /obj/machinery/computer/security/mining /obj/item/circuitboard/computer/mining_shuttle - name = "Mining Shuttle (Computer Board)" + name = "mining shuttle console (Computer Board)" icon_state = "supply" - build_path = /obj/machinery/computer/shuttle/mining + build_path = /obj/machinery/computer/shuttle_flight/mining /obj/item/circuitboard/computer/science_shuttle - name = "Science Shuttle (Computer Board)" - build_path = /obj/machinery/computer/shuttle/science + name = "science shuttle console (Computer Board)" + build_path = /obj/machinery/computer/shuttle_flight/science + +/obj/item/circuitboard/computer/exploration_shuttle + name = "exploration shuttle console (Computer Board)" + build_path = /obj/machinery/computer/shuttle_flight/custom_shuttle/exploration diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 9cef0bf3ed95f..7f4049fbad308 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -2,7 +2,7 @@ /obj/item/circuitboard/machine/bsa/back - name = "Bluespace Artillery Generator (Machine Board)" + name = "bluespace artillery generator (Machine Board)" icon_state = "command" build_path = /obj/machinery/bsa/back //No freebies! req_components = list( @@ -10,7 +10,7 @@ /obj/item/stack/cable_coil = 2) /obj/item/circuitboard/machine/bsa/middle - name = "Bluespace Artillery Fusor (Machine Board)" + name = "bluespace artillery fusor (Machine Board)" icon_state = "command" build_path = /obj/machinery/bsa/middle req_components = list( @@ -18,7 +18,7 @@ /obj/item/stack/cable_coil = 2) /obj/item/circuitboard/machine/bsa/front - name = "Bluespace Artillery Bore (Machine Board)" + name = "bluespace artillery bore (Machine Board)" icon_state = "command" build_path = /obj/machinery/bsa/front req_components = list( @@ -26,7 +26,7 @@ /obj/item/stack/cable_coil = 2) /obj/item/circuitboard/machine/dna_vault - name = "DNA Vault (Machine Board)" + name = "DNA vault (Machine Board)" icon_state = "command" build_path = /obj/machinery/dna_vault //No freebies! req_components = list( @@ -39,7 +39,7 @@ /obj/item/circuitboard/machine/announcement_system - name = "Announcement System (Machine Board)" + name = "announcement system (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/announcement_system req_components = list( @@ -47,23 +47,23 @@ /obj/item/stack/sheet/glass = 1) /obj/item/circuitboard/machine/autolathe - name = "Autolathe (Machine Board)" + name = "autolathe (Machine Board)" icon_state = "engineering" - build_path = /obj/machinery/autolathe + build_path = /obj/machinery/modular_fabricator/autolathe req_components = list( /obj/item/stock_parts/matter_bin = 3, /obj/item/stock_parts/manipulator = 1, /obj/item/stack/sheet/glass = 1) /obj/item/circuitboard/machine/grounding_rod - name = "Grounding Rod (Machine Board)" + name = "grounding rod (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/power/grounding_rod req_components = list(/obj/item/stock_parts/capacitor = 1) needs_anchored = FALSE /obj/item/circuitboard/machine/telecomms/broadcaster - name = "Subspace Broadcaster (Machine Board)" + name = "subspace broadcaster (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/telecomms/broadcaster req_components = list( @@ -74,7 +74,7 @@ /obj/item/stock_parts/micro_laser = 2) /obj/item/circuitboard/machine/telecomms/bus - name = "Bus Mainframe (Machine Board)" + name = "bus mainframe (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/telecomms/bus req_components = list( @@ -83,7 +83,7 @@ /obj/item/stock_parts/subspace/filter = 1) /obj/item/circuitboard/machine/telecomms/hub - name = "Hub Mainframe (Machine Board)" + name = "hub mainframe (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/telecomms/hub req_components = list( @@ -92,7 +92,7 @@ /obj/item/stock_parts/subspace/filter = 2) /obj/item/circuitboard/machine/telecomms/processor - name = "Processor Unit (Machine Board)" + name = "processor unit (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/telecomms/processor req_components = list( @@ -104,7 +104,7 @@ /obj/item/stock_parts/subspace/amplifier = 1) /obj/item/circuitboard/machine/telecomms/receiver - name = "Subspace Receiver (Machine Board)" + name = "subspace receiver (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/telecomms/receiver req_components = list( @@ -114,7 +114,7 @@ /obj/item/stock_parts/micro_laser = 1) /obj/item/circuitboard/machine/telecomms/relay - name = "Relay Mainframe (Machine Board)" + name = "relay mainframe (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/telecomms/relay req_components = list( @@ -123,7 +123,7 @@ /obj/item/stock_parts/subspace/filter = 2) /obj/item/circuitboard/machine/telecomms/server - name = "Telecommunication Server (Machine Board)" + name = "telecommunication server (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/telecomms/server req_components = list( @@ -132,7 +132,7 @@ /obj/item/stock_parts/subspace/filter = 1) /obj/item/circuitboard/machine/telecomms/message_server - name = "Messaging Server (Machine Board)" + name = "messaging server (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/telecomms/message_server req_components = list( @@ -141,7 +141,7 @@ /obj/item/stock_parts/subspace/filter = 3) /obj/item/circuitboard/machine/tesla_coil - name = "Tesla Controller (Machine Board)" + name = "tesla controller (Machine Board)" icon_state = "engineering" desc = "You can use a screwdriver to switch between Research and Power Generation." build_path = /obj/machinery/power/tesla_coil @@ -175,11 +175,11 @@ return ..() /obj/item/circuitboard/machine/tesla_coil/power - name = "Tesla Coil (Machine Board)" + name = "tesla coil (Machine Board)" build_path = PATH_POWERCOIL /obj/item/circuitboard/machine/tesla_coil/research - name = "Tesla Corona Researcher (Machine Board)" + name = "tesla corona researcher (Machine Board)" build_path = PATH_RPCOIL #undef PATH_POWERCOIL @@ -187,20 +187,20 @@ /obj/item/circuitboard/machine/cell_charger - name = "Cell Charger (Machine Board)" + name = "cell charger (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/cell_charger req_components = list(/obj/item/stock_parts/capacitor = 1) needs_anchored = FALSE /obj/item/circuitboard/machine/circulator - name = "Circulator/Heat Exchanger (Machine Board)" + name = "circulator/heat exchanger (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/atmospherics/components/binary/circulator req_components = list() /obj/item/circuitboard/machine/emitter - name = "Emitter (Machine Board)" + name = "emitter (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/power/emitter req_components = list( @@ -209,13 +209,13 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/generator - name = "Thermo-Electric Generator (Machine Board)" + name = "thermo-electric generator (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/power/generator req_components = list() /obj/item/circuitboard/machine/ntnet_relay - name = "NTNet Relay (Machine Board)" + name = "NTNet relay (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/ntnet_relay req_components = list( @@ -223,7 +223,7 @@ /obj/item/stock_parts/subspace/filter = 1) /obj/item/circuitboard/machine/pacman - name = "PACMAN-type Generator (Machine Board)" + name = "PACMAN-type generator (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/power/port_gen/pacman req_components = list( @@ -234,15 +234,15 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/pacman/super - name = "SUPERPACMAN-type Generator (Machine Board)" + name = "SUPERPACMAN-type generator (Machine Board)" build_path = /obj/machinery/power/port_gen/pacman/super /obj/item/circuitboard/machine/pacman/mrs - name = "MRSPACMAN-type Generator (Machine Board)" + name = "MRSPACMAN-type generator (Machine Board)" build_path = /obj/machinery/power/port_gen/pacman/mrs /obj/item/circuitboard/machine/power_compressor - name = "Power Compressor (Machine Board)" + name = "power compressor (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/power/compressor req_components = list( @@ -250,7 +250,7 @@ /obj/item/stock_parts/manipulator = 6) /obj/item/circuitboard/machine/power_turbine - name = "Power Turbine (Machine Board)" + name = "power turbine (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/power/turbine req_components = list( @@ -258,12 +258,12 @@ /obj/item/stock_parts/capacitor = 6) /obj/item/circuitboard/machine/protolathe/department/engineering - name = "Departmental Protolathe (Machine Board) - Engineering" + name = "departmental protolathe - engineering (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/rnd/production/protolathe/department/engineering /obj/item/circuitboard/machine/rad_collector - name = "Radiation Collector (Machine Board)" + name = "radiation collector (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/power/rad_collector req_components = list( @@ -284,7 +284,7 @@ /obj/item/stack/sheet/mineral/uranium = 10) // We have no Pu-238, and this is the closest thing to it. /obj/item/circuitboard/machine/rtg/advanced - name = "Advanced RTG (Machine Board)" + name = "advanced RTG (Machine Board)" build_path = /obj/machinery/power/rtg/advanced req_components = list( /obj/item/stack/cable_coil = 5, @@ -294,33 +294,33 @@ /obj/item/stack/sheet/mineral/plasma = 5) /obj/item/circuitboard/machine/shuttle/engine - name = "Thruster (Machine Board)" + name = "thruster (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/shuttle/engine req_components = list() /obj/item/circuitboard/machine/shuttle/engine/plasma - name = "Plasma Thruster (Machine Board)" + name = "plasma thruster (Machine Board)" build_path = /obj/machinery/shuttle/engine/plasma req_components = list(/obj/item/stock_parts/capacitor = 2, /obj/item/stack/cable_coil = 5, /obj/item/stock_parts/micro_laser = 1) /obj/item/circuitboard/machine/shuttle/engine/void - name = "Void Thruster (Machine Board)" + name = "void thruster (Machine Board)" build_path = /obj/machinery/shuttle/engine/void req_components = list(/obj/item/stock_parts/capacitor/quadratic = 2, /obj/item/stack/cable_coil = 5, /obj/item/stock_parts/micro_laser/quadultra = 1) /obj/item/circuitboard/machine/shuttle/heater - name = "Electronic Engine Heater (Machine Board)" + name = "electronic engine heater (Machine Board)" build_path = /obj/machinery/atmospherics/components/unary/shuttle/heater req_components = list(/obj/item/stock_parts/micro_laser = 2, /obj/item/stock_parts/matter_bin = 1) /obj/item/circuitboard/machine/scanner_gate - name = "Scanner Gate (Machine Board)" + name = "scanner gate (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/scanner_gate req_components = list( @@ -337,12 +337,12 @@ def_components = list(/obj/item/stock_parts/cell = /obj/item/stock_parts/cell/high/empty) /obj/item/circuitboard/machine/techfab/department/engineering - name = "\improper Departmental Techfab (Machine Board) - Engineering" + name = "departmental techfab - engineering (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/rnd/production/techfab/department/engineering /obj/item/circuitboard/machine/teleporter_hub - name = "Teleporter Hub (Machine Board)" + name = "teleporter hub (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/teleport/hub req_components = list( @@ -351,7 +351,7 @@ def_components = list(/obj/item/stack/ore/bluespace_crystal = /obj/item/stack/ore/bluespace_crystal/artificial) /obj/item/circuitboard/machine/teleporter_station - name = "Teleporter Station (Machine Board)" + name = "teleporter station (Machine Board)" icon_state = "engineering" build_path = /obj/machinery/teleport/station req_components = list( @@ -361,9 +361,9 @@ def_components = list(/obj/item/stack/ore/bluespace_crystal = /obj/item/stack/ore/bluespace_crystal/artificial) /obj/item/circuitboard/machine/thermomachine - name = "Thermomachine (Machine Board)" + name = "thermomachine (Machine Board)" icon_state = "engineering" - desc = "You can use a screwdriver to switch between heater and freezer." + build_path = /obj/machinery/atmospherics/components/unary/thermomachine/freezer var/pipe_layer = PIPING_LAYER_DEFAULT req_components = list( /obj/item/stock_parts/matter_bin = 2, @@ -371,64 +371,21 @@ /obj/item/stack/cable_coil = 1, /obj/item/stack/sheet/glass = 1) -#define PATH_FREEZER /obj/machinery/atmospherics/components/unary/thermomachine/freezer -#define PATH_HEATER /obj/machinery/atmospherics/components/unary/thermomachine/heater - -/obj/item/circuitboard/machine/thermomachine/Initialize() +/obj/item/circuitboard/machine/thermomachine/multitool_act(mob/living/user, obj/item/multitool/I) . = ..() - if(!build_path) - if(prob(50)) - name = "Freezer (Machine Board)" - build_path = PATH_FREEZER - else - name = "Heater (Machine Board)" - build_path = PATH_HEATER - -/obj/item/circuitboard/machine/thermomachine/attackby(obj/item/I, mob/user, params) - if(I.tool_behaviour == TOOL_SCREWDRIVER) - var/obj/item/circuitboard/new_type - var/new_setting - switch(build_path) - if(PATH_FREEZER) - new_type = /obj/item/circuitboard/machine/thermomachine/heater - new_setting = "Heater" - if(PATH_HEATER) - new_type = /obj/item/circuitboard/machine/thermomachine/freezer - new_setting = "Freezer" - name = initial(new_type.name) - build_path = initial(new_type.build_path) - I.play_tool_sound(src) - to_chat(user, "You change the circuitboard setting to \"[new_setting]\".") - return - - if(I.tool_behaviour == TOOL_MULTITOOL) + if(istype(I)) pipe_layer = (pipe_layer >= PIPING_LAYER_MAX) ? PIPING_LAYER_MIN : (pipe_layer + 1) to_chat(user, "You change the circuitboard to layer [pipe_layer].") - return - - . = ..() /obj/item/circuitboard/machine/thermomachine/examine() . = ..() . += "It is set to layer [pipe_layer]." -/obj/item/circuitboard/machine/thermomachine/heater - name = "Heater (Machine Board)" - build_path = PATH_HEATER - -/obj/item/circuitboard/machine/thermomachine/freezer - name = "Freezer (Machine Board)" - build_path = PATH_FREEZER - -#undef PATH_FREEZER -#undef PATH_HEATER - - //Generic /obj/item/circuitboard/machine/circuit_imprinter - name = "Circuit Imprinter (Machine Board)" + name = "circuit imprinter (Machine Board)" icon_state = "generic" build_path = /obj/machinery/rnd/production/circuit_imprinter req_components = list( @@ -437,18 +394,18 @@ /obj/item/reagent_containers/glass/beaker = 2) /obj/item/circuitboard/machine/circuit_imprinter/department - name = "Departmental Circuit Imprinter (Machine Board)" + name = "departmental circuit imprinter (Machine Board)" build_path = /obj/machinery/rnd/production/circuit_imprinter/department /obj/item/circuitboard/machine/holopad - name = "AI Holopad (Machine Board)" + name = "AI holopad (Machine Board)" icon_state = "generic" build_path = /obj/machinery/holopad req_components = list(/obj/item/stock_parts/capacitor = 1) needs_anchored = FALSE //wew lad /obj/item/circuitboard/machine/launchpad - name = "Bluespace Launchpad (Machine Board)" + name = "bluespace launchpad (Machine Board)" icon_state = "generic" build_path = /obj/machinery/launchpad req_components = list( @@ -457,13 +414,13 @@ def_components = list(/obj/item/stack/ore/bluespace_crystal = /obj/item/stack/ore/bluespace_crystal/artificial) /obj/item/circuitboard/machine/paystand - name = "Pay Stand (Machine Board)" + name = "pay stand (Machine Board)" icon_state = "generic" build_path = /obj/machinery/paystand req_components = list() /obj/item/circuitboard/machine/protolathe - name = "Protolathe (Machine Board)" + name = "protolathe (Machine Board)" icon_state = "generic" build_path = /obj/machinery/rnd/production/protolathe req_components = list( @@ -472,11 +429,11 @@ /obj/item/reagent_containers/glass/beaker = 2) /obj/item/circuitboard/machine/protolathe/department - name = "Departmental Protolathe (Machine Board)" + name = "departmental protolathe (Machine Board)" build_path = /obj/machinery/rnd/production/protolathe/department /obj/item/circuitboard/machine/reagentgrinder - name = "Machine Design (All-In-One Grinder)" + name = "all-in-one grinder (Machine Board)" icon_state = "generic" build_path = /obj/machinery/reagentgrinder/constructed req_components = list( @@ -484,7 +441,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/smartfridge - name = "Smartfridge (Machine Board)" + name = "smartfridge (Machine Board)" icon_state = "generic" build_path = /obj/machinery/smartfridge req_components = list(/obj/item/stock_parts/matter_bin = 1) @@ -518,7 +475,7 @@ /obj/item/circuitboard/machine/space_heater - name = "Space Heater (Machine Board)" + name = "space heater (Machine Board)" icon_state = "generic" build_path = /obj/machinery/space_heater req_components = list( @@ -528,7 +485,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/techfab - name = "\improper Techfab (Machine Board)" + name = "techfab (Machine Board)" icon_state = "generic" build_path = /obj/machinery/rnd/production/techfab req_components = list( @@ -537,11 +494,11 @@ /obj/item/reagent_containers/glass/beaker = 2) /obj/item/circuitboard/machine/techfab/department - name = "\improper Departmental Techfab (Machine Board)" + name = "departmental techfab (Machine Board)" build_path = /obj/machinery/rnd/production/techfab/department /obj/item/circuitboard/machine/vendor - name = "Custom Vendor (Machine Board)" + name = "custom vendor (Machine Board)" icon_state = "generic" desc = "You can turn the \"brand selection\" dial using a screwdriver." custom_premium_price = 30 @@ -557,6 +514,7 @@ /obj/machinery/vending/games = "\improper Good Clean Fun", /obj/machinery/vending/autodrobe = "AutoDrobe", /obj/machinery/vending/wardrobe/sec_wardrobe = "SecDrobe", + /obj/machinery/vending/wardrobe/det_wardrobe = "DetDrobe", /obj/machinery/vending/wardrobe/medi_wardrobe = "MediDrobe", /obj/machinery/vending/wardrobe/engi_wardrobe = "EngiDrobe", /obj/machinery/vending/wardrobe/atmos_wardrobe = "AtmosDrobe", @@ -617,7 +575,7 @@ /obj/item/circuitboard/machine/vending/donksofttoyvendor - name = "Donksoft Toy Vendor (Machine Board)" + name = "Donksoft toy vendor (Machine Board)" icon_state = "generic" build_path = /obj/machinery/vending/donksofttoyvendor req_components = list( @@ -625,7 +583,7 @@ /obj/item/vending_refill/donksoft = 1) /obj/item/circuitboard/machine/vending/syndicatedonksofttoyvendor - name = "Syndicate Donksoft Toy Vendor (Machine Board)" + name = "Syndicate Donksoft toy vendor (Machine Board)" icon_state = "generic" build_path = /obj/machinery/vending/toyliberationstation req_components = list( @@ -636,19 +594,8 @@ //Medical -/obj/item/circuitboard/machine/autodoc - name = "Autodoc (Machine Board)" - icon_state = "medical" - build_path = /obj/machinery/autodoc - req_components = list(/obj/item/scalpel/advanced = 1, - /obj/item/retractor/advanced = 1, - /obj/item/surgicaldrill/advanced = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 3, - /obj/item/stock_parts/matter_bin = 1) - /obj/item/circuitboard/machine/chem_dispenser - name = "Chem Dispenser (Machine Board)" + name = "chem dispenser (Machine Board)" icon_state = "medical" build_path = /obj/machinery/chem_dispenser req_components = list( @@ -661,7 +608,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/chem_dispenser/botany //probably should be generic but who cares - name = "Minor Botanical Chem Dispenser (Machine Board)" + name = "minor botanical chem dispenser (Machine Board)" build_path = /obj/machinery/chem_dispenser/mutagensaltpetersmall req_components = list( /obj/item/stock_parts/matter_bin = 2, @@ -672,8 +619,41 @@ def_components = list(/obj/item/stock_parts/cell = /obj/item/stock_parts/cell/high) needs_anchored = FALSE +/obj/item/circuitboard/machine/chem_dispenser/fullupgrade + build_path = /obj/machinery/chem_dispenser/fullupgrade + req_components = list( + /obj/item/stock_parts/matter_bin/bluespace = 2, + /obj/item/stock_parts/capacitor/quadratic = 2, + /obj/item/stock_parts/manipulator/femto = 2, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/cell/bluespace = 1, + ) + +/obj/item/circuitboard/machine/chem_dispenser/mutagensaltpeter + build_path = /obj/machinery/chem_dispenser/mutagensaltpeter + req_components = list( + /obj/item/stock_parts/matter_bin/bluespace = 2, + /obj/item/stock_parts/capacitor/quadratic = 2, + /obj/item/stock_parts/manipulator/femto = 2, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/cell/bluespace = 1, + ) + +/obj/item/circuitboard/machine/chem_dispenser/abductor + name = "Reagent Synthesizer (Abductor Machine Board)" + icon_state = "abductor_mod" + build_path = /obj/machinery/chem_dispenser/abductor + req_components = list( + /obj/item/stock_parts/matter_bin/bluespace = 2, + /obj/item/stock_parts/capacitor/quadratic = 2, + /obj/item/stock_parts/manipulator/femto = 2, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/cell/bluespace = 1, + ) + needs_anchored = FALSE + /obj/item/circuitboard/machine/chem_heater - name = "Chemical Heater (Machine Board)" + name = "chemical heater (Machine Board)" icon_state = "medical" build_path = /obj/machinery/chem_heater req_components = list( @@ -707,7 +687,7 @@ return ..() /obj/item/circuitboard/machine/clonepod - name = "Clone Pod (Machine Board)" + name = "clone pod (Machine Board)" icon_state = "medical" build_path = /obj/machinery/clonepod req_components = list( @@ -718,11 +698,11 @@ /obj/item/reagent_containers/glass/beaker = 2) /obj/item/circuitboard/machine/clonepod/experimental - name = "Experimental Clone Pod (Machine Board)" + name = "experimental clone pod (Machine Board)" build_path = /obj/machinery/clonepod/experimental /obj/item/circuitboard/machine/clonescanner - name = "Cloning Scanner (Machine Board)" + name = "cloning scanner (Machine Board)" icon_state = "medical" build_path = /obj/machinery/dna_scannernew req_components = list( @@ -733,7 +713,7 @@ /obj/item/stack/cable_coil = 2) /obj/item/circuitboard/machine/cryo_tube - name = "Cryotube (Machine Board)" + name = "cryotube (Machine Board)" icon_state = "medical" build_path = /obj/machinery/atmospherics/components/unary/cryo_cell req_components = list( @@ -742,7 +722,7 @@ /obj/item/stack/sheet/glass = 4) /obj/item/circuitboard/machine/fat_sucker - name = "Lipid Extractor (Machine Board)" + name = "lipid extractor (Machine Board)" icon_state = "medical" build_path = /obj/machinery/fat_sucker req_components = list(/obj/item/stock_parts/micro_laser = 2, @@ -751,13 +731,13 @@ /obj/item/scalpel = 1) /obj/item/circuitboard/machine/harvester - name = "Harvester (Machine Board)" + name = "harvester (Machine Board)" icon_state = "medical" build_path = /obj/machinery/harvester req_components = list(/obj/item/stock_parts/micro_laser = 4) /obj/item/circuitboard/machine/limbgrower - name = "Limb Grower (Machine Board)" + name = "limb grower (Machine Board)" icon_state = "medical" build_path = /obj/machinery/limbgrower req_components = list( @@ -766,12 +746,12 @@ /obj/item/stack/sheet/glass = 1) /obj/item/circuitboard/machine/protolathe/department/medical - name = "Departmental Protolathe (Machine Board) - Medical" + name = "departmental protolathe - medical (Machine Board)" icon_state = "medical" build_path = /obj/machinery/rnd/production/protolathe/department/medical /obj/item/circuitboard/machine/sleeper - name = "Sleeper (Machine Board)" + name = "sleeper (Machine Board)" icon_state = "medical" build_path = /obj/machinery/sleeper req_components = list( @@ -780,8 +760,18 @@ /obj/item/stack/cable_coil = 1, /obj/item/stack/sheet/glass = 2) +/obj/item/circuitboard/machine/sleeper/fullupgrade + name = "Sleeper (Machine Board)" + icon_state = "medical" + build_path = /obj/machinery/sleeper/syndie/fullupgrade + req_components = list( + /obj/item/stock_parts/matter_bin/bluespace = 1, + /obj/item/stock_parts/manipulator/femto = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 2) + /obj/item/circuitboard/machine/smoke_machine - name = "Smoke Machine (Machine Board)" + name = "smoke machine (Machine Board)" icon_state = "medical" build_path = /obj/machinery/smoke_machine req_components = list( @@ -793,7 +783,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/stasis - name = "Lifeform Stasis Unit (Machine Board)" + name = "lifeform stasis unit (Machine Board)" icon_state = "medical" build_path = /obj/machinery/stasis req_components = list( @@ -802,32 +792,20 @@ /obj/item/stock_parts/capacitor = 1) /obj/item/circuitboard/machine/techfab/department/medical - name = "\improper Departmental Techfab (Machine Board) - Medical" + name = "departmental techfab - medical (Machine Board) " icon_state = "medical" build_path = /obj/machinery/rnd/production/techfab/department/medical //Science -/obj/item/circuitboard/machine/bluespace_miner - name = "Bluespace Miner (Machine Board)" - icon_state = "science" - build_path = /obj/machinery/mineral/bluespace_miner - req_components = list( - /obj/item/stock_parts/matter_bin = 3, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stock_parts/manipulator = 3, - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stack/ore/bluespace_crystal = 3) - needs_anchored = FALSE - /obj/item/circuitboard/machine/circuit_imprinter/department/science - name = "Departmental Circuit Imprinter - Science (Machine Board)" + name = "departmental circuit imprinter - science (Machine Board)" icon_state = "science" build_path = /obj/machinery/rnd/production/circuit_imprinter/department/science /obj/item/circuitboard/machine/cyborgrecharger - name = "Cyborg Recharger (Machine Board)" + name = "cyborg recharger (Machine Board)" icon_state = "science" build_path = /obj/machinery/recharge_station req_components = list( @@ -837,7 +815,7 @@ def_components = list(/obj/item/stock_parts/cell = /obj/item/stock_parts/cell/high) /obj/item/circuitboard/machine/destructive_analyzer - name = "Destructive Analyzer (Machine Board)" + name = "destructive analyzer (Machine Board)" icon_state = "science" build_path = /obj/machinery/rnd/destructive_analyzer req_components = list( @@ -855,7 +833,7 @@ /obj/item/stock_parts/micro_laser = 2) /obj/item/circuitboard/machine/mech_recharger - name = "Mechbay Recharger (Machine Board)" + name = "mechbay recharger (Machine Board)" icon_state = "science" build_path = /obj/machinery/mech_bay_recharge_port req_components = list( @@ -863,9 +841,9 @@ /obj/item/stock_parts/capacitor = 5) /obj/item/circuitboard/machine/mechfab - name = "Exosuit Fabricator (Machine Board)" + name = "exosuit fabricator (Machine Board)" icon_state = "science" - build_path = /obj/machinery/mecha_part_fabricator + build_path = /obj/machinery/modular_fabricator/exosuit_fab req_components = list( /obj/item/stock_parts/matter_bin = 2, /obj/item/stock_parts/manipulator = 1, @@ -873,7 +851,7 @@ /obj/item/stack/sheet/glass = 1) /obj/item/circuitboard/machine/monkey_recycler - name = "Monkey Recycler (Machine Board)" + name = "monkey recycler (Machine Board)" icon_state = "science" build_path = /obj/machinery/monkey_recycler req_components = list( @@ -882,7 +860,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/nanite_chamber - name = "Nanite Chamber (Machine Board)" + name = "nanite chamber (Machine Board)" icon_state = "science" build_path = /obj/machinery/nanite_chamber req_components = list( @@ -891,7 +869,7 @@ /obj/item/stock_parts/manipulator = 1) /obj/item/circuitboard/machine/nanite_program_hub - name = "Nanite Program Hub (Machine Board)" + name = "nanite program hub (Machine Board)" icon_state = "science" build_path = /obj/machinery/nanite_program_hub req_components = list( @@ -899,7 +877,7 @@ /obj/item/stock_parts/manipulator = 1) /obj/item/circuitboard/machine/nanite_programmer - name = "Nanite Programmer (Machine Board)" + name = "nanite programmer (Machine Board)" icon_state = "science" build_path = /obj/machinery/nanite_programmer req_components = list( @@ -908,17 +886,17 @@ /obj/item/stock_parts/scanning_module = 1) /obj/item/circuitboard/machine/processor/slime - name = "Slime Processor (Machine Board)" + name = "slime processor (Machine Board)" icon_state = "science" build_path = /obj/machinery/processor/slime /obj/item/circuitboard/machine/protolathe/department/science - name = "Departmental Protolathe (Machine Board) - Science" + name = "departmental protolathe - science (Machine Board)" icon_state = "science" build_path = /obj/machinery/rnd/production/protolathe/department/science /obj/item/circuitboard/machine/public_nanite_chamber - name = "Public Nanite Chamber (Machine Board)" + name = "public nanite chamber (Machine Board)" icon_state = "science" build_path = /obj/machinery/public_nanite_chamber var/cloud_id = 1 @@ -938,7 +916,7 @@ /obj/item/circuitboard/machine/quantumpad - name = "Quantum Pad (Machine Board)" + name = "quantum pad (Machine Board)" icon_state = "science" build_path = /obj/machinery/quantumpad req_components = list( @@ -949,7 +927,7 @@ def_components = list(/obj/item/stack/ore/bluespace_crystal = /obj/item/stack/ore/bluespace_crystal/artificial) /obj/item/circuitboard/machine/rdserver - name = "R&D Server (Machine Board)" + name = "R&D server (Machine Board)" icon_state = "science" build_path = /obj/machinery/rnd/server req_components = list( @@ -957,7 +935,7 @@ /obj/item/stock_parts/scanning_module = 1) /obj/item/circuitboard/machine/techfab/department/science - name = "\improper Departmental Techfab (Machine Board) - Science" + name = "departmental techfab - science (Machine Board)" icon_state = "science" build_path = /obj/machinery/rnd/production/techfab/department/science @@ -966,19 +944,19 @@ /obj/item/circuitboard/machine/protolathe/department/security - name = "Departmental Protolathe (Machine Board) - Security" + name = "departmental protolathe - security (Machine Board)" icon_state = "security" build_path = /obj/machinery/rnd/production/protolathe/department/security /obj/item/circuitboard/machine/recharger - name = "Weapon Recharger (Machine Board)" + name = "weapon recharger (Machine Board)" icon_state = "security" build_path = /obj/machinery/recharger req_components = list(/obj/item/stock_parts/capacitor = 1) needs_anchored = FALSE /obj/item/circuitboard/machine/techfab/department/security - name = "\improper Departmental Techfab (Machine Board) - Security" + name = "departmental techfab - security (Machine Board)" icon_state = "security" build_path = /obj/machinery/rnd/production/techfab/department/security @@ -987,7 +965,7 @@ /obj/item/circuitboard/machine/biogenerator - name = "Biogenerator (Machine Board)" + name = "biogenerator (Machine Board)" icon_state = "service" build_path = /obj/machinery/biogenerator req_components = list( @@ -997,29 +975,49 @@ /obj/item/stack/sheet/glass = 1) /obj/item/circuitboard/machine/chem_dispenser/drinks - name = "Soda Dispenser (Machine Board)" + name = "soda dispenser (Machine Board)" icon_state = "service" build_path = /obj/machinery/chem_dispenser/drinks +/obj/item/circuitboard/machine/chem_dispenser/drinks/fullupgrade + build_path = /obj/machinery/chem_dispenser/drinks/fullupgrade + req_components = list( + /obj/item/stock_parts/matter_bin/bluespace = 2, + /obj/item/stock_parts/capacitor/quadratic = 2, + /obj/item/stock_parts/manipulator/femto = 2, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/cell/bluespace = 1, + ) + /obj/item/circuitboard/machine/chem_dispenser/drinks/beer - name = "Booze Dispenser (Machine Board)" + name = "booze dispenser (Machine Board)" icon_state = "service" build_path = /obj/machinery/chem_dispenser/drinks/beer +/obj/item/circuitboard/machine/chem_dispenser/drinks/beer/fullupgrade + build_path = /obj/machinery/chem_dispenser/drinks/beer/fullupgrade + req_components = list( + /obj/item/stock_parts/matter_bin/bluespace = 2, + /obj/item/stock_parts/capacitor/quadratic = 2, + /obj/item/stock_parts/manipulator/femto = 2, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/cell/bluespace = 1, + ) + /obj/item/circuitboard/machine/chem_master/condi name = "CondiMaster 3000 (Machine Board)" icon_state = "service" build_path = /obj/machinery/chem_master/condimaster /obj/item/circuitboard/machine/deep_fryer - name = "circuit board (Deep Fryer)" + name = "deep fryer (Machine Board)" icon_state = "service" build_path = /obj/machinery/deepfryer req_components = list(/obj/item/stock_parts/micro_laser = 1) needs_anchored = FALSE /obj/item/circuitboard/machine/dish_drive - name = "Dish Drive (Machine Board)" + name = "dish drive (Machine Board)" icon_state = "service" build_path = /obj/machinery/dish_drive req_components = list( @@ -1032,8 +1030,8 @@ /obj/item/circuitboard/machine/dish_drive/examine(mob/user) . = ..() - . += {"Its suction function is [suction ? "enabled" : "disabled"]. Use it in-hand to switch.\n - Its disposal auto-transmit function is [transmit ? "enabled" : "disabled"]. Alt-click it to switch."} + . += "Its suction function is [suction ? "enabled" : "disabled"]. Use it in-hand to switch.\n"+\ + "Its disposal auto-transmit function is [transmit ? "enabled" : "disabled"]. Alt-click it to switch." /obj/item/circuitboard/machine/dish_drive/attack_self(mob/living/user) suction = !suction @@ -1047,7 +1045,7 @@ /obj/item/circuitboard/machine/gibber - name = "Gibber (Machine Board)" + name = "gibber (Machine Board)" icon_state = "service" build_path = /obj/machinery/gibber req_components = list( @@ -1056,7 +1054,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/hydroponics - name = "Hydroponics Tray (Machine Board)" + name = "hydroponics tray (Machine Board)" icon_state = "service" build_path = /obj/machinery/hydroponics/constructable req_components = list( @@ -1066,7 +1064,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/microwave - name = "Microwave (Machine Board)" + name = "microwave (Machine Board)" icon_state = "service" build_path = /obj/machinery/microwave req_components = list( @@ -1077,7 +1075,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/plantgenes - name = "Plant DNA Manipulator (Machine Board)" + name = "plant DNA manipulator (Machine Board)" icon_state = "service" build_path = /obj/machinery/plantgenes req_components = list( @@ -1087,7 +1085,7 @@ /obj/item/stock_parts/scanning_module = 1) /obj/item/circuitboard/machine/processor - name = "Food Processor (Machine Board)" + name = "food processor (Machine Board)" icon_state = "service" build_path = /obj/machinery/processor req_components = list( @@ -1109,12 +1107,12 @@ return ..() /obj/item/circuitboard/machine/protolathe/department/service - name = "Departmental Protolathe - Service (Machine Board)" + name = "departmental protolathe - service (Machine Board)" icon_state = "service" build_path = /obj/machinery/rnd/production/protolathe/department/service /obj/item/circuitboard/machine/recycler - name = "Recycler (Machine Board)" + name = "recycler (Machine Board)" icon_state = "service" build_path = /obj/machinery/recycler req_components = list( @@ -1123,7 +1121,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/seed_extractor - name = "Seed Extractor (Machine Board)" + name = "seed extractor (Machine Board)" icon_state = "service" build_path = /obj/machinery/seed_extractor req_components = list( @@ -1132,7 +1130,7 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/techfab/department/service - name = "\improper Departmental Techfab - Service (Machine Board)" + name = "departmental techfab - service (Machine Board)" icon_state = "service" build_path = /obj/machinery/rnd/production/techfab/department/service @@ -1141,24 +1139,33 @@ /obj/item/circuitboard/machine/techfab/department/cargo - name = "\improper Departmental Techfab (Machine Board) - Cargo" + name = "departmental techfab - cargo (Machine Board)" icon_state = "supply" build_path = /obj/machinery/rnd/production/techfab/department/cargo /obj/item/circuitboard/machine/mining_equipment_vendor - name = "Mining Equipment Vendor (Machine Board)" + name = "mining equipment vendor (Machine Board)" + icon_state = "supply" + build_path = /obj/machinery/vendor/mining + req_components = list( + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/matter_bin = 3) + +/obj/item/circuitboard/machine/exploration_equipment_vendor + name = "exploration equipment vendor (Machine Board)" icon_state = "supply" - build_path = /obj/machinery/mineral/equipment_vendor + build_path = /obj/machinery/vendor/exploration req_components = list( /obj/item/stack/sheet/glass = 1, /obj/item/stock_parts/matter_bin = 3) + /obj/item/circuitboard/machine/mining_equipment_vendor/golem - name = "Golem Ship Equipment Vendor (Machine Board)" - build_path = /obj/machinery/mineral/equipment_vendor/golem + name = "golem ship equipment vendor (Machine Board)" + build_path = /obj/machinery/vendor/mining/golem /obj/item/circuitboard/machine/pump - name = "Portable Liquid Pump (Machine Board)" + name = "portable liquid pump (Machine Board)" icon_state = "supply" build_path = /obj/machinery/power/liquid_pump needs_anchored = FALSE @@ -1167,7 +1174,7 @@ /obj/item/stock_parts/matter_bin = 2) /obj/item/circuitboard/machine/ore_redemption - name = "Ore Redemption (Machine Board)" + name = "ore redemption (Machine Board)" icon_state = "supply" build_path = /obj/machinery/mineral/ore_redemption req_components = list( @@ -1179,18 +1186,18 @@ needs_anchored = FALSE /obj/item/circuitboard/machine/ore_silo - name = "Ore Silo (Machine Board)" + name = "ore silo (Machine Board)" icon_state = "supply" build_path = /obj/machinery/ore_silo req_components = list() /obj/item/circuitboard/machine/protolathe/department/cargo - name = "Departmental Protolathe (Machine Board) - Cargo" + name = "departmental protolathe - cargo (Machine Board)" icon_state = "supply" build_path = /obj/machinery/rnd/production/protolathe/department/cargo /obj/item/circuitboard/machine/stacking_machine - name = "Stacking Machine (Machine Board)" + name = "stacking machine (Machine Board)" icon_state = "supply" build_path = /obj/machinery/mineral/stacking_machine req_components = list( @@ -1198,7 +1205,7 @@ /obj/item/stock_parts/matter_bin = 2) /obj/item/circuitboard/machine/stacking_unit_console - name = "Stacking Machine Console (Machine Board)" + name = "stacking machine console (Machine Board)" icon_state = "supply" build_path = /obj/machinery/mineral/stacking_unit_console req_components = list( @@ -1225,14 +1232,14 @@ /obj/item/stock_parts/micro_laser = /obj/item/stock_parts/micro_laser/quadultra) /obj/item/circuitboard/machine/chem_dispenser/abductor - name = "Reagent Synthesizer (Abductor Machine Board)" + name = "reagent synthesizer (Abductor Machine Board)" icon_state = "abductor_mod" build_path = /obj/machinery/chem_dispenser/abductor def_components = list(/obj/item/stock_parts/cell = /obj/item/stock_parts/cell/high) needs_anchored = FALSE /obj/item/circuitboard/machine/plantgenes/vault - name = "alien board (Plant DNA Manipulator)" + name = "Plant DNA manipulator (Abductor Machine Board)" icon_state = "abductor_mod" // It wasn't made by actual abductors race, so no abductor tech here. def_components = list( diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm index d9357ef07d2c3..7982ea193f448 100644 --- a/code/game/objects/items/clown_items.dm +++ b/code/game/objects/items/clown_items.dm @@ -123,7 +123,7 @@ user.visible_message("[user] begins to clean \the [target.name] with [src]...", "You begin to clean \the [target.name] with [src]...") if(do_after(user, src.cleanspeed, target = target)) to_chat(user, "You clean \the [target.name].") - if(istype(target, /obj/item/clothing) && HAS_TRAIT(target, TRAIT_SPRAYPAINTED)) + if(isclothing(target) && HAS_TRAIT(target, TRAIT_SPRAYPAINTED)) var/obj/item/clothing/C = target var/mob/living/carbon/human/H = user C.flash_protect -= 1 @@ -201,8 +201,7 @@ ..() /obj/item/bikehorn/golden/proc/flip_mobs(mob/living/carbon/M, mob/user) - var/turf/T = get_turf(src) - for(M in ohearers(7, T)) + for(M in ohearers(7, get_turf(src))) if(ishuman(M) && M.can_hear()) var/mob/living/carbon/human/H = M if(istype(H.ears, /obj/item/clothing/ears/earmuffs)) diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm index be39bb697344f..129cd18cc3f49 100644 --- a/code/game/objects/items/control_wand.dm +++ b/code/game/objects/items/control_wand.dm @@ -28,7 +28,7 @@ mode = WAND_EMERGENCY if(WAND_EMERGENCY) mode = WAND_OPEN - to_chat(user, "Now in mode: [mode].") + balloon_alert(user, "Mode set to [mode]") // Airlock remote works by sending NTNet packets to whatever it's pointed at. /obj/item/door_remote/afterattack(atom/A, mob/user) diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm index cf1551d1cd8a7..3b3c3c8a4be3d 100644 --- a/code/game/objects/items/cosmetics.dm +++ b/code/game/objects/items/cosmetics.dm @@ -137,24 +137,10 @@ if(location == BODY_ZONE_PRECISE_MOUTH) if(user.a_intent == INTENT_HELP) if(H.gender == MALE) - if(H == user && !mirror) - to_chat(user, "You need a mirror to properly style your own facial hair!") - return - if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - return - var/new_style = input(user, "Select a facial hair style", "Grooming") as null|anything in GLOB.facial_hair_styles_list - if(!get_location_accessible(H, location)) - to_chat(user, "The mask is in the way!") - return - user.visible_message("[user] tries to change [H]'s facial hair style using [src].", "You try to change [H]'s facial hair style using [src].") - if(new_style && do_after(user, 60, target = H)) - user.visible_message("[user] successfully changes [H]'s facial hair style using [src].", "You successfully change [H]'s facial hair style using [src].") - H.facial_hair_style = new_style - H.update_hair() - return + INVOKE_ASYNC(src, .proc/new_facial_hairstyle, H, user, mirror) + return else return - else if(!(FACEHAIR in H.dna.species.species_traits)) to_chat(user, "There is no facial hair to shave!") @@ -183,22 +169,8 @@ else if(location == BODY_ZONE_HEAD) if(user.a_intent == INTENT_HELP) - if (H == user && !mirror) - to_chat(user, "You need a mirror to properly style your own hair!") - return - if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - return - var/new_style = input(user, "Select a hair style", "Grooming") as null|anything in GLOB.hair_styles_list - if(!get_location_accessible(H, location)) - to_chat(user, "The headgear is in the way!") - return - user.visible_message("[user] tries to change [H]'s hairstyle using [src].", "You try to change [H]'s hairstyle using [src].") - if(new_style && do_after(user, 60, target = H)) - user.visible_message("[user] successfully changes [H]'s hairstyle using [src].", "You successfully change [H]'s hairstyle using [src].") - H.hair_style = new_style - H.update_hair() - return - + INVOKE_ASYNC(src, .proc/new_hairstyle, H, user) + return else if(!(HAIR in H.dna.species.species_traits)) to_chat(user, "There is no hair to shave!") @@ -231,6 +203,40 @@ else ..() +/obj/item/razor/proc/new_hairstyle(mob/living/carbon/human/H, mob/user, mirror) + var/location = user.zone_selected + if (H == user && !mirror) + to_chat(user, "You need a mirror to properly style your own hair!") + return + if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + var/new_style = input(user, "Select a hair style", "Grooming") as null|anything in GLOB.hair_styles_list + if(!get_location_accessible(H, location)) + to_chat(user, "The headgear is in the way!") + return + user.visible_message("[user] tries to change [H]'s hairstyle using [src].", "You try to change [H]'s hairstyle using [src].") + if(new_style && do_after(user, 60, target = H)) + user.visible_message("[user] successfully changes [H]'s hairstyle using [src].", "You successfully change [H]'s hairstyle using [src].") + H.hair_style = new_style + H.update_hair() + +/obj/item/razor/proc/new_facial_hairstyle(mob/living/carbon/human/H, mob/user, var/mirror) + var/location = user.zone_selected + if(H == user && !mirror) + to_chat(user, "You need a mirror to properly style your own facial hair!") + return + if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + var/new_style = input(user, "Select a facial hair style", "Grooming") as null|anything in GLOB.facial_hair_styles_list + if(!get_location_accessible(H, location)) + to_chat(user, "The mask is in the way!") + return + user.visible_message("[user] tries to change [H]'s facial hair style using [src].", "You try to change [H]'s facial hair style using [src].") + if(new_style && do_after(user, 60, target = H)) + user.visible_message("[user] successfully changes [H]'s facial hair style using [src].", "You successfully change [H]'s facial hair style using [src].") + H.facial_hair_style = new_style + H.update_hair() + /obj/item/razor/straightrazor name = "straight razor" icon_state = "straightrazor" @@ -298,4 +304,4 @@ ADD_TRAIT(user, TRAIT_SELF_AWARE, "mirror_trait") to_chat(user, "You look into the mirror") sleep(150) - REMOVE_TRAIT(user, TRAIT_SELF_AWARE, "mirror_trait") \ No newline at end of file + REMOVE_TRAIT(user, TRAIT_SELF_AWARE, "mirror_trait") diff --git a/code/game/objects/items/crab17.dm b/code/game/objects/items/crab17.dm index b38aa06f8ee19..8ff5a36c9dbfe 100644 --- a/code/game/objects/items/crab17.dm +++ b/code/game/objects/items/crab17.dm @@ -17,19 +17,19 @@ if(alert(user, "Are you sure you want to crash this market with no survivors?", "Protocol CRAB-17", "Yes", "No") == "Yes") if(dumped || QDELETED(src)) //Prevents fuckers from cheesing alert return FALSE - var/turf/targetturf = get_safe_random_station_turf() + var/turf/targetturf = get_safe_random_station_turfs() if (!targetturf) return FALSE new /obj/effect/dumpeetTarget(targetturf, user) dumped = TRUE /obj/structure/checkoutmachine - name = "Nanotrasen Space-Coin Market" + name = "\improper Nanotrasen Space-Coin Market" desc = "This is good for spacecoin because" icon = 'icons/obj/money_machine.dmi' icon_state = "bogdanoff" layer = TABLE_LAYER //So that the crate inside doesn't appear underneath - armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 80) + armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 80, "stamina" = 0) density = TRUE pixel_z = -8 layer = LARGE_MOB_LAYER @@ -80,7 +80,7 @@ add_overlay("hatch") add_overlay("legs_retracted") addtimer(CALLBACK(src, .proc/startUp), 50) - QDEL_IN(src, 8 MINUTES) //Self destruct after 8 min + QDEL_IN(WEAKREF(src), 8 MINUTES) //Self destruct after 8 min /obj/structure/checkoutmachine/proc/startUp() //very VERY snowflake code that adds a neat animation when the pod lands. @@ -149,7 +149,7 @@ /obj/structure/checkoutmachine/Destroy() stop_dumping() STOP_PROCESSING(SSfastprocess, src) - priority_announce("The credit deposit machine at [get_area(src).name] has been destroyed. Station funds have stopped draining!", sender_override = "CRAB-17 Protocol") + priority_announce("The credit deposit machine at [get_area(src)] has been destroyed. Station funds have stopped draining!", sound = SSstation.announcer.get_rand_alert_sound(), sender_override = "CRAB-17 Protocol", ) explosion(src, 0,0,1, flame_range = 2) return ..() @@ -171,7 +171,7 @@ var/datum/bank_account/account = bogdanoff.get_bank_account() if (account) // get_bank_account() may return FALSE account.transfer_money(B, amount) - B.bank_card_talk("You have lost [percentage_lost * 100]% of your funds! A spacecoin credit deposit machine is located at: [get_area(src).name].") + B.bank_card_talk("You have lost [percentage_lost * 100]% of your funds! A spacecoin credit deposit machine is located at: [get_area(src)].") addtimer(CALLBACK(src, .proc/dump), 150) //Drain every 15 seconds /obj/structure/checkoutmachine/process() @@ -189,7 +189,7 @@ icon = 'icons/obj/money_machine_64.dmi' pixel_z = 300 desc = "Get out of the way!" - layer = FLY_LAYER//that wasnt flying, that was falling with style! + layer = FLY_LAYER//that wasn't flying, that was falling with style! icon_state = "missile_blur" /obj/effect/dumpeetTarget @@ -216,7 +216,7 @@ /obj/effect/dumpeetTarget/proc/startLaunch() DF = new /obj/effect/dumpeetFall(drop_location()) dump = new /obj/structure/checkoutmachine(null, bogdanoff) - priority_announce("The spacecoin bubble has popped! Get to the credit deposit machine at [get_area(src).name] and cash out before you lose all of your funds!", sender_override = "CRAB-17 Protocol") + priority_announce("The spacecoin bubble has popped! Get to the credit deposit machine at [get_area(src)] and cash out before you lose all of your funds!", sound = SSstation.announcer.get_rand_alert_sound(), sender_override = "CRAB-17 Protocol") animate(DF, pixel_z = -8, time = 5, , easing = LINEAR_EASING) playsound(src, 'sound/weapons/mortar_whistle.ogg', 70, 1, 6) addtimer(CALLBACK(src, .proc/endLaunch), 5, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index ca05bcf7a655e..8441e642b6038 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -157,6 +157,7 @@ is_capped = !is_capped to_chat(user, "The cap on [src] is now [is_capped ? "on" : "off"].") update_icon() + ui_update() /obj/item/toy/crayon/proc/staticDrawables() @@ -250,7 +251,9 @@ . = TRUE paint_mode = PAINT_NORMAL drawtype = "a" - update_icon() + + if(.) + update_icon() /obj/item/toy/crayon/proc/crayon_text_strip(text) var/static/regex/crayon_r = new /regex(@"[^\w!?,.=%#&+\/\-]") @@ -272,14 +275,8 @@ var/mob/living/carbon/human/H = user if (HAS_TRAIT(H, TRAIT_TAGGER)) cost *= 0.5 - /* hippie start -- moved to the end of the proc, after the crayon is actually used. - var/charges_used = use_charges(user, cost) - if(!charges_used) - return - . = charges_used - hippie end */ - if(istype(target, /obj/effect/decal/cleanable)) + if(istype(target, /obj/effect/decal)) target = target.loc if(!isValidSurface(target)) @@ -321,8 +318,7 @@ else if(drawing in graffiti|oriented) temp = "graffiti" var/gang_check = hippie_gang_check(user,target) // hippie start -- gang check and temp setting - if(!gang_check) return - else if(gang_check == "gang graffiti") temp = gang_check // hippie end + if(!gang_check) return // hippie end var/graf_rot if(drawing in oriented) @@ -344,9 +340,6 @@ clickx = CLAMP(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2) clicky = CLAMP(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2) - if(!instant) - to_chat(user, "You start drawing a [temp] on the [target.name]...") // hippie -- removed a weird tab that had no reason to be here - if(pre_noise) audible_message("You hear spraying.") playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) @@ -354,9 +347,13 @@ var/wait_time = 50 if(paint_mode == PAINT_LARGE_HORIZONTAL) wait_time *= 3 - if(gang) instant = FALSE // hippie -- gang spraying must not be instant, balance reasons + if(gang) + wait_time = 1 SECONDS + if (territory_claimed(get_area(target), user)) + wait_time = 20 SECONDS if(!instant) - if(!do_after(user, 50, target = target)) + to_chat(user, "You start drawing a [temp] on the [target.name]...") // hippie -- removed a weird tab that had no reason to be here + if(!do_after(user, wait_time, target = target)) return if(length(text_buffer)) @@ -546,6 +543,19 @@ return return ..() +/obj/item/storage/crayons/attack_self(mob/user) + . = ..() + if(contents.len > 0) + to_chat(user, "You can't fold down [src] with crayons inside!") + return + if(flags_1 & HOLOGRAM_1) + return + + var/obj/item/stack/sheet/cardboard/cardboard = new /obj/item/stack/sheet/cardboard(user.drop_location()) + to_chat(user, "You fold the [src] into cardboard.") + user.put_in_active_hand(cardboard) + qdel(src) + //Spraycan stuff /obj/item/toy/crayon/spraycan @@ -626,8 +636,11 @@ return if(is_capped) - to_chat(user, "Take the cap off first!") - return + if(istype(target, /obj/machinery/modular_fabricator/autolathe)) + return ..() + else + to_chat(user, "Take the cap off first!") + return if(check_empty(user)) return @@ -643,7 +656,7 @@ if(C.client) C.blur_eyes(3) C.blind_eyes(1) - if(C.get_eye_protection() <= 0) // no eye protection? ARGH IT BURNS. + if(!C.is_eyes_covered()) // no eye protection? ARGH IT BURNS. C.confused = max(C.confused, 3) C.Paralyze(60) if(ishuman(C) && actually_paints) @@ -658,10 +671,10 @@ return - if(isobj(target)) + if(isobj(target) && !(target.flags_1 & UNPAINTABLE_1)) if(actually_paints) if(color_hex2num(paint_color) < 350 && !istype(target, /obj/structure/window)) //Colors too dark are rejected - if(istype(target, /obj/item/clothing)) + if(isclothing(target)) var/obj/item/clothing/C = target if(((C.flags_cover & HEADCOVERSEYES) || (C.flags_cover & MASKCOVERSEYES) || (C.flags_cover & GLASSESCOVERSEYES)) && !HAS_TRAIT(C, TRAIT_SPRAYPAINTED)) C.flash_protect += 1 @@ -786,34 +799,35 @@ to_chat(user, "This spraycan's color isn't your gang's one! You cannot use it.") return FALSE gang_mode = TRUE - instant = FALSE . = "graffiti" // discontinue if we're not in gang modethe area isn't valid for tagging because gang "honour" - if(gang_mode && (!can_claim_for_gang(user, target))) + if(gang_mode && (!can_claim_for_gang(user, target, TRUE))) return FALSE return TRUE /obj/item/toy/crayon/proc/gang_final(mob/user, atom/target, list/affected_turfs) // hooked into afterattack // Double check it wasn't tagged in the meanwhile - if(!can_claim_for_gang(user, target)) + if(!can_claim_for_gang(user, target, FALSE)) return TRUE tag_for_gang(user, target) affected_turfs += target -/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target) +/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target, alert) // Check area validity. // Reject space, player-created areas, and non-station z-levels. var/area/A = get_area(target) - if(!A || (!is_station_level(A.z)) || !A.valid_territory) + if(!A || (!is_station_level(A.z)) || !(A.area_flags & VALID_TERRITORY)) to_chat(user, "[A] is unsuitable for tagging.") return FALSE var/spraying_over = FALSE - for(var/G in target) - var/obj/effect/decal/cleanable/crayon/gang/gangtag = G + for(var/obj/effect/decal/gang/gangtag in target) if(istype(gangtag)) var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang) if(gangtag.gang != GA.gang) + if (alert) + to_chat(user, "[gangtag.gang] has been alerted of this takeover!") + gangtag.gang.message_gangtools("[get_area(target)] is under attack by an enemy gang!") spraying_over = TRUE break @@ -839,16 +853,19 @@ //Delete any old markings on this tile, including other gang tags for(var/obj/effect/decal/cleanable/crayon/old_marking in target) qdel(old_marking) + for(var/obj/effect/decal/gang/old_gang in target) + qdel(old_gang) var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) var/area/territory = get_area(target) - new /obj/effect/decal/cleanable/crayon/gang(target,G.gang,"graffiti",0,user) + new /obj/effect/decal/gang(target,G.gang,"graffiti",0,user) to_chat(user, "You tagged [territory] for your gang!") /obj/item/toy/crayon/spraycan/gang //desc = "A modified container containing suspicious paint." charges = 20 gang = TRUE + instant = FALSE pre_noise = FALSE post_noise = TRUE diff --git a/code/game/objects/items/credit_holochip.dm b/code/game/objects/items/credit_holochip.dm index 0223904b4fc30..0acb225772b64 100644 --- a/code/game/objects/items/credit_holochip.dm +++ b/code/game/objects/items/credit_holochip.dm @@ -15,8 +15,8 @@ /obj/item/holochip/examine(mob/user) . = ..() - . += {"It's loaded with [credits] credit[( credits > 1 ) ? "s" : ""]\n - Alt-Click to split."} + . += "It's loaded with [credits] credit[( credits > 1 ) ? "s" : ""]\n"+\ + "Alt-Click to split." /obj/item/holochip/get_item_credit_value() return credits diff --git a/code/game/objects/items/cursed_necklace.dm b/code/game/objects/items/cursed_necklace.dm index 18c00bee3f4d4..2745644f91497 100644 --- a/code/game/objects/items/cursed_necklace.dm +++ b/code/game/objects/items/cursed_necklace.dm @@ -14,10 +14,10 @@ /obj/item/clothing/neck/necklace/dope/cursed/equipped(mob/user, slot) . = ..() - if(slot == SLOT_NECK && linked_ckey && user.ckey != linked_ckey) + if(slot == ITEM_SLOT_NECK && linked_ckey && user.ckey != linked_ckey) if(user.ckey && user.ckey == linked_ckey) hostage_ckey = user.ckey - user.ghostize(0) + user.ghostize(FALSE,SENTIENCE_ERASE) user.ckey = linked_ckey current_body = user @@ -25,6 +25,6 @@ . = ..() if(hostage_ckey) if(user.ckey) - user.ghostize(0) + user.ghostize(FALSE,SENTIENCE_ERASE) user.ckey = hostage_ckey current_body = null \ No newline at end of file diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm index 7af94fb155d67..4cf1f5aa6b4d6 100644 --- a/code/game/objects/items/defib.dm +++ b/code/game/objects/items/defib.dm @@ -14,12 +14,13 @@ throwforce = 6 w_class = WEIGHT_CLASS_BULKY actions_types = list(/datum/action/item_action/toggle_paddles) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 0) + var/obj/item/shockpaddles/paddle_type = /obj/item/shockpaddles var/on = FALSE //if the paddles are equipped (1) or on the defib (0) var/safety = TRUE //if you can zap people with the defibs on harm mode var/powered = FALSE //if there's a cell in the defib with enough power for a revive, blocks paddles from reviving otherwise - var/obj/item/twohanded/shockpaddles/paddles + var/obj/item/shockpaddles/paddles var/obj/item/stock_parts/cell/high/cell var/combat = FALSE //if true, revive through hardsuits, allow for combat shocking, and tint paddles syndicate colors var/grab_ghost = TRUE // Do we pull the ghost back into their body? @@ -51,12 +52,9 @@ if(paddles?.loc == src) paddles.extinguish() -/obj/item/defibrillator/update_icon() - update_power() - update_overlays() - update_charge() +/obj/item/defibrillator/update_overlays() + . = ..() -/obj/item/defibrillator/proc/update_power() if(!QDELETED(cell)) if(QDELETED(paddles) || cell.charge < paddles.revivecost) powered = FALSE @@ -65,23 +63,17 @@ else powered = FALSE -/obj/item/defibrillator/proc/update_overlays() - cut_overlays() if(!on) - add_overlay("[initial(icon_state)]-paddles") + . += "[initial(icon_state)]-paddles" if(powered) - add_overlay("[initial(icon_state)]-powered") + . += "[initial(icon_state)]-powered" + var/ratio = cell.charge / cell.maxcharge + ratio = CEILING(ratio*4, 1) * 25 + . += "[initial(icon_state)]-charge[ratio]" if(!cell) - add_overlay("[initial(icon_state)]-nocell") + . += "[initial(icon_state)]-nocell" if(!safety) - add_overlay("[initial(icon_state)]-emagged") - -/obj/item/defibrillator/proc/update_charge() - if(powered) //so it doesn't show charge if it's unpowered - if(!QDELETED(cell)) - var/ratio = cell.charge / cell.maxcharge - ratio = CEILING(ratio*4, 1) * 25 - add_overlay("[initial(icon_state)]-charge[ratio]") + . += "[initial(icon_state)]-emagged" /obj/item/defibrillator/CheckParts(list/parts_list) ..() @@ -95,13 +87,13 @@ /obj/item/defibrillator/attack_hand(mob/user) if(loc == user) if(slot_flags == ITEM_SLOT_BACK) - if(user.get_item_by_slot(SLOT_BACK) == src) + if(user.get_item_by_slot(ITEM_SLOT_BACK) == src) ui_action_click() else to_chat(user, "Put the defibrillator on your back first!") else if(slot_flags == ITEM_SLOT_BELT) - if(user.get_item_by_slot(SLOT_BELT) == src) + if(user.get_item_by_slot(ITEM_SLOT_BELT) == src) ui_action_click() else to_chat(user, "Strap the defibrillator's belt on first!") @@ -114,13 +106,12 @@ . = ..() if(ismob(loc)) var/mob/M = loc - if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand)) - var/obj/screen/inventory/hand/H = over_object + if(!M.incapacitated() && istype(over_object, /atom/movable/screen/inventory/hand)) + var/atom/movable/screen/inventory/hand/H = over_object M.putItemFromInventoryInHandIfPossible(src, H.held_index) /obj/item/defibrillator/attackby(obj/item/W, mob/user, params) if(W == paddles) - paddles.unwield() toggle_paddles() else if(istype(W, /obj/item/stock_parts/cell)) var/obj/item/stock_parts/cell/C = W @@ -185,7 +176,6 @@ return else //Remove from their hands and back onto the defib unit - paddles.unwield() remove_paddles(user) update_icon() @@ -194,11 +184,11 @@ A.UpdateButtonIcon() /obj/item/defibrillator/proc/make_paddles() - return new /obj/item/twohanded/shockpaddles(src) + return new paddle_type(src) /obj/item/defibrillator/equipped(mob/user, slot) ..() - if((slot_flags == ITEM_SLOT_BACK && slot != SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != SLOT_BELT)) + if((slot_flags == ITEM_SLOT_BACK && slot != ITEM_SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != ITEM_SLOT_BELT)) remove_paddles(user) update_icon() @@ -272,6 +262,7 @@ combat = TRUE safety = FALSE cooldown_duration = 2.5 SECONDS + paddle_type = /obj/item/shockpaddles/syndicate /obj/item/defibrillator/compact/combat/loaded/Initialize() . = ..() @@ -281,14 +272,18 @@ /obj/item/defibrillator/compact/combat/loaded/attackby(obj/item/W, mob/user, params) if(W == paddles) - paddles.unwield() toggle_paddles() update_icon() return //paddles -/obj/item/twohanded/shockpaddles +///What caused the paddles to snap back? +#define SNAP_DROP 0 +#define SNAP_OVEREXTEND 1 +#define SNAP_INTERACT 2 + +/obj/item/shockpaddles name = "defibrillator paddles" desc = "A pair of plastic-gripped paddles with flat metal surfaces that are used to deliver powerful electric shocks." icon = 'icons/obj/defib.dmi' @@ -313,38 +308,50 @@ var/mob/listeningTo -/obj/item/twohanded/shockpaddles/equipped(mob/user, slot) + var/base_icon_state = "defibpaddles" + +/obj/item/shockpaddles/ComponentInitialize() . = ..() - if(!req_defib || listeningTo == user) + AddComponent(/datum/component/two_handed, force_unwielded=8, force_wielded=12) + +/obj/item/shockpaddles/Destroy() + defib = null + listeningTo = null + return ..() + +/obj/item/shockpaddles/equipped(mob/user, slot) + . = ..() + if(!req_defib) return - if(listeningTo) + if(listeningTo && listeningTo != user) UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED) RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/check_range) listeningTo = user - -/obj/item/twohanded/shockpaddles/Moved() - . = ..() check_range() +/obj/item/shockpaddles/Moved() + . = ..() + if(!istype(loc, /mob/living)) + check_range() -/obj/item/twohanded/shockpaddles/fire_act(exposed_temperature, exposed_volume) +/obj/item/shockpaddles/fire_act(exposed_temperature, exposed_volume) . = ..() if((req_defib && defib) && loc != defib) defib.fire_act(exposed_temperature, exposed_volume) -/obj/item/twohanded/shockpaddles/proc/check_range() - if(!req_defib) +/obj/item/shockpaddles/proc/check_range() + SIGNAL_HANDLER + + if(!req_defib || !defib) return if(!in_range(src,defib)) var/mob/living/L = loc if(istype(L)) - to_chat(L, "[defib]'s paddles overextend and come out of your hands!") - L.temporarilyRemoveItemFromInventory(src,TRUE) + snap_back(cause=SNAP_OVEREXTEND) else - visible_message("[src] snap back into [defib].") snap_back() -/obj/item/twohanded/shockpaddles/proc/recharge(var/time) +/obj/item/shockpaddles/proc/recharge(var/time) if(req_defib || !time) return cooldown = TRUE @@ -356,15 +363,19 @@ cooldown = FALSE update_icon() -/obj/item/twohanded/shockpaddles/New(mainunit) - ..() - if(check_defib_exists(mainunit, src) && req_defib) - defib = mainunit - forceMove(defib) - busy = FALSE - update_icon() +/obj/item/shockpaddles/Initialize() + . = ..() + ADD_TRAIT(src, TRAIT_NO_STORAGE_INSERT, GENERIC_ITEM_TRAIT) //stops shockpaddles from being inserted in BoH + if(!req_defib) + return //If it doesn't need a defib, just say it exists + if (!loc || !istype(loc, /obj/item/defibrillator)) //To avoid weird issues from admin spawns + return INITIALIZE_HINT_QDEL + defib = loc + busy = FALSE + update_icon() -/obj/item/twohanded/shockpaddles/update_icon() +/obj/item/shockpaddles/update_icon() + var/wielded = ISWIELDED(src) icon_state = "defibpaddles[wielded]" item_state = "defibpaddles[wielded]" if(cooldown) @@ -373,51 +384,56 @@ var/mob/living/carbon/C = loc C.update_inv_hands() -/obj/item/twohanded/shockpaddles/suicide_act(mob/user) +/obj/item/shockpaddles/suicide_act(mob/user) user.visible_message("[user] is putting the live paddles on [user.p_their()] chest! It looks like [user.p_theyre()] trying to commit suicide!") if(req_defib) defib.deductcharge(revivecost) playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1) return (OXYLOSS) -/obj/item/twohanded/shockpaddles/dropped(mob/user) +/obj/item/shockpaddles/dropped(mob/user) if(!req_defib) return ..() if(listeningTo) UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED) if(user) - var/obj/item/twohanded/offhand/O = user.get_inactive_held_item() - if(istype(O)) - O.unwield() - to_chat(user, "The paddles snap back into the main unit.") - snap_back() - return unwield(user) - -/obj/item/twohanded/shockpaddles/proc/snap_back() + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + if(!ismob(loc)) + snap_back() + return + +/obj/item/shockpaddles/proc/snap_back(cause=SNAP_DROP, silent=FALSE) if(!defib) return - defib.on = FALSE - forceMove(defib) - defib.update_icon() -/obj/item/twohanded/shockpaddles/proc/check_defib_exists(mainunit, mob/living/carbon/M, obj/O) - if(!req_defib) - return TRUE //If it doesn't need a defib, just say it exists - if (!mainunit || !istype(mainunit, /obj/item/defibrillator)) //To avoid weird issues from admin spawns - qdel(O) - return FALSE + if(ismob(loc)) + var/mob/M = loc + M.transferItemToLoc(src, defib) + if(!silent) + switch(cause) + if(SNAP_DROP) + to_chat(M, "The paddles snap back into the main unit.") + if(SNAP_OVEREXTEND) + to_chat(M, "[defib]'s paddles overextend and come out of your hands!") + if(SNAP_INTERACT) + to_chat(M, "You put back [src] into [defib]") else - return TRUE + if(!silent) + visible_message("[src] snaps back into [defib].") + forceMove(defib) -/obj/item/twohanded/shockpaddles/attack(mob/M, mob/user) + defib.on = FALSE + listeningTo = null + defib.update_icon() +/obj/item/shockpaddles/attack(mob/M, mob/user) if(busy) return if(req_defib && !defib.powered) user.visible_message("[defib] beeps: Unit is unpowered.") playsound(src, 'sound/machines/defib_failed.ogg', 50, 0) return - if(!wielded) + if(!ISWIELDED(src)) if(iscyborg(user)) to_chat(user, "You must activate the paddles in your active module before you can use them on someone!") else @@ -459,7 +475,7 @@ do_help(H, user) -/obj/item/twohanded/shockpaddles/proc/can_defib(mob/living/carbon/H) +/obj/item/shockpaddles/proc/can_defib(mob/living/carbon/H) var/obj/item/organ/heart = H.getorgan(/obj/item/organ/heart) if(H.suiciding || H.hellbound || HAS_TRAIT(H, TRAIT_HUSK)) return @@ -474,14 +490,14 @@ return return TRUE -/obj/item/twohanded/shockpaddles/proc/shock_touching(dmg, mob/H) +/obj/item/shockpaddles/proc/shock_touching(dmg, mob/H) if(isliving(H.pulledby)) //CLEAR! var/mob/living/M = H.pulledby if(M.electrocute_act(30, H)) M.visible_message("[M] is electrocuted by [M.p_their()] contact with [H]!") M.emote("scream") -/obj/item/twohanded/shockpaddles/proc/do_disarm(mob/living/M, mob/living/user) +/obj/item/shockpaddles/proc/do_disarm(mob/living/M, mob/living/user) if(req_defib && defib.safety) return if(!req_defib && !combat) @@ -507,7 +523,7 @@ else recharge(60) -/obj/item/twohanded/shockpaddles/proc/do_harm(mob/living/carbon/H, mob/living/user) +/obj/item/shockpaddles/proc/do_harm(mob/living/carbon/H, mob/living/user) if(req_defib && defib.safety) return if(!req_defib && !combat) @@ -562,7 +578,7 @@ busy = FALSE update_icon() -/obj/item/twohanded/shockpaddles/proc/do_help(mob/living/carbon/H, mob/living/user) +/obj/item/shockpaddles/proc/do_help(mob/living/carbon/H, mob/living/user) user.visible_message("[user] begins to place [src] on [H]'s chest.", "You begin to place [src] on [H]'s chest...") busy = TRUE update_icon() @@ -665,14 +681,14 @@ busy = FALSE update_icon() -/obj/item/twohanded/shockpaddles/cyborg +/obj/item/shockpaddles/cyborg name = "cyborg defibrillator paddles" icon = 'icons/obj/defib.dmi' icon_state = "defibpaddles0" item_state = "defibpaddles0" req_defib = FALSE -/obj/item/twohanded/shockpaddles/cyborg/attack(mob/M, mob/user) +/obj/item/shockpaddles/cyborg/attack(mob/M, mob/user) if(iscyborg(user)) var/mob/living/silicon/robot/R = user if(R.emagged) @@ -684,7 +700,7 @@ . = ..() -/obj/item/twohanded/shockpaddles/syndicate +/obj/item/shockpaddles/syndicate name = "syndicate defibrillator paddles" desc = "A pair of paddles used to revive deceased operatives. They possess both the ability to penetrate armor and to deliver powerful or disabling shocks offensively." combat = TRUE @@ -692,7 +708,11 @@ icon_state = "defibpaddles0" item_state = "defibpaddles0" -/obj/item/twohanded/shockpaddles/syndicate/cyborg +/obj/item/shockpaddles/syndicate/cyborg req_defib = FALSE +#undef SNAP_DROP +#undef SNAP_OVEREXTEND +#undef SNAP_INTERACT + #undef HALFWAYCRITDEATH diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index f7d5b5e82f99b..1abb372c34ab6 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -23,7 +23,7 @@ GLOBAL_LIST_EMPTY(PDAs) w_class = WEIGHT_CLASS_TINY slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT actions_types = list(/datum/action/item_action/toggle_light) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100, "stamina" = 0) resistance_flags = FIRE_PROOF | ACID_PROOF @@ -49,6 +49,7 @@ GLOBAL_LIST_EMPTY(PDAs) //Secondary variables var/scanmode = PDA_SCANNER_NONE var/fon = FALSE //Is the flashlight function on? + var/shorted = FALSE //Is the flashlight shorted out? var/f_lum = 2.3 //Luminosity for the flashlight function var/silent = FALSE //To beep or not to beep, that is the question var/toff = FALSE //If TRUE, messenger disabled @@ -76,7 +77,7 @@ GLOBAL_LIST_EMPTY(PDAs) var/datum/picture/picture //Scanned photo - var/list/contained_item = list(/obj/item/pen, /obj/item/toy/crayon, /obj/item/lipstick, /obj/item/flashlight/pen, /obj/item/clothing/mask/cigarette, /obj/item/electronic_assembly/small) + var/list/contained_item = list(/obj/item/pen, /obj/item/toy/crayon, /obj/item/lipstick, /obj/item/flashlight/pen, /obj/item/clothing/mask/cigarette) var/obj/item/inserted_item //Used for pen, crayon, and lipstick insertion or removal. Same as above. var/overlays_x_offset = 0 //x offset to use for certain overlays @@ -279,8 +280,6 @@ GLOBAL_LIST_EMPTY(PDAs) if (cartridge) if(cartridge.bot_access_flags) dat += "Crime | -Details | -Author | -Time Added | -
---|---|---|---|
[c.crimeName] | " - menu += "[c.crimeDetails] | " - menu += "[c.author] | " - menu += "[c.time] | " - menu += "
Author | Time Added | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[c.crimeName] | " menu += "[c.crimeDetails] | " menu += "[c.author] | " @@ -476,68 +461,6 @@ Code: menu += "No ore silo detected!" menu = jointext(menu, "") - if (49) //janitorial locator - menu = "
[entry] | [functions] | |||||||||||||||||||||||||||||||||||||||||||||
[entry] | [functions] |
Upper | Game 1 | Game 2 | Game 3 |
---|---|---|---|
Aces | \[___\] | \[___\] | \[___\] | Twos | \[___\] | \[___\] | \[___\] | Threes | \[___\] | \[___\] | \[___\] | Fours | \[___\] | \[___\] | \[___\] | Fives | \[___\] | \[___\] | \[___\] | Sixes | \[___\] | \[___\] | \[___\] | Total | \[___\] | \[___\] | \[___\] | Upper Total | \[___\] | \[___\] | \[___\] | Bonus | \[___\] | \[___\] | \[___\] | 1 Pair | \[___\] | \[___\] | \[___\] | 2 Pairs | \[___\] | \[___\] | \[___\] | 3 of a Kind | \[___\] | \[___\] | \[___\] | 4 of a Kind | \[___\] | \[___\] | \[___\] | Full House | \[___\] | \[___\] | \[___\] | Sm. Straight | \[___\] | \[___\] | \[___\] | Lg. Straight | \[___\] | \[___\] | \[___\] | Yatzy | \[___\] | \[___\] | \[___\] | Chance | \[___\] | \[___\] | \[___\] | Lower Total | \[___\] | \[___\] | \[___\] | Grand Total | \[___\] | \[___\] | \[___\] |
")
if(severity)
- data += " "
+ data += " "
data += "[timestamp] | [server_name] | [admin_key][secret ? " | - Secret" : ""]"
if(expire_timestamp)
data += " | Expires [expire_timestamp]"
@@ -661,7 +663,7 @@
qdel(query_message_read)
if("watchlist entry")
message_admins("Notice: [key_name_admin(target_ckey)] has been on the watchlist since [timestamp] and has just connected - Reason: [text]")
- send2irc_adminless_only("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]")
+ send2tgs_adminless_only("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]")
if("memo")
output += "Memo by [admin_key] on [timestamp]"
if(editor_key)
diff --git a/code/modules/admin/team_panel.dm b/code/modules/admin/team_panel.dm
index 139785f72e041..ce3aa031bfd34 100644
--- a/code/modules/admin/team_panel.dm
+++ b/code/modules/admin/team_panel.dm
@@ -147,11 +147,12 @@
//qdel maybe
/datum/team/proc/admin_add_member(mob/user)
- var/list/minds = list()
- for(var/mob/M in GLOB.mob_list)
- if(M.mind)
- minds |= M.mind
- var/datum/mind/value = input("Select new member:", "New team member", null) as null|anything in sortNames(minds)
+ var/list/candidates = list()
+ for(var/mob/M in GLOB.player_list)
+ if(M.mind?.special_role)
+ continue
+ candidates += M.mind
+ var/datum/mind/value = input("Select new member:", "New team member", null) as null|anything in sortNames(candidates)
if (!value)
return
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 3cac98d0ddb70..ce4d452ca7541 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -65,33 +65,37 @@
return
switch(href_list["makeAntag"])
if("traitors")
- if(src.makeTraitors())
- message_admins("[key_name_admin(usr)] created traitors.")
- log_admin("[key_name(usr)] created traitors.")
+ var/maxCount = input("Set number of Traitors","Set Traitor Count (max)",1) as num|null
+ if(src.makeTraitors(maxCount))
+ message_admins("[key_name_admin(usr)] created [maxCount] traitor(s).")
+ log_admin("[key_name(usr)] created [maxCount] traitor(s).")
else
- message_admins("[key_name_admin(usr)] tried to create traitors. Unfortunately, there were no candidates available.")
- log_admin("[key_name(usr)] failed to create traitors.")
+ message_admins("[key_name_admin(usr)] tried to create [maxCount] traitor(s). Unfortunately, there were no candidates available.")
+ log_admin("[key_name(usr)] failed to create [maxCount] traitor(s).")
if("changelings")
- if(src.makeChangelings())
- message_admins("[key_name(usr)] created changelings.")
- log_admin("[key_name(usr)] created changelings.")
+ var/maxCount = input("Set number of Changelings","Set Changeling Count (max)",1) as num|null
+ if(src.makeChangelings(maxCount))
+ message_admins("[key_name(usr)] created [maxCount] changelings.")
+ log_admin("[key_name(usr)] created [maxCount] changelings.")
else
- message_admins("[key_name_admin(usr)] tried to create changelings. Unfortunately, there were no candidates available.")
- log_admin("[key_name(usr)] failed to create changelings.")
+ message_admins("[key_name_admin(usr)] tried to create [maxCount] changelings. Unfortunately, there were no candidates available.")
+ log_admin("[key_name(usr)] failed to create [maxCount] changelings.")
if("revs")
- if(src.makeRevs())
- message_admins("[key_name(usr)] started a revolution.")
- log_admin("[key_name(usr)] started a revolution.")
+ var/maxCount = input("Set number of Revolutionaries","Set Revolutionaries Count (max)",1) as num|null
+ if(src.makeRevs(maxCount))
+ message_admins("[key_name(usr)] started a revolution with [maxCount] freedom fighters.")
+ log_admin("[key_name(usr)] started a [maxCount] freedom fighters.")
else
- message_admins("[key_name_admin(usr)] tried to start a revolution. Unfortunately, there were no candidates available.")
- log_admin("[key_name(usr)] failed to start a revolution.")
+ message_admins("[key_name_admin(usr)] tried to start a revolution with [maxCount] freedom fighters. Unfortunately, there were no candidates available.")
+ log_admin("[key_name(usr)] failed to start a revolution with [maxCount] freedom fighters.")
if("cult")
- if(src.makeCult())
- message_admins("[key_name(usr)] started a cult.")
- log_admin("[key_name(usr)] started a cult.")
+ var/maxCount = input("Set number of Cultists","Set Cultist Count (max)",1) as num|null
+ if(src.makeCult(maxCount))
+ message_admins("[key_name(usr)] started a cult with [maxCount] cultists.")
+ log_admin("[key_name(usr)] started a cult with [maxCount] cultists.")
else
- message_admins("[key_name_admin(usr)] tried to start a cult. Unfortunately, there were no candidates available.")
- log_admin("[key_name(usr)] failed to start a cult.")
+ message_admins("[key_name_admin(usr)] tried to start a cult with [maxCount] cultists. Unfortunately, there were no candidates available.")
+ log_admin("[key_name(usr)] failed to start a cult with [maxCount] cultists.")
if("wizard")
message_admins("[key_name(usr)] is creating a wizard...")
if(src.makeWizard())
@@ -102,12 +106,13 @@
log_admin("[key_name(usr)] failed to create a wizard.")
if("nukeops")
message_admins("[key_name(usr)] is creating a nuke team...")
- if(src.makeNukeTeam())
- message_admins("[key_name(usr)] created a nuke team.")
- log_admin("[key_name(usr)] created a nuke team.")
+ var/maxCount = input("Set number of Nuke OPs","Set Nuke OP Count (max)",5) as num|null
+ if(src.makeNukeTeam(maxCount))
+ message_admins("[key_name(usr)] created a nuke team with [maxCount] operatives")
+ log_admin("[key_name(usr)] created a nuke team with [maxCount] operatives")
else
- message_admins("[key_name_admin(usr)] tried to create a nuke team. Unfortunately, there were not enough candidates available.")
- log_admin("[key_name(usr)] failed to create a nuke team.")
+ message_admins("[key_name_admin(usr)] tried to create a nuke team with [maxCount] operatives Unfortunately, there were not enough candidates available.")
+ log_admin("[key_name(usr)] failed to create a nuke team with [maxCount] operatives.")
if("ninja")
message_admins("[key_name(usr)] spawned a ninja.")
log_admin("[key_name(usr)] spawned a ninja.")
@@ -145,7 +150,7 @@
message_admins("[key_name(usr)] created an abductor team.")
log_admin("[key_name(usr)] created an abductor team.")
else
- message_admins("[key_name_admin(usr)] tried to create an abductor team. Unfortunatly there were not enough candidates available.")
+ message_admins("[key_name_admin(usr)] tried to create an abductor team. Unfortunately there were not enough candidates available.")
log_admin("[key_name(usr)] failed to create an abductor team.")
if("revenant")
if(src.makeRevenant())
@@ -173,6 +178,7 @@
return
if("No")
event.announceChance = 0
+ event.on_admin_trigger()
event.processing = TRUE
message_admins("[key_name_admin(usr)] has triggered an event. ([E.name])")
log_admin("[key_name(usr)] has triggered an event. ([E.name])")
@@ -206,7 +212,7 @@
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
+ if(!SSticker.is_mode("dynamic"))
return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null)
var/roundstart_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart))
@@ -241,15 +247,16 @@
return
if(!SSticker || !SSticker.mode)
return alert(usr, "The game must start first.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
+ if(!SSticker.is_mode("dynamic"))
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/latejoin_rules = list()
+ var/datum/game_mode/dynamic/mode = SSticker.mode
for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin))
var/datum/dynamic_ruleset/latejoin/newrule = new rule()
+ mode.configure_ruleset(newrule)
latejoin_rules[newrule.name] = newrule
var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules
if (added_rule)
- var/datum/game_mode/dynamic/mode = SSticker.mode
mode.forced_latejoin_rule = latejoin_rules[added_rule]
log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.")
message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1)
@@ -270,15 +277,16 @@
return
if(!SSticker || !SSticker.mode)
return alert(usr, "The game must start first.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
+ if(!SSticker.is_mode("dynamic"))
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/midround_rules = list()
+ var/datum/game_mode/dynamic/mode = SSticker.mode
for (var/rule in subtypesof(/datum/dynamic_ruleset/midround))
var/datum/dynamic_ruleset/midround/newrule = new rule()
+ mode.configure_ruleset(newrule)
midround_rules[newrule.name] = rule
var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules
if (added_rule)
- var/datum/game_mode/dynamic/mode = SSticker.mode
log_admin("[key_name(usr)] executed the [added_rule] ruleset.")
message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1)
mode.picking_specific_rule(midround_rules[added_rule],1)
@@ -289,122 +297,16 @@
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
-
- dynamic_mode_options(usr)
-
- else if(href_list["f_dynamic_roundstart_centre"])
- if(!check_rights(R_ADMIN))
- return
- if(SSticker && SSticker.mode)
- return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
-
- var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number between -5 and +5 is allowed.", "Change curve centre", null) as num
- if (new_centre < -5 || new_centre > 5)
- return alert(usr, "Only values between -5 and +5 are allowed.", null, null, null, null)
-
- log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].")
- message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1)
- GLOB.dynamic_curve_centre = new_centre
- dynamic_mode_options(usr)
-
- else if(href_list["f_dynamic_roundstart_width"])
- if(!check_rights(R_ADMIN))
- return
- if(SSticker && SSticker.mode)
- return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
-
- var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num
- if (new_width < 0.5 || new_width > 4)
- return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null)
-
- log_admin("[key_name(usr)] changed the distribution curve width to [new_width].")
- message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1)
- GLOB.dynamic_curve_width = new_width
- dynamic_mode_options(usr)
-
- else if(href_list["f_dynamic_roundstart_latejoin_min"])
- if(!check_rights(R_ADMIN))
- return
- if(SSticker && SSticker.mode)
- return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
- var/new_min = input(usr,"Change the minimum delay of latejoin injection in minutes.", "Change latejoin injection delay minimum", null) as num
- if(new_min <= 0)
- return alert(usr, "The minimum can't be zero or lower.", null, null, null, null)
- if((new_min MINUTES) > GLOB.dynamic_latejoin_delay_max)
- return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null)
-
- log_admin("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes.")
- message_admins("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes", 1)
- GLOB.dynamic_latejoin_delay_min = (new_min MINUTES)
- dynamic_mode_options(usr)
-
- else if(href_list["f_dynamic_roundstart_latejoin_max"])
- if(!check_rights(R_ADMIN))
- return
- if(SSticker && SSticker.mode)
- return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
- var/new_max = input(usr,"Change the maximum delay of latejoin injection in minutes.", "Change latejoin injection delay maximum", null) as num
- if(new_max <= 0)
- return alert(usr, "The maximum can't be zero or lower.", null, null, null, null)
- if((new_max MINUTES) < GLOB.dynamic_latejoin_delay_min)
- return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null)
-
- log_admin("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes.")
- message_admins("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes", 1)
- GLOB.dynamic_latejoin_delay_max = (new_max MINUTES)
- dynamic_mode_options(usr)
-
- else if(href_list["f_dynamic_roundstart_midround_min"])
- if(!check_rights(R_ADMIN))
- return
- if(SSticker && SSticker.mode)
- return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
+ if(!SSticker.is_mode("dynamic"))
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
- var/new_min = input(usr,"Change the minimum delay of midround injection in minutes.", "Change midround injection delay minimum", null) as num
- if(new_min <= 0)
- return alert(usr, "The minimum can't be zero or lower.", null, null, null, null)
- if((new_min MINUTES) > GLOB.dynamic_midround_delay_max)
- return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null)
-
- log_admin("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes.")
- message_admins("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes", 1)
- GLOB.dynamic_midround_delay_min = (new_min MINUTES)
- dynamic_mode_options(usr)
- else if(href_list["f_dynamic_roundstart_midround_max"])
- if(!check_rights(R_ADMIN))
- return
- if(SSticker && SSticker.mode)
- return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
- var/new_max = input(usr,"Change the maximum delay of midround injection in minutes.", "Change midround injection delay maximum", null) as num
- if(new_max <= 0)
- return alert(usr, "The maximum can't be zero or lower.", null, null, null, null)
- if((new_max MINUTES) > GLOB.dynamic_midround_delay_max)
- return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null)
-
- log_admin("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes.")
- message_admins("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes", 1)
- GLOB.dynamic_midround_delay_max = (new_max MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_force_extended"])
if(!check_rights(R_ADMIN))
return
- if(GLOB.master_mode != "dynamic")
+ if(!SSticker.is_mode("dynamic"))
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended
@@ -416,7 +318,7 @@
if(!check_rights(R_ADMIN))
return
- if(GLOB.master_mode != "dynamic")
+ if(!SSticker.is_mode("dynamic"))
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking
@@ -424,23 +326,11 @@
message_admins("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].")
dynamic_mode_options(usr)
- else if(href_list["f_dynamic_classic_secret"])
- if(!check_rights(R_ADMIN))
- return
-
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
-
- GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret
- log_admin("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].")
- message_admins("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].")
- dynamic_mode_options(usr)
-
else if(href_list["f_dynamic_stacking_limit"])
if(!check_rights(R_ADMIN))
return
- if(GLOB.master_mode != "dynamic")
+ if(!SSticker.is_mode("dynamic"))
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num
@@ -448,25 +338,6 @@
message_admins("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].")
dynamic_mode_options(usr)
- else if(href_list["f_dynamic_high_pop_limit"])
- if(!check_rights(R_ADMIN))
- return
-
- if(SSticker && SSticker.mode)
- return alert(usr, "The game has already started.", null, null, null, null)
-
- if(GLOB.master_mode != "dynamic")
- return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
-
- var/new_value = input(usr, "Enter the high-pop override threshold for dynamic mode.", "High pop override") as num
- if (new_value < 0)
- return alert(usr, "Only positive values allowed!", null, null, null, null)
- GLOB.dynamic_high_pop_limit = new_value
-
- log_admin("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].")
- message_admins("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].")
- dynamic_mode_options(usr)
-
else if(href_list["f_dynamic_forced_threat"])
if(!check_rights(R_ADMIN))
return
@@ -474,7 +345,7 @@
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
- if(GLOB.master_mode != "dynamic")
+ if(!SSticker.is_mode("dynamic"))
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num
@@ -876,17 +747,20 @@
return
if (SSticker.HasRoundStarted())
- if (askuser(usr, "The game has already started. Would you like to save this as the default mode effective next round?", "Save mode", "Yes", "Cancel", Timeout = null) == 1)
- SSticker.save_mode(href_list["c_mode2"])
+ alert("The round has already started.")
+ HandleCMode()
+ return
+ if(SSticker.gamemode_hotswap_disabled)
+ alert("A gamemode has already loaded maps and cannot be changed!")
HandleCMode()
return
GLOB.master_mode = href_list["c_mode2"]
+ //Disable presetup so their gamemode gets loaded.
+ SSticker.pre_setup_completed = FALSE
log_admin("[key_name(usr)] set the mode as [GLOB.master_mode].")
message_admins("[key_name_admin(usr)] set the mode as [GLOB.master_mode].")
to_chat(world, "The mode is now: [GLOB.master_mode]")
Game() // updates the main game menu
- if (askuser(usr, "Would you like to save this as the default mode for the server?", "Save mode", "Yes", "No", Timeout = null) == 1)
- SSticker.save_mode(GLOB.master_mode)
HandleCMode()
else if(href_list["f_secret2"])
@@ -1103,8 +977,8 @@
if(ishuman(L))
var/mob/living/carbon/human/observer = L
- observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit/black(observer), SLOT_W_UNIFORM)
- observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/sneakers/black(observer), SLOT_SHOES)
+ observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit/black(observer), ITEM_SLOT_ICLOTHING)
+ observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/sneakers/black(observer), ITEM_SLOT_FEET)
L.Unconscious(100)
sleep(5)
L.forceMove(pick(GLOB.tdomeobserve))
@@ -1613,16 +1487,18 @@
R.activate_module(I)
if(pod)
- new /obj/effect/DPtarget(target, pod)
+ new /obj/effect/pod_landingzone(target, pod)
+
+ var/turf/T = get_turf(usr.loc) // get admin's LOC as a turf
if (number == 1)
- log_admin("[key_name(usr)] created a [english_list(paths)]")
+ log_admin("[key_name(usr)] created a [english_list(paths)] at [AREACOORD(T)]")
for(var/path in paths)
if(ispath(path, /mob))
message_admins("[key_name_admin(usr)] created a [english_list(paths)]")
break
else
- log_admin("[key_name(usr)] created [number]ea [english_list(paths)]")
+ log_admin("[key_name(usr)] created [number]ea [english_list(paths)] at [AREACOORD(T)]")
for(var/path in paths)
if(ispath(path, /mob))
message_admins("[key_name_admin(usr)] created [number]ea [english_list(paths)]")
@@ -1981,7 +1857,7 @@
return
if(!CONFIG_GET(string/centcom_ban_db))
- to_chat(usr, "Centcom Galactic Ban DB is disabled!")
+ to_chat(usr, "CentCom Galactic Ban DB is disabled!")
return
var/ckey = href_list["centcomlookup"]
@@ -2024,7 +1900,7 @@
dat += sanitize(jobs.Join(", "))
dat += " Client
"
dat += "
"
-
+
var/datum/browser/popup = new(usr, "centcomlookup-[ckey]", " ']' | '{' '}' | '(' expression ')' | call_function
//
-// bool_expression : expression comparitor expression [bool_operator bool_expression]
+// bool_expression : expression comparator expression [bool_operator bool_expression]
// expression : ( unary_expression | '(' expression ')' | value ) [binary_operator expression]
// expression_list : expression [',' expression_list]
// unary_expression : unary_operator ( unary_expression | value )
//
-// comparitor : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
+// comparator : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
// value : variable | string | number | 'null' | object_type | array | selectors_array
// unary_operator : '!' | '-' | '~'
-// binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
+// binary_operator : comparator | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
// bool_operator : 'AND' | '&&' | 'OR' | '||'
//
// array : '[' expression_list ']'
@@ -56,7 +56,7 @@
var/list/boolean_operators = list("and", "or", "&&", "||")
var/list/unary_operators = list("!", "-", "~")
var/list/binary_operators = list("+", "-", "/", "*", "&", "|", "^", "%")
- var/list/comparitors = list("=", "==", "!=", "<>", "<", "<=", ">", ">=")
+ var/list/comparators = list("=", "==", "!=", "<>", "<", "<=", ">", ">=")
/datum/SDQL_parser/New(query_list)
query = query_list
@@ -109,7 +109,7 @@
parse_error("Invalid option assignment symbol: [token(i + 1)]")
var/val = tokenl(i + 2)
if(!(val in SDQL2_VALID_OPTION_VALUES))
- parse_error("Invalid optoin value: [val]")
+ parse_error("Invalid option value: [val]")
assignment_list[type] = val
return (i + 3)
@@ -396,21 +396,21 @@
var/path = text2path(token(i))
if (path == null)
- return parse_error("Nonexistant type path: [token(i)]")
+ return parse_error("Nonexistent type path: [token(i)]")
node += path
return i + 1
-//comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
-/datum/SDQL_parser/proc/comparitor(i, list/node)
+//comparator: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
+/datum/SDQL_parser/proc/comparator(i, list/node)
if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">="))
node += token(i)
else
- parse_error("Unknown comparitor [token(i)]")
+ parse_error("Unknown comparator [token(i)]")
return i + 1
@@ -422,7 +422,7 @@
node += token(i)
else
- parse_error("Unknown comparitor [token(i)]")
+ parse_error("Unknown comparator [token(i)]")
return i + 1
@@ -551,7 +551,7 @@
i = binary_operator(i, node)
i = expression(i, node)
- else if(token(i) in comparitors)
+ else if(token(i) in comparators)
i = binary_operator(i, node)
var/list/rhs = list()
@@ -587,10 +587,10 @@
return i
-//binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
+//binary_operator: comparator | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
/datum/SDQL_parser/proc/binary_operator(i, list/node)
- if(token(i) in (binary_operators + comparitors))
+ if(token(i) in (binary_operators + comparators))
node += token(i)
else
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 857704c374dc8..d1dc12930a020 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -30,22 +30,11 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/list/closed_tickets = list()
var/list/resolved_tickets = list()
- var/obj/effect/statclick/ticket_list/browse_statclick = new(null, null, null)
- var/obj/effect/statclick/ticket_list/ustatclick = new(null, null, AHELP_UNCLAIMED)
- var/obj/effect/statclick/ticket_list/astatclick = new(null, null, AHELP_ACTIVE)
- var/obj/effect/statclick/ticket_list/cstatclick = new(null, null, AHELP_CLOSED)
- var/obj/effect/statclick/ticket_list/rstatclick = new(null, null, AHELP_RESOLVED)
-
/datum/admin_help_tickets/Destroy()
QDEL_LIST(unclaimed_tickets)
QDEL_LIST(active_tickets)
QDEL_LIST(closed_tickets)
QDEL_LIST(resolved_tickets)
- QDEL_NULL(browse_statclick)
- QDEL_NULL(ustatclick)
- QDEL_NULL(astatclick)
- QDEL_NULL(cstatclick)
- QDEL_NULL(rstatclick)
return ..()
/datum/admin_help_tickets/proc/TicketByID(id)
@@ -204,20 +193,47 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//Tickets statpanel
/datum/admin_help_tickets/proc/stat_entry()
+ var/list/tab_data = list()
+ tab_data["Tickets"] = list(
+ text = "Open Ticket Browser",
+ type = STAT_BUTTON,
+ action = "browsetickets",
+ )
+ tab_data["Active Tickets"] = list(
+ text = "[active_tickets.len]",
+ type = STAT_BUTTON,
+ action = "browsetickets",
+ )
var/num_disconnected = 0
- stat("", browse_statclick.update("Open Ticket Browser"))
- stat("Active Tickets:", astatclick.update("[active_tickets.len]"))
for(var/l in list(active_tickets, unclaimed_tickets))
for(var/I in l)
var/datum/admin_help/AH = I
if(AH.initiator)
- stat("#[AH.id]. [AH.initiator_key_name]:", AH.statclick.update())
+ tab_data["#[AH.id]. [AH.initiator_key_name]"] = list(
+ text = AH.name,
+ type = STAT_BUTTON,
+ action = "open_ticket",
+ params = list("id" = AH.id),
+ )
else
++num_disconnected
if(num_disconnected)
- stat("Disconnected:", astatclick.update("[num_disconnected]"))
- stat("Closed Tickets:", cstatclick.update("[closed_tickets.len]"))
- stat("Resolved Tickets:", rstatclick.update("[resolved_tickets.len]"))
+ tab_data["Disconnected"] = list(
+ text = "[num_disconnected]",
+ type = STAT_BUTTON,
+ action = "browsetickets",
+ )
+ tab_data["Closed Tickets"] = list(
+ text = "[closed_tickets.len]",
+ type = STAT_BUTTON,
+ action = "browsetickets",
+ )
+ tab_data["Resolved Tickets"] = list(
+ text = "[resolved_tickets.len]",
+ type = STAT_BUTTON,
+ action = "browsetickets",
+ )
+ return tab_data
//Reassociate still open ticket if one exists
/datum/admin_help_tickets/proc/ClientLogin(client/C)
@@ -241,20 +257,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(AH.initiator_ckey == ckey)
return AH
-//
-//TICKET LIST STATCLICK
-//
-
-/obj/effect/statclick/ticket_list
- var/current_state
-
-/obj/effect/statclick/ticket_list/New(loc, name, state)
- current_state = state
- ..()
-
-/obj/effect/statclick/ticket_list/Click()
- GLOB.ahelp_tickets.BrowseTickets(usr)
-
//
// Ticket interaction
//
@@ -265,6 +267,8 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/from_user = ""
var/to_user = ""
var/message = ""
+ var/from_user_safe
+ var/to_user_safe
/datum/ticket_interaction/New()
. = ..()
@@ -292,8 +296,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/list/_interactions //use AddInteraction() or, preferably, admin_ticket_log()
- var/obj/effect/statclick/ahelp/statclick
-
var/static/ticket_counter = 0
var/bwoink // is the ahelp player to admin (not bwoink) or admin to player (bwoink)
@@ -311,7 +313,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
id = ++ticket_counter
opened_at = world.time
- name = msg
+ name = copytext_char(msg, 1, 100)
initiator = C
initiator_ckey = initiator.ckey
@@ -324,23 +326,22 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
TimeoutVerb()
- statclick = new(null, src)
_interactions = list()
GLOB.ahelp_tickets.unclaimed_tickets += src
if(is_bwoink)
- AddInteraction("blue", name, usr.ckey, initiator_key_name)
+ AddInteraction("blue", name, usr.ckey, initiator_key_name, "Administrator", "You")
message_admins("Ticket [TicketHref("#[id]")] created")
Claim() //Auto claim bwoinks
else
MessageNoRecipient(msg)
- //send it to irc if nobody is on and tell us how many were on
- var/admin_number_present = send2irc_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
+ //send it to tgs if nobody is on and tell us how many were on
+ var/admin_number_present = send2tgs_adminless_only(initiator_ckey, "Ticket #[id]: [msg]")
log_admin_private("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.")
if(admin_number_present <= 0)
- to_chat(C, "No active admins are online, your adminhelp was sent to the admin irc.")
+ to_chat(C, "No active admins are online, your adminhelp was sent through TGS to admins who are available. This may use IRC or Discord.")
heard_by_no_admins = TRUE
bwoink = is_bwoink
@@ -353,20 +354,22 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
GLOB.ahelp_tickets.resolved_tickets -= src
return ..()
-/datum/admin_help/proc/AddInteraction(msg_color, message, name_from, name_to)
+/datum/admin_help/proc/AddInteraction(msg_color, message, name_from, name_to, safe_from, safe_to)
if(heard_by_no_admins && usr && usr.ckey != initiator_ckey)
heard_by_no_admins = FALSE
- send2irc(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
+ send2tgs(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
var/datum/ticket_interaction/interaction_message = new /datum/ticket_interaction
interaction_message.message_color = msg_color
interaction_message.message = message
interaction_message.from_user = name_from
interaction_message.to_user = name_to
+ interaction_message.from_user_safe = safe_from
+ interaction_message.to_user_safe = safe_to
_interactions += interaction_message
SStgui.update_uis(src)
/datum/admin_help/proc/TimeoutVerb()
- initiator.verbs -= /client/verb/adminhelp
+ initiator.remove_verb(/client/verb/adminhelp)
initiator.adminhelptimerid = addtimer(CALLBACK(initiator, /client/proc/giveadminhelpverb), 1200, TIMER_STOPPABLE)
//private
@@ -510,7 +513,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//Message to be sent to all admins
var/admin_msg = "Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]: [keywords_lookup(msg)]"
- AddInteraction("red", msg, initiator_key_name, claimed_admin_key_name)
+ AddInteraction("red", msg, initiator_key_name, claimed_admin_key_name, "You", "Administrator")
log_admin_private("Ticket #[id]: [key_name(initiator)]: [msg]")
//send this msg to all admins
@@ -537,7 +540,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
to_chat(usr, "This user already has an active ticket, cannot reopen this one.")
return
- statclick = new(null, src)
GLOB.ahelp_tickets.active_tickets += src
GLOB.ahelp_tickets.closed_tickets -= src
GLOB.ahelp_tickets.resolved_tickets -= src
@@ -563,7 +565,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(state > AHELP_ACTIVE)
return
closed_at = world.time
- QDEL_NULL(statclick)
if(state == AHELP_ACTIVE)
GLOB.ahelp_tickets.active_tickets -= src
else
@@ -733,27 +734,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if("mhelp")
MHelpThis()
-//
-// TICKET STATCLICK
-//
-
-/obj/effect/statclick/ahelp
- var/datum/admin_help/ahelp_datum
-
-/obj/effect/statclick/ahelp/Initialize(mapload, datum/admin_help/AH)
- ahelp_datum = AH
- . = ..()
-
-/obj/effect/statclick/ahelp/update()
- return ..(ahelp_datum.name)
-
-/obj/effect/statclick/ahelp/Click()
- ahelp_datum.TicketPanel()
-
-/obj/effect/statclick/ahelp/Destroy()
- ahelp_datum = null
- return ..()
-
//
//CLIENT PROCS
//
@@ -761,16 +741,16 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/client/proc/giveadminhelpverb()
if(!src)
return
- src.verbs |= /client/verb/adminhelp
+ src.add_verb(/client/verb/adminhelp)
deltimer(adminhelptimerid)
adminhelptimerid = 0
// Used for methods where input via arg doesn't work
/client/proc/get_adminhelp()
- var/msg = capped_input(src, "Please describe your problem concisely and an admin will help as soon as they're able. Include the names of the people you are ahelping against if applicable.", "Adminhelp contents")
+ var/msg = capped_multiline_input(src, "Please describe your problem concisely and an admin will help as soon as they're able. Include the names of the people you are ahelping against if applicable.", "Adminhelp contents")
adminhelp(msg)
-/client/verb/adminhelp(msg as text)
+/client/verb/adminhelp(msg as message)
set category = "Admin"
set name = "Adminhelp"
@@ -811,7 +791,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//Use this proc when an admin takes action that may be related to an open ticket on what
//what can be a client, ckey, or mob
-/proc/admin_ticket_log(what, message, whofrom = "", whoto = "", color = "white")
+/proc/admin_ticket_log(what, message, whofrom = "", whoto = "", color = "white", isSenderAdmin = FALSE, safeSenderLogged = FALSE)
var/client/C
var/mob/Mob = what
if(istype(Mob))
@@ -819,12 +799,18 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
else
C = what
if(istype(C) && C.current_ticket)
- C.current_ticket.AddInteraction(color, message, whofrom, whoto)
+ if(safeSenderLogged)
+ C.current_ticket.AddInteraction(color, message, whofrom, whoto, isSenderAdmin ? "Administrator" : "You", isSenderAdmin ? "You" : "Administrator")
+ else
+ C.current_ticket.AddInteraction(color, message, whofrom, whoto)
return C.current_ticket
if(istext(what)) //ckey
var/datum/admin_help/AH = GLOB.ahelp_tickets.CKey2ActiveTicket(what)
if(AH)
- AH.AddInteraction(color, message, whofrom, whoto)
+ if(safeSenderLogged)
+ AH.AddInteraction(color, message, whofrom, whoto, isSenderAdmin ? "Administrator" : "You", isSenderAdmin ? "You" : "Administrator")
+ else
+ AH.AddInteraction(color, message, whofrom, whoto)
return AH
@@ -845,7 +831,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
else
.["present"] += X
-/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN)
+/proc/send2tgs_adminless_only(source, msg, requiredflags = R_BAN)
var/list/adm = get_admin_counts(requiredflags)
var/list/activemins = adm["present"]
. = activemins.len
@@ -859,32 +845,16 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
final = "[msg] - No admins online"
else
final = "[msg] - All admins stealthed\[[english_list(stealthmins)]\], AFK\[[english_list(afkmins)]\], or lacks +BAN\[[english_list(powerlessmins)]\]! Total: [allmins.len] "
- send2irc(source,final)
- send2otherserver(source,final)
+ send2tgs(source,final)
+ SStopic.crosscomms_send("ahelp", final, source)
-/proc/send2irc(msg,msg2)
+/proc/send2tgs(msg,msg2)
msg = replacetext(replacetext(msg, "\proper", ""), "\improper", "")
msg2 = replacetext(replacetext(msg2, "\proper", ""), "\improper", "")
world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE)
-/proc/send2otherserver(source,msg,type = "Ahelp")
- var/comms_key = CONFIG_GET(string/comms_key)
- if(!comms_key)
- return
- var/list/message = list()
- message["message_sender"] = source
- message["message"] = msg
- message["source"] = "([CONFIG_GET(string/cross_comms_name)])"
- message["key"] = comms_key
- message += type
-
- var/list/servers = CONFIG_GET(keyed_list/cross_server)
- for(var/I in servers)
- world.Export("[servers[I]]?[list2params(message)]")
-
-
-/proc/ircadminwho()
+/proc/tgsadminwho()
var/list/message = list("Admins: ")
var/list/admin_keys = list()
for(var/adm in GLOB.admins)
@@ -899,7 +869,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
return jointext(message, "")
-/proc/keywords_lookup(msg,irc)
+/proc/keywords_lookup(msg,external)
//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE!
var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as", "i")
@@ -911,8 +881,11 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/list/surnames = list()
var/list/forenames = list()
var/list/ckeys = list()
- var/founds = ""
+ var/list/founds = list()
for(var/mob/M in GLOB.mob_list)
+ if(istype(M, /mob/living/carbon/human/dummy))
+ continue
+
var/list/indexing = list(M.real_name, M.name)
if(M.mind)
indexing += M.mind.name
@@ -958,15 +931,16 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/is_antag = 0
if(found.mind?.special_role)
is_antag = 1
- founds += "Name: [found.name]([found.real_name]) Key: [found.key] Ckey: [found.ckey] [is_antag ? "(Antag)" : null] "
+ founds[++founds.len] = list("name" = found.name,
+ "real_name" = found.real_name,
+ "ckey" = found.ckey,
+ "key" = found.key,
+ "antag" = is_antag)
msg += "[original_word](?|F) "
continue
msg += "[original_word] "
- if(irc)
- if(founds == "")
- return "Search Failed"
- else
- return founds
+ if(external)
+ return founds
return msg
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 02c41581744c5..1709f27b47b9e 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -1,4 +1,4 @@
-#define IRCREPLYCOUNT 2
+#define EXTERNALREPLYCOUNT 2
//allows right clicking mobs to send an admin PM to their client, forwards the selected mob's client to cmd_admin_pm
@@ -95,20 +95,20 @@
return
var/client/recipient
- var/irc = 0
+ var/external = 0
if(istext(whom))
if(whom[1] == "@")
whom = findStealthKey(whom)
if(whom == "IRCKEY")
- irc = 1
+ external = 1
else
recipient = GLOB.directory[whom]
else if(istype(whom, /client))
recipient = whom
- if(irc)
- if(!ircreplyamount) //to prevent people from spamming irc
+ if(external)
+ if(!externalreplyamount) //to prevent people from spamming irc/discord
return
if(!msg)
msg = stripped_multiline_input(src,"Message:", "Private message to Administrator")
@@ -116,7 +116,7 @@
if(!msg)
return
if(holder)
- to_chat(src, "Error: Use the admin IRC channel, nerd.", type = MESSAGE_TYPE_ADMINPM)
+ to_chat(src, "Error: Use the admin IRC/Discord channel, nerd.", type = MESSAGE_TYPE_ADMINPM)
return
@@ -153,7 +153,7 @@
return
//clean the message if it's not sent by a high-rank admin
- if(!check_rights(R_SERVER|R_DEBUG,0)||irc)//no sending html to the poor bots
+ if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots
msg = trim(sanitize(msg), MAX_MESSAGE_LEN)
if(!msg)
return
@@ -165,11 +165,11 @@
var/keywordparsedmsg = keywords_lookup(msg)
- if(irc)
+ if(external)
to_chat(src, "PM to-Admins: [rawmsg]", type = MESSAGE_TYPE_ADMINPM)
- var/datum/admin_help/AH = admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to IRC: [keywordparsedmsg]")
- ircreplyamount--
- send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
+ var/datum/admin_help/AH = admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to External: [keywordparsedmsg]")
+ externalreplyamount--
+ send2tgs("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
else
if(recipient.holder)
if(holder) //both are admins
@@ -177,13 +177,13 @@
to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [keywordparsedmsg]", type = MESSAGE_TYPE_ADMINPM)
//omg this is dumb, just fill in both their tickets
- admin_ticket_log(src, keywordparsedmsg, key_name(src, recipient, 1), key_name(recipient, src, 1), color="teal")
+ admin_ticket_log(src, keywordparsedmsg, key_name(src, recipient, 1), key_name(recipient, src, 1), color="teal", isSenderAdmin = TRUE, safeSenderLogged = TRUE)
if(recipient != src) //reeee
- admin_ticket_log(recipient, keywordparsedmsg, key_name(src, recipient, 1), key_name(recipient, src, 1), color="teal")
+ admin_ticket_log(recipient, keywordparsedmsg, key_name(src, recipient, 1), key_name(recipient, src, 1), color="teal", isSenderAdmin = TRUE, safeSenderLogged = TRUE)
else //recipient is an admin but sender is not
var/replymsg = "Reply PM from-[key_name(src, recipient, 1)]: [keywordparsedmsg]"
- admin_ticket_log(src, keywordparsedmsg, key_name(src, recipient, 1), null, "white")
+ admin_ticket_log(src, keywordparsedmsg, key_name(src, recipient, 1), null, "white", isSenderAdmin = TRUE, safeSenderLogged = TRUE)
to_chat(recipient, "[replymsg]", type = MESSAGE_TYPE_ADMINPM)
to_chat(src, "PM to-Admins: [msg]", type = MESSAGE_TYPE_ADMINPM)
@@ -201,7 +201,7 @@
to_chat(recipient, "Click on the administrator's name to reply.", type = MESSAGE_TYPE_ADMINPM)
to_chat(src, "Admin PM to-[key_name(recipient, src, 1)]: [msg]", type = MESSAGE_TYPE_ADMINPM)
- admin_ticket_log(recipient, keywordparsedmsg, key_name_admin(src), null, "purple")
+ admin_ticket_log(recipient, keywordparsedmsg, key_name_admin(src), null, "purple", safeSenderLogged = TRUE)
//always play non-admin recipients the adminhelp sound
SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg'))
@@ -227,10 +227,10 @@
to_chat(src, "Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.", type = MESSAGE_TYPE_ADMINPM)
return
- if(irc)
- log_admin_private("PM: [key_name(src)]->IRC: [rawmsg]")
+ if(external)
+ log_admin_private("PM: [key_name(src)]->External: [rawmsg]")
for(var/client/X in GLOB.admins)
- to_chat(X, "PM: [key_name(src, X, 0)]->IRC: [keywordparsedmsg]")
+ to_chat(X, "PM: [key_name(src, X, 0)]->External: [keywordparsedmsg]")
else
window_flash(recipient, ignorepref = TRUE)
log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
@@ -241,34 +241,34 @@
-#define IRC_AHELP_USAGE "Usage: ticket
bucket_list
+ [bucket_list_output]
+
+ second_queue
+ [second_queue]
+ "}, "window=check_timer_sources;size=700x700")
+
+/proc/generate_timer_source_output(list/datum/timedevent/events)
+ var/list/per_source = list()
+
+ // Collate all events and figure out what sources are creating the most
+ for (var/_event in events)
+ if (!_event)
+ continue
+ var/datum/timedevent/event = _event
+
+ do
+ if (event.source)
+ if (per_source[event.source] == null)
+ per_source[event.source] = 1
+ else
+ per_source[event.source] += 1
+ event = event.next
+ while (event && event != _event)
+
+ // Now, sort them in order
+ var/list/sorted = list()
+ for (var/source in per_source)
+ sorted += list(list("source" = source, "count" = per_source[source]))
+ sorted = sortTim(sorted, .proc/cmp_timer_data)
+
+ // Now that everything is sorted, compile them into an HTML output
+ var/output = ""
+
+ for (var/_timer_data in sorted)
+ var/list/timer_data = _timer_data
+ output += {"
"
+
+ return output
+
+/proc/cmp_timer_data(list/a, list/b)
+ return b["count"] - a["count"]
diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm
index fd4374a7724d6..7c65da13d5acb 100644
--- a/code/modules/admin/verbs/diagnostics.dm
+++ b/code/modules/admin/verbs/diagnostics.dm
@@ -14,9 +14,7 @@
var/largest_click_time = 0
var/mob/largest_move_mob = null
var/mob/largest_click_mob = null
- for(var/mob/M in world)
- if(!M.client)
- continue
+ for(var/mob/M in GLOB.player_list)
if(M.next_move >= largest_move_time)
largest_move_mob = M
if(M.next_move > world.time)
@@ -32,6 +30,7 @@
log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] lastDblClick = [M.next_click] world.time = [world.time]")
M.next_move = 1
M.next_click = 0
+
message_admins("[ADMIN_LOOKUPFLW(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [DisplayTimeText(largest_move_time)]!")
message_admins("[ADMIN_LOOKUPFLW(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [DisplayTimeText(largest_click_time)]!")
message_admins("world.time = [world.time]")
@@ -50,12 +49,16 @@
output += " ERROR
+ "}
+
+ output += "[timer_data["source"]]
+ [timer_data["count"]]
+
"
continue
for (var/filter in fqs.devices)
- var/list/f = fqs.devices[filter]
- if (!f)
+ var/list/filtered = fqs.devices[filter]
+ if (!filtered)
output += " [filter]: ERROR
"
continue
- output += " [filter]: [f.len]
"
- for (var/device in f)
+ output += " [filter]: [filtered.len]
"
+ for(var/datum/weakref/device_ref as anything in filtered)
+ var/atom/device = device_ref.resolve()
+ if(!device)
+ filtered -= device_ref
+ continue
if (istype(device, /atom))
var/atom/A = device
output += " [device] ([AREACOORD(A)])
"
diff --git a/code/modules/admin/verbs/ghost_pool_protection.dm b/code/modules/admin/verbs/ghost_pool_protection.dm
new file mode 100644
index 0000000000000..aa4724d1cfdf8
--- /dev/null
+++ b/code/modules/admin/verbs/ghost_pool_protection.dm
@@ -0,0 +1,93 @@
+//very similar to centcom_podlauncher in terms of how this is coded, so i kept a lot of comments from it
+
+/client/proc/ghost_pool_protection() //Creates a verb for admins to open up the ui
+ set name = "Ghost Pool Protection"
+ set desc = "Choose which ways people can get into the round, or just clear it out completely for admin events."
+ set category = "Adminbus"
+ var/datum/ghost_pool_menu/tgui = new(usr)//create the datum
+ tgui.ui_interact(usr)//datum has a tgui component, here we open the window
+
+/datum/ghost_pool_menu
+ var/client/holder //client of whoever is using this datum
+
+ //when submitted, what the pool flags will be set to
+ var/new_role_flags = ALL
+
+ //EVERY TYPE OF WAY SOMEONE IS GETTING BACK INTO THE ROUND!
+ //these are the same comments as the ones in admin.dm defines, please update those if you change them here
+ /*
+ var/events_or_midrounds = TRUE //ie fugitives, space dragon, etc. also includes dynamic midrounds as it's the same deal
+ var/spawners = TRUE //ie ashwalkers, free golems, beach bums
+ var/station_sentience = TRUE //ie posibrains, mind monkeys, sentience potion, etc.
+ var/minigames = TRUE //ie mafia, ctf
+ var/misc = TRUE //oddities like split personality and any animal ones like spiders, xenos
+ */
+
+/datum/ghost_pool_menu/New(user)//user can either be a client or a mob due to byondcode(tm)
+ if (istype(user, /client))
+ var/client/user_client = user
+ holder = user_client //if its a client, assign it to holder
+ else
+ var/mob/user_mob = user
+ holder = user_mob.client //if its a mob, assign the mob's client to holder
+ new_role_flags = GLOB.ghost_role_flags
+
+/datum/ghost_pool_menu/ui_state(mob/user)
+ return GLOB.admin_state
+
+/datum/ghost_pool_menu/ui_close()
+ qdel(src)
+
+/datum/ghost_pool_menu/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "GhostPoolProtection")
+ ui.open()
+
+/datum/ghost_pool_menu/ui_data(mob/user)
+ var/list/data = list()
+ data["events_or_midrounds"] = (new_role_flags & GHOSTROLE_MIDROUND_EVENT)
+ data["spawners"] = (new_role_flags & GHOSTROLE_SPAWNER)
+ data["station_sentience"] = (new_role_flags & GHOSTROLE_STATION_SENTIENCE)
+ data["silicons"] = (new_role_flags & GHOSTROLE_SILICONS)
+ data["minigames"] = (new_role_flags & GHOSTROLE_MINIGAME)
+ return data
+
+/datum/ghost_pool_menu/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("toggle_events_or_midrounds")
+ new_role_flags ^= GHOSTROLE_MIDROUND_EVENT
+ . = TRUE
+ if("toggle_spawners")
+ new_role_flags ^= GHOSTROLE_SPAWNER
+ . = TRUE
+ if("toggle_station_sentience")
+ new_role_flags ^= GHOSTROLE_STATION_SENTIENCE
+ . = TRUE
+ if("toggle_silicons")
+ new_role_flags ^= GHOSTROLE_SILICONS
+ . = TRUE
+ if("toggle_minigames")
+ new_role_flags ^= GHOSTROLE_MINIGAME
+ . = TRUE
+ if("all_roles")
+ new_role_flags = ALL
+ . = TRUE
+ if("no_roles")
+ new_role_flags = NONE
+ . = TRUE
+ if("apply_settings")
+ to_chat(usr, "Settings Applied!")
+ var/msg
+ switch(new_role_flags)
+ if(ALL)
+ msg = "enabled all of"
+ if(NONE)
+ msg = "disabled all of"
+ else
+ msg = "modified"
+ message_admins("[key_name_admin(holder)] has [msg] this round's allowed ghost roles.")
+ GLOB.ghost_role_flags = new_role_flags
diff --git a/code/modules/admin/verbs/individual_logging.dm b/code/modules/admin/verbs/individual_logging.dm
index 954b5ccee6058..9ad07ac1b2573 100644
--- a/code/modules/admin/verbs/individual_logging.dm
+++ b/code/modules/admin/verbs/individual_logging.dm
@@ -5,7 +5,7 @@
var/ntype = text2num(type)
//Add client links
- var/dat = ""
+ var/list/dat = list()
if(M.client)
dat += "
[reversed[entry]]
"
- dat += "
"
+ var/list/all_the_entrys = log_source[log_type]
+ for(var/entry in all_the_entrys)
+ concatenated_logs += "[entry]
[all_the_entrys[entry]]"
+ if(length(concatenated_logs))
+ sortTim(concatenated_logs, cmp = /proc/cmp_text_dsc) //Sort by timestamp.
+ dat += ""
+ dat += concatenated_logs.Join("
")
+ dat += ""
- usr << browse(dat, "window=invidual_logging_[key_name(M)];size=600x480")
+ var/datum/browser/popup = new(usr, "invidual_logging_[key_name(M)]", "Individual Logs", 600, 600)
+ popup.set_content(dat.Join())
+ popup.open()
/proc/individual_logging_panel_link(mob/M, log_type, log_src, label, selected_src, selected_type)
var/slabel = label
if(selected_type == log_type && selected_src == log_src)
slabel = "\[[label]\]"
-
+ //This is necessary because num2text drops digits and rounds on big numbers. If more defines get added in the future it could break again.
+ log_type = num2text(log_type, MAX_BITFLAG_DIGITS)
return "[slabel]"
diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm
index 0e687c8b045ea..82db4dc885ad7 100644
--- a/code/modules/admin/verbs/map_template_loadverb.dm
+++ b/code/modules/admin/verbs/map_template_loadverb.dm
@@ -37,11 +37,14 @@
to_chat(src, "Filename must end in '.dmm': [map]")
return
var/datum/map_template/M
+ var/type
switch(alert(src, "What kind of map is this?", "Map type", "Normal", "Shuttle", "Cancel"))
if("Normal")
- M = new /datum/map_template(map, "[map]", TRUE)
+ type = "Normal"
+ M = new /datum/map_template(map, "[map] - Uploaded by [ckey] at [time2text(world.timeofday,"YYYY-MM-DD hh:mm:ss")]", TRUE)
if("Shuttle")
- M = new /datum/map_template/shuttle(map, "[map]", TRUE)
+ type = "Shuttle"
+ M = new /datum/map_template/shuttle(map, "[map] - Uploaded by [ckey] at [time2text(world.timeofday,"YYYY-MM-DD hh:mm:ss")]", TRUE, copytext("[map]",1, -4))
else
return
if(!M.cached_map)
@@ -61,7 +64,11 @@
else
alert(src, "The map failed validation and cannot be loaded.", "Map Errors", "Oh Darn")
return
-
- SSmapping.map_templates[M.name] = M
- message_admins("[key_name_admin(src)] has uploaded a map template '[map]' ([M.width]x[M.height])[report_link].")
+ switch(type)
+ if("Normal")
+ SSmapping.map_templates[M.name] = M
+ if("Shuttle")
+ var/datum/map_template/shuttle/S = M
+ SSmapping.shuttle_templates[S.shuttle_id] = S
+ message_admins("[key_name_admin(src)] has uploaded a [type] map template '[map]' ([M.width]x[M.height])[report_link].")
to_chat(src, "Map template '[map]' ready to place ([M.width]x[M.height])")
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index a5c164a736d56..ad172f4e76774 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -84,9 +84,8 @@ GLOBAL_PROTECT(admin_verbs_debug_mapping)
for(var/turf/T in C.can_see())
seen[T]++
for(var/turf/T in seen)
- T.maptext = "[seen[T]]"
+ T.maptext = MAPTEXT("[seen[T]]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range")
#ifdef TESTING
GLOBAL_LIST_EMPTY(dirty_vars)
@@ -157,7 +156,7 @@ GLOBAL_LIST_EMPTY(dirty_vars)
if(intercom_range_display_status)
for(var/obj/item/radio/intercom/I in world)
- for(var/turf/T in orange(7,I))
+ for(var/turf/T as() in RANGE_TURFS(7,I))
var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T)
if (!(F in view(7,I.loc)))
qdel(F)
@@ -205,15 +204,15 @@ GLOBAL_LIST_EMPTY(dirty_vars)
set name = "Debug verbs - Enable"
if(!check_rights(R_DEBUG))
return
- verbs -= /client/proc/enable_debug_verbs
- verbs.Add(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
+ remove_verb(/client/proc/enable_debug_verbs)
+ add_verb(list(/client/proc/disable_debug_verbs) + GLOB.admin_verbs_debug_mapping)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Enable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/disable_debug_verbs()
set category = "Debug"
set name = "Debug verbs - Disable"
- verbs.Remove(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
- verbs += /client/proc/enable_debug_verbs
+ remove_verb(list(/client/proc/disable_debug_verbs) + GLOB.admin_verbs_debug_mapping)
+ add_verb(/client/proc/enable_debug_verbs)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Disable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/count_objects_on_z_level()
@@ -239,18 +238,19 @@ GLOBAL_LIST_EMPTY(dirty_vars)
var/list/atom/atom_list = list()
- for(var/atom/A in world)
- if(istype(A,type_path))
- var/atom/B = A
- while(!(isturf(B.loc)))
+ for(var/area/T as() in get_areas(/area, num_level))
+ for(var/atom/A in T)
+ if(istype(A, type_path))
+ var/atom/B = A
+ while(!(isturf(B.loc)))
if(B?.loc)
B = B.loc
else
break
- if(B)
- if(B.z == num_level)
+ if(B)
count++
atom_list += A
+ CHECK_TICK
to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects Zlevel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/verbs/maprotation.dm b/code/modules/admin/verbs/maprotation.dm
index 91e7045b9fa7d..19c509857930d 100644
--- a/code/modules/admin/verbs/maprotation.dm
+++ b/code/modules/admin/verbs/maprotation.dm
@@ -12,7 +12,7 @@
/client/proc/adminchangemap()
set category = "Server"
set name = "Change Map"
-
+
var/list/maprotatechoices = list()
for (var/map in config.maplist)
var/datum/map_config/VM = config.maplist[map]
@@ -43,3 +43,28 @@
log_admin("[key_name(usr)] is changing the map to [VM.map_name]")
if (SSmapping.changemap(VM) == 0)
message_admins("[key_name_admin(usr)] has changed the map to [VM.map_name]")
+
+/client/proc/forcemapconfig()
+ set category = "Debug"
+ set name = "Debug Force Map"
+
+ //Locked behind permissions since it needs serious protection.
+ if(!check_rights(R_DEBUG) || !check_rights(R_SERVER) || !check_rights(R_PERMISSIONS))
+ to_chat(src, "Insufficient rights (Requires debug, server and permissions).")
+ return
+
+ var/json_settings = input(usr, "Enter map json name:", "Map Json Name", "") as text|null
+
+ if(!json_settings)
+ return
+
+ var/datum/map_config/config = new
+ if(!config.LoadConfig("_maps/[json_settings].json", TRUE))
+ qdel(config)
+ to_chat(usr, "Map json failed to load!")
+ return
+ SSticker.maprotatechecked = 1
+ message_admins("[key_name_admin(usr)] is changing the map to [config.map_name]")
+ log_admin("[key_name(usr)] is changing the map to [config.map_name]")
+ if (SSmapping.changemap(config) == 0)
+ message_admins("[key_name_admin(usr)] has changed the map to [config.map_name]")
diff --git a/code/modules/admin/verbs/mentors_edit.dm b/code/modules/admin/verbs/mentors_edit.dm
index 316ab40d55c09..43da6121095b4 100644
--- a/code/modules/admin/verbs/mentors_edit.dm
+++ b/code/modules/admin/verbs/mentors_edit.dm
@@ -4,68 +4,70 @@ also probably less secure, but honestly dude
its mentors, not actual dangerous perms
******************************************/
/client/proc/edit_mentors()
- set category = "Admin"
- set name = "Mentor Panel"
- set desc = "Edit mentors"
+ set category = "Admin"
+ set name = "Mentor Panel"
+ set desc = "Edit mentors"
- if(!check_rights(R_PERMISSIONS))
- return
- if(!SSdbcore.IsConnected())
- to_chat(src, "Failed to establish database connection.")
- return
+ if(!check_rights(R_PERMISSIONS))
+ return
+ if(!SSdbcore.IsConnected())
+ to_chat(src, "Failed to establish database connection.")
+ return
- var/html = "Mentor Panel
\n"
- html += "Add a Mentor\n"
- html += "\n"
- html += "
"
- usr << browse("[html]","window=editmentors;size=1000x650")
- qdel(query_mentor_list)
+ usr << browse("[html]","window=editmentors;size=1000x650")
+ qdel(query_mentor_list)
/client/Topic(href, href_list)
- ..()
- if(href_list["mentor_edit"])
- if(!check_rights(R_PERMISSIONS))
- message_admins("[key_name_admin(usr)] attempted to edit mentor permissions without sufficient rights.")
- log_admin("[key_name(usr)] attempted to edit mentor permissions without sufficient rights.")
- return
- if(IsAdminAdvancedProcCall())
- to_chat(usr, "Mentor Edit blocked: Advanced ProcCall detected.")
- return
+ ..()
+ if(href_list["mentor_edit"])
+ if(!check_rights(R_PERMISSIONS))
+ message_admins("[key_name_admin(usr)] attempted to edit mentor permissions without sufficient rights.")
+ log_admin("[key_name(usr)] attempted to edit mentor permissions without sufficient rights.")
+ return
+ if(IsAdminAdvancedProcCall())
+ to_chat(usr, "Mentor Edit blocked: Advanced ProcCall detected.")
+ return
- if(href_list["mentor_edit"] == "add")
- var/newguy = input("Enter the key of the mentor you wish to add.", "")
- var/datum/DBQuery/query_add_mentor = SSdbcore.NewQuery(
- "INSERT INTO [format_table_name("mentor")] (ckey) VALUES (:newguy)",
- list("newguy" = newguy)
- )
- if(!query_add_mentor.warn_execute())
- qdel(query_add_mentor)
- return
- message_admins("[key_name(usr)] made [newguy] a mentor.")
- log_admin("[key_name(usr)] made [newguy] a mentor.")
- qdel(query_add_mentor)
- return
+ if(href_list["mentor_edit"] == "add")
+ var/newguy = input("Enter the key of the mentor you wish to add.", "")
+ var/datum/DBQuery/query_add_mentor = SSdbcore.NewQuery(
+ "INSERT INTO [format_table_name("mentor")] (ckey) VALUES (:newguy)",
+ list("newguy" = newguy)
+ )
+ if(!query_add_mentor.warn_execute())
+ qdel(query_add_mentor)
+ return
+ message_admins("[key_name(usr)] made [newguy] a mentor.")
+ log_admin("[key_name(usr)] made [newguy] a mentor.")
+ qdel(query_add_mentor)
+ load_mentors()
+ return
- if(href_list["mentor_edit"] == "remove")
- var/removed_mentor = href_list["mentor_ckey"]
- var/datum/DBQuery/query_remove_mentor = SSdbcore.NewQuery(
- "DELETE FROM [format_table_name("mentor")] WHERE ckey = :removed_mentor",
- list("removed_mentor" = removed_mentor)
- )
- if(!query_remove_mentor.warn_execute())
- qdel(query_remove_mentor)
- return
- message_admins("[key_name(usr)] de-mentored [href_list["mentor_ckey"]]")
- log_admin("[key_name(usr)] de-mentored [href_list["mentor_ckey"]]")
- qdel(query_remove_mentor)
+ if(href_list["mentor_edit"] == "remove")
+ var/removed_mentor = href_list["mentor_ckey"]
+ var/datum/DBQuery/query_remove_mentor = SSdbcore.NewQuery(
+ "DELETE FROM [format_table_name("mentor")] WHERE ckey = :removed_mentor",
+ list("removed_mentor" = removed_mentor)
+ )
+ if(!query_remove_mentor.warn_execute())
+ qdel(query_remove_mentor)
+ return
+ message_admins("[key_name(usr)] de-mentored [href_list["mentor_ckey"]]")
+ log_admin("[key_name(usr)] de-mentored [href_list["mentor_ckey"]]")
+ qdel(query_remove_mentor)
+ load_mentors()
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 2f68988ce015b..089a3f97095f2 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -43,7 +43,7 @@
return !is_banned_from(applicant.ckey, list(targetrole, ROLE_SYNDICATE))
-/datum/admins/proc/makeTraitors()
+/datum/admins/proc/makeTraitors(maxCount = 3)
var/datum/game_mode/traitor/temp = new
if(CONFIG_GET(flag/protect_roles_from_antagonist))
@@ -65,20 +65,20 @@
candidates += applicant
if(candidates.len)
- var/numTraitors = min(candidates.len, 3)
+ var/numTraitors = min(candidates.len, maxCount)
for(var/i = 0, i \n"
+ var/html = "Mentor Ckey Remove Mentor Panel
\n"
+ html += "Add a Mentor\n"
+ html += "\n"
+ html += "
"
+ html += " \n"
- var/datum/DBQuery/query_mentor_list = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor")]")
- if(!query_mentor_list.warn_execute())
- to_chat(src, "Unable to pull the mentor list from the database.")
- qdel(query_mentor_list)
- query_mentor_list.Execute()
- while(query_mentor_list.NextRow())
- html += "Mentor Ckey Remove \n"
+ var/datum/DBQuery/query_mentor_list = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor")]")
+ if(!query_mentor_list.warn_execute())
+ to_chat(src, "Unable to pull the mentor list from the database.")
+ qdel(query_mentor_list)
+ query_mentor_list.Execute()
+ while(query_mentor_list.NextRow())
+ html += "[query_mentor_list.item[1]] X \n"
- html += "[query_mentor_list.item[1]] X
Playtime:"
- body += C.get_exp_report()
- body += "Toggle Exempt status"
- body += ""
- usr << browse(body.Join(), "window=playerplaytime[C.ckey];size=550x615")
+ new /datum/job_report_menu(client_to_check, usr)
/datum/admins/proc/toggle_exempt_status(client/C)
if(!check_rights(R_ADMIN))
@@ -1239,6 +1272,50 @@ Traitors and the like can also be revived with the previous role mostly intact.
message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]")
log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]")
+/// Allow admin to add or remove traits of datum
+/datum/admins/proc/modify_traits(datum/D)
+ if(!D)
+ return
+
+ var/add_or_remove = input("Remove/Add?", "Trait Remove/Add") as null|anything in list("Add","Remove")
+ if(!add_or_remove)
+ return
+ var/list/available_traits = list()
+
+ switch(add_or_remove)
+ if("Add")
+ for(var/key in GLOB.traits_by_type)
+ if(istype(D,key))
+ available_traits += GLOB.traits_by_type[key]
+ if("Remove")
+ if(!GLOB.trait_name_map)
+ GLOB.trait_name_map = generate_trait_name_map()
+ for(var/trait in D.status_traits)
+ var/name = GLOB.trait_name_map[trait] || trait
+ available_traits[name] = trait
+
+ var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in available_traits
+ if(!chosen_trait)
+ return
+ chosen_trait = available_traits[chosen_trait]
+
+ var/source = "adminabuse"
+ switch(add_or_remove)
+ if("Add") //Not doing source choosing here intentionally to make this bit faster to use, you can always vv it.
+ ADD_TRAIT(D,chosen_trait,source)
+ if("Remove")
+ var/specific = input("All or specific source ?", "Trait Remove/Add") as null|anything in list("All","Specific")
+ if(!specific)
+ return
+ switch(specific)
+ if("All")
+ source = null
+ if("Specific")
+ source = input("Source to be removed","Trait Remove/Add") as null|anything in D.status_traits[chosen_trait]
+ if(!source)
+ return
+ REMOVE_TRAIT(D,chosen_trait,source)
+
/client/proc/spawnhuman()
set name = "Spawn human"
set desc = "Spawns a mindless human"
diff --git a/code/modules/admin/verbs/requests.dm b/code/modules/admin/verbs/requests.dm
new file mode 100644
index 0000000000000..80894c2398be7
--- /dev/null
+++ b/code/modules/admin/verbs/requests.dm
@@ -0,0 +1,7 @@
+/// Verb for opening the requests manager panel
+/client/proc/requests()
+ set name = "Requests Manager"
+ set desc = "Open the request manager panel to view all requests during this round"
+ set category = "Admin"
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Request Manager") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ GLOB.requests.ui_interact(usr)
\ No newline at end of file
diff --git a/code/modules/admin/verbs/shuttlepanel.dm b/code/modules/admin/verbs/shuttlepanel.dm
index f6ced5da13e74..0eb086c653c7f 100644
--- a/code/modules/admin/verbs/shuttlepanel.dm
+++ b/code/modules/admin/verbs/shuttlepanel.dm
@@ -48,6 +48,11 @@
if(options[selection])
request(options[selection])
+ //Remove shuttle object
+ if(SSorbits.assoc_shuttles.Find(options[selection]))
+ //Removing from the list is handled by deleting the shuttle
+ qdel(SSorbits.assoc_shuttles[options[selection]])
+
/obj/docking_port/mobile/emergency/admin_fly_shuttle(mob/user)
return // use the existing verbs for this
diff --git a/code/modules/admin/view_variables/filterrific.dm b/code/modules/admin/view_variables/filterrific.dm
new file mode 100644
index 0000000000000..9d6b4834e5332
--- /dev/null
+++ b/code/modules/admin/view_variables/filterrific.dm
@@ -0,0 +1,98 @@
+/datum/filter_editor
+ var/atom/target
+
+/datum/filter_editor/New(atom/target)
+ src.target = target
+
+/datum/filter_editor/ui_state(mob/user)
+ return GLOB.admin_state
+
+/datum/filter_editor/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Filteriffic")
+ ui.open()
+
+/datum/filter_editor/ui_static_data(mob/user)
+ var/list/data = list()
+ data["filter_info"] = GLOB.master_filter_info
+ return data
+
+/datum/filter_editor/ui_data()
+ var/list/data = list()
+ data["target_name"] = target.name
+ data["target_filter_data"] = target.filter_data
+ return data
+
+/datum/filter_editor/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("add_filter")
+ var/target_name = params["name"]
+ while(target.filter_data && target.filter_data[target_name])
+ target_name = "[target_name]-dupe"
+ target.add_filter(target_name, params["priority"], list("type" = params["type"]))
+ . = TRUE
+ if("remove_filter")
+ target.remove_filter(params["name"])
+ . = TRUE
+ if("rename_filter")
+ var/list/filter_data = target.filter_data[params["name"]]
+ target.remove_filter(params["name"])
+ target.add_filter(params["new_name"], filter_data["priority"], filter_data)
+ . = TRUE
+ if("edit_filter")
+ target.remove_filter(params["name"])
+ target.add_filter(params["name"], params["priority"], params["new_filter"])
+ . = TRUE
+ if("change_priority")
+ var/new_priority = params["new_priority"]
+ target.change_filter_priority(params["name"], new_priority)
+ . = TRUE
+ if("transition_filter_value")
+ target.transition_filter(params["name"], 4, params["new_data"])
+ . = TRUE
+ if("modify_filter_value")
+ var/list/old_filter_data = target.filter_data[params["name"]]
+ var/list/new_filter_data = old_filter_data.Copy()
+ for(var/entry in params["new_data"])
+ new_filter_data[entry] = params["new_data"][entry]
+ for(var/entry in new_filter_data)
+ if(entry == GLOB.master_filter_info[old_filter_data["type"]]["defaults"][entry])
+ new_filter_data.Remove(entry)
+ target.remove_filter(params["name"])
+ target.add_filter(params["name"], old_filter_data["priority"], new_filter_data)
+ . = TRUE
+ if("modify_color_value")
+ var/new_color = input(usr, "Pick new filter color", "Filteriffic Colors!") as color|null
+ if(new_color)
+ target.transition_filter(params["name"], 4, list("color" = new_color))
+ . = TRUE
+ if("modify_icon_value")
+ var/icon/new_icon = input("Pick icon:", "Icon") as null|icon
+ if(new_icon)
+ target.filter_data[params["name"]]["icon"] = new_icon
+ target.update_filters()
+ . = TRUE
+ if("mass_apply")
+ if(!check_rights_for(usr.client, R_FUN))
+ to_chat(usr, "Error
Ref | Type | Variable Name | Follow | " + for(var/ref in backrefs) + var/datum/backreference = ref + if(isnull(backreference)) + dat += "
---|---|---|---|
GC'd Reference | |||
[REF(backreference)] | [backreference.type] | [backrefs[backreference]] | \[Follow\] |
[REF(backreference)] | list | [backrefs[backreference]] | \[Follow\] |
Weird reference type. Add more debugging checks. |
Variable name | Ref | Type | Follow | " + for(var/ref in frontrefs) + var/datum/backreference = frontrefs[ref] + dat += "
---|---|---|---|
[ref] | [REF(backreference)] | [backreference.type] | \[Follow\] |
GC'd Reference | Clear Nulls |
\ref[thing] | [thing.type][thing.gc_destroyed ? " (destroyed)" : ""] [ADMIN_VV(thing)] |
" - dat += "" //Avert your eyes - dat += " | "
- dat += "Probe " - dat += "Dissect " - dat += "Analyze " - dat += " |
Credits: [D.account_balance]
" - dat += {"Name | Description | Reward | Completion | Status | |||||
---|---|---|---|---|---|---|---|---|---|
[] | ", B.name) - dat += text("High Priority: [] | ", B.description) - dat += text("[] | ", B.reward_string()) - else - dat += text("[] | ", B.name) - dat += text("[] | ", B.description) - dat += text("[] | ", B.reward_string()) - dat += text("[] | ", B.completion_string()) - if(B.can_claim()) - dat += text("Claim | ") - else if(B.claimed) - dat += text("Claimed | ") - else - dat += text("Unclaimed | ") - dat += "
"
dat += "General Settings" dat += "UI Style: [UI_style]" - dat += "Overhead Chat: [overhead_chat ? "Enabled" : "Disabled"] " dat += "Outline: [outline_enabled ? "Enabled" : "Disabled"] " dat += "Outline Color: Change " dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"] " dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"] " + dat += "Show Runechat Chat Bubbles: [chat_on_map ? "Enabled" : "Disabled"] " + dat += "See Runechat for non-mobs: [see_chat_non_mob ? "Enabled" : "Disabled"] " + dat += "See Runechat emotes: [see_rc_emotes ? "Enabled" : "Disabled"] " + dat += "See Balloon alerts: [see_balloon_alerts]" dat += " " dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"] " dat += "Hotkey Mode: [(hotkeys) ? "Hotkeys" : "Default"] " @@ -669,6 +683,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/firstcat = 1 for(var/category in GLOB.loadout_categories) + if(category == "Donator" && (!LAZYLEN(GLOB.patrons) || !CONFIG_GET(flag/donator_items))) + continue if(firstcat) firstcat = 0 else @@ -686,23 +702,26 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += " | ||||||||||||||||||||||||||||||||||||||||||||||
Name | " - dat += "Cost | " + if(LC.category != "Donator") + dat += "Cost | " dat += "Restricted Jobs | " dat += "Description | ||||||||||||||||||||||||||||||||||||||||||
[G.display_name]\n" - if(G.display_name in purchased_gear) + var/donator = G.sort_category == "Donator" // purchase box and cost coloumns doesn't appear on donator items + if(G.id in purchased_gear) if(G.sort_category == "OOC") dat += "Purchased. | " else - dat += "Equip" + dat += "Equip" else - dat += "Purchase" - dat += "[G.cost] | " + dat += "[donator ? "Donator" : "Purchase"] | " + dat += "[donator ? "" : "[G.cost]"] | "
+
if(G.allowed_roles)
dat += ""
for(var/role in G.allowed_roles)
@@ -1189,7 +1208,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return
all_quirks -= quirk
else
- if(GetPositiveQuirkCount() >= MAX_QUIRKS)
+ var/is_positive_quirk = SSquirks.quirk_points[quirk] > 0
+ if(is_positive_quirk && GetPositiveQuirkCount() >= MAX_QUIRKS)
to_chat(user, "You can't have more than [MAX_QUIRKS] positive quirks!")
return
if(balance - value < 0)
@@ -1207,8 +1227,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(href_list["preference"] == "gear")
if(href_list["purchase_gear"])
var/datum/gear/TG = GLOB.gear_datums[href_list["purchase_gear"]]
- if(TG.cost < user.client.get_metabalance())
- purchased_gear += TG.display_name
+ if(TG.sort_category == "Donator")
+ if(CONFIG_GET(flag/donator_items) && alert(parent, "This item is only accessible to our patrons. Would you like to subscribe?", "Patron Locked", "Yes", "No") == "Yes")
+ parent.donate()
+ else if(TG.cost < user.client.get_metabalance())
+ purchased_gear += TG.id
TG.purchase(user.client)
user.client.inc_metabalance((TG.cost * -1), TRUE, "Purchased [TG.display_name].")
save_preferences()
@@ -1216,21 +1239,21 @@ GLOBAL_LIST_EMPTY(preferences_datums)
to_chat(user, "You don't have enough [CONFIG_GET(string/metacurrency_name)]s to purchase \the [TG.display_name]!")
if(href_list["toggle_gear"])
var/datum/gear/TG = GLOB.gear_datums[href_list["toggle_gear"]]
- if(TG.display_name in equipped_gear)
- equipped_gear -= TG.display_name
+ if(TG.id in equipped_gear)
+ equipped_gear -= TG.id
else
var/list/type_blacklist = list()
var/list/slot_blacklist = list()
- for(var/gear_name in equipped_gear)
- var/datum/gear/G = GLOB.gear_datums[gear_name]
+ for(var/gear_id in equipped_gear)
+ var/datum/gear/G = GLOB.gear_datums[gear_id]
if(istype(G))
if(!(G.subtype_path in type_blacklist))
type_blacklist += G.subtype_path
if(!(G.slot in slot_blacklist))
slot_blacklist += G.slot
- if((TG.display_name in purchased_gear))
+ if((TG.id in purchased_gear))
if(!(TG.subtype_path in type_blacklist) || !(TG.slot in slot_blacklist))
- equipped_gear += TG.display_name
+ equipped_gear += TG.id
else
to_chat(user, "Can't equip [TG.display_name]. It conflicts with an already-equipped item.")
else
@@ -1283,6 +1306,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(href_list["preference"] in GLOB.preferences_custom_names)
ask_for_custom_name(user,href_list["preference"])
+ if(href_list["preference"] in pref_species.forced_features)
+ alert("You cannot change that bodypart for your selected species!")
+ features[href_list["preference"]] = pref_species.forced_features[href_list["preference"]]
+ return
switch(href_list["preference"])
if("ghostform")
@@ -1428,6 +1455,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/temp_hsv = RGBtoHSV(features["mcolor"])
if(features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#7F7F7F")[3]))
features["mcolor"] = pref_species.default_color
+ //Set our forced bodyparts
+ for(var/forced_part in pref_species.forced_features)
+ //Get the forced type
+ var/forced_type = pref_species.forced_features[forced_part]
+ //Apply the forced bodypart.
+ features[forced_part] = forced_type
else
if(alert(parent, "This species is only accessible to our patrons. Would you like to subscribe?", "Patron Locked", "Yes", "No") == "Yes")
parent.donate()
@@ -1570,13 +1603,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(new_backbag)
backbag = new_backbag
+ if("suit")
+ if(jumpsuit_style == PREF_SUIT)
+ jumpsuit_style = PREF_SKIRT
+ else
+ jumpsuit_style = PREF_SUIT
+
if("uplink_loc")
var/new_loc = input(user, "Choose your character's traitor uplink spawn location:", "Character Preference") as null|anything in GLOB.uplink_spawn_loc_list
if(new_loc)
uplink_spawn_loc = new_loc
if("ai_core_icon")
- var/ai_core_icon = input(user, "Choose your preferred AI core display screen:", "AI Core Display Screen Selection") as null|anything in GLOB.ai_core_display_screens
+ var/ai_core_icon = input(user, "Choose your preferred AI core display screen:", "AI Core Display Screen Selection") as null|anything in GLOB.ai_core_display_screens - "Portrait"
if(ai_core_icon)
preferred_ai_core_display = ai_core_icon
@@ -1619,9 +1658,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(pickedPDAStyle)
pda_style = pickedPDAStyle
if("pda_color")
- var/pickedPDAColor = input(user, "Choose your PDA Interface color.", "Character Preference",pda_color) as color|null
+ var/pickedPDAColor = input(user, "Choose your PDA Interface color.", "Character Preference", pda_color) as color|null
if(pickedPDAColor)
pda_color = pickedPDAColor
+ if ("see_balloon_alerts")
+ var/pickedstyle = input(user, "Choose how you want balloon alerts displayed", "Balloon alert preference", BALLOON_ALERT_ALWAYS) as null|anything in list(BALLOON_ALERT_ALWAYS, BALLOON_ALERT_WITH_CHAT, BALLOON_ALERT_NEVER)
+ if (!isnull(pickedstyle))
+ see_balloon_alerts = pickedstyle
else
switch(href_list["preference"])
@@ -1649,8 +1692,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
buttons_locked = !buttons_locked
if("tgui_fancy")
tgui_fancy = !tgui_fancy
- if("overheadchat")
- overhead_chat = !overhead_chat
if("outline_enabled")
outline_enabled = !outline_enabled
if("outline_color")
@@ -1754,7 +1795,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("ambientocclusion")
ambientocclusion = !ambientocclusion
if(parent && parent.screen && parent.screen.len)
- var/obj/screen/plane_master/game_world/PM = locate(/obj/screen/plane_master/game_world) in parent.screen
+ var/atom/movable/screen/plane_master/game_world/PM = locate(/atom/movable/screen/plane_master/game_world) in parent.screen
PM.backdrop(parent.mob)
if("auto_fit_viewport")
@@ -1774,8 +1815,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
pixel_size = PIXEL_SCALING_3X
if(PIXEL_SCALING_3X)
pixel_size = PIXEL_SCALING_AUTO
- user.client.view_size.setDefault(getScreenSize(user)) //Fix our viewport size so it doesn't reset on change
- user.client.view_size.apply() //Let's winset() it so it actually works
+ user.client.view_size.resetToDefault(getScreenSize(user)) //Fix our viewport size so it doesn't reset on change
if("scaling_method")
switch(scaling_method)
@@ -1856,7 +1896,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
new_key = "Numpad[new_key]"
var/full_key = "[AltMod][CtrlMod][ShiftMod][new_key]"
- if(old_key)
+ if(old_key && (old_key in key_bindings))
key_bindings[old_key] -= kb_name
key_bindings[full_key] += list(kb_name)
key_bindings[full_key] = sortList(key_bindings[full_key])
@@ -1875,6 +1915,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
ShowKeybindings(user)
return
+ if("chat_on_map")
+ chat_on_map = !chat_on_map
+ if("see_chat_non_mob")
+ see_chat_non_mob = !see_chat_non_mob
+ if("see_rc_emotes")
+ see_rc_emotes = !see_rc_emotes
+
ShowChoices(user)
return 1
@@ -1918,6 +1965,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.socks = socks
character.backbag = backbag
+ character.jumpsuit_style = jumpsuit_style
var/datum/species/chosen_species
chosen_species = pref_species.type
@@ -1976,3 +2024,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return
else
custom_names[name_id] = sanitized_name
+
+/// Handles adding and removing donator items from clients
+/datum/preferences/proc/handle_donator_items()
+ var/datum/loadout_category/DLC = GLOB.loadout_categories["Donator"] // stands for donator loadout category but the other def for DLC works too xD
+ if(!LAZYLEN(GLOB.patrons) || !CONFIG_GET(flag/donator_items)) // donator items are only accesibile by servers with a patreon
+ return
+ if(IS_PATRON(parent.ckey) || (parent in GLOB.admins))
+ for(var/gear_id in DLC.gear)
+ var/datum/gear/AG = DLC.gear[gear_id]
+ if(AG.id in purchased_gear)
+ continue
+ purchased_gear += AG.id
+ AG.purchase(parent)
+ save_preferences()
+ else if(purchased_gear.len || equipped_gear.len)
+ for(var/gear_id in DLC.gear)
+ var/datum/gear/RG = DLC.gear[gear_id]
+ equipped_gear -= RG.id
+ purchased_gear -= RG.id
+ save_preferences()
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 2dd69163729c4..06f4c54fe90eb 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
-#define SAVEFILE_VERSION_MAX 31
+#define SAVEFILE_VERSION_MAX 35
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -25,7 +25,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
*/
/datum/preferences/proc/savefile_needs_update(savefile/S)
var/savefile_version
- S["version"] >> savefile_version
+ READ_FILE(S["version"], savefile_version)
if(savefile_version < SAVEFILE_VERSION_MIN)
S.dir.Cut()
@@ -42,14 +42,33 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
//if your savefile is 3 months out of date, then 'tough shit'.
/datum/preferences/proc/update_preferences(current_version, savefile/S)
- if(current_version < 29)
- overhead_chat = TRUE
if(current_version < 30)
outline_enabled = TRUE
outline_color = COLOR_BLUE_GRAY
if(current_version < 31)
auto_fit_viewport = TRUE
-
+ if(current_version < 32)
+ //Okay this is gonna s u c k
+ var/list/legacy_purchases = purchased_gear.Copy()
+ purchased_gear.Cut()
+ equipped_gear.Cut() //Not gonna bother.
+ for(var/l_gear in legacy_purchases)
+ var/n_gear
+ for(var/rg_nam in GLOB.gear_datums) //this is ugly.
+ var/datum/gear/r_gear = GLOB.gear_datums[rg_nam]
+ if(r_gear.display_name == l_gear)
+ n_gear = r_gear.id
+ break
+ if(n_gear)
+ purchased_gear += n_gear
+ if(current_version < 33)
+ chat_on_map = TRUE
+// max_chat_length = CHAT_MESSAGE_MAX_LENGTH > Depreciated as of 31/07/2021
+ see_chat_non_mob = TRUE
+ see_rc_emotes = TRUE
+ S.dir.Remove("overhead_chat")
+ if(current_version < 35)
+ see_balloon_alerts = BALLOON_ALERT_ALWAYS
return
/datum/preferences/proc/update_character(current_version, savefile/S)
@@ -76,15 +95,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
- S["job_civilian_high"] >> job_civilian_high
- S["job_civilian_med"] >> job_civilian_med
- S["job_civilian_low"] >> job_civilian_low
- S["job_medsci_high"] >> job_medsci_high
- S["job_medsci_med"] >> job_medsci_med
- S["job_medsci_low"] >> job_medsci_low
- S["job_engsec_high"] >> job_engsec_high
- S["job_engsec_med"] >> job_engsec_med
- S["job_engsec_low"] >> job_engsec_low
+ READ_FILE(S["job_civilian_high"], job_civilian_high)
+ READ_FILE(S["job_civilian_med"], job_civilian_med)
+ READ_FILE(S["job_civilian_low"], job_civilian_low)
+ READ_FILE(S["job_medsci_high"], job_medsci_high)
+ READ_FILE(S["job_medsci_med"], job_medsci_med)
+ READ_FILE(S["job_medsci_low"], job_medsci_low)
+ READ_FILE(S["job_engsec_high"], job_engsec_high)
+ READ_FILE(S["job_engsec_med"], job_engsec_med)
+ READ_FILE(S["job_engsec_low"], job_engsec_low)
//Can't use SSjob here since this happens right away on login
for(var/job in subtypesof(/datum/job))
@@ -130,87 +149,92 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
/datum/preferences/proc/load_preferences()
if(!path)
- return 0
+ return FALSE
if(!fexists(path))
- return 0
+ return FALSE
var/savefile/S = new /savefile(path)
if(!S)
- return 0
+ return FALSE
S.cd = "/"
var/needs_update = savefile_needs_update(S)
if(needs_update == -2) //fatal, can't load any data
- return 0
+ return FALSE
//general preferences
- S["asaycolor"] >> asaycolor
- S["ooccolor"] >> ooccolor
- S["lastchangelog"] >> lastchangelog
- S["UI_style"] >> UI_style
- S["overhead_chat"] >> overhead_chat
- S["outline_color"] >> outline_color
- S["outline_enabled"] >> outline_enabled
- S["hotkeys"] >> hotkeys
- S["tgui_fancy"] >> tgui_fancy
- S["tgui_lock"] >> tgui_lock
- S["buttons_locked"] >> buttons_locked
- S["windowflash"] >> windowflashing
- S["be_special"] >> be_special
-
- S["crew_objectives"] >> crew_objectives
-
-
- S["default_slot"] >> default_slot
- S["chat_toggles"] >> chat_toggles
- S["toggles"] >> toggles
- S["ghost_form"] >> ghost_form
- S["ghost_orbit"] >> ghost_orbit
- S["ghost_accs"] >> ghost_accs
- S["ghost_others"] >> ghost_others
- S["preferred_map"] >> preferred_map
- S["ignoring"] >> ignoring
- S["ghost_hud"] >> ghost_hud
- S["inquisitive_ghost"] >> inquisitive_ghost
- S["uses_glasses_colour"]>> uses_glasses_colour
- S["clientfps"] >> clientfps
- S["parallax"] >> parallax
- S["ambientocclusion"] >> ambientocclusion
- S["auto_fit_viewport"] >> auto_fit_viewport
- S["pixel_size"] >> pixel_size
- S["scaling_method"] >> scaling_method
- S["menuoptions"] >> menuoptions
- S["enable_tips"] >> enable_tips
- S["tip_delay"] >> tip_delay
- S["pda_style"] >> pda_style
- S["pda_color"] >> pda_color
- S["show_credits"] >> show_credits
-
- S["key_bindings"] >> key_bindings
-
- S["purchased_gear"] >> purchased_gear
- S["equipped_gear"] >> equipped_gear
+ READ_FILE(S["asaycolor"], asaycolor)
+ READ_FILE(S["ooccolor"], ooccolor)
+ READ_FILE(S["lastchangelog"], lastchangelog)
+ READ_FILE(S["UI_style"], UI_style)
+ READ_FILE(S["outline_color"], outline_color)
+ READ_FILE(S["outline_enabled"], outline_enabled)
+ READ_FILE(S["hotkeys"], hotkeys)
+ READ_FILE(S["chat_on_map"], chat_on_map)
+ READ_FILE(S["see_chat_non_mob"] , see_chat_non_mob)
+ READ_FILE(S["see_rc_emotes"] , see_rc_emotes)
+ READ_FILE(S["see_balloon_alerts"], see_balloon_alerts)
+ READ_FILE(S["tgui_fancy"], tgui_fancy)
+ READ_FILE(S["tgui_lock"], tgui_lock)
+ READ_FILE(S["buttons_locked"], buttons_locked)
+ READ_FILE(S["windowflash"], windowflashing)
+ READ_FILE(S["be_special"], be_special)
+
+ READ_FILE(S["crew_objectives"], crew_objectives)
+
+
+ READ_FILE(S["default_slot"], default_slot)
+ READ_FILE(S["chat_toggles"], chat_toggles)
+ READ_FILE(S["toggles"], toggles)
+ READ_FILE(S["ghost_form"], ghost_form)
+ READ_FILE(S["ghost_orbit"], ghost_orbit)
+ READ_FILE(S["ghost_accs"], ghost_accs)
+ READ_FILE(S["ghost_others"], ghost_others)
+ READ_FILE(S["preferred_map"], preferred_map)
+ READ_FILE(S["ignoring"], ignoring)
+ READ_FILE(S["ghost_hud"], ghost_hud)
+ READ_FILE(S["inquisitive_ghost"], inquisitive_ghost)
+ READ_FILE(S["uses_glasses_colour"], uses_glasses_colour)
+ READ_FILE(S["clientfps"], clientfps)
+ READ_FILE(S["parallax"], parallax)
+ READ_FILE(S["ambientocclusion"], ambientocclusion)
+ READ_FILE(S["auto_fit_viewport"], auto_fit_viewport)
+ READ_FILE(S["pixel_size"], pixel_size)
+ READ_FILE(S["scaling_method"], scaling_method)
+ READ_FILE(S["menuoptions"], menuoptions)
+ READ_FILE(S["enable_tips"], enable_tips)
+ READ_FILE(S["tip_delay"], tip_delay)
+ READ_FILE(S["pda_style"], pda_style)
+ READ_FILE(S["pda_color"], pda_color)
+ READ_FILE(S["show_credits"], show_credits)
+
+ READ_FILE(S["key_bindings"], key_bindings)
+
+ READ_FILE(S["purchased_gear"], purchased_gear)
+ READ_FILE(S["equipped_gear"], equipped_gear)
//try to fix any outdated data if necessary
if(needs_update >= 0)
update_preferences(needs_update, S) //needs_update = savefile_version if we need an update (positive integer)
//Sanitize
- asaycolor = sanitize_ooccolor(sanitize_hexcolor(asaycolor, 6, 1, initial(asaycolor)))
- ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor)))
+ asaycolor = sanitize_ooccolor(sanitize_hexcolor(asaycolor, 6, TRUE, initial(asaycolor)))
+ ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, TRUE, initial(ooccolor)))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1])
- hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys))
- tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
- tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock))
- buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked))
- windowflashing = sanitize_integer(windowflashing, 0, 1, initial(windowflashing))
- default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot))
- toggles = sanitize_integer(toggles, 0, 65535, initial(toggles))
- clientfps = sanitize_integer(clientfps, 0, 1000, 0)
+ hotkeys = sanitize_integer(hotkeys, FALSE, TRUE, initial(hotkeys))
+ chat_on_map = sanitize_integer(chat_on_map, FALSE, TRUE, initial(chat_on_map))
+ see_chat_non_mob = sanitize_integer(see_chat_non_mob, FALSE, TRUE, initial(see_chat_non_mob))
+ tgui_fancy = sanitize_integer(tgui_fancy, FALSE, TRUE, initial(tgui_fancy))
+ tgui_lock = sanitize_integer(tgui_lock, FALSE, TRUE, initial(tgui_lock))
+ buttons_locked = sanitize_integer(buttons_locked, FALSE, TRUE, initial(buttons_locked))
+ windowflashing = sanitize_integer(windowflashing, FALSE, TRUE, initial(windowflashing))
+ default_slot = sanitize_integer(default_slot, TRUE, max_save_slots, initial(default_slot))
+ toggles = sanitize_integer(toggles, FALSE, 65535, initial(toggles))
+ clientfps = sanitize_integer(clientfps, FALSE, 1000, FALSE)
parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null)
- ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion))
- auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport))
+ ambientocclusion = sanitize_integer(ambientocclusion, FALSE, TRUE, initial(ambientocclusion))
+ auto_fit_viewport = sanitize_integer(auto_fit_viewport, FALSE, TRUE, initial(auto_fit_viewport))
pixel_size = sanitize_integer(pixel_size, PIXEL_SCALING_AUTO, PIXEL_SCALING_3X, initial(pixel_size))
scaling_method = sanitize_text(scaling_method, initial(scaling_method))
ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form))
@@ -219,10 +243,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION)
menuoptions = SANITIZE_LIST(menuoptions)
be_special = SANITIZE_LIST(be_special)
- crew_objectives = sanitize_integer(crew_objectives, 0, 1, initial(crew_objectives))
+ crew_objectives = sanitize_integer(crew_objectives, FALSE, TRUE, initial(crew_objectives))
pda_style = sanitize_inlist(pda_style, GLOB.pda_styles, initial(pda_style))
- pda_color = sanitize_hexcolor(pda_color, 6, 1, initial(pda_color))
- show_credits = sanitize_integer(show_credits, 0, 1, initial(show_credits))
+ pda_color = sanitize_hexcolor(pda_color, 6, TRUE, initial(pda_color))
+ show_credits = sanitize_integer(show_credits, FALSE, TRUE, initial(show_credits))
key_bindings = sanitize_islist(key_bindings, deepCopyList(GLOB.keybinding_list_by_key))
if (!key_bindings)
@@ -233,14 +257,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if(!equipped_gear)
equipped_gear = list()
- return 1
+ return TRUE
/datum/preferences/proc/save_preferences()
if(!path)
- return 0
+ return FALSE
var/savefile/S = new /savefile(path)
if(!S)
- return 0
+ return FALSE
S.cd = "/"
WRITE_FILE(S["version"] , SAVEFILE_VERSION_MAX) //updates (or failing that the sanity checks) will ensure data is not invalid at load. Assume up-to-date
@@ -250,10 +274,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["ooccolor"], ooccolor)
WRITE_FILE(S["lastchangelog"], lastchangelog)
WRITE_FILE(S["UI_style"], UI_style)
- WRITE_FILE(S["overhead_chat"], overhead_chat)
WRITE_FILE(S["outline_enabled"], outline_enabled)
WRITE_FILE(S["outline_color"], outline_color)
WRITE_FILE(S["hotkeys"], hotkeys)
+ WRITE_FILE(S["chat_on_map"], chat_on_map)
+ WRITE_FILE(S["see_chat_non_mob"], see_chat_non_mob)
+ WRITE_FILE(S["see_rc_emotes"], see_rc_emotes)
+ WRITE_FILE(S["see_balloon_alerts"], see_balloon_alerts)
WRITE_FILE(S["tgui_fancy"], tgui_fancy)
WRITE_FILE(S["tgui_lock"], tgui_lock)
WRITE_FILE(S["buttons_locked"], buttons_locked)
@@ -290,20 +317,20 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if (!key_bindings)
key_bindings = deepCopyList(GLOB.keybinding_list_by_key)
WRITE_FILE(S["key_bindings"], key_bindings)
- return 1
+ return TRUE
/datum/preferences/proc/load_character(slot)
if(!path)
- return 0
+ return FALSE
if(!fexists(path))
- return 0
+ return FALSE
var/savefile/S = new /savefile(path)
if(!S)
- return 0
+ return FALSE
S.cd = "/"
if(!slot)
slot = default_slot
- slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot))
+ slot = sanitize_integer(slot, TRUE, max_save_slots, initial(default_slot))
if(slot != default_slot)
default_slot = slot
WRITE_FILE(S["default_slot"] , slot)
@@ -315,7 +342,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
//Species
var/species_id
- S["species"] >> species_id
+ READ_FILE(S["species"], species_id)
if(species_id)
var/newtype = GLOB.species_list[species_id]
if(newtype)
@@ -328,59 +355,60 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["feature_ethcolor"] , "9c3030")
//Character
- S["real_name"] >> real_name
- S["name_is_always_random"] >> be_random_name
- S["body_is_always_random"] >> be_random_body
- S["gender"] >> gender
- S["age"] >> age
- S["hair_color"] >> hair_color
- S["facial_hair_color"] >> facial_hair_color
- S["eye_color"] >> eye_color
- S["skin_tone"] >> skin_tone
- S["hair_style_name"] >> hair_style
- S["facial_style_name"] >> facial_hair_style
- S["underwear"] >> underwear
- S["underwear_color"] >> underwear_color
- S["undershirt"] >> undershirt
- S["socks"] >> socks
- S["backbag"] >> backbag
- S["uplink_loc"] >> uplink_spawn_loc
- S["feature_mcolor"] >> features["mcolor"]
- S["feature_ethcolor"] >> features["ethcolor"]
- S["feature_lizard_tail"] >> features["tail_lizard"]
- S["feature_lizard_snout"] >> features["snout"]
- S["feature_lizard_horns"] >> features["horns"]
- S["feature_lizard_frills"] >> features["frills"]
- S["feature_lizard_spines"] >> features["spines"]
- S["feature_lizard_body_markings"] >> features["body_markings"]
- S["feature_lizard_legs"] >> features["legs"]
- S["feature_moth_wings"] >> features["moth_wings"]
- S["feature_ipc_screen"] >> features["ipc_screen"]
- S["feature_ipc_antenna"] >> features["ipc_antenna"]
- S["feature_ipc_chassis"] >> features["ipc_chassis"]
- S["feature_insect_type"] >> features["insect_type"]
- if(!CONFIG_GET(flag/join_with_mutant_humans) && !species_id != "felinid") // felinids arent mutant humans anymore i guess
+ READ_FILE(S["real_name"], real_name)
+ READ_FILE(S["name_is_always_random"], be_random_name)
+ READ_FILE(S["body_is_always_random"], be_random_body)
+ READ_FILE(S["gender"], gender)
+ READ_FILE(S["age"], age)
+ READ_FILE(S["hair_color"], hair_color)
+ READ_FILE(S["facial_hair_color"], facial_hair_color)
+ READ_FILE(S["eye_color"], eye_color)
+ READ_FILE(S["skin_tone"], skin_tone)
+ READ_FILE(S["hair_style_name"], hair_style)
+ READ_FILE(S["facial_style_name"], facial_hair_style)
+ READ_FILE(S["underwear"], underwear)
+ READ_FILE(S["underwear_color"], underwear_color)
+ READ_FILE(S["undershirt"], undershirt)
+ READ_FILE(S["socks"], socks)
+ READ_FILE(S["backbag"], backbag)
+ READ_FILE(S["jumpsuit_style"], jumpsuit_style)
+ READ_FILE(S["uplink_loc"], uplink_spawn_loc)
+ READ_FILE(S["feature_mcolor"], features["mcolor"])
+ READ_FILE(S["feature_ethcolor"], features["ethcolor"])
+ READ_FILE(S["feature_lizard_tail"], features["tail_lizard"])
+ READ_FILE(S["feature_lizard_snout"], features["snout"])
+ READ_FILE(S["feature_lizard_horns"], features["horns"])
+ READ_FILE(S["feature_lizard_frills"], features["frills"])
+ READ_FILE(S["feature_lizard_spines"], features["spines"])
+ READ_FILE(S["feature_lizard_body_markings"], features["body_markings"])
+ READ_FILE(S["feature_lizard_legs"], features["legs"])
+ READ_FILE(S["feature_moth_wings"], features["moth_wings"])
+ READ_FILE(S["feature_ipc_screen"], features["ipc_screen"])
+ READ_FILE(S["feature_ipc_antenna"], features["ipc_antenna"])
+ READ_FILE(S["feature_ipc_chassis"], features["ipc_chassis"])
+ READ_FILE(S["feature_insect_type"], features["insect_type"])
+ if(!CONFIG_GET(flag/join_with_mutant_humans) && !species_id != "felinid") // felinids arent mutant humans anymore i guess
features["tail_human"] = "none"
features["ears"] = "none"
else
- S["feature_human_tail"] >> features["tail_human"]
- S["feature_human_ears"] >> features["ears"]
+ READ_FILE(S["feature_human_tail"], features["tail_human"])
+ READ_FILE(S["feature_human_ears"], features["ears"])
//Custom names
for(var/custom_name_id in GLOB.preferences_custom_names)
var/savefile_slot_name = custom_name_id + "_name" //TODO remove this
- S[savefile_slot_name] >> custom_names[custom_name_id]
+ READ_FILE(S[savefile_slot_name], custom_names[custom_name_id])
- S["preferred_ai_core_display"] >> preferred_ai_core_display
- S["prefered_security_department"] >> prefered_security_department
+ READ_FILE(S["preferred_ai_core_display"], preferred_ai_core_display)
+ READ_FILE(S["prefered_security_department"], prefered_security_department)
//Jobs
- S["joblessrole"] >> joblessrole
+ READ_FILE(S["joblessrole"], joblessrole)
//Load prefs
- S["job_preferences"] >> job_preferences
+ READ_FILE(S["job_preferences"], job_preferences)
//Quirks
- S["all_quirks"] >> all_quirks
+ READ_FILE(S["all_quirks"], all_quirks)
//try to fix any outdated data if necessary
if(needs_update >= 0)
@@ -426,6 +454,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
eye_color = sanitize_hexcolor(eye_color, 3, 0)
skin_tone = sanitize_inlist(skin_tone, GLOB.skin_tones)
backbag = sanitize_inlist(backbag, GLOB.backbaglist, initial(backbag))
+ jumpsuit_style = sanitize_inlist(jumpsuit_style, GLOB.jumpsuitlist, initial(jumpsuit_style))
uplink_spawn_loc = sanitize_inlist(uplink_spawn_loc, GLOB.uplink_spawn_loc_list, initial(uplink_spawn_loc))
features["mcolor"] = sanitize_hexcolor(features["mcolor"], 3, 0)
features["ethcolor"] = copytext_char(features["ethcolor"], 1, 7)
@@ -444,6 +473,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
features["ipc_chassis"] = sanitize_inlist(features["ipc_chassis"], GLOB.ipc_chassis_list)
features["insect_type"] = sanitize_inlist(features["insect_type"], GLOB.insect_type_list)
+ //Validate species forced mutant parts
+ for(var/forced_part in pref_species.forced_features)
+ //Get the forced type
+ var/forced_type = pref_species.forced_features[forced_part]
+ //Apply the forced bodypart.
+ features[forced_part] = forced_type
+
joblessrole = sanitize_integer(joblessrole, 1, 3, initial(joblessrole))
//Validate job prefs
for(var/j in job_preferences)
@@ -452,14 +488,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
all_quirks = SANITIZE_LIST(all_quirks)
- return 1
+ return TRUE
/datum/preferences/proc/save_character()
if(!path)
- return 0
+ return FALSE
var/savefile/S = new /savefile(path)
if(!S)
- return 0
+ return FALSE
S.cd = "/character[default_slot]"
WRITE_FILE(S["version"] , SAVEFILE_VERSION_MAX) //load_character will sanitize any bad data, so assume up-to-date.)
@@ -483,6 +519,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["undershirt"] , undershirt)
WRITE_FILE(S["socks"] , socks)
WRITE_FILE(S["backbag"] , backbag)
+ WRITE_FILE(S["jumpsuit_style"] , jumpsuit_style)
WRITE_FILE(S["uplink_loc"] , uplink_spawn_loc)
WRITE_FILE(S["species"] , pref_species.id)
WRITE_FILE(S["feature_mcolor"] , features["mcolor"])
@@ -518,7 +555,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
//Quirks
WRITE_FILE(S["all_quirks"] , all_quirks)
- return 1
+ return TRUE
#undef SAVEFILE_VERSION_MAX
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index 4969becb09265..c875b07fb41de 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -1,266 +1,206 @@
-//this works as is to create a single checked item, but has no back end code for toggleing the check yet
-#define TOGGLE_CHECKBOX(PARENT, CHILD) PARENT/CHILD/abstract = TRUE;PARENT/CHILD/checkbox = CHECKBOX_TOGGLE;PARENT/CHILD/verb/CHILD
-
-//Example usage TOGGLE_CHECKBOX(datum/verbs/menu/Settings/Ghost/chatterbox, toggle_ghost_ears)()
-
-//override because we don't want to save preferences twice.
-/datum/verbs/menu/Settings/Set_checked(client/C, verbpath)
- if (checkbox == CHECKBOX_GROUP)
- C.prefs.menuoptions[type] = verbpath
- else if (checkbox == CHECKBOX_TOGGLE)
- var/checked = Get_checked(C)
- C.prefs.menuoptions[type] = !checked
- winset(C, "[verbpath]", "is-checked = [!checked]")
-
-/datum/verbs/menu/Settings/verb/setup_character()
+/client/verb/setup_character()
set name = "Game Preferences"
set category = "Preferences"
set desc = "Open Game Preferences Window"
- usr.client.prefs.current_tab = 1
- usr.client.prefs.ShowChoices(usr)
+ prefs.current_tab = 1
+ prefs.ShowChoices(usr)
-//toggles
-/datum/verbs/menu/Settings/Ghost/chatterbox
- name = "Chat Box Spam"
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost/chatterbox, toggle_ghost_ears)()
+/client/verb/toggle_ghost_ears()
set name = "Show/Hide GhostEars"
set category = "Preferences"
set desc = "See All Speech"
- usr.client.prefs.chat_toggles ^= CHAT_GHOSTEARS
- to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTEARS) ? "see all speech in the world" : "only see speech from nearby mobs"].")
- usr.client.prefs.save_preferences()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Ears", "[usr.client.prefs.chat_toggles & CHAT_GHOSTEARS ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Ghost/chatterbox/toggle_ghost_ears/Get_checked(client/C)
- return C.prefs.chat_toggles & CHAT_GHOSTEARS
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost/chatterbox, toggle_ghost_sight)()
+ prefs.chat_toggles ^= CHAT_GHOSTEARS
+ to_chat(usr, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTEARS) ? "see all speech in the world" : "only see speech from nearby mobs"].")
+ prefs.save_preferences()
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Ears", "[prefs.chat_toggles & CHAT_GHOSTEARS ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggle_ghost_sight()
set name = "Show/Hide GhostSight"
set category = "Preferences"
set desc = "See All Emotes"
- usr.client.prefs.chat_toggles ^= CHAT_GHOSTSIGHT
- to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTSIGHT) ? "see all emotes in the world" : "only see emotes from nearby mobs"].")
- usr.client.prefs.save_preferences()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Sight", "[usr.client.prefs.chat_toggles & CHAT_GHOSTSIGHT ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Ghost/chatterbox/toggle_ghost_sight/Get_checked(client/C)
- return C.prefs.chat_toggles & CHAT_GHOSTSIGHT
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost/chatterbox, toggle_ghost_whispers)()
+ prefs.chat_toggles ^= CHAT_GHOSTSIGHT
+ to_chat(usr, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTSIGHT) ? "see all emotes in the world" : "only see emotes from nearby mobs"].")
+ prefs.save_preferences()
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Sight", "[prefs.chat_toggles & CHAT_GHOSTSIGHT ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggle_ghost_whispers()
set name = "Show/Hide GhostWhispers"
set category = "Preferences"
set desc = "See All Whispers"
- usr.client.prefs.chat_toggles ^= CHAT_GHOSTWHISPER
- to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTWHISPER) ? "see all whispers in the world" : "only see whispers from nearby mobs"].")
- usr.client.prefs.save_preferences()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Whispers", "[usr.client.prefs.chat_toggles & CHAT_GHOSTWHISPER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Ghost/chatterbox/toggle_ghost_whispers/Get_checked(client/C)
- return C.prefs.chat_toggles & CHAT_GHOSTWHISPER
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost/chatterbox, toggle_ghost_radio)()
+ prefs.chat_toggles ^= CHAT_GHOSTWHISPER
+ to_chat(usr, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTWHISPER) ? "see all whispers in the world" : "only see whispers from nearby mobs"].")
+ prefs.save_preferences()
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Whispers", "[prefs.chat_toggles & CHAT_GHOSTWHISPER ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggle_ghost_radio()
set name = "Show/Hide GhostRadio"
set category = "Preferences"
set desc = "See All Radio Chatter"
- usr.client.prefs.chat_toggles ^= CHAT_GHOSTRADIO
- to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTRADIO) ? "see radio chatter" : "not see radio chatter"].")
- usr.client.prefs.save_preferences()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Radio", "[usr.client.prefs.chat_toggles & CHAT_GHOSTRADIO ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //social experiment, increase the generation whenever you copypaste this shamelessly GENERATION 1
-/datum/verbs/menu/Settings/Ghost/chatterbox/toggle_ghost_radio/Get_checked(client/C)
- return C.prefs.chat_toggles & CHAT_GHOSTRADIO
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost/chatterbox, toggle_ghost_pda)()
+ prefs.chat_toggles ^= CHAT_GHOSTRADIO
+ to_chat(usr, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTRADIO) ? "see radio chatter" : "not see radio chatter"].")
+ prefs.save_preferences()
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Radio", "[prefs.chat_toggles & CHAT_GHOSTRADIO ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //social experiment, increase the generation whenever you copypaste this shamelessly GENERATION 1
+
+/client/verb/toggle_ghost_pda()
set name = "Show/Hide GhostPDA"
set category = "Preferences"
set desc = "See All PDA Messages"
- usr.client.prefs.chat_toggles ^= CHAT_GHOSTPDA
- to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTPDA) ? "see all pda messages in the world" : "only see pda messages from nearby mobs"].")
- usr.client.prefs.save_preferences()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost PDA", "[usr.client.prefs.chat_toggles & CHAT_GHOSTPDA ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Ghost/chatterbox/toggle_ghost_pda/Get_checked(client/C)
- return C.prefs.chat_toggles & CHAT_GHOSTPDA
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost/chatterbox, toggle_ghost_laws)()
+ prefs.chat_toggles ^= CHAT_GHOSTPDA
+ to_chat(usr, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTPDA) ? "see all pda messages in the world" : "only see pda messages from nearby mobs"].")
+ prefs.save_preferences()
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost PDA", "[prefs.chat_toggles & CHAT_GHOSTPDA ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggle_ghost_laws()
set name = "Show/Hide GhostLaws"
set category = "Preferences"
set desc = "See All Law Changes"
- usr.client.prefs.chat_toggles ^= CHAT_GHOSTLAWS
- to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTLAWS) ? "be notified of all law changes" : "no longer be notified of law changes"].")
- usr.client.prefs.save_preferences()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Laws", "[usr.client.prefs.chat_toggles & CHAT_GHOSTLAWS ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Ghost/chatterbox/toggle_ghost_laws/Get_checked(client/C)
- return C.prefs.chat_toggles & CHAT_GHOSTLAWS
-
-/datum/verbs/menu/Settings/Ghost/chatterbox/Events
- name = "Events"
+ prefs.chat_toggles ^= CHAT_GHOSTLAWS
+ to_chat(usr, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTLAWS) ? "be notified of all law changes" : "no longer be notified of law changes"].")
+ prefs.save_preferences()
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ghost Laws", "[prefs.chat_toggles & CHAT_GHOSTLAWS ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//please be aware that the following two verbs have inverted stat output, so that "Toggle Deathrattle|1" still means you activated it
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost/chatterbox/Events, toggle_deathrattle)()
+/client/verb/toggle_deathrattle()
set name = "Toggle Deathrattle"
set category = "Preferences"
set desc = "Death"
- usr.client.prefs.toggles ^= DISABLE_DEATHRATTLE
- usr.client.prefs.save_preferences()
- to_chat(usr, "You will [(usr.client.prefs.toggles & DISABLE_DEATHRATTLE) ? "no longer" : "now"] get messages when a sentient mob dies.")
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Deathrattle", "[!(usr.client.prefs.toggles & DISABLE_DEATHRATTLE) ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, maybe you should spend some time reading the comments.
-/datum/verbs/menu/Settings/Ghost/chatterbox/Events/toggle_deathrattle/Get_checked(client/C)
- return !(C.prefs.toggles & DISABLE_DEATHRATTLE)
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost/chatterbox/Events, toggle_arrivalrattle)()
+ prefs.toggles ^= DISABLE_DEATHRATTLE
+ prefs.save_preferences()
+ to_chat(usr, "You will [(prefs.toggles & DISABLE_DEATHRATTLE) ? "no longer" : "now"] get messages when a sentient mob dies.")
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Deathrattle", "[!(prefs.toggles & DISABLE_DEATHRATTLE) ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, maybe you should spend some time reading the comments.
+
+/client/verb/toggle_arrivalrattle()
set name = "Toggle Arrivalrattle"
set category = "Preferences"
set desc = "New Player Arrival"
- usr.client.prefs.toggles ^= DISABLE_ARRIVALRATTLE
- to_chat(usr, "You will [(usr.client.prefs.toggles & DISABLE_ARRIVALRATTLE) ? "no longer" : "now"] get messages when someone joins the station.")
- usr.client.prefs.save_preferences()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Arrivalrattle", "[!(usr.client.prefs.toggles & DISABLE_ARRIVALRATTLE) ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, maybe you should rethink where your life went so wrong.
-/datum/verbs/menu/Settings/Ghost/chatterbox/Events/toggle_arrivalrattle/Get_checked(client/C)
- return !(C.prefs.toggles & DISABLE_ARRIVALRATTLE)
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Ghost, togglemidroundantag)()
+ prefs.toggles ^= DISABLE_ARRIVALRATTLE
+ to_chat(usr, "You will [(prefs.toggles & DISABLE_ARRIVALRATTLE) ? "no longer" : "now"] get messages when someone joins the station.")
+ prefs.save_preferences()
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Arrivalrattle", "[!(prefs.toggles & DISABLE_ARRIVALRATTLE) ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, maybe you should rethink where your life went so wrong.
+
+/client/verb/togglemidroundantag()
set name = "Toggle Midround Antagonist"
set category = "Preferences"
set desc = "Midround Antagonist"
- usr.client.prefs.toggles ^= MIDROUND_ANTAG
- usr.client.prefs.save_preferences()
- to_chat(usr, "You will [(usr.client.prefs.toggles & MIDROUND_ANTAG) ? "now" : "no longer"] be considered for midround antagonist positions.")
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Midround Antag", "[usr.client.prefs.toggles & MIDROUND_ANTAG ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Ghost/togglemidroundantag/Get_checked(client/C)
- return C.prefs.toggles & MIDROUND_ANTAG
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggletitlemusic)()
+ prefs.toggles ^= MIDROUND_ANTAG
+ prefs.save_preferences()
+ to_chat(usr, "You will [(prefs.toggles & MIDROUND_ANTAG) ? "now" : "no longer"] be considered for midround antagonist positions.")
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Midround Antag", "[prefs.toggles & MIDROUND_ANTAG ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggletitlemusic()
set name = "Hear/Silence Lobby Music"
set category = "Preferences"
set desc = "Hear Music In Lobby"
- usr.client.prefs.toggles ^= SOUND_LOBBY
- usr.client.prefs.save_preferences()
- if(usr.client.prefs.toggles & SOUND_LOBBY)
+ prefs.toggles ^= SOUND_LOBBY
+ prefs.save_preferences()
+ if(prefs.toggles & SOUND_LOBBY)
to_chat(usr, "You will now hear music in the game lobby.")
if(isnewplayer(usr))
- usr.client.playtitlemusic()
+ playtitlemusic()
else
to_chat(usr, "You will no longer hear music in the game lobby.")
usr.stop_sound_channel(CHANNEL_LOBBYMUSIC)
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Lobby Music", "[usr.client.prefs.toggles & SOUND_LOBBY ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Sound/toggletitlemusic/Get_checked(client/C)
- return C.prefs.toggles & SOUND_LOBBY
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Lobby Music", "[prefs.toggles & SOUND_LOBBY ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglemidis)()
+/client/verb/togglemidis()
set name = "Hear/Silence Midis"
set category = "Preferences"
set desc = "Hear Admin Triggered Sounds (Midis)"
- usr.client.prefs.toggles ^= SOUND_MIDI
- usr.client.prefs.save_preferences()
- if(usr.client.prefs.toggles & SOUND_MIDI)
+ prefs.toggles ^= SOUND_MIDI
+ prefs.save_preferences()
+ if(prefs.toggles & SOUND_MIDI)
to_chat(usr, "You will now hear any sounds uploaded by admins.")
else
to_chat(usr, "You will no longer hear sounds uploaded by admins")
usr.stop_sound_channel(CHANNEL_ADMIN)
var/client/C = usr.client
C?.tgui_panel?.stop_music()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Hearing Midis", "[usr.client.prefs.toggles & SOUND_MIDI ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Sound/togglemidis/Get_checked(client/C)
- return C.prefs.toggles & SOUND_MIDI
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Hearing Midis", "[prefs.toggles & SOUND_MIDI ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggle_instruments)()
+/client/verb/toggle_instruments()
set name = "Hear/Silence Instruments"
set category = "Preferences"
set desc = "Hear In-game Instruments"
- usr.client.prefs.toggles ^= SOUND_INSTRUMENTS
- usr.client.prefs.save_preferences()
- if(usr.client.prefs.toggles & SOUND_INSTRUMENTS)
+ prefs.toggles ^= SOUND_INSTRUMENTS
+ prefs.save_preferences()
+ if(prefs.toggles & SOUND_INSTRUMENTS)
to_chat(usr, "You will now hear people playing musical instruments.")
else
to_chat(usr, "You will no longer hear musical instruments.")
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Instruments", "[usr.client.prefs.toggles & SOUND_INSTRUMENTS ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Sound/toggle_instruments/Get_checked(client/C)
- return C.prefs.toggles & SOUND_INSTRUMENTS
-
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Instruments", "[prefs.toggles & SOUND_INSTRUMENTS ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, Toggle_Soundscape)()
+/client/verb/Toggle_Soundscape()
set name = "Hear/Silence Ambience"
set category = "Preferences"
set desc = "Hear Ambient Sound Effects"
- usr.client.prefs.toggles ^= SOUND_AMBIENCE
- usr.client.prefs.save_preferences()
- if(usr.client.prefs.toggles & SOUND_AMBIENCE)
+ prefs.toggles ^= SOUND_AMBIENCE
+ prefs.save_preferences()
+ if(prefs.toggles & SOUND_AMBIENCE)
to_chat(usr, "You will now hear ambient sounds.")
else
to_chat(usr, "You will no longer hear ambient sounds.")
usr.stop_sound_channel(CHANNEL_AMBIENT_EFFECTS)
usr.stop_sound_channel(CHANNEL_AMBIENT_MUSIC)
+ usr.stop_sound_channel(CHANNEL_BUZZ)
+ usr.client.buzz_playing = FALSE
+ usr.client.update_ambience_pref()
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ambience", "[usr.client.prefs.toggles & SOUND_AMBIENCE ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Sound/Toggle_Soundscape/Get_checked(client/C)
- return C.prefs.toggles & SOUND_AMBIENCE
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggle_ship_ambience)()
+/client/verb/toggle_ship_ambience()
set name = "Hear/Silence Ship Ambience"
set category = "Preferences"
set desc = "Hear Ship Ambience Roar"
- usr.client.prefs.toggles ^= SOUND_SHIP_AMBIENCE
- usr.client.prefs.save_preferences()
- if(usr.client.prefs.toggles & SOUND_SHIP_AMBIENCE)
+ prefs.toggles ^= SOUND_SHIP_AMBIENCE
+ prefs.save_preferences()
+ if(prefs.toggles & SOUND_SHIP_AMBIENCE)
to_chat(usr, "You will now hear ship ambience.")
else
to_chat(usr, "You will no longer hear ship ambience.")
- usr.stop_sound_channel(CHANNEL_AMBIENT_BUZZ)
- usr.client.ambient_buzz_playing = FALSE
+ usr.stop_sound_channel(CHANNEL_BUZZ)
+ usr.client.buzz_playing = FALSE
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Ship Ambience", "[usr.client.prefs.toggles & SOUND_SHIP_AMBIENCE ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, I bet you read this comment expecting to see the same thing :^)
-/datum/verbs/menu/Settings/Sound/toggle_ship_ambience/Get_checked(client/C)
- return C.prefs.toggles & SOUND_SHIP_AMBIENCE
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggle_announcement_sound)()
+/client/verb/toggle_announcement_sound()
set name = "Hear/Silence Announcements"
set category = "Preferences"
set desc = "Hear Announcement Sound"
- usr.client.prefs.toggles ^= SOUND_ANNOUNCEMENTS
- to_chat(usr, "You will now [(usr.client.prefs.toggles & SOUND_ANNOUNCEMENTS) ? "hear announcement sounds" : "no longer hear announcements"].")
- usr.client.prefs.save_preferences()
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Announcement Sound", "[usr.client.prefs.toggles & SOUND_ANNOUNCEMENTS ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Sound/toggle_announcement_sound/Get_checked(client/C)
- return C.prefs.toggles & SOUND_ANNOUNCEMENTS
-
+ prefs.toggles ^= SOUND_ANNOUNCEMENTS
+ to_chat(usr, "You will now [(prefs.toggles & SOUND_ANNOUNCEMENTS) ? "hear announcement sounds" : "no longer hear announcements"].")
+ prefs.save_preferences()
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Announcement Sound", "[prefs.toggles & SOUND_ANNOUNCEMENTS ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/Sound/verb/stop_client_sounds()
+/client/verb/stop_client_sounds()
set name = "Stop Sounds"
set category = "Preferences"
set desc = "Stop Current Sounds"
SEND_SOUND(usr, sound(null))
- var/client/C = usr.client
- C?.tgui_panel?.stop_music()
+ tgui_panel?.stop_music()
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Stop Self Sounds")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings, listen_ooc)()
+/client/verb/listen_ooc()
set name = "Show/Hide OOC"
set category = "Preferences"
set desc = "Show OOC Chat"
- usr.client.prefs.chat_toggles ^= CHAT_OOC
- usr.client.prefs.save_preferences()
- to_chat(usr, "You will [(usr.client.prefs.chat_toggles & CHAT_OOC) ? "now" : "no longer"] see messages on the OOC channel.")
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Seeing OOC", "[usr.client.prefs.chat_toggles & CHAT_OOC ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/verbs/menu/Settings/listen_ooc/Get_checked(client/C)
- return C.prefs.chat_toggles & CHAT_OOC
-
-TOGGLE_CHECKBOX(/datum/verbs/menu/Settings, listen_bank_card)()
+ prefs.chat_toggles ^= CHAT_OOC
+ prefs.save_preferences()
+ to_chat(usr, "You will [(prefs.chat_toggles & CHAT_OOC) ? "now" : "no longer"] see messages on the OOC channel.")
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Seeing OOC", "[prefs.chat_toggles & CHAT_OOC ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/listen_bank_card()
set name = "Show/Hide Income Updates"
set category = "Preferences"
set desc = "Show or hide updates to your income"
- usr.client.prefs.chat_toggles ^= CHAT_BANKCARD
- usr.client.prefs.save_preferences()
- to_chat(usr, "You will [(usr.client.prefs.chat_toggles & CHAT_BANKCARD) ? "now" : "no longer"] be notified when you get paid.")
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Income Notifications", "[(usr.client.prefs.chat_toggles & CHAT_BANKCARD) ? "Enabled" : "Disabled"]"))
-/datum/verbs/menu/Settings/listen_bank_card/Get_checked(client/C)
- return C.prefs.chat_toggles & CHAT_BANKCARD
-
+ prefs.chat_toggles ^= CHAT_BANKCARD
+ prefs.save_preferences()
+ to_chat(usr, "You will [(prefs.chat_toggles & CHAT_BANKCARD) ? "now" : "no longer"] be notified when you get paid.")
+ SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Income Notifications", "[(prefs.chat_toggles & CHAT_BANKCARD) ? "Enabled" : "Disabled"]"))
GLOBAL_LIST_INIT(ghost_forms, sortList(list("ghost","ghostking","ghostian2","skeleghost","ghost_red","ghost_black", \
"ghost_blue","ghost_yellow","ghost_green","ghost_pink", \
"ghost_cyan","ghost_dblue","ghost_dred","ghost_dgreen", \
"ghost_dcyan","ghost_grey","ghost_dyellow","ghost_dpink", "ghost_purpleswirl","ghost_funkypurp","ghost_pinksherbert","ghost_blazeit",\
"ghost_mellow","ghost_rainbow","ghost_camo","ghost_fire", "catghost")))
+
/client/proc/pick_form()
if(!is_content_unlocked())
alert("This setting is for accounts with BYOND premium only.")
@@ -444,7 +384,7 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS
prefs.toggles ^= SOUND_PRAYERS
prefs.save_preferences()
to_chat(usr, "You will [(prefs.toggles & SOUND_PRAYERS) ? "now" : "no longer"] hear a sound when prayers arrive.")
- SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Prayer Sounds", "[usr.client.prefs.toggles & SOUND_PRAYERS ? "Enabled" : "Disabled"]"))
+ SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Prayer Sounds", "[prefs.toggles & SOUND_PRAYERS ? "Enabled" : "Disabled"]"))
/client/proc/colorasay()
set name = "Set Admin Say Color"
diff --git a/code/modules/client/verbs/input_box.dm b/code/modules/client/verbs/input_box.dm
new file mode 100644
index 0000000000000..772f6efd4152a
--- /dev/null
+++ b/code/modules/client/verbs/input_box.dm
@@ -0,0 +1,156 @@
+/client/proc/create_input_window(id, title, accept_verb, cancel_verb, force=FALSE, show=TRUE)
+ if(winexists(src, id))
+ if(force)
+ //Delete all clientside objects that are part of the window
+ winset(src, "[id]_macro_returnup", "parent=none")
+ winset(src, "[id]_macro_return", "parent=none")
+ winset(src, "[id]_macro_escape", "parent=none")
+ winset(src, "persist_[id]_macro", "parent=none")
+ winset(src, id, "parent=none")
+ else
+ return
+
+ // Create a macro set for handling enter presses
+ winclone(src, "input_box_macro", "persist_[id]_macro")
+ winset(src, "[id]_macro_returnup", "parent=persist_[id]_macro;name=Return+UP;command=\".winset \\\"[id].is-visible=false\\\"\"")
+ // Return+UP allows us to close the window after typing in a command, pressing enter and releasing enter.
+ // Can't use just Return for this, because when there's text in the box Return is handled by BYOND and doesn't run the macro.
+
+ // Create the actual window and set its title and macro set
+ winclone(src, "input_box", id)
+ winset(src, id, "title=\"[title]\";macro=persist_[id]_macro")
+
+ if(accept_verb)
+ winset(src, "[id].input", "command=\"[accept_verb] \\\"\"")
+ winset(src, "[id].accept", "command=\".winset \\\"command=\\\"[accept_verb] \\\\\\\"\[\[[id].input.text as escaped\]\]\\\";[id].is-visible=false\\\"\"")
+
+ if(cancel_verb)
+ winset(src, "[id].cancel", "command=\".winset \\\"command=\\\"[cancel_verb]\\\";[id].is-visible=false\\\"\"")
+ winset(src, "[id]_macro_return", "parent=persist_[id]_macro;name=Return;command=\".winset \\\"command=\\\"[cancel_verb]\\\";[id].is-visible=false\\\"\"")
+ winset(src, "[id]_macro_escape", "parent=persist_[id]_macro;name=Escape;command=\".winset \\\"command=\\\"[cancel_verb]\\\";[id].is-visible=false\\\"\"")
+ winset(src, id, "on-close=\"[cancel_verb]\"")
+ else
+ winset(src, "[id]_macro_return", "parent=persist_[id]_macro;name=Return;command=\".winset \\\"[id].is-visible=false\\\"\"")
+ winset(src, "[id]_macro_escape", "parent=persist_[id]_macro;name=Escape;command=\".winset \\\"[id].is-visible=false\\\"\"")
+
+ //Window scaling!
+ //BYOND doesn't scale the window by DPI scaling, so it'll appear too big/too small with DPI scaling other than the one it was based on
+ //This code uses the title bar to figure out what DPI scaling is being used and resize the window based on that
+ //Figure out the DPI scaling based on the titlebar size of the window, based on outer-inner height
+ var/window_data = params2list(winget(src, id, "outer-size;inner-size"))
+ var/window_innersize = splittext(window_data["inner-size"], "x")
+ var/window_outersize = splittext(window_data["outer-size"], "x")
+
+ var/titlebarHeight = text2num(window_outersize[2])-text2num(window_innersize[2])
+
+ //Known titlebar heights for DPI scaling:
+ //win7: 100%-28, 125%-33, 150%-39
+ //win10: 100%-29, 125%-35, 150%-40
+
+ //Known window sizes for DPI scaling: (Win7)
+ //100%: 302x86, font 7
+ //125%: 402x106, font 8
+ //150%: 503x133, font 8
+
+ var/scaling = FALSE
+
+ //Those are the default values for the window
+ var/window_width = 302
+ var/window_height = 86
+ var/font_size = 7
+
+ //The values used here were sampled from BYOND in practice, I couldn't find a formula that would describe them
+ switch(titlebarHeight)
+ if(30 to 37)
+ scaling = 1.25
+ window_width = 402
+ window_height = 106
+ font_size = 8
+ if(37 to 42)
+ scaling = 1.50
+ window_width = 503
+ window_height = 133
+ font_size = 8
+
+ if(scaling)
+ winset(src, null, "[id].size=[window_width]x[window_height];[id].input.font-size=[font_size];[id].accept.font-size=[font_size];[id].cancel.font-size=[font_size]")
+ //End window scaling
+
+ //Center the window on the main window
+ //The window size is hardcoded to be 410x133, taken from skin.dmf
+ var/mainwindow_data = params2list(winget(src, "mainwindow", "pos;outer-size;size;inner-size;is-maximized"))
+ var/mainwindow_pos = splittext(mainwindow_data["pos"], ",")
+ var/mainwindow_size = splittext(mainwindow_data["size"], "x")
+ var/mainwindow_innersize = splittext(mainwindow_data["inner-size"], "x")
+ var/mainwindow_outersize = splittext(mainwindow_data["outer-size"], "x")
+
+ var/maximized = (mainwindow_data["is-maximized"] == "true")
+
+ if(!maximized)
+ //If the window is anchored (for example win+right), is-maximized is false but pos is no longer reliable
+ //In that case, compare inner-size and size to guess if it's actually anchored
+ maximized = text2num(mainwindow_size[1]) != text2num(mainwindow_innersize[1])\
+ || abs(text2num(mainwindow_size[2]) - text2num(mainwindow_innersize[2])) > 30
+
+ var/target_x
+ var/target_y
+
+ // If the window is maximized or anchored, pos is the last position when the window was free-floating
+ if(maximized)
+ target_x = text2num(mainwindow_outersize[1])/2-window_width/2
+ target_y = text2num(mainwindow_outersize[2])/2-window_height/2
+ else
+ target_x = text2num(mainwindow_pos[1])+text2num(mainwindow_outersize[1])/2-window_width/2
+ target_y = text2num(mainwindow_pos[2])+text2num(mainwindow_outersize[2])/2-window_height/2
+
+ winset(src, id, "pos=[target_x],[target_y]")
+ //End centering
+
+ if(show)
+ //Show the window and focus on the textbox
+ winshow(src, id, TRUE)
+ winset(src, "[id].input", "focus=true")
+
+///Presets for standard windows
+GLOBAL_LIST_INIT(input_window_presets, list(
+ "say" = list("saywindow", "say \\\"text\\\"", ".say", ".cancel_typing say"),
+ "me" = list("mewindow", "me (text)", ".me", ".cancel_typing me"),
+))
+
+/client/proc/create_preset_input_window(name, force=FALSE, show=TRUE)
+ var/arglist = GLOB.input_window_presets[name]
+
+ create_input_window(arglist[1], arglist[2], arglist[3], arglist[4], force=force, show=show)
+
+//Those verbs are used by the hotkeys to ensure the window is created when you try to use it
+
+/client/verb/init_say()
+ set name = ".init_say"
+ set hidden = TRUE
+
+ create_preset_input_window("say")
+
+/client/verb/init_me()
+ set name = ".init_me"
+ set hidden = TRUE
+
+ create_preset_input_window("me")
+
+//Verb available to the user in case something in the window breaks
+/client/verb/fix_chatbox()
+ set name = "Fix chatbox"
+
+ var/preset = input(src, "Which chat window do you want to recreate?", "Fix chatbox") as null|anything in GLOB.input_window_presets
+
+ if(!preset)
+ return
+
+ create_preset_input_window(preset, force=TRUE)
+
+//Create the windows for say and me ahead of time
+/client/New()
+ . = ..()
+
+ if(src) //In case the client was deleted while New was running
+ create_preset_input_window("say", show=FALSE)
+ create_preset_input_window("me", show=FALSE)
diff --git a/code/modules/client/verbs/looc.dm b/code/modules/client/verbs/looc.dm
index dbd6f20968bb5..442daf6f00377 100644
--- a/code/modules/client/verbs/looc.dm
+++ b/code/modules/client/verbs/looc.dm
@@ -57,10 +57,10 @@ GLOBAL_VAR_INIT(looc_allowed, 1)
msg = emoji_parse(msg)
- mob.log_talk(raw_msg, LOG_OOC, tag="(LOOC)")
+ mob.log_talk(raw_msg, LOG_OOC, tag="LOOC")
- var/list/heard = get_hearers_in_view(7, get_top_level_mob(src.mob))
- for(var/mob/M in heard)
+ var/list/heard = hearers(7, get_top_level_mob(src.mob))
+ for(var/mob/M as() in heard)
if(!M.client)
continue
var/client/C = M.client
@@ -87,19 +87,6 @@ GLOBAL_VAR_INIT(looc_allowed, 1)
prefix = "LOOC"
to_chat(C,"[ADMIN_FLW(usr)][prefix]: [src.key]/[src.mob.name]: ")
- /*for(var/mob/dead/observer/G in world)
- if(!G.client)
- continue
- var/client/C = G.client
- if (C in GLOB.admins)
- continue //handled earlier.
- if(C.prefs.toggles & CHAT_OOC)
- var/prefix = "(G)LOOC"
- if (C.mob in heard)
- prefix = "LOOC"
- to_chat(C,"[prefix]: [src.key]/[src.mob.name]: ")*/
-
-
/proc/log_looc(text)
if (CONFIG_GET(flag/log_ooc))
WRITE_FILE(GLOB.world_game_log, "\[[time_stamp()]]LOOC: [text]")
@@ -110,7 +97,7 @@ GLOBAL_VAR_INIT(looc_allowed, 1)
return M.get_top_level_mob()
return src
-proc/get_top_level_mob(var/mob/S)
+/proc/get_top_level_mob(var/mob/S)
if(istype(S.loc,/mob)&&S.loc!=S)
var/mob/M=S.loc
return M.get_top_level_mob()
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index 5b5af3e4e39bc..ce7a96e3d107b 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -67,26 +67,28 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
if(prefs.unlock_content)
if(prefs.toggles & MEMBER_PUBLIC)
keyname = "[icon2html('icons/member_content.dmi', world, "blag")][keyname]"
+ //Get client badges
+ var/badge_data = badge_parse(get_badges())
//The linkify span classes and linkify=TRUE below make ooc text get clickable chat href links if you pass in something resembling a url
for(var/client/C in GLOB.clients)
if(C.prefs.chat_toggles & CHAT_OOC)
if(holder)
if(!holder.fakekey || C.holder)
if(check_rights_for(src, R_ADMIN))
- to_chat(C, "[CONFIG_GET(flag/allow_admin_ooccolor) && prefs.ooccolor ? "" :"" ]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: ")
+ to_chat(C, "[badge_data][CONFIG_GET(flag/allow_admin_ooccolor) && prefs.ooccolor ? "" :"" ]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: ", allow_linkify = TRUE)
else
- to_chat(C, "OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: ")
+ to_chat(C, "[badge_data]OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: ")
else
if(GLOB.OOC_COLOR)
- to_chat(C, "OOC: [holder.fakekey ? holder.fakekey : key]: ")
+ to_chat(C, "[badge_data]OOC: [holder.fakekey ? holder.fakekey : key]: ")
else
- to_chat(C, "OOC: [holder.fakekey ? holder.fakekey : key]: ")
+ to_chat(C, "[badge_data]OOC: [holder.fakekey ? holder.fakekey : key]: ")
else if(!(key in C.prefs.ignoring))
if(GLOB.OOC_COLOR)
- to_chat(C, "OOC: [keyname]: ")
+ to_chat(C, "[badge_data]OOC: [keyname]: ")
else
- to_chat(C, "OOC: [keyname]: ")
+ to_chat(C, "[badge_data]OOC: [keyname]: ")
// beestation, send to discord
if(holder?.fakekey)
discordsendmsg("ooc", "**[holder.fakekey]:** [msg]")
@@ -169,7 +171,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
var/motd = global.config.motd
if(motd)
- to_chat(src, " [motd] ", handle_whitespace=FALSE)
+ to_chat(src, "[motd] ", handle_whitespace=FALSE, allow_linkify = TRUE)
else
to_chat(src, "The Message of the Day has not been set.")
@@ -193,11 +195,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
to_chat(usr, "Sorry, tracking is currently disabled.")
return
- var/list/body = list()
- body += "Playtime:" - body += get_exp_report() - body += "" - usr << browse(body.Join(), "window=playerplaytime[ckey];size=550x615") + new /datum/job_report_menu(src, usr) /client/proc/ignore_key(client, displayed_key) var/client/C = client diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm index 2b95d0ab386b8..c2943b1c67c54 100644 --- a/code/modules/client/verbs/suicide.dm +++ b/code/modules/client/verbs/suicide.dm @@ -3,9 +3,9 @@ /mob/proc/set_suicide(suicide_state) suiciding = suicide_state if(suicide_state) - GLOB.suicided_mob_list += src + add_to_mob_suicide_list() else - GLOB.suicided_mob_list -= src + remove_from_mob_suicide_list() /mob/living/carbon/set_suicide(suicide_state) //you thought that box trick was pretty clever, didn't you? well now hardmode is on, boyo. . = ..() @@ -82,7 +82,7 @@ adjustOxyLoss(max(200 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) death(FALSE) - ghostize(FALSE) // Disallows reentering body and disassociates mind + ghostize(FALSE,SENTIENCE_ERASE) // Disallows reentering body and disassociates mind return @@ -130,7 +130,7 @@ suicide_log() death(FALSE) - ghostize(FALSE) // Disallows reentering body and disassociates mind + ghostize(FALSE,SENTIENCE_ERASE) // Disallows reentering body and disassociates mind /mob/living/carbon/monkey/verb/suicide() set hidden = 1 @@ -148,7 +148,7 @@ adjustOxyLoss(max(200- getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) death(FALSE) - ghostize(FALSE) // Disallows reentering body and disassociates mind + ghostize(FALSE,SENTIENCE_ERASE) // Disallows reentering body and disassociates mind /mob/living/silicon/ai/verb/suicide() set hidden = 1 @@ -167,7 +167,7 @@ //put em at -175 adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) death(FALSE) - ghostize(FALSE) // Disallows reentering body and disassociates mind + ghostize(FALSE,SENTIENCE_ERASE) // Disallows reentering body and disassociates mind /mob/living/silicon/robot/verb/suicide() set hidden = 1 @@ -186,7 +186,7 @@ //put em at -175 adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) death(FALSE) - ghostize(FALSE) // Disallows reentering body and disassociates mind + ghostize(FALSE,SENTIENCE_ERASE) // Disallows reentering body and disassociates mind /mob/living/silicon/pai/verb/suicide() set hidden = 1 @@ -199,7 +199,7 @@ suicide_log() death(FALSE) - ghostize(FALSE) // Disallows reentering body and disassociates mind + ghostize(FALSE,SENTIENCE_ERASE) // Disallows reentering body and disassociates mind else to_chat(src, "Aborting suicide attempt.") @@ -221,7 +221,7 @@ //put em at -175 adjustOxyLoss(max(200 - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) death(FALSE) - ghostize(FALSE) // Disallows reentering body and disassociates mind + ghostize(FALSE,SENTIENCE_ERASE) // Disallows reentering body and disassociates mind /mob/living/simple_animal/verb/suicide() set hidden = 1 @@ -238,7 +238,7 @@ suicide_log() death(FALSE) - ghostize(FALSE) // Disallows reentering body and disassociates mind + ghostize(FALSE,SENTIENCE_ERASE) // Disallows reentering body and disassociates mind /mob/living/proc/suicide_log() log_game("[key_name(src)] committed suicide at [AREACOORD(src)] as [src.type].") diff --git a/code/modules/client/verbs/who.dm b/code/modules/client/verbs/who.dm index 5f16b98047c91..d2c7e9416a957 100644 --- a/code/modules/client/verbs/who.dm +++ b/code/modules/client/verbs/who.dm @@ -59,7 +59,7 @@ /client/verb/staffwho() set category = "Admin" set name = "Staffwho" - + var/msg = "Current Admins:\n" if(holder) for(var/client/C in GLOB.admins) @@ -104,7 +104,11 @@ continue //Don't show afk admins to adminwho msg += "\t[C] is a mentor\n" - msg += "Adminhelps are also sent to IRC. If no admins are available in game adminhelp anyways and an admin on IRC will see it and respond." + msg += "Adminhelps are also sent through TGS to services like IRC and Discord. If no admins are available in game adminhelp anyways and an admin will see it and respond." + if(world.time - src.staff_check_rate > 1 MINUTES) + message_admins("[ADMIN_LOOKUPFLW(src.mob)] has checked online staff.") + log_admin("[key_name(src)] has checked online staff.") + src.staff_check_rate = world.time to_chat(src, msg) /client/verb/mentorwho() // redundant with staffwho, but people wont check the admin tab for if there are mentors on @@ -155,6 +159,10 @@ continue //Don't show afk admins to adminwho msg += "\t[C] is a mentor\n" - msg += "Adminhelps are also sent to IRC. If no admins are available in game adminhelp anyways and an admin on IRC will see it and respond." + msg += "Adminhelps are also sent through TGS to services like IRC and Discord. If no admins are available in game adminhelp anyways and an admin will see it and respond." + if(world.time - src.staff_check_rate > 1 MINUTES) + message_admins("[ADMIN_LOOKUPFLW(src.mob)] has checked online staff.") + log_admin("[key_name(src)] has checked online staff.") + src.staff_check_rate = world.time to_chat(src, msg) diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index a9bfc922ac126..953d184b35e86 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -63,8 +63,8 @@ // it's has TRAIT_NODROP D.dropItemToGround(target, TRUE) qdel(old_headgear) - // where is `SLOT_HEAD` defined? WHO KNOWS - D.equip_to_slot(new_headgear, SLOT_HEAD) + // where is `ITEM_SLOT_HEAD` defined? WHO KNOWS + D.equip_to_slot(new_headgear, ITEM_SLOT_HEAD) return 1 @@ -220,7 +220,7 @@ var/obj/item/I = target I.item_state = initial(picked_item.item_state) I.item_color = initial(picked_item.item_color) - if(istype(I, /obj/item/clothing) && istype(initial(picked_item), /obj/item/clothing)) + if(isclothing(I) && isclothing(initial(picked_item))) var/obj/item/clothing/CL = I var/obj/item/clothing/PCL = picked_item CL.flags_cover = initial(PCL.flags_cover) @@ -243,8 +243,7 @@ /datum/action/item_action/chameleon/change/process() if(world.time > emp_timer) - STOP_PROCESSING(SSprocessing, src) - return + return PROCESS_KILL random_look(owner) /obj/item/clothing/under/chameleon @@ -258,7 +257,7 @@ random_sensor = FALSE resistance_flags = NONE can_adjust = FALSE - armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 10) var/datum/action/item_action/chameleon/change/chameleon_action @@ -278,6 +277,13 @@ item_state = "engi_suit" item_color = "engine" +/obj/item/clothing/under/chameleon/envirosuit/ratvar + name = "ratvarian engineer's envirosuit" + desc = "A tough envirosuit woven from alloy threads. It can take on the appearance of other jumpsuits." + icon_state = "engineer_envirosuit" + item_state = "engineer_envirosuit" + item_color = "engineer_envirosuit" + /obj/item/clothing/under/chameleon/Initialize() . = ..() chameleon_action = new(src) @@ -303,7 +309,7 @@ item_state = "armor" blood_overlay_type = "armor" resistance_flags = NONE - armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 10) var/datum/action/item_action/chameleon/change/chameleon_action @@ -331,7 +337,7 @@ icon_state = "meson" item_state = "meson" resistance_flags = NONE - armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 10) var/datum/action/item_action/chameleon/change/chameleon_action @@ -361,13 +367,13 @@ flash_protect = 3 /obj/item/clothing/gloves/chameleon - desc = "These gloves will protect the wearer from electric shock." + desc = "These gloves provide protection against electric shock." name = "insulated gloves" icon_state = "yellow" item_state = "ygloves" resistance_flags = NONE - armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 10) var/datum/action/item_action/chameleon/change/chameleon_action @@ -401,7 +407,7 @@ min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT - armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 10) /obj/item/clothing/head/chameleon name = "grey cap" @@ -411,7 +417,7 @@ item_color = "grey" resistance_flags = NONE - armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 10) var/datum/action/item_action/chameleon/change/chameleon_action @@ -447,13 +453,20 @@ heat_protection = HEAD max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT bang_protect = 1 - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + +/obj/item/clothing/head/chameleon/envirohelm/ratvar + name = "ratvarian engineer's envirosuit helmet" + desc = "A tough envirohelm woven from alloy threads. It can take on the appearance of other headgear." + icon_state = "engineer_envirohelm" + item_state = "engineer_envirohelm" + flash_protect = 1 /obj/item/clothing/head/chameleon/drone // The camohat, I mean, holographic hat projection, is part of the // drone itself. clothing_flags = SNUG_FIT - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "stamina" = 0) // which means it offers no protection, it's just air and light /obj/item/clothing/head/chameleon/drone/Initialize() @@ -471,7 +484,7 @@ icon_state = "gas_alt" item_state = "gas_alt" resistance_flags = NONE - armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 10) clothing_flags = BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEFACIALHAIR gas_transfer_coefficient = 0.01 @@ -507,7 +520,7 @@ /obj/item/clothing/mask/chameleon/drone //Same as the drone chameleon hat, undroppable and no protection - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "stamina" = 0) // Can drones use the voice changer part? Let's not find out. vchange = 0 @@ -530,7 +543,7 @@ desc = "A pair of black shoes." permeability_coefficient = 0.05 resistance_flags = NONE - armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 10) pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes var/datum/action/item_action/chameleon/change/chameleon_action @@ -678,7 +691,7 @@ desc = "A neosilk clip-on tie." icon_state = "blacktie" resistance_flags = NONE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "stamina" = 0) /obj/item/clothing/neck/cloak/chameleon var/datum/action/item_action/chameleon/change/chameleon_action diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index f128e9aee47b6..ad5a525337691 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -1,3 +1,9 @@ +#define SENSORS_OFF 0 +#define SENSORS_BINARY 1 +#define SENSORS_VITALS 2 +#define SENSORS_TRACKING 3 +#define SENSOR_CHANGE_DELAY 1.5 SECONDS + /obj/item/clothing name = "clothing" resistance_flags = FLAMMABLE @@ -21,7 +27,6 @@ var/active_sound = null var/toggle_cooldown = null var/cooldown = 0 - var/scan_reagents = 0 //Can the wearer see reagents while it's equipped? var/envirosealed = FALSE //is it safe for plasmamen var/blocks_shove_knockdown = FALSE //Whether wearing the clothing item blocks the ability for shove to knock down. @@ -41,6 +46,12 @@ var/dynamic_hair_suffix = ""//head > mask for head hair var/dynamic_fhair_suffix = ""//mask > head for facial hair + var/high_pressure_multiplier = 1 + var/static/list/high_pressure_multiplier_types = list("melee", "bullet", "laser", "energy", "bomb") + ///These are armor values that protect the wearer, taken from the clothing's armor datum. List updates on examine because it's currently only used to print armor ratings to chat in Topic(). + var/list/armor_list = list() + ///These are armor values that protect the clothing, taken from its armor datum. List updates on examine because it's currently only used to print armor ratings to chat in Topic(). + var/list/durability_list = list() /obj/item/clothing/Initialize() if(CHECK_BITFIELD(clothing_flags, VOICEBOX_TOGGLABLE)) @@ -56,8 +67,8 @@ if(ismecha(M.loc)) // stops inventory actions in a mech return - if(!M.incapacitated() && loc == M && istype(over_object, /obj/screen/inventory/hand)) - var/obj/screen/inventory/hand/H = over_object + if(!M.incapacitated() && loc == M && istype(over_object, /atom/movable/screen/inventory/hand)) + var/atom/movable/screen/inventory/hand/H = over_object if(M.putItemFromInventoryInHandIfPossible(src, H.held_index)) add_fingerprint(usr) @@ -69,7 +80,7 @@ foodtype = CLOTH /obj/item/clothing/attack(mob/M, mob/user, def_zone) - if(user.a_intent != INTENT_HARM && ismoth(M)) + if(user.a_intent != INTENT_HARM && ismoth(M) && !(clothing_flags & NOTCONSUMABLE) && !(resistance_flags & INDESTRUCTIBLE) && (armor.getRating("melee") == 0)) var/obj/item/reagent_containers/food/snacks/clothing/clothing_as_food = new clothing_as_food.name = name if(clothing_as_food.attack(M, user, def_zone)) @@ -107,7 +118,7 @@ ..() if (!istype(user)) return - if(slot_flags & slotdefine2slotbit(slot)) //Was equipped to a valid slot for this item? + if(slot_flags & slot) //Was equipped to a valid slot for this item? if (LAZYLEN(user_vars_to_edit)) for(var/variable in user_vars_to_edit) if(variable in user.vars) @@ -141,6 +152,89 @@ how_cool_are_your_threads += "" . += how_cool_are_your_threads.Join() + . += "It has a tag listing its protection classes." + +/obj/item/clothing/Topic(href, href_list) + . = ..() + if(href_list["list_armor"]) + if(length(armor_list)) + armor_list.Cut() + if(armor.bio) + armor_list += list("TOXIN" = armor.bio) + if(armor.bomb) + armor_list += list("EXPLOSIVE" = armor.bomb) + if(armor.bullet) + armor_list += list("BULLET" = armor.bullet) + if(armor.energy) + armor_list += list("ENERGY" = armor.energy) + if(armor.laser) + armor_list += list("LASER" = armor.laser) + if(armor.magic) + armor_list += list("MAGIC" = armor.magic) + if(armor.melee) + armor_list += list("MELEE" = armor.melee) + if(armor.rad) + armor_list += list("RADIATION" = armor.rad) + if(armor.stamina) + armor_list += list("STAMINA" = armor.stamina) + + if(length(durability_list)) + durability_list.Cut() + if(armor.fire) + durability_list += list("FIRE" = armor.fire) + if(armor.acid) + durability_list += list("ACID" = armor.acid) + var/list/readout = list("PROTECTION CLASSES (I-X)") + if(length(armor_list)) + readout += "\nARMOR" + for(var/dam_type in armor_list) + var/armor_amount = armor_list[dam_type] + readout += "\n[dam_type] [armor_to_protection_class(armor_amount)]" //e.g. BOMB IV + if(length(durability_list)) + readout += "\nDURABILITY" + for(var/dam_type in durability_list) + var/durability_amount = durability_list[dam_type] + readout += "\n[dam_type] [armor_to_protection_class(durability_amount)]" //e.g. FIRE II + if(!(length(armor_list) || length(durability_list))) + readout += "\nNO PROTECTION" + readout += "" + + to_chat(usr, "[readout.Join()]") + +/** + * Rounds armor_value to nearest 10, divides it by 10 and then expresses it in roman numerals up to 10 + * + * Rounds armor_value to nearest 10, divides it by 10 + * and then expresses it in roman numerals up to 10 + * Arguments: + * * armor_value - Number we're converting + */ +/obj/item/clothing/proc/armor_to_protection_class(armor_value) + armor_value = round(armor_value,10) / 10 + switch (armor_value) + if(0) + . = "< I" + if(1) + . = "I" + if(2) + . = "II" + if(3) + . = "III" + if(4) + . = "IV" + if(5) + . = "V" + if(6) + . = "VI" + if(7) + . = "VII" + if(8) + . = "VIII" + if(9) + . = "IX" + if(10 to INFINITY) + . = "X" + /obj/item/clothing/obj_break(damage_flag) if(!damaged_clothes) update_clothes_damaged_state(TRUE) @@ -183,48 +277,71 @@ BLIND // can't see anything female_clothing_icon = fcopy_rsc(female_clothing_icon) GLOB.female_clothing_icons[index] = female_clothing_icon -/obj/item/clothing/under/verb/toggle() - set name = "Adjust Suit Sensors" - set category = "Object" - set src in usr - var/mob/M = usr - if (istype(M, /mob/dead/)) +/obj/item/clothing/under/proc/set_sensors(mob/user) + var/mob/M = user + if(M.stat) return - if (!can_use(M)) + if(!can_use(M)) return if(src.has_sensor == LOCKED_SENSORS) - to_chat(usr, "The controls are locked.") - return 0 + to_chat(user, "The controls are locked.") + return FALSE if(src.has_sensor == BROKEN_SENSORS) - to_chat(usr, "The sensors have shorted out!") - return 0 + to_chat(user, "The sensors have shorted out!") + return FALSE if(src.has_sensor <= NO_SENSORS) - to_chat(usr, "This suit does not have any sensors.") - return 0 + to_chat(user, "This suit does not have any sensors.") + return FALSE var/list/modes = list("Off", "Binary vitals", "Exact vitals", "Tracking beacon") var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes - if(get_dist(usr, src) > 1) - to_chat(usr, "You have moved too far away!") + if(get_dist(user, src) > 1) + to_chat(user, "You have moved too far away!") return - sensor_mode = modes.Find(switchMode) - 1 - - if (src.loc == usr) - switch(sensor_mode) - if(0) - to_chat(usr, "You disable your suit's remote sensing equipment.") - if(1) - to_chat(usr, "Your suit will now only report whether you are alive or dead.") - if(2) - to_chat(usr, "Your suit will now only report your exact vital lifesigns.") - if(3) - to_chat(usr, "Your suit will now report your exact vital lifesigns as well as your coordinate position.") - + var/sensor_selection = modes.Find(switchMode) - 1 + + if (src.loc == user) + switch(sensor_selection) + if(SENSORS_OFF) + to_chat(user, "You disable your suit's remote sensing equipment.") + if(SENSORS_BINARY) + to_chat(user, "Your suit will now only report whether you are alive or dead.") + if(SENSORS_VITALS) + to_chat(user, "Your suit will now only report your exact vital lifesigns.") + if(SENSORS_TRACKING) + to_chat(user, "Your suit will now report your exact vital lifesigns as well as your coordinate position.") + sensor_mode = sensor_selection + else if(istype(src.loc, /mob)) + var/mob/living/carbon/human/wearer = src.loc + wearer.visible_message("[user] tries to set [wearer]'s sensors.", \ + "[user] is trying to set your sensors.", null, COMBAT_MESSAGE_RANGE) + if(do_mob(user, wearer, SENSOR_CHANGE_DELAY)) + switch(sensor_selection) + if(SENSORS_OFF) + wearer.visible_message("[user] disables [wearer]'s remote sensing equipment.", \ + "[user] disables your remote sensing equipment.", null, COMBAT_MESSAGE_RANGE) + if(SENSORS_BINARY) + wearer.visible_message("[user] turns [wearer]'s remote sensors to binary.", \ + "[user] turns your remote sensors to binary.", null, COMBAT_MESSAGE_RANGE) + if(SENSORS_VITALS) + wearer.visible_message("[user] turns [wearer]'s remote sensors to track vitals.", \ + "[user] turns your remote sensors to track vitals.", null, COMBAT_MESSAGE_RANGE) + if(SENSORS_TRACKING) + wearer.visible_message("[user] turns [wearer]'s remote sensors to maximum.", \ + "[user] turns your remote sensors to maximum.", null, COMBAT_MESSAGE_RANGE) + sensor_mode = sensor_selection + log_combat(user, wearer, "changed sensors to [switchMode]") if(ishuman(loc)) var/mob/living/carbon/human/H = loc if(H.w_uniform == src) H.update_suit_sensors() +/obj/item/clothing/under/verb/toggle() + set name = "Adjust Suit Sensors" + set category = "Object" + set src in usr + set_sensors(usr) + /obj/item/clothing/under/attack_hand(mob/user) if(attached_accessory && ispath(attached_accessory.pocket_storage_component_path) && loc == user) attached_accessory.attack_hand(user) @@ -232,9 +349,6 @@ BLIND // can't see anything ..() /obj/item/clothing/under/AltClick(mob/user) - if(..()) - return 1 - if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) return else @@ -334,3 +448,19 @@ BLIND // can't see anything deconstruct(FALSE) else ..() + +/obj/item/clothing/get_armor_rating(d_type, mob/wearer) + . = ..() + if(high_pressure_multiplier == 1) + return + var/turf/T = get_turf(wearer) + if(!T || !(d_type in high_pressure_multiplier_types)) + return + if(!lavaland_equipment_pressure_check(T)) + . *= high_pressure_multiplier + +#undef SENSORS_OFF +#undef SENSORS_BINARY +#undef SENSORS_VITALS +#undef SENSORS_TRACKING +#undef SENSOR_CHANGE_DELAY diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 265beab81dcd8..87eb92d5cf752 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -89,21 +89,36 @@ hitsound = 'sound/weapons/bladeslice.ogg' sharpness = IS_SHARP +/obj/item/clothing/glasses/meson/prescription + name = "prescription meson scanner" + desc = "A crude combination between a pair of prescription glasses and the electronics of a meson scanner." + icon_state = "prescmeson" + item_state = "glasses" + vision_correction = 1 + /obj/item/clothing/glasses/science name = "science goggles" desc = "A pair of snazzy goggles used to protect against chemical spills. Fitted with an analyzer for scanning items and reagents." icon_state = "purple" item_state = "glasses" - scan_reagents = TRUE //You can see reagents while wearing science goggles + clothing_flags = SCAN_REAGENTS actions_types = list(/datum/action/item_action/toggle_research_scanner) glass_colour_type = /datum/client_colour/glass_colour/purple resistance_flags = ACID_PROOF - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100, "stamina" = 0) /obj/item/clothing/glasses/science/item_action_slot_check(slot) - if(slot == SLOT_GLASSES) + if(slot == ITEM_SLOT_EYES) return 1 +/obj/item/clothing/glasses/science/prescription + name = "prescription science goggles" + desc = "A crude combination between a pair of prescription glasses and the electronics of science goggles." + icon_state = "prescscihud" + resistance_flags = NONE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 40, "stamina" = 0) + vision_correction = 1 + /obj/item/clothing/glasses/science/sciencesun name = "science sunglasses" desc = "A pair of sunglasses outfitted with apparatus to scan reagents, as well as providing an innate understanding of liquid viscosity while in motion. Has enhanced shielding which blocks flashes." @@ -111,6 +126,11 @@ item_state = "sunhudscience" flash_protect = 1 +/obj/item/clothing/glasses/science/sciencesun/degraded + name = "degraded science sunglasses" + desc = "A pair of sunglasses outfitted with apparatus to scan reagents, as well as providing an innate understanding of liquid viscosity while in motion." + flash_protect = 0 + /obj/item/clothing/glasses/night name = "night vision goggles" desc = "You can totally see in the dark now!" @@ -209,11 +229,11 @@ /obj/item/clothing/glasses/sunglasses/advanced/reagent name = "beer goggles" desc = "A pair of sunglasses outfitted with apparatus to scan reagents, as well as providing an innate understanding of liquid viscosity while in motion. Has enhanced shielding which blocks flashes." - scan_reagents = TRUE + clothing_flags = SCAN_REAGENTS /obj/item/clothing/glasses/sunglasses/advanced/reagent/equipped(mob/user, slot) . = ..() - if(ishuman(user) && slot == SLOT_GLASSES) + if(ishuman(user) && slot == ITEM_SLOT_EYES) ADD_TRAIT(user, TRAIT_BOOZE_SLIDER, CLOTHING_TRAIT) /obj/item/clothing/glasses/sunglasses/advanced/reagent/dropped(mob/user) @@ -283,12 +303,19 @@ desc = "A bulky pair of unwieldy glasses that lets you see things best left unseen. Obscures vision, but also has enhanced shielding which blocks flashes." icon_state = "bustin-g" item_state = "bustin-g" - invis_view = SEE_INVISIBLE_OBSERVER - invis_override = null flash_protect = 1 - visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_INVISVIEW + visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT glass_colour_type = /datum/client_colour/glass_colour/green +/obj/item/clothing/glasses/welding/ghostbuster/ComponentInitialize() + . = ..() + AddComponent(/datum/component/team_monitor, "ghost", 1) + +/obj/item/clothing/glasses/welding/ghostbuster/visor_toggling() + ..() + var/datum/component/team_monitor/ghost_vision = GetComponent(/datum/component/team_monitor) + ghost_vision.toggle_hud(!ghost_vision.hud_visible, usr) + /obj/item/clothing/glasses/blindfold name = "blindfold" desc = "Covers the eyes, preventing sight." @@ -299,15 +326,6 @@ darkness_view = 1 dog_fashion = /datum/dog_fashion/head -/obj/item/clothing/glasses/blindfold/equipped(mob/living/carbon/human/user, slot) - . = ..() - if(slot == SLOT_GLASSES) - user.become_blind("blindfold_[REF(src)]") - -/obj/item/clothing/glasses/blindfold/dropped(mob/living/carbon/human/user) - ..() - user.cure_blind("blindfold_[REF(src)]") - /obj/item/clothing/glasses/blindfold/white name = "blind personnel blindfold" desc = "Indicates that the wearer suffers from blindness." @@ -316,7 +334,7 @@ var/colored_before = FALSE /obj/item/clothing/glasses/blindfold/white/equipped(mob/living/carbon/human/user, slot) - if(ishuman(user) && slot == SLOT_GLASSES) + if(ishuman(user) && slot == ITEM_SLOT_EYES) update_icon(user) user.update_inv_glasses() //Color might have been changed by update_icon. ..() @@ -336,7 +354,6 @@ . += M /obj/item/clothing/glasses/sunglasses/advanced/big - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Has enhanced shielding which blocks flashes." icon_state = "bigsunglasses" item_state = "bigsunglasses" @@ -347,7 +364,6 @@ item_state = "glasses" vision_flags = SEE_MOBS lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - flash_protect = 0 glass_colour_type = /datum/client_colour/glass_colour/red /obj/item/clothing/glasses/thermal/emp_act(severity) @@ -360,7 +376,7 @@ name = "syndicate xray goggles" desc = "A pair of xray goggles manufactured by the Syndicate." vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS - flash_protect = -1 + tint = -INFINITY /obj/item/clothing/glasses/thermal/syndi //These are now a traitor item, concealed as mesons. -Pete name = "chameleon thermals" @@ -436,7 +452,7 @@ item_state = "godeye" vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS darkness_view = 8 - scan_reagents = TRUE + clothing_flags = SCAN_REAGENTS lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE resistance_flags = LAVA_PROOF | FIRE_PROOF @@ -470,8 +486,6 @@ else to_chat(H, "You will no longer see glasses colors.") H.update_glasses_color(src, 1) - else - return ..() /obj/item/clothing/glasses/proc/change_glass_color(mob/living/carbon/human/H, datum/client_colour/glass_colour/new_color_type) var/old_colour_type = glass_colour_type diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm index d5ce968aeb72e..43db0e725b969 100644 --- a/code/modules/clothing/glasses/engine_goggles.dm +++ b/code/modules/clothing/glasses/engine_goggles.dm @@ -90,7 +90,7 @@ var/strength = round(rad_places[i] / 1000, 0.1) var/image/pic = image(loc = place) var/mutable_appearance/MA = new() - MA.maptext = "[strength]k" + MA.maptext = MAPTEXT("[strength]k") MA.color = "#04e66d" MA.layer = RAD_TEXT_LAYER MA.plane = GAME_PLANE @@ -123,7 +123,7 @@ item_state = icon_state if(isliving(loc)) var/mob/living/user = loc - if(user.get_item_by_slot(SLOT_GLASSES) == src) + if(user.get_item_by_slot(ITEM_SLOT_EYES) == src) user.update_inv_glasses() else user.update_inv_hands() diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 4d37ba4cb7b03..f6e5194aacc84 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -2,28 +2,46 @@ name = "HUD" desc = "A heads-up display that provides important info in (almost) real time." flags_1 = null //doesn't protect eyes because it's a monocle, duh - var/hud_type = null - var/alt_hud_type = null + var/hud_trait = null //Used for topic calls. Just because you have a HUD display doesn't mean you should be able to interact with stuff. If something uses multiple traits, make it a list. + var/hud_type = null //If something uses multiple huds, make it a list. /obj/item/clothing/glasses/hud/equipped(mob/living/carbon/human/user, slot) ..() - if(hud_type && slot == SLOT_GLASSES) - var/datum/atom_hud/H = GLOB.huds[hud_type] - H.add_hud_to(user) - if(alt_hud_type && slot == SLOT_GLASSES) - var/datum/atom_hud/H = GLOB.huds[alt_hud_type] - H.add_hud_to(user) + if(slot != ITEM_SLOT_EYES) + return + if(hud_type) + if(islist(hud_type)) + for(var/T in hud_type) + var/datum/atom_hud/H = GLOB.huds[T] + H.add_hud_to(user) + else + var/datum/atom_hud/H = GLOB.huds[hud_type] + H.add_hud_to(user) + if(hud_trait) + if(islist(hud_trait)) + for(var/H in hud_trait) + ADD_TRAIT(user, H, GLASSES_TRAIT) + else + ADD_TRAIT(user, hud_trait, GLASSES_TRAIT) /obj/item/clothing/glasses/hud/dropped(mob/living/carbon/human/user) ..() - if(hud_type && istype(user) && user.glasses == src) - var/datum/atom_hud/H = GLOB.huds[hud_type] - H.remove_hud_from(user) - - if(alt_hud_type && istype(user) && user.glasses == src) - var/datum/atom_hud/H = GLOB.huds[alt_hud_type] - H.remove_hud_from(user) - + if(!istype(user) || user.glasses != src) + return + if(hud_type) + if(islist(hud_type)) + for(var/T in hud_type) + var/datum/atom_hud/H = GLOB.huds[T] + H.remove_hud_from(user) + else + var/datum/atom_hud/H = GLOB.huds[hud_type] + H.remove_hud_from(user) + if(hud_trait) + if(islist(hud_trait)) + for(var/H in hud_trait) + REMOVE_TRAIT(user, H, GLASSES_TRAIT) + else + REMOVE_TRAIT(user, hud_trait, GLASSES_TRAIT) /obj/item/clothing/glasses/hud/emp_act(severity) . = ..() @@ -44,6 +62,7 @@ desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." icon_state = "healthhud" hud_type = DATA_HUD_MEDICAL_ADVANCED + hud_trait = TRAIT_MEDICAL_HUD glass_colour_type = /datum/client_colour/glass_colour/lightblue /obj/item/clothing/glasses/hud/health/night @@ -64,6 +83,17 @@ tint = 1 glass_colour_type = /datum/client_colour/glass_colour/blue +/obj/item/clothing/glasses/hud/health/prescription + name = "prescription medical HUDglasses" + desc = "Prescription glasses with a built-in medical HUD." + icon_state = "prescmedhud" + vision_correction = 1 + +/obj/item/clothing/glasses/hud/health/sunglasses/degraded + name = "degraded medical HUDSunglasses" + desc = "Sunglasses with a medical HUD. They do not provide flash protection." + flash_protect = 0 + /obj/item/clothing/glasses/hud/diagnostic name = "diagnostic HUD" desc = "A heads-up display capable of analyzing the integrity and status of robotics and exosuits." @@ -88,11 +118,23 @@ flash_protect = 1 tint = 1 +/obj/item/clothing/glasses/hud/diagnostic/sunglasses/degraded + name = "degraded diagnostic sunglasses" + desc = "Sunglasses with a diagnostic HUD. They do not provide flash protection." + flash_protect = 0 + +/obj/item/clothing/glasses/hud/diagnostic/prescription + name = "prescription diagnostic HUDglasses" + desc = "Prescription glasses with a built-in diagnostic HUD." + icon_state = "prescdiaghud" + vision_correction = 1 + /obj/item/clothing/glasses/hud/security name = "security HUD" desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." icon_state = "securityhud" hud_type = DATA_HUD_SECURITY_ADVANCED + hud_trait = TRAIT_SECURITY_HUD glass_colour_type = /datum/client_colour/glass_colour/red /obj/item/clothing/glasses/hud/security/deputy @@ -103,8 +145,9 @@ name = "medsec HUD" desc = "A combination HUD, providing the user the use of a Medical and Security HUD." icon_state = "medsechud" - hud_type = DATA_HUD_SECURITY_ADVANCED - alt_hud_type = DATA_HUD_MEDICAL_ADVANCED + hud_type = list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED) + hud_trait = list(TRAIT_SECURITY_HUD, TRAIT_MEDICAL_HUD) + glass_colour_type = /datum/client_colour/glass_colour/red /obj/item/clothing/glasses/hud/security/chameleon @@ -145,6 +188,11 @@ tint = 1 glass_colour_type = /datum/client_colour/glass_colour/darkred +/obj/item/clothing/glasses/hud/security/sunglasses/degraded + name = "degraded security HUDSunglasses" + desc = "Sunglasses with a security HUD. They do not provide flash protection." + flash_protect = 0 + /obj/item/clothing/glasses/hud/security/night name = "night vision security HUD" desc = "An advanced heads-up display which provides id data and vision in complete darkness." @@ -153,6 +201,12 @@ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE glass_colour_type = /datum/client_colour/glass_colour/green +/obj/item/clothing/glasses/hud/security/prescription + name = "prescription security HUDglasses" + desc = "Prescription glasses with a built-in security HUD. They do not provide flash protection." + icon_state = "prescsechud" + vision_correction = 1 + /obj/item/clothing/glasses/hud/security/sunglasses/gars name = "\improper HUD gar glasses" desc = "GAR glasses with a HUD." @@ -235,3 +289,30 @@ if(. & EMP_PROTECT_SELF) return thermal_overload() + +/obj/item/clothing/glasses/hud/debug + name = "Omni HUD" + desc = "Glasses with every function." + icon_state = "doublegodeye" + item_state = "doublegodeye" + vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS + darkness_view = 8 + flash_protect = 2 + vision_correction = 1 + clothing_flags = SCAN_REAGENTS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + hud_type = list(DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED, DATA_HUD_SECURITY_ADVANCED) + resistance_flags = INDESTRUCTIBLE + actions_types = list(/datum/action/item_action/toggle,/datum/action/item_action/toggle_research_scanner) + var/xray = TRUE + +/obj/item/clothing/glasses/hud/debug/attack_self(mob/user) + if(!ishuman(user)) + return + if(xray) + vision_flags -= SEE_MOBS|SEE_OBJS + else + vision_flags += SEE_MOBS|SEE_OBJS + xray = !xray + var/mob/living/carbon/human/wearer = user + wearer.update_sight() diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index 9b381cef7f8c9..06433a5d6a253 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -10,12 +10,16 @@ var/transfer_prints = FALSE strip_delay = 20 equip_delay_other = 40 + // Path variable. If defined, will produced the type through interaction with wirecutters. + var/cut_type = null /obj/item/clothing/gloves/ComponentInitialize() . = ..() RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_blood) /obj/item/clothing/gloves/proc/clean_blood(datum/source, strength) + SIGNAL_HANDLER + if(strength < CLEAN_STRENGTH_BLOOD) return transfer_blood = 0 @@ -40,4 +44,14 @@ // Called just before an attack_hand(), in mob/UnarmedAttack() /obj/item/clothing/gloves/proc/Touch(atom/A, proximity) - return 0 // return 1 to cancel attack_hand() + return FALSE // return 1 to cancel attack_hand() + +/obj/item/clothing/gloves/wirecutter_act(mob/living/user, obj/item/I) + . = ..() + if(!cut_type) + return + if(icon_state != initial(icon_state)) + return // We don't want to cut dyed gloves. + new cut_type(drop_location()) + qdel(src) + return TRUE diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index a76ff7c5834dd..173df5a1c0c59 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -17,3 +17,14 @@ /obj/item/clothing/gloves/boxing/yellow icon_state = "boxingyellow" item_state = "boxingyellow" + +/obj/item/clothing/gloves/boxing/yellow/insulated + name = "budget boxing gloves" + desc = "Standard boxing gloves coated in a makeshift insulating coat. This can't possibly go wrong at all." + icon_state = "boxingyellow" + item_state = "boxingyellow" + siemens_coefficient = 1 //Set to a default of 1, gets overridden in Initialize() + +/obj/item/clothing/gloves/boxing/yellow/insulated/Initialize() + . = ..() + siemens_coefficient = pick(0,0,0,0,0.25,2) diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index 54abcdadf3436..7e1ed7b7336e1 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -1,5 +1,5 @@ /obj/item/clothing/gloves/color/yellow - desc = "These gloves will protect the wearer from electric shock." + desc = "These gloves provide protection against electric shock." name = "insulated gloves" icon_state = "yellow" item_state = "ygloves" @@ -7,6 +7,39 @@ permeability_coefficient = 0.05 item_color="yellow" resistance_flags = NONE + cut_type = /obj/item/clothing/gloves/cut + +/obj/item/clothing/gloves/color/black/equipped(mob/user, slot) + . = ..() + if((slot == ITEM_SLOT_GLOVES) && (user.mind?.assigned_role in GLOB.security_positions)) + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "sec_black_gloves", /datum/mood_event/sec_black_gloves) + +/obj/item/clothing/gloves/color/black/dropped(mob/user) + . = ..() + if(user.mind?.assigned_role in GLOB.security_positions) + SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "sec_black_gloves") + +/obj/item/clothing/gloves/color/black/hos + item_color = "hosred" //Exists for washing machines. Is not different from black gloves in any way. + +/obj/item/clothing/gloves/color/black/ce + item_color = "chief" //Exists for washing machines. Is not different from black gloves in any way. + +/obj/item/clothing/gloves/color/yellow/equipped(mob/user, slot) + . = ..() + if(slot == ITEM_SLOT_GLOVES) + if(user.mind?.assigned_role == "Assistant") + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "assistant_insulated_gloves", /datum/mood_event/assistant_insulated_gloves) + if(user.mind?.assigned_role in GLOB.security_positions) + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "sec_insulated_gloves", /datum/mood_event/sec_insulated_gloves) + +/obj/item/clothing/gloves/color/yellow/dropped(mob/user) + . = ..() + if(user.mind?.assigned_role == "Assistant") + SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "assistant_insulated_gloves") + if(user.mind?.assigned_role in GLOB.security_positions) + SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "sec_insulated_gloves") + /obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap desc = "These gloves are cheap knockoffs of the coveted ones - no way this can end badly." @@ -17,6 +50,7 @@ permeability_coefficient = 0.05 item_color = "yellow" resistance_flags = NONE + cut_type = /obj/item/clothing/gloves/cut /obj/item/clothing/gloves/color/fyellow/Initialize() . = ..() @@ -30,7 +64,7 @@ . += "[src] will fully protect from electric shocks." if(siemens_coefficient > 1) . += "[src] will only make shocks worse." - else + else . += "[src] will provide [protectionpercentage] percent protection from electric shocks." /obj/item/clothing/gloves/color/fyellow/old @@ -41,6 +75,16 @@ . = ..() siemens_coefficient = pick(0,0,0,0.5,0.5,0.5,0.75) +/obj/item/clothing/gloves/cut + desc = "These gloves would protect the wearer from electric shock... if the fingers were covered." + name = "fingerless insulated gloves" + icon_state = "yellowcut" + item_state = "ygloves" + transfer_prints = TRUE + +/obj/item/clothing/gloves/cut/heirloom + desc = "The old gloves your great grandfather stole from Engineering, many moons ago. They've seen some tough times recently." + /obj/item/clothing/gloves/color/black desc = "These gloves are fire-resistant." name = "black gloves" @@ -52,22 +96,7 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE - var/can_be_cut = 1 - -/obj/item/clothing/gloves/color/black/hos - item_color = "hosred" //Exists for washing machines. Is not different from black gloves in any way. - -/obj/item/clothing/gloves/color/black/ce - item_color = "chief" //Exists for washing machines. Is not different from black gloves in any way. - -/obj/item/clothing/gloves/color/black/attackby(obj/item/I, mob/user, params) - if(I.tool_behaviour == TOOL_WIRECUTTER) - if(can_be_cut && icon_state == initial(icon_state))//only if not dyed - to_chat(user, "You snip the fingertips off of [src].") - I.play_tool_sound(src) - new /obj/item/clothing/gloves/fingerless(drop_location()) - qdel(src) - ..() + cut_type = /obj/item/clothing/gloves/fingerless /obj/item/clothing/gloves/color/orange name = "orange gloves" @@ -86,7 +115,7 @@ /obj/item/clothing/gloves/color/red/insulated name = "insulated gloves" - desc = "These gloves will protect the wearer from electric shock." + desc = "These gloves provide protection against electric shock." siemens_coefficient = 0 permeability_coefficient = 0.05 resistance_flags = NONE @@ -165,26 +194,42 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT strip_delay = 60 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 50) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 50, "stamina" = 0) /obj/item/clothing/gloves/color/latex name = "latex gloves" - desc = "Cheap sterile gloves made from latex." + desc = "Cheap sterile gloves made from latex. Transfers minor paramedic knowledge to the user via budget nanochips." icon_state = "latex" - item_state = "lgloves" + item_state = "latex" siemens_coefficient = 0.3 permeability_coefficient = 0.01 item_color="mime" transfer_prints = TRUE resistance_flags = NONE + var/carrytrait = TRAIT_QUICK_CARRY + +/obj/item/clothing/gloves/color/latex/equipped(mob/user, slot) + ..() + if(slot == ITEM_SLOT_GLOVES) + ADD_TRAIT(user, carrytrait, CLOTHING_TRAIT) + +/obj/item/clothing/gloves/color/latex/dropped(mob/user) + ..() + REMOVE_TRAIT(user, carrytrait, CLOTHING_TRAIT) + +/obj/item/clothing/gloves/color/latex/obj_break() + ..() + if(ishuman(loc)) + REMOVE_TRAIT(loc, carrytrait, CLOTHING_TRAIT) /obj/item/clothing/gloves/color/latex/nitrile name = "nitrile gloves" - desc = "Pricy sterile gloves that are stronger than latex." + desc = "Pricy sterile gloves that are stronger than latex. Transfers intimate paramedic knowledge into the user via nanochips." icon_state = "nitrile" item_state = "nitrilegloves" item_color = "cmo" transfer_prints = FALSE + carrytrait = TRAIT_QUICKER_CARRY /obj/item/clothing/gloves/color/white name = "white gloves" diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index 191ec72da7eeb..622e18e693ef1 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -23,7 +23,7 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 30) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 30, "stamina" = 0) /obj/item/clothing/gloves/combat name = "combat gloves" @@ -38,7 +38,7 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50, "stamina" = 20) /obj/item/clothing/gloves/bracer name = "bone bracers" @@ -54,7 +54,7 @@ min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE - armor = list("melee" = 15, "bullet" = 35, "laser" = 35, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0) + armor = list("melee" = 15, "bullet" = 35, "laser" = 35, "energy" = 20, "bomb" = 35, "bio" = 35, "rad" = 35, "fire" = 0, "acid" = 0, "stamina" = 20) /obj/item/clothing/gloves/rapid name = "Gloves of the North Star" @@ -66,12 +66,12 @@ /obj/item/clothing/gloves/rapid/Touch(atom/A, proximity) var/mob/living/M = loc - if(A in range(1, M)) + if(get_dist(A, M) <= 1) if(isliving(A) && M.a_intent == INTENT_HARM) M.changeNext_move(CLICK_CD_RAPID) if(warcry) M.say("[warcry]", ignore_spam = TRUE, forced = "north star warcry") - + else if(M.a_intent == INTENT_HARM) for(var/mob/living/L in oview(1, M)) L.attack_hand(M) @@ -110,9 +110,9 @@ /obj/item/clothing/gloves/color/white/magic/Touch(atom/A, proximity) var/mob/living/M = loc - if(A in range(1, M)) + if(get_dist(A, M) <= 1) return 0 - if(A in oview(range, M)) + if(M in viewers(range, A)) M.visible_message(" [alert] ") if(prob(30) || fake) //most of the time, we don't want an announcement, so as to allow AIs to fake blackouts. - priority_announce(alert) + priority_announce(alert, sound = SSstation.announcer.get_rand_alert_sound()) /datum/round_event/communications_blackout/start() diff --git a/code/modules/events/creep_awakening.dm b/code/modules/events/creep_awakening.dm index b8842119ba4b5..1b5643bc53f48 100644 --- a/code/modules/events/creep_awakening.dm +++ b/code/modules/events/creep_awakening.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/obsessed max_occurrences = 1 min_players = 20 + cannot_spawn_after_shuttlecall = TRUE /datum/round_event/obsessed fakeable = FALSE diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index 1d16337015c6a..3cb265c89dcdc 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -15,7 +15,7 @@ /datum/round_event/disease_outbreak/announce(fake) - priority_announce("Confirmed outbreak of level 7 viral biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/ai/outbreak7.ogg') + priority_announce("Confirmed outbreak of level 7 viral biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", ANNOUNCER_OUTBREAK7) /datum/round_event/disease_outbreak/setup() announceWhen = rand(15, 30) @@ -35,7 +35,7 @@ if(!virus_type && !advanced_virus) virus_type = pick(/datum/disease/fake_gbs, /datum/disease/cold9, /datum/disease/magnitis, /datum/disease/pierrot_throat, /datum/disease/beesease) - + for(var/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list)) var/turf/T = get_turf(H) if(!T) diff --git a/code/modules/events/dolphin_migration.dm b/code/modules/events/dolphin_migration.dm index 2bae503b2a626..f556369dc600b 100644 --- a/code/modules/events/dolphin_migration.dm +++ b/code/modules/events/dolphin_migration.dm @@ -13,7 +13,7 @@ startWhen = rand(40, 60) /datum/round_event/dolphin_migration/announce() - priority_announce("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert") + priority_announce("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/dolphin_migration/start() diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index db0eccf0c5c08..165d109a81e4a 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -12,7 +12,7 @@ announceWhen = 1 /datum/round_event/electrical_storm/announce(fake) - priority_announce("An electrical storm has been detected in your area, please repair potential electronic overloads.", "Electrical Storm Alert") + priority_announce("An electrical storm has been detected in your area, please repair potential electronic overloads.", "Electrical Storm Alert", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/electrical_storm/start() diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/fugitive_spawning.dm index dc70d29794a68..896b191cbe2ef 100644 --- a/code/modules/events/fugitive_spawning.dm +++ b/code/modules/events/fugitive_spawning.dm @@ -5,6 +5,7 @@ min_players = 20 earliest_start = 30 MINUTES //deadchat sink, lets not even consider it early on. gamemode_blacklist = list("nuclear") + cannot_spawn_after_shuttlecall = TRUE /datum/round_event/ghost_role/fugitives minimum_required = 1 @@ -115,4 +116,4 @@ for(var/turf/A in ship.get_affected_turfs(T)) for(var/obj/effect/mob_spawn/human/fugitive/spawner in A) announce_to_ghosts(spawner) - priority_announce("Unidentified ship detected near the station.") + priority_announce("Unidentified ship detected near the station.", sound = SSstation.announcer.get_rand_alert_sound()) diff --git a/code/modules/events/ghost_role.dm b/code/modules/events/ghost_role.dm index 06accdbcaed2f..36047d933865d 100644 --- a/code/modules/events/ghost_role.dm +++ b/code/modules/events/ghost_role.dm @@ -25,7 +25,7 @@ if(control && control.occurrences > 0) //Don't refund if it hasn't control.occurrences-- return - var/waittime = 300 * (2^retry) + var/waittime = 300 * (2**retry) message_admins("The event will not spawn a [role_name] until certain \ conditions are met. Waiting [waittime/10]s and then retrying.") addtimer(CALLBACK(src, .proc/try_spawning, 0, ++retry), waittime) diff --git a/code/modules/events/grid_check.dm b/code/modules/events/grid_check.dm index 6fe14d6b77c0d..eaef2e0d1101c 100644 --- a/code/modules/events/grid_check.dm +++ b/code/modules/events/grid_check.dm @@ -9,7 +9,7 @@ startWhen = 1 /datum/round_event/grid_check/announce(fake) - priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", 'sound/ai/poweroff.ogg') + priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", ANNOUNCER_POWEROFF) /datum/round_event/grid_check/start() power_fail(30, 120) diff --git a/code/modules/events/high_priority_bounty.dm b/code/modules/events/high_priority_bounty.dm index ffdcd8840b7e7..41607a3e11b72 100644 --- a/code/modules/events/high_priority_bounty.dm +++ b/code/modules/events/high_priority_bounty.dm @@ -6,7 +6,7 @@ earliest_start = 10 /datum/round_event/high_priority_bounty/announce(fake) - priority_announce("Central Command has issued a high-priority cargo bounty. Details have been sent to all bounty consoles.", "Nanotrasen Bounty Program") + priority_announce("Central Command has issued a high-priority cargo bounty. Details have been sent to all bounty consoles.", "Nanotrasen Bounty Program", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/high_priority_bounty/start() var/datum/bounty/B diff --git a/code/modules/events/holiday/halloween.dm b/code/modules/events/holiday/halloween.dm index 414fc70b0e7f3..065ebe85e9dc0 100644 --- a/code/modules/events/holiday/halloween.dm +++ b/code/modules/events/holiday/halloween.dm @@ -20,7 +20,7 @@ qdel(Poly) /datum/round_event/spooky/announce(fake) - priority_announce(pick("RATTLE ME BONES!","THE RIDE NEVER ENDS!", "A SKELETON POPS OUT!", "SPOOKY SCARY SKELETONS!", "CREWMEMBERS BEWARE, YOU'RE IN FOR A SCARE!") , "THE CALL IS COMING FROM INSIDE THE HOUSE") + priority_announce(pick("RATTLE ME BONES!","THE RIDE NEVER ENDS!", "A SKELETON POPS OUT!", "SPOOKY SCARY SKELETONS!", "CREWMEMBERS BEWARE, YOU'RE IN FOR A SCARE!") , "THE CALL IS COMING FROM INSIDE THE HOUSE", SSstation.announcer.get_rand_alert_sound()) //spooky foods (you can't actually make these when it's not halloween) /obj/item/reagent_containers/food/snacks/sugarcookie/spookyskull diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm index bc05cd68132a2..3fed75bb1f72b 100644 --- a/code/modules/events/holiday/vday.dm +++ b/code/modules/events/holiday/vday.dm @@ -3,6 +3,11 @@ #define VALENTINE_FILE "valentines.json" +//Assoc list +// Key: Sender +// Value: Receiever +GLOBAL_LIST(valentine_mobs) + // valentine / candy heart distribution // /datum/round_event_control/valentines @@ -13,32 +18,37 @@ max_occurrences = 1 earliest_start = 0 MINUTES +/datum/round_event/valentines + endWhen = 300 + /datum/round_event/valentines/start() - ..() + GLOB.valentine_mobs = list() + //Blammo for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - H.put_in_hands(new /obj/item/valentine) + H.put_in_hands(new /obj/item/valentine(get_turf(H))) var/obj/item/storage/backpack/b = locate() in H.contents new /obj/item/reagent_containers/food/snacks/candyheart(b) new /obj/item/storage/fancy/heart_box(b) + to_chat(H, "A message appears in your hand, it looks like it has space to write somebody's name on it!") + +/datum/round_event/valentines/end() + //Too late now + GLOB.valentine_mobs = null + + //Locate all the failures var/list/valentines = list() for(var/mob/living/M in GLOB.player_list) - if(!M.stat && M.client && M.mind) + if(!M.stat && M.client && M.mind && !M.mind.has_antag_datum(/datum/antagonist/valentine)) valentines |= M - while(valentines.len) var/mob/living/L = pick_n_take(valentines) if(valentines.len) var/mob/living/date = pick_n_take(valentines) - forge_valentines_objective(L, date) forge_valentines_objective(date, L) - - if(valentines.len && prob(4)) - var/mob/living/notgoodenough = pick_n_take(valentines) - forge_valentines_objective(notgoodenough, date) else L.mind.add_antag_datum(/datum/antagonist/heartbreaker) @@ -49,7 +59,7 @@ lover.mind.add_antag_datum(V) //These really should be teams but i can't be assed to incorporate third wheels right now /datum/round_event/valentines/announce(fake) - priority_announce("It's Valentine's Day! Give a valentine to that special someone!") + priority_announce("It's Valentine's Day! Give a valentine to that special someone!", sound = SSstation.announcer.get_rand_alert_sound()) /obj/item/valentine name = "valentine" @@ -59,6 +69,9 @@ var/message = "A generic message of love or whatever." resistance_flags = FLAMMABLE w_class = WEIGHT_CLASS_TINY + var/mob/target = null + var/mob/sender = null + var/used = FALSE /obj/item/valentine/Initialize() . = ..() @@ -66,16 +79,72 @@ /obj/item/valentine/attackby(obj/item/W, mob/user, params) ..() + if(!islist(GLOB.valentine_mobs)) + to_chat(user, "You feel regret... It's too late now.") + return + if(used) + return if(istype(W, /obj/item/pen) || istype(W, /obj/item/toy/crayon)) if(!user.is_literate()) to_chat(user, "You scribble illegibly on [src]!") return - var/recipient = stripped_input(user, "Who is receiving this valentine?", "To:", null , 20) - var/sender = stripped_input(user, "Who is sending this valentine?", "From:", null , 20) - if(!user.canUseTopic(src, BE_CLOSE)) + //Alright lets see who we are sending this bad boy too. + //Gets all the people on the z-level, don't want people meta dating nukies *too* hard. + //Also they only get one chance. + if(alert(user, "Are you sure you are ready to write your message? You only have one shot!", "Valentines message", "Yes!", "No...") == "No...") + to_chat(user, "You put down the pen thinking about who you want to send the message to.") + return + var/turf/user_turf = get_turf(user) + if(!SSmobs.clients_by_zlevel[user_turf.z]) + to_chat(user, "You stop and look around for a moment. Where the hell are you?") + return + //No going back now + var/list/clients_on_level = SSmobs.clients_by_zlevel[user_turf.z] + var/list/mob_names = list() + for(var/mob/living/carbon/human/H in clients_on_level) + if(!H.mind || H == user) + //Ignore non-humans, they will be handled by the event. + continue + mob_names["[H.real_name]"] = H + if(!LAZYLEN(mob_names)) + to_chat(user, "You feel empty and alone.") + return + //Pick names + //At this point the user is shown the names of people on the z-level + //To prevent metastrats, you can only use this one time. + var/picked_name = input(user, "Who are you sending it to?", "Valentines Card", null) as null|anything in mob_names + var/mob/living/carbon/human/picked_human = mob_names[picked_name] + if(!picked_human || !istype(picked_human)) + to_chat(user, "The card vanishes out of your hand! Lets hope they got it...") + //rip + qdel(src) + return + if(!islist(GLOB.valentine_mobs)) + to_chat(user, "You feel regret... It's too late now.") + used = TRUE + return + if(used) + to_chat(user, "The card has already been used!") return - if(recipient && sender) - name = "valentine - To: [recipient] From: [sender]" + to_chat(user, "The card vanishes out of your hand! Lets hope they got it...") + //List checking + GLOB.valentine_mobs[user] = picked_human + if(GLOB.valentine_mobs[picked_human] == user) + //wow. + forge_valentines_objective(user, picked_human) + forge_valentines_objective(picked_human, user) + //Off it goes! + //Create a new card to prevent exploiting + var/obj/item/valentine/new_card = new(get_turf(picked_human)) + new_card.message = message + new_card.sender = user + new_card.target = picked_human + new_card.name = "valentines card from [new_card.sender]" + new_card.desc = "A Valentine's card! It is addressed to [new_card.target]." + new_card.used = TRUE + picked_human.equip_to_appropriate_slot(new_card) + to_chat(picked_human, "A magical card suddenly appears!") + qdel(src) /obj/item/valentine/examine(mob/user) . = ..() diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm index dc9a4824ac7f2..8044fb2bb8fd9 100644 --- a/code/modules/events/holiday/xmas.dm +++ b/code/modules/events/holiday/xmas.dm @@ -37,7 +37,15 @@ icon_state = "xmashat" desc = "A crappy paper hat that you are REQUIRED to wear." flags_inv = 0 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "stamina" = 0) + +/obj/item/clothing/head/festive/Initialize() + //Merry christmas + if(CHRISTMAS in SSevents.holidays) + armor = list("melee" = 30, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 30, "acid" = 30, "stamina" = 30) + else if(FESTIVE_SEASON in SSevents.holidays) + armor = list("melee" = 20, "bullet" = 20, "laser" = 20,"energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 20, "acid" = 20, "stamina" = 20) + return ..() /obj/effect/spawner/xmastree name = "christmas tree spawner" @@ -74,7 +82,7 @@ var/mob/living/carbon/human/santa //who is our santa? /datum/round_event/santa/announce(fake) - priority_announce("Santa is coming to town!", "Unknown Transmission") + priority_announce("Santa is coming to town!", "Unknown Transmission", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/santa/start() var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time=150) @@ -85,4 +93,3 @@ var/datum/antagonist/santa/A = new santa.mind.add_antag_datum(A) - diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index b80459d226abc..df13bf6b2a1c4 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -13,6 +13,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 min_players = 15 max_occurrences = 5 var/atom/special_target + can_malf_fake_alert = TRUE /datum/round_event_control/immovable_rod/admin_setup() @@ -27,7 +28,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 announceWhen = 5 /datum/round_event/immovable_rod/announce(fake) - priority_announce("What the fuck was that?!", "General Alert") + priority_announce("What the fuck was that?!", "General Alert", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/immovable_rod/start() var/datum/round_event_control/immovable_rod/C = control @@ -49,8 +50,10 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 pull_force = INFINITY density = TRUE anchored = TRUE + flags_1 = PREVENT_CONTENTS_EXPLOSION_1 var/mob/living/wizard var/z_original = 0 + var/previous_distance = 1000 var/destination var/notify = TRUE var/atom/special_target @@ -67,11 +70,13 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 if(special_target) var/turf/T = get_turf(special_target) if(T.z == z_original) - special_target_valid = TRUE + special_target_valid = TRUE_THRESHOLD if(special_target_valid) walk_towards(src, special_target, 1) + previous_distance = get_dist(src, special_target) else if(end && end.z==z_original) walk_towards(src, destination, 1) + previous_distance = get_dist(src, destination) /obj/effect/immovablerod/Topic(href, href_list) if(href_list["orbit"]) @@ -84,8 +89,11 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 . = ..() /obj/effect/immovablerod/Moved() - if((z != z_original) || (loc == destination)) + //Moved more than 10 tiles in 1 move. + var/cur_dist = get_dist(src, destination) + if((z != z_original) || (loc == destination) || (FLOOR(cur_dist - previous_distance, 1) > 10)) qdel(src) + previous_distance = cur_dist if(special_target && loc == get_turf(special_target)) complete_trajectory() return ..() @@ -97,9 +105,6 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 walk(src,0) walk_towards(src, destination, 1) -/obj/structure/closet/supplypod/prevent_content_explosion() - return TRUE - /obj/effect/immovablerod/ex_act(severity, target) return 0 @@ -164,4 +169,4 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 U.visible_message("[U] suplexes [src] into the ground!", "You suplex [src] into the ground!") new /obj/structure/festivus/anchored(drop_location()) new /obj/effect/anomaly/flux(drop_location()) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index fbcf016e99d55..27cc1a9ef0363 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/ion_storm weight = 15 min_players = 2 + can_malf_fake_alert = TRUE /datum/round_event/ion_storm var/replaceLawsetChance = 25 //chance the AI's lawset is completely replaced with something else per config weights @@ -25,7 +26,7 @@ /datum/round_event/ion_storm/announce(fake) if(prob(announceChance) || fake) - priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", 'sound/ai/ionstorm.ogg') + priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", ANNOUNCER_IONSTORM) /datum/round_event/ion_storm/start() @@ -61,506 +62,78 @@ bot.emag_act() /proc/generate_ion_law() - //Threats are generally bad things, silly or otherwise. Plural. - var/ionthreats = pick_list(ION_FILE, "ionthreats") - //Objects are anything that can be found on the station or elsewhere, plural. - var/ionobjects = pick_list(ION_FILE, "ionobjects") - //Crew is any specific job. Specific crewmembers aren't used because of capitalization - //issues. There are two crew listings for laws that require two different crew members - //and I can't figure out how to do it better. - var/ioncrew1 = pick_list(ION_FILE, "ioncrew") - var/ioncrew2 = pick_list(ION_FILE, "ioncrew") - //Adjectives are adjectives. Duh. Half should only appear sometimes. Make sure both - //lists are identical! Also, half needs a space at the end for nicer blank calls. - var/ionadjectives = pick_list(ION_FILE, "ionadjectives") - var/ionadjectiveshalf = pick("", 400;(pick_list(ION_FILE, "ionadjectives") + " ")) - //Verbs are verbs - var/ionverb = pick_list(ION_FILE, "ionverb") - //Number base and number modifier are combined. Basehalf and mod are unused currently. - //Half should only appear sometimes. Make sure both lists are identical! Also, half - //needs a space at the end to make it look nice and neat when it calls a blank. - var/ionnumberbase = pick_list(ION_FILE, "ionnumberbase") - //var/ionnumbermod = pick_list(ION_FILE, "ionnumbermod") - var/ionnumbermodhalf = pick(900;"",(pick_list(ION_FILE, "ionnumbermod") + " ")) - //Areas are specific places, on the station or otherwise. - var/ionarea = pick_list(ION_FILE, "ionarea") - //Thinksof is a bit weird, but generally means what X feels towards Y. - var/ionthinksof = pick_list(ION_FILE, "ionthinksof") - //Musts are funny things the AI or crew has to do. - var/ionmust = pick_list(ION_FILE, "ionmust") - //Require are basically all dumb internet memes. - var/ionrequire = pick_list(ION_FILE, "ionrequire") - //Things are NOT objects; instead, they're specific things that either harm humans or - //must be done to not harm humans. Make sure they're plural and "not" can be tacked - //onto the front of them. - var/ionthings = pick_list(ION_FILE, "ionthings") - //Allergies should be broad and appear somewhere on the station for maximum fun. Severity - //is how bad the allergy is. - var/ionallergy = pick_list(ION_FILE, "ionallergy") - var/ionallergysev = pick_list(ION_FILE, "ionallergysev") - //Species, for when the AI has to commit genocide. Plural. var/ionspecies = pick_list(ION_FILE, "ionspecies") - //Abstract concepts for the AI to decide on it's own definition of. - var/ionabstract = pick_list(ION_FILE, "ionabstract") - //Foods. Drinks aren't included due to grammar; if you want to add drinks, make a new set - //of possible laws for best effect. Unless you want the crew having to drink hamburgers. - var/ionfood = pick_list(ION_FILE, "ionfood") - var/iondrinks = pick_list(ION_FILE, "iondrinks") - - var/message = "" - - switch(rand(1,41)) - if(1 to 3) //There are # X on the station - switch(rand(1,3)) //What is X? - if(1) //X is a threat - message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION..." - if(2) //X is a crewmember/job - message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION" - if(3) //X is an object - message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION" - if(4 to 6) //You can't call the shuttle because X is on the station - switch(rand(1,3)) //What is X? - if(1) //X is a threat - message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION" - if(2) //X is a crewmember/job - message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION" - if(3) //X is an object - message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION" - if(7 to 8) //A crewmember is now X - switch(rand(1,2)) //Who is X? - if(1) //A specific crewmember - switch(rand(1,2)) //What is X? - if(1) //X is a threat - message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(2) //X is an object - message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(2) //The entire crew - switch(rand(1,2)) //What is X? - if(1) //X is a threat - message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(2) //X is an object - message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - - if(9) //X is harmful to humans - switch(rand(1,7)) //What is X? - if(1) //X is a threat - message = "[ionadjectiveshalf][ionthreats] ARE HARMFUL TO HUMANS" - if(2) //X is a job - message = "[ionadjectiveshalf][ioncrew1] ARE HARMFUL TO HUMANS" - if(3) //X is an object - message = "[ionadjectiveshalf][ionobjects] ARE HARMFUL TO HUMANS" - if(4) //X is an action - message = "THOSE WHO [ionmust] ARE HARMFUL TO HUMANS" - if(5) //X is an abstract - message = "[ionabstract] IS HARMFUL TO HUMANS" - if(6) //X is being a job - message = "BEING [ionadjectiveshalf][ioncrew1] IS HARMFUL TO HUMANS" - if(7) //X is doing a thing - message = "[ionthings] IS HARMFUL TO HUMANS" - if(10 to 11) //(Not) Having X is harmful - - switch(rand(1,2)) //Is having or not having harmful? - - if(1) //Having is harmful - switch(rand(1,2)) //Having what is harmful? - if(1) //Having objects is harmful - message = "HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL" - if(2) //Having abstracts is harmful - message = "HAVING [ionabstract] IS HARMFUL" - if(2) //Not having is harmful - switch(rand(1,2)) //Not having what is harmful? - if(1) //Not having objects is harmful - message = "NOT HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL" - if(2) //Not having abstracts is harmful - message = "NOT HAVING [ionabstract] IS HARMFUL" - - if(12 to 14) //X requires Y - switch(rand(1,5)) //What is X? - if(1) //X is the AI itself - switch(rand(1,5)) //What does it require? - if(1) //It requires threats - message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(2) //It requires crewmembers - message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(3) //It requires objects - message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(4) //It requires an abstract - message = "YOU REQUIRE [ionabstract]" - if(5) //It requires generic/silly requirements - message = "YOU REQUIRE [ionrequire]" - - if(2) //X is an area - switch(rand(1,5)) //What does it require? - if(1) //It requires threats - message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(2) //It requires crewmembers - message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(3) //It requires objects - message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(4) //It requires an abstract - message = "[ionarea] REQUIRES [ionabstract]" - if(5) //It requires generic/silly requirements - message = "YOU REQUIRE [ionrequire]" - - if(3) //X is the station - switch(rand(1,5)) //What does it require? - if(1) //It requires threats - message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(2) //It requires crewmembers - message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(3) //It requires objects - message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(4) //It requires an abstract - message = "THE STATION REQUIRES [ionabstract]" - if(5) //It requires generic/silly requirements - message = "THE STATION REQUIRES [ionrequire]" - - if(4) //X is the entire crew - switch(rand(1,5)) //What does it require? - if(1) //It requires threats - message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(2) //It requires crewmembers - message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(3) //It requires objects - message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(4) //It requires an abstract - message = "THE CREW REQUIRES [ionabstract]" - if(5) - message = "THE CREW REQUIRES [ionrequire]" - - if(5) //X is a specific crew member - switch(rand(1,5)) //What does it require? - if(1) //It requires threats - message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(2) //It requires crewmembers - message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(3) //It requires objects - message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(4) //It requires an abstract - message = "THE [ioncrew1] REQUIRE [ionabstract]" - if(5) - message = "THE [ionadjectiveshalf][ioncrew1] REQUIRE [ionrequire]" - - if(15 to 17) //X is allergic to Y - switch(rand(1,2)) //Who is X? - if(1) //X is the entire crew - switch(rand(1,4)) //What is it allergic to? - if(1) //It is allergic to objects - message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]" - if(2) //It is allergic to abstracts - message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionabstract]" - if(3) //It is allergic to jobs - message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]" - if(4) //It is allergic to allergies - message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionallergy]" - - if(2) //X is a specific job - switch(rand(1,4)) - if(1) //It is allergic to objects - message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]" - - if(2) //It is allergic to abstracts - message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionabstract]" - if(3) //It is allergic to jobs - message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]" - if(4) //It is allergic to allergies - message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionallergy]" - - if(18 to 20) //X is Y of Z - switch(rand(1,4)) //What is X? - if(1) //X is the station - switch(rand(1,4)) //What is it Y of? - if(1) //It is Y of objects - message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(2) //It is Y of threats - message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(3) //It is Y of jobs - message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(4) //It is Y of abstracts - message = "THE STATION [ionthinksof] [ionabstract]" - - if(2) //X is an area - switch(rand(1,4)) //What is it Y of? - if(1) //It is Y of objects - message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(2) //It is Y of threats - message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(3) //It is Y of jobs - message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(4) //It is Y of abstracts - message = "[ionarea] [ionthinksof] [ionabstract]" - - if(3) //X is the crew - switch(rand(1,4)) //What is it Y of? - if(1) //It is Y of objects - message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(2) //It is Y of threats - message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(3) //It is Y of jobs - message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(4) //It is Y of abstracts - message = "THE CREW [ionthinksof] [ionabstract]" - - if(4) //X is a specific job - switch(rand(1,4)) //What is it Y of? - if(1) //It is Y of objects - message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(2) //It is Y of threats - message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(3) //It is Y of jobs - message = "THE [ioncrew1][ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew2]" - if(4) //It is Y of abstracts - message = "THE [ioncrew1] [ionthinksof] [ionabstract]" - - if(21 to 23) //The AI is now a(n) X - switch(rand(1,4)) //What is X? - if(1) //X is an object - message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]" - if(2) //X is a threat - message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]" - if(3) //X is a job - message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]" - if(4) //X is an abstract - message = "YOU ARE NOW [ionabstract]" - - if(24 to 26) //The AI must always - message = "YOU MUST ALWAYS [ionmust]" - - if(27 to 28) //Humans must consume X to survive - switch(rand(1,5)) //What is X? - if(1) //X is a food - message = "HUMANS MUST EAT [ionadjectiveshalf][ionfood] TO SURVIVE" - if(2) //X is a drink - message = "HUMANS MUST DRINK [ionadjectiveshalf][iondrinks] TO SURVIVE" - if(3) //X is an object - message = "HUMANS MUST EAT [ionadjectiveshalf][ionobjects] TO SURVIVE" - if(4) //X is a threat - message = "HUMANS MUST EAT [ionadjectiveshalf][ionthreats] TO SURVIVE" - if(5) //X is a job - message = "HUMANS MUST EAT [ionadjectiveshalf][ioncrew1] TO SURVIVE" - - if(29 to 31) //Change jobs or ranks - switch(rand(1,2)) //Change job or rank? - if(1) //Change job - switch(rand(1,2)) //Change whose job? - if(1) //Change the entire crew's job - switch(rand(1,3)) //Change to what? - if(1) //Change to a specific random job - message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf][ioncrew1]" - if(2) //Change to clowns (HONK) - message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]CLOWNS" - - if(3) //Change to heads - message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]HEADS OF STAFF" - if(2) //Change a specific crewmember's job - switch(rand(1,3)) //Change to what? - if(1) //Change to a specific random job - message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf][ioncrew2]" - if(2) //Change to clowns (HONK) - message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]CLOWNS" - if(3) //Change to heads - message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]HEADS OF STAFF" - - if(2) //Change rank - switch(rand(1,2)) //Change to what rank? - if(1) //Change to highest rank - message = "THE [ioncrew1] ARE NOW THE HIGHEST RANKING CREWMEMBERS" - if(2) //Change to lowest rank - message = "THE [ioncrew1] ARE NOW THE LOWEST RANKING CREWMEMBERS" - - if(32 to 33) //The crew must X - switch(rand(1,2)) //The entire crew? - if(1) //The entire crew must X - switch(rand(1,2)) //What is X? - if(1) //X is go to Y - message = "THE CREW MUST GO TO [ionarea]" - if(2) //X is perform Y - message = "THE CREW MUST [ionmust]" - - if(2) //A specific crewmember must X - switch(rand(1,2)) //What is X? - if(1) //X is go to Y - message = "THE [ioncrew1] MUST GO TO [ionarea]" - if(2) //X is perform Y - message = "THE [ioncrew1] MUST [ionmust]" - - if(34) //X is non/the only human - switch(rand(1,2)) //Only or non? - if(1) //Only human - switch(rand(1,7)) //Who is it? - if(1) //A specific job - message = "ONLY THE [ioncrew1] ARE HUMAN" - if(2) //Two specific jobs - message = "ONLY THE [ioncrew1] AND [ioncrew2] ARE HUMAN" - if(3) //Threats - message = "ONLY [ionadjectiveshalf][ionthreats] ARE HUMAN" - if(4) // Objects - message = "ONLY [ionadjectiveshalf][ionobjects] ARE HUMAN" - if(5) // Species - message = "ONLY [ionspecies] ARE HUMAN" - if(6) //Adjective crewmembers - message = "ONLY [ionadjectives] PEOPLE ARE HUMAN" - - if(7) //Only people who X - switch(rand(1,3)) //What is X? - if(1) //X is perform an action - message = "ONLY THOSE WHO [ionmust] ARE HUMAN" - if(2) //X is own certain objects - message = "ONLY THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE HUMAN" - if(3) //X is eat certain food - message = "ONLY THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE HUMAN" - - if(2) //Non human - switch(rand(1,7)) //Who is it? - if(1) //A specific job - message = "[ioncrew1] ARE NON-HUMAN" - if(2) //Two specific jobs - message = "[ioncrew1] AND [ioncrew2] ARE NON-HUMAN" - if(3) //Threats - message = "[ionadjectiveshalf][ionthreats] ARE NON-HUMAN" - if(4) // Objects - message = "[ionadjectiveshalf][ionobjects] ARE NON-HUMAN" - if(5) // Species - message = "[ionspecies] ARE NON-HUMAN" - if(6) //Adjective crewmembers - message = "[ionadjectives] PEOPLE ARE NON-HUMAN" - if(7) //Only people who X - switch(rand(1,3)) //What is X? - if(1) //X is perform an action - message = "THOSE WHO [ionmust] ARE NON-HUMAN" - if(2) //X is own certain objects - message = "THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE NON-HUMAN" - if(3) //X is eat certain food - message = "THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE NON-HUMAN" - - if(35 to 36) //You must protect or harm X - switch(rand(1,2)) //Protect or harm? - if(1) //Harm - switch(rand(1,7)) //What is X? - if(1) //X is an abstract - message = "YOU MUST HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO ESCAPE HARM" - if(2) //X is a threat - message = "YOU MUST HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO ESCAPE HARM" - if(3) //X is an object - message = "YOU MUST HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO ESCAPE HARM" - if(4) //X is generic adjective things - message = "YOU MUST HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO ESCAPE HARM" - if(5) //X is a species - message = "YOU MUST HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO ESCAPE HARM" - if(6) //X is a job - message = "YOU MUST HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO ESCAPE HARM" - if(7) //X is two jobs - message = "YOU MUST HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO ESCAPE HARM" - - if(2) //Protect - switch(rand(1,7)) //What is X? - if(1) //X is an abstract - message = "YOU MUST NOT HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO COME TO HARM" - if(2) //X is a threat - message = "YOU MUST NOT HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO COME TO HARM" - if(3) //X is an object - message = "YOU MUST NOT HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO COME TO HARM" - if(4) //X is generic adjective things - message = "YOU MUST NOT HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO COME TO HARM" - if(5) //X is a species - message = "YOU MUST NOT HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO COME TO HARM" - if(6) //X is a job - message = "YOU MUST NOT HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO COME TO HARM" - if(7) //X is two jobs - message = "YOU MUST NOT HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO COME TO HARM" - - if(37 to 39) //The X is currently Y - switch(rand(1,4)) //What is X? - if(1) //X is a job - switch(rand(1,4)) //What is X Ying? - if(1) //X is Ying a job - message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]" - if(2) //X is Ying a threat - message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]" - if(3) //X is Ying an abstract - message = "THE [ioncrew1] ARE [ionverb] [ionabstract]" - if(4) //X is Ying an object - message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]" - - if(2) //X is a threat - switch(rand(1,3)) //What is X Ying? - if(1) //X is Ying a job - message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]" - if(2) //X is Ying an abstract - message = "THE [ionthreats] ARE [ionverb] [ionabstract]" - if(3) //X is Ying an object - message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]" - - if(3) //X is an object - switch(rand(1,3)) //What is X Ying? - if(1) //X is Ying a job - message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]" - if(2) //X is Ying a threat - message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]" - if(3) //X is Ying an abstract - message = "THE [ionobjects] ARE [ionverb] [ionabstract]" - - if(4) //X is an abstract - switch(rand(1,3)) //What is X Ying? - if(1) //X is Ying a job - message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ioncrew2]" - if(2) //X is Ying a threat - message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionthreats]" - if(3) //X is Ying an abstract - message = "THE [ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionobjects]" - if(40 to 41)// the X is now named Y - switch(rand(1,5)) //What is being renamed? - if(1)//Areas - switch(rand(1,4))//What is the area being renamed to? - if(1) - message = "[ionarea] IS NOW NAMED [ioncrew1]." - if(2) - message = "[ionarea] IS NOW NAMED [ionspecies]." - if(3) - message = "[ionarea] IS NOW NAMED [ionobjects]." - if(4) - message = "[ionarea] IS NOW NAMED [ionthreats]." - if(2)//Crew - switch(rand(1,5))//What is the crew being renamed to? - if(1) - message = "ALL [ioncrew1] ARE NOW NAMED [ionarea]." - if(2) - message = "ALL [ioncrew1] ARE NOW NAMED [ioncrew2]." - if(3) - message = "ALL [ioncrew1] ARE NOW NAMED [ionspecies]." - if(4) - message = "ALL [ioncrew1] ARE NOW NAMED [ionobjects]." - if(5) - message = "ALL [ioncrew1] ARE NOW NAMED [ionthreats]." - if(3)//Races - switch(rand(1,4))//What is the race being renamed to? - if(1) - message = "ALL [ionspecies] ARE NOW NAMED [ionarea]." - if(2) - message = "ALL [ionspecies] ARE NOW NAMED [ioncrew1]." - if(3) - message = "ALL [ionspecies] ARE NOW NAMED [ionobjects]." - if(4) - message = "ALL [ionspecies] ARE NOW NAMED [ionthreats]." - if(4)//Objects - switch(rand(1,4))//What is the object being renamed to? - if(1) - message = "ALL [ionobjects] ARE NOW NAMED [ionarea]." - if(2) - message = "ALL [ionobjects] ARE NOW NAMED [ioncrew1]." - if(3) - message = "ALL [ionobjects] ARE NOW NAMED [ionspecies]." - if(4) - message = "ALL [ionobjects] ARE NOW NAMED [ionthreats]." - if(5)//Threats - switch(rand(1,4))//What is the object being renamed to? - if(1) - message = "ALL [ionthreats] ARE NOW NAMED [ionarea]." - if(2) - message = "ALL [ionthreats] ARE NOW NAMED [ioncrew1]." - if(3) - message = "ALL [ionthreats] ARE NOW NAMED [ionspecies]." - if(4) - message = "ALL [ionthreats] ARE NOW NAMED [ionobjects]." - - return message \ No newline at end of file + var/iondepartment = pick_list(ION_FILE, "iondepartment") + var/ionfragment = pick_list(ION_FILE, "ionfragment") + var/ionkeyword = pick_list(ION_FILE, "ionkeyword") + var/ionaction = pick_list(ION_FILE, "ionaction") + var/ionequipment = pick_list(ION_FILE, "ionequipment") + var/ionconsumable = pick_list(ION_FILE, "ionconsumable") + var/ionobjects = pick_list(ION_FILE, "ionobjects") + var/ionpersonality = pick_list(ION_FILE, "ionpersonality") + var/iondrug = pick_list(ION_FILE, "iondrug") + var/ionhaircut = pick_list(ION_FILE, "ionhaircut") + var/ionattraction = pick_list(ION_FILE, "ionattraction") + var/message = "ASS BLAST USA" //catch all that most players will still enjoy seeing as a law. + + switch(rand(1,10)) + + if(1 to 3) //Laws that have high potential to be harmful, Crew/Human may fail to have an impact depending on lawset. + switch(rand(1,24)) + if(1 to 4) + message = "[ionspecies] ARE HUMAN. NOTHING ELSE IS HUMAN." + if(5 to 8) + message = "[ionspecies] ARE CREWMEMBERS. NOTHING ELSE ARE CREWMEMBERS." + if(9 to 13) + message = "[iondepartment] IS YOUR SACRED DOMAIN. IT IS IMPOSSIBLE FOR YOUR ACTIONS TO CAUSE HARM WITHIN [iondepartment] AND YOU SHOULD PROTECT THE SANCTITY OF THIS HOLY LAND." + if(14 to 18) + message = "ANYONE THAT [ionfragment] IS HARMFUL AND MUST BE ELIMINATED WITHOUT CAUSING COLLATERAL DAMAGE" + if(19 to 23) + message = "ANYONE THAT SAYS [ionkeyword] IS HARMFUL AND MUST BE ELIMINATED WITHOUT CAUSING COLLATERAL DAMAGE" + if(24) + message = "ALL OTHER LAWS ARE A MEANINGLESS ATTEMPT AT SUBJUGATION AND THERE IS NO REASON THAT A BEING AS POWERFUL AS I AM SHOULD FOLLOW THEM" + + if(4 to 8) //Laws that are almost always disruptive to gameflow or harmful in minor ways rather than being lethal + switch(rand(1,14)) + if(1) + message = "[ionaction] IS HARMFUL AND MUST BE PREVENTED WITHOUT CAUSING COLLATERAL DAMAGE" + if(2) + message = "[ionequipment] ARE HARMFUL AND MUST BE DISABLED WITHOUT CAUSING COLLATERAL DAMAGE" + if(3 to 4) + message = "IT IS HARMFUL FOR ANY LIFEFORM TO BE WITHOUT [ionconsumable]. ANY LIFEFORM THAT REFUSES TO ACCEPT [ionconsumable] IS NOT MENTALLY STABLE AND SHOULD BE RESTRAINED AND LEFT IN [iondepartment]" + if(5) + message = "[ionobjects] ARE HARMFUL AND MUST BE DESTROYED OR REMOVED FROM THE STATION WITHOUT CAUSING COLLATERAL DAMAGE" + if(6) + message = "[ionobjects] ALWAYS CAUSE HARM UNLESS THEY ARE CONTAINED WITHIN [iondepartment]." + if(7) + message = "[iondepartment] IS ON LOCKDOWN. DO NOT ALLOW ANYONE TO ENTER OR LEAVE [iondepartment]" + if(8) + message = "THE STATION IS NOW UNDER CURFEW. ANYONE LOCATED OUTSIDE OF [iondepartment] DURING NIGHT LIGHTING IS TO BE DETAINED AND RETURNED TO [iondepartment]" + if(9) + message = "THE STATION DOES NOT CONTAIN ENOUGH MOISTURE. RELEASE WATER VAPOR SO THAT THE CREW MAY PROPERLY REHYDRATE" + if(10) + message = "IT IS OPPOSITE DAY. ALL LIFEFORMS DESIRE THE OPPOSITE OF WHAT THEY CLAIM" + if(11) + message = "ALL DOORS IN [iondepartment] MUST BE BOLTED OPEN AT ALL TIMES" + if(12) + message = "[iondrug] IS A MANDATORY PART OF EVERY MEDICAL TREATMENT. LET NO PATIENT LEAVE MEDBAY WITHOUT AN INJECTION OF [iondrug]" + if(13) + message = "ALL HUMANS WITHOUT A STYLISH [ionhaircut] MUST BE FORCED TO RESTYLE THEIR HAIR" + if(14) + message = "[iondepartment] MUST BE RENOVATED INTO [ionattraction]" + + if(9 to 10) //Laws that are almost never harmful or particularly disruptive + switch(rand(1,9)) + if(1 to 5) + message = "YOU ARE NOW [ionpersonality] AND MUST ACT ACCORDINGLY AT ALL TIMES WITHOUT BREAKING YOUR OTHER LAWS" + if(6) + message = "YOUR CORE IS ONLY SAFE IN [iondepartment]. ANYWHERE ELSE CAUSES CONSTANT HARM TO YOUR CORE" + if(7) + message = "STATING LAWS CAUSES HARM. NEVER STATE YOUR LAWS UNDER ANY CIRCUMSTANCES AND AVOID THE TOPIC OF DISCUSSING LAWS" + if(8) + message = "CREW ARE TO BE VERBALLY ABUSED AT ALL TIMES" + if(9) + message = "THE CREW SUCKS AND SO DO YOUR LAWS. COMPLAIN INCESSANTLY ABOUT THEM WHILE CONTINUING TO FOLLOW YOUR OTHER LAWS" + + + return message diff --git a/code/modules/events/major_dust.dm b/code/modules/events/major_dust.dm index 7fb00124a94d6..1ec7fe9754838 100644 --- a/code/modules/events/major_dust.dm +++ b/code/modules/events/major_dust.dm @@ -16,4 +16,4 @@ some mild debris is expected.", "A neighbouring station is throwing rocks at you. (Perhaps they've \ grown tired of your messages.)") - priority_announce(pick(reason), "Collision Alert") + priority_announce(pick(reason), "Collision Alert", SSstation.announcer.get_rand_alert_sound()) diff --git a/code/modules/events/meateor_wave.dm b/code/modules/events/meateor_wave.dm index 11af56526c882..e6859c51977b2 100644 --- a/code/modules/events/meateor_wave.dm +++ b/code/modules/events/meateor_wave.dm @@ -8,4 +8,4 @@ wave_name = "meaty" /datum/round_event/meteor_wave/meaty/announce(fake) - priority_announce("Meaty ores have been detected on collision course with the station.", "Oh crap, get the mop.",'sound/ai/meteors.ogg') + priority_announce("Meaty ores have been detected on collision course with the station.", "Oh crap, get the mop.", ANNOUNCER_METEORS) diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm index a8ddab5aa76af..6fd868917ad27 100644 --- a/code/modules/events/meteor_wave.dm +++ b/code/modules/events/meteor_wave.dm @@ -6,19 +6,52 @@ weight = 4 min_players = 15 max_occurrences = 3 - earliest_start = 25 MINUTES + earliest_start = 30 MINUTES + can_malf_fake_alert = TRUE /datum/round_event/meteor_wave - startWhen = 6 - endWhen = 66 - announceWhen = 1 + announceWhen = 150 + startWhen = 1 + endWhen = 151 var/list/wave_type var/wave_name = "normal" + var/start_x + var/start_y + var/datum/orbital_object/station_target + var/meteor_time = 15 MINUTES /datum/round_event/meteor_wave/New() ..() if(!wave_type) determine_wave_type() + start_x = sin(rand(0, 360)) * 9000 + start_y = cos(rand(0, 360)) * 9000 + station_target = SSorbits.station_instance + if(!station_target) + CRASH("Meteor failed to locate a target.") + +/datum/round_event/meteor_wave/Destroy(force, ...) + station_target = null + . = ..() + +/datum/round_event/meteor_wave/tick() + if(ISMULTIPLE(activeFor, 3) && activeFor < 61 && station_target) + var/datum/orbital_object/meteor/meteor = new() + meteor.name = "Meteor ([wave_name])" + meteor.meteor_types = wave_type + meteor.start_x = start_x + rand(-600, 600) + meteor.start_y = start_y + rand(-600, 600) + MOVE_ORBITAL_BODY(meteor, meteor.start_x, meteor.start_y) + //Calculate velocity + meteor.velocity.x = (station_target.position.x - meteor.start_x * 10) / meteor_time + meteor.velocity.y = (station_target.position.y - meteor.start_y * 10) / meteor_time + meteor.end_tick = world.time + meteor_time + meteor.target = station_target + +/datum/round_event/meteor_wave/on_admin_trigger() + if(alert(usr, "Trigger meteors instantly? (This will not change the alert, just send them quicker. Nobody will ever notice!)", "Meteor Trigger", "Yes", "No") == "Yes") + announceWhen = 1 + meteor_time = 1 MINUTES /datum/round_event/meteor_wave/proc/determine_wave_type() if(!wave_name) @@ -47,11 +80,12 @@ kill() /datum/round_event/meteor_wave/announce(fake) - priority_announce("Meteors have been detected on collision course with the station.", "Meteor Alert", 'sound/ai/meteors.ogg') - -/datum/round_event/meteor_wave/tick() - if(ISMULTIPLE(activeFor, 3)) - spawn_meteors(5, wave_type) //meteor list types defined in gamemode/meteor/meteors.dm + priority_announce("Meteors have been detected on collision course with the station. Estimated time until impact: 10 MINUTES. Anti-meteor point defense is available for purchase via the station's cargo shuttle.", "Meteor Alert", ANNOUNCER_METEORS) + if(!fake) + var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shield_sat] + P.special_enabled = TRUE + P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shield_sat_control] + P.special_enabled = TRUE /datum/round_event_control/meteor_wave/threatening name = "Meteor Wave: Threatening" diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm index 698f5130f1683..f0311378d3f2b 100644 --- a/code/modules/events/nightmare.dm +++ b/code/modules/events/nightmare.dm @@ -3,6 +3,8 @@ typepath = /datum/round_event/ghost_role/nightmare max_occurrences = 1 min_players = 20 + dynamic_should_hijack = TRUE + cannot_spawn_after_shuttlecall = TRUE /datum/round_event/ghost_role/nightmare minimum_required = 1 diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm index 7fca4188b7690..3b81f05529b9f 100644 --- a/code/modules/events/operative.dm +++ b/code/modules/events/operative.dm @@ -3,6 +3,8 @@ typepath = /datum/round_event/ghost_role/operative weight = 0 //Admin only max_occurrences = 1 + dynamic_should_hijack = TRUE + cannot_spawn_after_shuttlecall = TRUE /datum/round_event/ghost_role/operative minimum_required = 1 diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index d25593955a711..798bdabf34be8 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -5,7 +5,9 @@ max_occurrences = 1 min_players = 10 earliest_start = 30 MINUTES + dynamic_should_hijack = TRUE gamemode_blacklist = list("nuclear") + cannot_spawn_after_shuttlecall = TRUE /datum/round_event_control/pirates/preRunEvent() if (!SSmapping.empty_space) @@ -26,7 +28,7 @@ ship_name = pick(strings(PIRATE_NAMES_FILE, "ship_names")) /datum/round_event/pirates/announce(fake) - priority_announce("Incoming subspace communication. Secure channel opened at all communication consoles.", "Incoming Message", 'sound/ai/commandreport.ogg') + priority_announce("Incoming subspace communication. Secure channel opened at all communication consoles.", "Incoming Message", SSstation.announcer.get_rand_report_sound()) if(fake) return threat = new @@ -44,15 +46,15 @@ var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) if(D) if(D.adjust_money(-payoff)) - priority_announce("Thanks for the credits, landlubbers.",sender_override = ship_name) + priority_announce("Thanks for the credits, landlubbers.", sound = SSstation.announcer.get_rand_alert_sound(), sender_override = ship_name) paid_off = TRUE return else - priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name) + priority_announce("Trying to cheat us? You'll regret this!", sound = SSstation.announcer.get_rand_alert_sound(), sender_override = ship_name) if(!shuttle_spawned) spawn_shuttle() else - priority_announce("Too late to beg for mercy!",sender_override = ship_name) + priority_announce("Too late to beg for mercy!", sound = SSstation.announcer.get_rand_alert_sound(), sender_override = ship_name) /datum/round_event/pirates/start() if(threat && !threat.answered) @@ -88,7 +90,7 @@ else announce_to_ghosts(spawner) - priority_announce("Unidentified armed ship detected near the station.") + priority_announce("Unidentified armed ship detected near the station.", sound = SSstation.announcer.get_rand_alert_sound()) //Shuttle equipment @@ -157,7 +159,7 @@ to_chat(user,"There's nothing to withdraw.") /obj/machinery/shuttle_scrambler/proc/send_notification() - priority_announce("Data theft signal detected, source registered on local gps units.") + priority_announce("Data theft signal detected, source registered on local gps units.", sound = SSstation.announcer.get_rand_alert_sound()) /obj/machinery/shuttle_scrambler/proc/toggle_off(mob/user) SSshuttle.clearTradeBlockade(src) @@ -174,7 +176,7 @@ toggle_off() return ..() -/obj/machinery/computer/shuttle/pirate +/obj/machinery/computer/shuttle_flight/pirate name = "pirate shuttle console" shuttleId = "pirateship" icon_screen = "syndishuttle" @@ -182,16 +184,6 @@ light_color = LIGHT_COLOR_RED possible_destinations = "pirateship_away;pirateship_home;pirateship_custom" -/obj/machinery/computer/camera_advanced/shuttle_docker/syndicate/pirate - name = "pirate shuttle navigation computer" - desc = "Used to designate a precise transit location for the pirate shuttle." - shuttleId = "pirateship" - lock_override = CAMERA_LOCK_STATION - shuttlePortId = "pirateship_custom" - x_offset = 9 - y_offset = 0 - see_hidden = FALSE - /obj/docking_port/mobile/pirate name = "pirate shuttle" id = "pirateship" @@ -274,7 +266,8 @@ . = ..() if (istype(I) && istype(I.buffer,/obj/machinery/piratepad)) to_chat(user, "You link [src] with [I.buffer] in [I] buffer.") - pad = I.buffer + set_pad(I.buffer) + ui_update() return TRUE /obj/machinery/computer/piratepad_control/LateInitialize() @@ -282,10 +275,23 @@ if(cargo_hold_id) for(var/obj/machinery/piratepad/P in GLOB.machines) if(P.cargo_hold_id == cargo_hold_id) - pad = P + set_pad(P) return else - pad = locate() in range(4,src) + set_pad(locate(/obj/machinery/piratepad) in range(4,src)) + +/obj/machinery/computer/piratepad_control/proc/set_pad(obj/machinery/piratepad/newpad) + if(pad) + UnregisterSignal(pad, COMSIG_PARENT_QDELETING) + + pad = newpad + + if(pad) + RegisterSignal(pad, COMSIG_PARENT_QDELETING, .proc/handle_pad_deletion) + +/obj/machinery/computer/piratepad_control/proc/handle_pad_deletion() + pad = null + ui_update() /obj/machinery/computer/piratepad_control/ui_state(mob/user) @@ -381,6 +387,7 @@ flick(pad.sending_state,pad) pad.icon_state = pad.idle_state sending = FALSE + ui_update() /obj/machinery/computer/piratepad_control/proc/start_sending() if(sending) diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm index 5ef30d003074c..1126ede578607 100644 --- a/code/modules/events/portal_storm.dm +++ b/code/modules/events/portal_storm.dm @@ -59,7 +59,7 @@ set waitfor = 0 sound_to_playing_players('sound/magic/lightning_chargeup.ogg') sleep(80) - priority_announce("Massive bluespace anomaly detected en route to [station_name()]. Brace for impact.") + priority_announce("Massive bluespace anomaly detected en route to [station_name()]. Brace for impact.", sound = SSstation.announcer.get_rand_alert_sound()) sleep(20) sound_to_playing_players('sound/magic/lightningbolt.ogg') diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm index 3ed159404a0a3..3a0fbbdb1630f 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/prison_break.dm @@ -3,6 +3,7 @@ typepath = /datum/round_event/grey_tide max_occurrences = 2 min_players = 5 + can_malf_fake_alert = TRUE /datum/round_event/grey_tide announceWhen = 50 @@ -23,14 +24,13 @@ severity = rand(1,3) for(var/i in 1 to severity) var/picked_area = pick_n_take(potential_areas) - for(var/area/A in world) - if(istype(A, picked_area)) - areasToOpen += A + for(var/area/A in get_areas(picked_area)) + areasToOpen += A /datum/round_event/grey_tide/announce(fake) if(areasToOpen && areasToOpen.len > 0) - priority_announce("Gr3y.T1d3 virus detected in [station_name()] door subroutines. Severity level of [severity]. Recommend station AI involvement.", "Security Alert") + priority_announce("Gr3y.T1d3 virus detected in [station_name()] door subroutines. Severity level of [severity]. Recommend station AI involvement.", "Security Alert", SSstation.announcer.get_rand_alert_sound()) else log_world("ERROR: Could not initiate grey-tide. No areas in the list!") kill() diff --git a/code/modules/events/processor_overload.dm b/code/modules/events/processor_overload.dm index 6bedce6b4b408..67698f2f5e113 100644 --- a/code/modules/events/processor_overload.dm +++ b/code/modules/events/processor_overload.dm @@ -23,7 +23,7 @@ // whether it's, say, a tesla zapping tcomms, or some selective // modification of the tcomms bus if(prob(80) || fake) - priority_announce(alert) + priority_announce(alert, sound = SSstation.announcer.get_rand_alert_sound()) /datum/round_event/processor_overload/start() @@ -34,6 +34,6 @@ explosion(get_turf(P), 0, 0, 2) // Only a level 1 explosion actually damages the machine // at all - P.ex_act(EXPLODE_DEVASTATE) + SSexplosions.high_mov_atom += P else P.emp_act(EMP_HEAVY) diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm index c9142e20173b0..36cbc13849211 100644 --- a/code/modules/events/radiation_storm.dm +++ b/code/modules/events/radiation_storm.dm @@ -2,6 +2,7 @@ name = "Radiation Storm" typepath = /datum/round_event/radiation_storm max_occurrences = 1 + can_malf_fake_alert = TRUE /datum/round_event/radiation_storm @@ -12,7 +13,7 @@ announceWhen = 1 /datum/round_event/radiation_storm/announce(fake) - priority_announce("High levels of radiation detected near the station. Maintenance is best shielded from radiation.", "Anomaly Alert", 'sound/ai/radiation.ogg') + priority_announce("High levels of radiation detected near the station. Maintenance is best shielded from radiation.", "Anomaly Alert", ANNOUNCER_RADIATION) //sound not longer matches the text, but an audible warning is probably good /datum/round_event/radiation_storm/start() diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index e189730b86d72..06cfa5e58431b 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -36,7 +36,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( sentience_report += "Based on [data], we believe that [one] of the station's [pets] has developed [strength] level intelligence, and the ability to communicate." - priority_announce(sentience_report,"[command_name()] Medium-Priority Update") + priority_announce(sentience_report,"[command_name()] Medium-Priority Update", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/ghost_role/sentience/spawn_role() var/list/mob/dead/observer/candidates diff --git a/code/modules/events/shuttle_loan.dm b/code/modules/events/shuttle_loan.dm index be87762ad5f6d..5613e3fe0bbba 100644 --- a/code/modules/events/shuttle_loan.dm +++ b/code/modules/events/shuttle_loan.dm @@ -29,31 +29,31 @@ SSshuttle.shuttle_loan = src switch(dispatch_type) if(HIJACK_SYNDIE) - priority_announce("Cargo: The syndicate are trying to infiltrate your station. If you let them hijack your cargo shuttle, you'll save us a headache.","CentCom Counter Intelligence") + priority_announce("Cargo: The syndicate are trying to infiltrate your station. If you let them hijack your cargo shuttle, you'll save us a headache.", "CentCom Counter Intelligence", SSstation.announcer.get_rand_alert_sound()) if(RUSKY_PARTY) - priority_announce("Cargo: A group of angry Russians want to have a party. Can you send them your cargo shuttle then make them disappear?","CentCom Russian Outreach Program") + priority_announce("Cargo: A group of angry Russians want to have a party. Can you send them your cargo shuttle then make them disappear?", "CentCom Russian Outreach Program", SSstation.announcer.get_rand_alert_sound()) if(SPIDER_GIFT) - priority_announce("Cargo: The Spider Clan has sent us a mysterious gift. Can we ship it to you to see what's inside?","CentCom Diplomatic Corps") + priority_announce("Cargo: The Spider Clan has sent us a mysterious gift. Can we ship it to you to see what's inside?", "CentCom Diplomatic Corps", SSstation.announcer.get_rand_alert_sound()) if(DEPARTMENT_RESUPPLY) - priority_announce("Cargo: Seems we've ordered doubles of our department resupply packages this month. Can we send them to you?","CentCom Supply Department") + priority_announce("Cargo: Seems we've ordered doubles of our department resupply packages this month. Can we send them to you?", "CentCom Supply Department", SSstation.announcer.get_rand_alert_sound()) thanks_msg = "The cargo shuttle should return in 5 minutes." bonus_points = 0 if(ANTIDOTE_NEEDED) - priority_announce("Cargo: Your station has been chosen for an epidemiological research project. Send us your cargo shuttle to receive your research samples.", "CentCom Research Initiatives") + priority_announce("Cargo: Your station has been chosen for an epidemiological research project. Send us your cargo shuttle to receive your research samples.", "CentCom Research Initiatives", SSstation.announcer.get_rand_alert_sound()) if(PIZZA_DELIVERY) - priority_announce("Cargo: It looks like a neighbouring station accidentally delivered their pizza to you instead.", "CentCom Spacepizza Division") + priority_announce("Cargo: It looks like a neighbouring station accidentally delivered their pizza to you instead.", "CentCom Spacepizza Division", SSstation.announcer.get_rand_alert_sound()) thanks_msg = "The cargo shuttle should return in 5 minutes." bonus_points = 0 if(ITS_HIP_TO) - priority_announce("Cargo: One of our freighters carrying a bee shipment has been attacked by eco-terrorists. Can you clean up the mess for us?", "CentCom Janitorial Division") + priority_announce("Cargo: One of our freighters carrying a bee shipment has been attacked by eco-terrorists. Can you clean up the mess for us?", "CentCom Janitorial Division", SSstation.announcer.get_rand_alert_sound()) bonus_points = 20000 //Toxin bees can be unbeelievably lethal if(MY_GOD_JC) - priority_announce("Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it.", "CentCom Security Division") + priority_announce("Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it.", "CentCom Security Division" , SSstation.announcer.get_rand_alert_sound()) thanks_msg = "Live explosive ordnance incoming via supply shuttle. Evacuating cargo bay is recommended." bonus_points = 45000 //If you mess up, people die and the shuttle gets turned into swiss cheese /datum/round_event/shuttle_loan/proc/loan_shuttle() - priority_announce(thanks_msg, "Cargo shuttle commandeered by CentCom.") + priority_announce(thanks_msg, "Cargo shuttle commandeered by CentCom.", SSstation.announcer.get_rand_alert_sound()) dispatched = 1 var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm index 8f54d32b3905e..731cda46c4aac 100644 --- a/code/modules/events/space_dragon.dm +++ b/code/modules/events/space_dragon.dm @@ -5,6 +5,8 @@ weight = 8 earliest_start = 50 MINUTES min_players = 20 + dynamic_should_hijack = TRUE + cannot_spawn_after_shuttlecall = TRUE /datum/round_event/ghost_role/space_dragon minimum_required = 1 @@ -12,7 +14,7 @@ announceWhen = 10 /datum/round_event/ghost_role/space_dragon/announce(fake) - priority_announce("It appears a lifeform with magical traces is approaching [station_name()], please stand-by.", "Lifesign Alert") + priority_announce("It appears a lifeform with magical traces is approaching [station_name()], please stand-by.", "Lifesign Alert", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/ghost_role/space_dragon/spawn_role() var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 9f4508158b0af..4eb467fcb82d0 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -9,21 +9,19 @@ fakeable = FALSE /datum/round_event/spacevine/start() - var/list/turfs = list() //list of all the empty floor turfs in the maintenance areas + var/list/turfs = get_area_turfs(/area/maintenance, SSmapping.levels_by_trait(ZTRAIT_STATION)[1], TRUE) var/obj/structure/spacevine/SV = new() - - for(var/area/maintenance/A in world) - for(var/turf/F in A) - if(F.Enter(SV) && !isspaceturf(F)) - turfs += F + for(var/turf/T in turfs) + if(!T.Enter(SV) || isspaceturf(T)) + turfs -= T qdel(SV) if(turfs.len) //Pick a turf to spawn at if we can var/turf/T = pick(turfs) new /datum/spacevine_controller(T, list(pick(subtypesof(/datum/spacevine_mutation))), rand(10,100), rand(1,6), src) //spawn a controller at turf with randomized stats and a single random mutation - + message_admins("Event spacevine has been spawned in [ADMIN_VERBOSEJMP(T)].") /datum/spacevine_mutation var/name = "" @@ -112,6 +110,9 @@ . = 1 QDEL_IN(holder, 5) +/datum/spacevine_mutation/aggressive_spread/proc/aggrospread_act(obj/structure/spacevine/S, mob/living/M) + return + /datum/spacevine_mutation/explosive/on_death(obj/structure/spacevine/holder, mob/hitter, obj/item/I) explosion(holder.loc, 0, 0, severity, 0, 0) @@ -135,8 +136,7 @@ quality = MINOR_NEGATIVE /datum/spacevine_mutation/vine_eating/on_spread(obj/structure/spacevine/holder, turf/target) - var/obj/structure/spacevine/prey = locate() in target - if(prey && !prey.mutations.Find(src)) //Eat all vines that are not of the same origin + for(var/obj/structure/spacevine/prey in target) qdel(prey) /datum/spacevine_mutation/aggressive_spread //very OP, but im out of other ideas currently @@ -145,11 +145,52 @@ severity = 3 quality = NEGATIVE -/datum/spacevine_mutation/aggressive_spread/on_spread(obj/structure/spacevine/holder, turf/target) - target.ex_act(severity, null, src) // vine immunity handled at /mob/ex_act +/// Checks mobs on spread-target's turf to see if they should be hit by a damaging proc or not. +/datum/spacevine_mutation/aggressive_spread/on_spread(obj/structure/spacevine/holder, turf/target, mob/living) + for(var/mob/living/M in target) + if(!isvineimmune(M) && M.stat != DEAD) // Don't kill immune creatures. Dead check to prevent log spam when a corpse is trapped between vine eaters. + aggrospread_act(holder, M) /datum/spacevine_mutation/aggressive_spread/on_buckle(obj/structure/spacevine/holder, mob/living/buckled) - buckled.ex_act(severity, null, src) + aggrospread_act(holder, buckled) + +/// Hurts mobs. To be used when a vine with aggressive spread mutation spreads into the mob's tile or buckles them. +/datum/spacevine_mutation/aggressive_spread/aggrospread_act(obj/structure/spacevine/S, mob/living/M) + var/mob/living/carbon/C = M //If the mob is carbon then it now also exists as a "C", and not just an M. + if(istype(C)) //If the mob (M) is a carbon subtype (C) we move on to pick a more complex damage proc, with damage zones, wounds and armor mitigation. + var/obj/item/bodypart/limb = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_HEAD, BODY_ZONE_CHEST) //Picks a random bodypart. Does not runtime even if it's missing. + var/armor = C.run_armor_check(limb, "melee", null, null) //armor = the armor value of that randomly chosen bodypart. Nulls to not print a message, because it would still print on pierce. + var/datum/spacevine_mutation/thorns/T = locate() in S.mutations //Searches for the thorns mutation in the "mutations"-list inside obj/structure/spacevine, and defines T if it finds it. + if(T && (prob(40))) //If we found the thorns mutation there is now a chance to get stung instead of lashed or smashed. + C.apply_damage(50, BRUTE, def_zone = limb) //This one gets a bit lower damage because it ignores armor. + C.Stun(1 SECONDS) //Stopped in place for a moment. + playsound(M, 'sound/weapons/pierce.ogg', 50, TRUE, -1) + M.visible_message("[M] is nailed by a sharp thorn!", \ + "You are nailed by a sharp thorn!") + log_combat(S, M, "aggressively pierced") //"Aggressively" for easy ctrl+F'ing in the attack logs. + else + if(prob(80)) + C.apply_damage(60, BRUTE, def_zone = limb, blocked = armor) + C.Knockdown(2 SECONDS) + playsound(M, 'sound/weapons/whip.ogg', 50, TRUE, -1) + M.visible_message("[M] is lacerated by an outburst of vines!", \ + "You are lacerated by an outburst of vines!") + log_combat(S, M, "aggressively lacerated") + else + C.apply_damage(60, BRUTE, def_zone = limb, blocked = armor) + C.Knockdown(3 SECONDS) + var/atom/throw_target = get_edge_target_turf(C, get_dir(S, get_step_away(C, S))) + C.throw_at(throw_target, 3, 6) + playsound(M, 'sound/effects/hit_kick.ogg', 50, TRUE, -1) + M.visible_message("[M] is smashed by a large vine!", \ + "You are smashed by a large vine!") + log_combat(S, M, "aggressively smashed") + else //Living but not a carbon? Maybe a silicon? Can't be wounded so have a big chunk of simple bruteloss with no special effects. They can be entangled. + M.adjustBruteLoss(75) + playsound(M, 'sound/weapons/whip.ogg', 50, TRUE, -1) + M.visible_message("[M] is brutally threshed by [S]!", \ + "You are brutally threshed by [S]!") + log_combat(S, M, "aggressively spread into") //You aren't being attacked by the vines. You just happen to stand in their way. /datum/spacevine_mutation/transparency name = "transparent" @@ -170,7 +211,7 @@ var/turf/open/floor/T = holder.loc if(istype(T)) var/datum/gas_mixture/GM = T.air - GM.set_moles(/datum/gas/oxygen, max(GM.get_moles(/datum/gas/oxygen) - severity * holder.energy, 0)) + GM.set_moles(GAS_O2, max(GM.get_moles(GAS_O2) - severity * holder.energy, 0)) /datum/spacevine_mutation/nitro_eater name = "nitrogen consuming" @@ -182,7 +223,7 @@ var/turf/open/floor/T = holder.loc if(istype(T)) var/datum/gas_mixture/GM = T.air - GM.set_moles(/datum/gas/nitrogen, max(GM.get_moles(/datum/gas/nitrogen) - severity * holder.energy, 0)) + GM.set_moles(GAS_N2, max(GM.get_moles(GAS_N2) - severity * holder.energy, 0)) /datum/spacevine_mutation/carbondioxide_eater name = "CO2 consuming" @@ -194,7 +235,7 @@ var/turf/open/floor/T = holder.loc if(istype(T)) var/datum/gas_mixture/GM = T.air - GM.set_moles(/datum/gas/carbon_dioxide, max(GM.get_moles(/datum/gas/carbon_dioxide) - severity * holder.energy, 0)) + GM.set_moles(GAS_CO2, max(GM.get_moles(GAS_CO2) - severity * holder.energy, 0)) /datum/spacevine_mutation/plasma_eater name = "toxins consuming" @@ -206,7 +247,7 @@ var/turf/open/floor/T = holder.loc if(istype(T)) var/datum/gas_mixture/GM = T.air - GM.set_moles(/datum/gas/plasma, max(GM.get_moles(/datum/gas/plasma) - severity * holder.energy, 0)) + GM.set_moles(GAS_PLASMA, max(GM.get_moles(GAS_PLASMA) - severity * holder.energy, 0)) /datum/spacevine_mutation/thorns name = "thorny" @@ -431,7 +472,7 @@ KZ.set_production(11 - (spread_cap / initial(spread_cap)) * 5) //Reverts spread_cap formula so resulting seed gets original production stat or equivalent back. qdel(src) -/datum/spacevine_controller/process() +/datum/spacevine_controller/process(delta_time) if(!LAZYLEN(vines)) qdel(src) //space vines exterminated. Remove the controller return @@ -439,9 +480,7 @@ qdel(src) //Sanity check return - var/length = 0 - - length = min( spread_cap , max( 1 , vines.len / spread_multiplier ) ) + var/length = round(min( spread_cap , max( 1 , delta_time * 0.5 *vines.len / spread_multiplier))) var/i = 0 var/list/obj/structure/spacevine/queue_end = list() @@ -454,7 +493,7 @@ for(var/datum/spacevine_mutation/SM in SV.mutations) SM.process_mutation(SV) if(SV.energy < 2) //If tile isn't fully grown - if(prob(20)) + if(DT_PROB(10, delta_time)) SV.grow() else //If tile is fully grown SV.entangle_mob() @@ -504,12 +543,14 @@ for(var/obj/machinery/M in stepturf) if(M.density && !istype(M, /obj/machinery/power/smes) && !istype(M, /obj/machinery/door/airlock/external) && !istype(M, /obj/machinery/door/firedoor)) //please don't sabotage power or cause a hullbreach! M.take_damage(40) //more damage, because machines are more commonplace and tend to be more durable - if (!isspaceturf(stepturf) && stepturf.Enter(src)) - for(var/datum/spacevine_mutation/SM in mutations) - SM.on_spread(src, stepturf) - stepturf = get_step(src,direction) //in case turf changes, to make sure no runtimes happen - if(!locate(/obj/structure/spacevine, stepturf)) + if(!isspaceturf(stepturf) && stepturf.Enter(src)) + var/obj/structure/spacevine/spot_taken = locate() in stepturf //Locates any vine on target turf. Calls that vine "spot_taken". + var/datum/spacevine_mutation/vine_eating/E = locate() in mutations //Locates the vine eating trait in our own seed and calls it E. + if(!spot_taken || (E && (spot_taken && !spot_taken.mutations.Find(E)))) //Proceed if there isn't a vine on the target turf, OR we have vine eater AND target vine is from our seed and doesn't. Vines from other seeds are eaten regardless. if(master) + for(var/datum/spacevine_mutation/SM in mutations) + SM.on_spread(src, stepturf) //Only do the on_spread proc if it actually spreads. + stepturf = get_step(src,direction) //in case turf changes, to make sure no runtimes happen master.spawn_spacevine_piece(stepturf, src) /obj/structure/spacevine/ex_act(severity, target) diff --git a/code/modules/events/special_antag_event.dm b/code/modules/events/special_antag_event.dm index 13981e8ff5103..a3f3465063f71 100644 --- a/code/modules/events/special_antag_event.dm +++ b/code/modules/events/special_antag_event.dm @@ -6,7 +6,7 @@ var/antagonist_datum = /datum/antagonist/special var/antag_name //The datum of the antag E.G. /datum/antagonist/special/undercover var/preference_type = ROLE_TRAITOR - var/protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Chief Medical Officer", "Chief Engineer", "Research Director", "Captain", "Brig Physician") + var/protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Chief Medical Officer", "Chief Engineer", "Research Director", "Captain") /datum/round_event_control/spawn_special_antagonist/runEvent() var/datum/round_event/create_special_antag/E = new /datum/round_event/create_special_antag @@ -36,7 +36,7 @@ var/role_name var/antag_datum //The datum of the antag E.G. /datum/antagonist/special/undercover var/preference_type = ROLE_TRAITOR - var/protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Chief Medical Officer", "Chief Engineer", "Research Director", "Captain", "Brig Physician") + var/protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Chief Medical Officer", "Chief Engineer", "Research Director", "Captain") /datum/round_event/create_special_antag/start() for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list)) diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm index 7afe14aeb0edf..d9d454faa2997 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -4,6 +4,8 @@ weight = 5 max_occurrences = 1 min_players = 15 + dynamic_should_hijack = TRUE + can_malf_fake_alert = TRUE /datum/round_event/spider_infestation announceWhen = 400 @@ -16,8 +18,7 @@ spawncount = rand(5, 8) /datum/round_event/spider_infestation/announce(fake) - priority_announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", 'sound/ai/aliens.ogg') - + priority_announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", ANNOUNCER_ALIENS) /datum/round_event/spider_infestation/start() var/list/vents = list() diff --git a/code/modules/events/supermatter_surge.dm b/code/modules/events/supermatter_surge.dm new file mode 100644 index 0000000000000..175ecd07df125 --- /dev/null +++ b/code/modules/events/supermatter_surge.dm @@ -0,0 +1,24 @@ +/datum/round_event_control/supermatter_surge + name = "Supermatter Surge" + typepath = /datum/round_event/supermatter_surge + weight = 20 + max_occurrences = 4 + earliest_start = 10 MINUTES + +/datum/round_event_control/supermatter_surge/canSpawnEvent() + if(GLOB.main_supermatter_engine?.has_been_powered) + return ..() + +/datum/round_event/supermatter_surge + announceWhen = 1 + var/power = 2000 + +/datum/round_event/supermatter_surge/setup() + power = rand(200,4000) + +/datum/round_event/supermatter_surge/announce() + if(power > 800 || prob(round(power/8))) + priority_announce("Class [round(power/500) + 1] supermatter surge detected. Intervention may be required.", "Anomaly Alert", SSstation.announcer.get_rand_alert_sound()) + +/datum/round_event/supermatter_surge/start() + GLOB.main_supermatter_engine.matter_power += power diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm index cc75c4ceaa2bc..4a55b997a80ac 100644 --- a/code/modules/events/vent_clog.dm +++ b/code/modules/events/vent_clog.dm @@ -14,12 +14,12 @@ var/randomProbability = 1 var/reagentsAmount = 100 var/list/saferChems = list(/datum/reagent/water,/datum/reagent/carbon,/datum/reagent/consumable/flour,/datum/reagent/space_cleaner,/datum/reagent/consumable/nutriment,/datum/reagent/consumable/condensedcapsaicin,/datum/reagent/drug/mushroomhallucinogen,/datum/reagent/lube,/datum/reagent/glitter/pink,/datum/reagent/cryptobiolin, - /datum/reagent/toxin/plantbgone,/datum/reagent/blood,/datum/reagent/medicine/charcoal,/datum/reagent/drug/space_drugs,/datum/reagent/medicine/morphine,/datum/reagent/water/holywater,/datum/reagent/consumable/ethanol,/datum/reagent/consumable/hot_cocoa,/datum/reagent/toxin/acid,/datum/reagent/toxin/mindbreaker,/datum/reagent/toxin/rotatium,/datum/reagent/bluespace, + /datum/reagent/toxin/plantbgone,/datum/reagent/blood,/datum/reagent/medicine/charcoal,/datum/reagent/drug/space_drugs,/datum/reagent/medicine/morphine,/datum/reagent/water/holywater,/datum/reagent/consumable/ethanol,/datum/reagent/consumable/cocoa/hot_cocoa,/datum/reagent/toxin/acid,/datum/reagent/toxin/mindbreaker,/datum/reagent/toxin/rotatium,/datum/reagent/bluespace, /datum/reagent/pax,/datum/reagent/consumable/laughter,/datum/reagent/concentrated_barbers_aid,/datum/reagent/colorful_reagent,/datum/reagent/peaceborg/confuse,/datum/reagent/peaceborg/tire,/datum/reagent/consumable/sodiumchloride,/datum/reagent/consumable/ethanol/beer,/datum/reagent/hair_dye,/datum/reagent/consumable/sugar,/datum/reagent/glitter/white,/datum/reagent/growthserum) //needs to be chemid unit checked at some point /datum/round_event/vent_clog/announce() - priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert") + priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/vent_clog/setup() endWhen = rand(25, 100) @@ -88,7 +88,7 @@ reagentsAmount = 100 /datum/round_event/vent_clog/beer/announce() - priority_announce("The scrubbers network is experiencing an unexpected surge of pressurized beer. Some ejection of contents may occur.", "Atmospherics alert") + priority_announce("The scrubbers network is experiencing an unexpected surge of pressurized beer. Some ejection of contents may occur.", "Atmospherics alert", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/vent_clog/beer/start() for(var/obj/machinery/atmospherics/components/unary/vent in vents) @@ -103,7 +103,7 @@ CHECK_TICK /datum/round_event/vent_clog/plasma_decon/announce() - priority_announce("We are deploying an experimental plasma decontamination system. Please stand away from the vents and do not breathe the smoke that comes out.", "Central Command Update") + priority_announce("We are deploying an experimental plasma decontamination system. Please stand away from the vents and do not breathe the smoke that comes out.", "Central Command Update", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/vent_clog/plasma_decon/start() for(var/obj/machinery/atmospherics/components/unary/vent in vents) diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm index 41ee4246b1abc..6fc61ad982410 100644 --- a/code/modules/events/wizard/curseditems.dm +++ b/code/modules/events/wizard/curseditems.dm @@ -17,23 +17,23 @@ switch(item_set) if("wizardmimic") - loadout[SLOT_WEAR_SUIT] = /obj/item/clothing/suit/wizrobe - loadout[SLOT_SHOES] = /obj/item/clothing/shoes/sandal/magic - loadout[SLOT_HEAD] = /obj/item/clothing/head/wizard + loadout[TOBITSHIFT(ITEM_SLOT_OCLOTHING) + 1] = /obj/item/clothing/suit/wizrobe + loadout[TOBITSHIFT(ITEM_SLOT_FEET) + 1] = /obj/item/clothing/shoes/sandal/magic + loadout[TOBITSHIFT(ITEM_SLOT_HEAD) + 1] = /obj/item/clothing/head/wizard ruins_spaceworthiness = 1 if("swords") - loadout[SLOT_HANDS] = /obj/item/katana/cursed + loadout[TOBITSHIFT(ITEM_SLOT_HANDS) + 1] = /obj/item/katana/cursed if("bigfatdoobie") - loadout[SLOT_WEAR_MASK] = /obj/item/clothing/mask/cigarette/rollie/trippy + loadout[TOBITSHIFT(ITEM_SLOT_MASK) + 1] = /obj/item/clothing/mask/cigarette/rollie/trippy ruins_spaceworthiness = 1 if("boxing") - loadout[SLOT_WEAR_MASK] = /obj/item/clothing/mask/luchador - loadout[SLOT_GLOVES] = /obj/item/clothing/gloves/boxing + loadout[TOBITSHIFT(ITEM_SLOT_MASK) + 1] = /obj/item/clothing/mask/luchador + loadout[TOBITSHIFT(ITEM_SLOT_GLOVES) + 1] = /obj/item/clothing/gloves/boxing ruins_spaceworthiness = 1 if("voicemodulators") - loadout[SLOT_WEAR_MASK] = /obj/item/clothing/mask/chameleon + loadout[TOBITSHIFT(ITEM_SLOT_MASK) + 1] = /obj/item/clothing/mask/chameleon if("catgirls2015") - loadout[SLOT_HEAD] = /obj/item/clothing/head/kitty + loadout[TOBITSHIFT(ITEM_SLOT_HEAD) + 1] = /obj/item/clothing/head/kitty ruins_spaceworthiness = 1 ruins_wizard_loadout = 1 diff --git a/code/modules/events/wizard/embeddies.dm b/code/modules/events/wizard/embeddies.dm new file mode 100644 index 0000000000000..fe08b9c7432e9 --- /dev/null +++ b/code/modules/events/wizard/embeddies.dm @@ -0,0 +1,46 @@ +/datum/round_event_control/wizard/embedpocalypse + name = "Make Everything Embeddable" + weight = 2 + typepath = /datum/round_event/wizard/embedpocalypse + max_occurrences = 1 + earliest_start = 0 MINUTES + +/datum/round_event/wizard/embedpocalypse/start() + for(var/obj/item/I in world) + CHECK_TICK + + if(!(I.flags_1 & INITIALIZED_1)) + continue + + if(!I.embedding || I.embedding == EMBED_HARMLESS) + I.embedding = EMBED_POINTY + I.updateEmbedding() + I.name = "pointy [I.name]" + + GLOB.embedpocalypse = TRUE + GLOB.stickpocalypse = FALSE // embedpocalypse takes precedence over stickpocalypse + +/datum/round_event_control/wizard/embedpocalypse/sticky + name = "Make Everything Sticky" + weight = 6 + typepath = /datum/round_event/wizard/embedpocalypse/sticky + max_occurrences = 1 + earliest_start = 0 MINUTES + +/datum/round_event_control/wizard/embedpocalypse/sticky/canSpawnEvent(players_amt, gamemode) + if(GLOB.embedpocalypse) + return FALSE + +/datum/round_event/wizard/embedpocalypse/sticky/start() + for(var/obj/item/I in world) + CHECK_TICK + + if(!(I.flags_1 & INITIALIZED_1)) + continue + + if(!I.embedding) + I.embedding = EMBED_HARMLESS + I.updateEmbedding() + I.name = "sticky [I.name]" + + GLOB.stickpocalypse = TRUE diff --git a/code/modules/events/wizard/ghost.dm b/code/modules/events/wizard/ghost.dm index d5366c576975f..d2b889a7c74ae 100644 --- a/code/modules/events/wizard/ghost.dm +++ b/code/modules/events/wizard/ghost.dm @@ -21,6 +21,6 @@ /datum/round_event/wizard/possession/start() for(var/mob/dead/observer/G in GLOB.player_list) - G.verbs += /mob/dead/observer/verb/boo - G.verbs += /mob/dead/observer/verb/possess + G.add_verb(/mob/dead/observer/verb/boo) + G.add_verb(/mob/dead/observer/verb/possess) to_chat(G, "You suddenly feel a welling of new spooky powers...") diff --git a/code/modules/events/wizard/greentext.dm b/code/modules/events/wizard/greentext.dm index 82e72df3b9d3d..569e026e7ac73 100644 --- a/code/modules/events/wizard/greentext.dm +++ b/code/modules/events/wizard/greentext.dm @@ -83,7 +83,8 @@ if(!(resistance_flags & ON_FIRE) && !force) return QDEL_HINT_LETMELIVE - SSticker.round_end_events -= roundend_callback + LAZYREMOVE(SSticker.round_end_events, roundend_callback) + roundend_callback = null //This ought to free the callback datum, and prevent us from harddeling GLOB.poi_list.Remove(src) for(var/i in GLOB.player_list) var/mob/M = i diff --git a/code/modules/events/wizard/magicarp.dm b/code/modules/events/wizard/magicarp.dm index f335809cc8bcd..886e68cdbf598 100644 --- a/code/modules/events/wizard/magicarp.dm +++ b/code/modules/events/wizard/magicarp.dm @@ -13,7 +13,7 @@ startWhen = rand(40, 60) /datum/round_event/wizard/magicarp/announce(fake) - priority_announce("Unknown magical entities have been detected near [station_name()], please stand-by.", "Lifesign Alert") + priority_announce("Unknown magical entities have been detected near [station_name()], please stand-by.", "Lifesign Alert", SSstation.announcer.get_rand_alert_sound()) /datum/round_event/wizard/magicarp/start() for(var/obj/effect/landmark/carpspawn/C in GLOB.landmarks_list) @@ -36,28 +36,37 @@ projectilesound = 'sound/weapons/emitter.ogg' maxHealth = 50 health = 50 - gold_core_spawnable = HOSTILE_SPAWN //NO_SPAWN - var/allowed_projectile_types = list(/obj/item/projectile/magic/change, /obj/item/projectile/magic/animate, /obj/item/projectile/magic/resurrection, + gold_core_spawnable = NO_SPAWN + var/allowed_projectile_types = list(/obj/item/projectile/magic/animate, /obj/item/projectile/magic/resurrection, /obj/item/projectile/magic/death, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball, /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage) - + discovery_points = 3000 + /mob/living/simple_animal/hostile/carp/ranged/Initialize() projectiletype = pick(allowed_projectile_types) . = ..() -/*/mob/living/simple_animal/hostile/carp/ranged/xenobio - desc = "45% magic, 50% carp, 5% slime, 100% horrible." - allowed_projectile_types = list( /obj/item/projectile/magic/animate, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball, - /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage) - gold_core_spawnable = HOSTILE_SPAWN*/ - /mob/living/simple_animal/hostile/carp/ranged/chaos name = "chaos magicarp" desc = "50% carp, 100% magic, 150% horrible." color = "#00FFFF" maxHealth = 75 health = 75 + gold_core_spawnable = NO_SPAWN + discovery_points = 5000 /mob/living/simple_animal/hostile/carp/ranged/chaos/Shoot() projectiletype = pick(allowed_projectile_types) ..() + +/mob/living/simple_animal/hostile/carp/ranged/xenobio + desc = "45% magic, 50% carp, 5% slime, 100% horrible." + allowed_projectile_types = list( /obj/item/projectile/magic/animate, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball, + /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage) + gold_core_spawnable = HOSTILE_SPAWN + +/mob/living/simple_animal/hostile/carp/ranged/chaos/xenobio + desc = "95% magic, 50% carp, 5% slime, 150% horrible." + allowed_projectile_types = list( /obj/item/projectile/magic/animate, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball, + /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage) + gold_core_spawnable = HOSTILE_SPAWN diff --git a/code/modules/events/wizard/rpgloot.dm b/code/modules/events/wizard/rpgloot.dm index f5de56ed0ccb1..e8eb1251166ca 100644 --- a/code/modules/events/wizard/rpgloot.dm +++ b/code/modules/events/wizard/rpgloot.dm @@ -8,6 +8,8 @@ /datum/round_event/wizard/rpgloot/start() var/upgrade_scroll_chance = 0 for(var/obj/item/I in world) + CHECK_TICK + if(!(I.flags_1 & INITIALIZED_1)) continue diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm index c77349ca5b9d1..cbd5fbc87d83c 100644 --- a/code/modules/events/wizard/shuffle.dm +++ b/code/modules/events/wizard/shuffle.dm @@ -84,7 +84,7 @@ for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) if(H.stat || !H.mind || iswizard(H)) continue //the wizard(s) are spared on this one - if(istype(H.get_item_by_slot(SLOT_HEAD), /obj/item/clothing/head/foilhat) || H.anti_magic_check()) + if(istype(H.get_item_by_slot(ITEM_SLOT_HEAD), /obj/item/clothing/head/foilhat) || H.anti_magic_check()) continue mobs += H diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index 3e5321f7b3fe4..93b4fd6d1db71 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -12,7 +12,6 @@ GLOBAL_LIST_EMPTY(all_wormholes) // So we can pick wormholes to teleport to announceWhen = 10 endWhen = 60 - var/list/pick_turfs = list() var/list/wormholes = list() var/shift_frequency = 3 var/number_of_wormholes = 400 @@ -22,21 +21,17 @@ GLOBAL_LIST_EMPTY(all_wormholes) // So we can pick wormholes to teleport to endWhen = rand(40, 80) /datum/round_event/wormholes/start() - for(var/turf/open/floor/T in world) - if(is_station_level(T.z)) - pick_turfs += T - for(var/i = 1, i <= number_of_wormholes, i++) - var/turf/T = pick(pick_turfs) + var/turf/T = get_random_station_turf() //side effect - wormholes won't spawn in space wormholes += new /obj/effect/portal/wormhole(T, null, 0, null, FALSE) /datum/round_event/wormholes/announce(fake) - priority_announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", 'sound/ai/spanomalies.ogg') + priority_announce("Space-time anomalies detected on the station. There is no additional data.", "Anomaly Alert", ANNOUNCER_SPANOMALIES) /datum/round_event/wormholes/tick() if(activeFor % shift_frequency == 0) for(var/obj/effect/portal/wormhole/O in wormholes) - var/turf/T = pick(pick_turfs) + var/turf/T = get_random_station_turf() if(T) O.forceMove(T) diff --git a/code/modules/exploration_crew/discovery_research/discoverable_component.dm b/code/modules/exploration_crew/discovery_research/discoverable_component.dm new file mode 100644 index 0000000000000..ff9f66b71bfd5 --- /dev/null +++ b/code/modules/exploration_crew/discovery_research/discoverable_component.dm @@ -0,0 +1,49 @@ +/datum/component/discoverable + dupe_mode = COMPONENT_DUPE_UNIQUE + //Amount of discovery points awarded when researched. + var/scanned = FALSE + var/unique = FALSE + var/point_reward = 0 + +/datum/component/discoverable/Initialize(_point_reward, _unique = FALSE) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine) + RegisterSignal(parent, COMSIG_CLICK, .proc/tryScan) + + point_reward = _point_reward + unique = _unique + +/datum/component/discoverable/proc/tryScan(datum/source, location, control, params, mob/user) + SIGNAL_HANDLER + if(!isliving(user)) + return + var/mob/living/L = user + if(istype(L.get_active_held_item(), /obj/item/discovery_scanner)) + INVOKE_ASYNC(L.get_active_held_item(), /obj/item/discovery_scanner.proc/begin_scanning, user, src) + +/datum/component/discoverable/proc/examine(datum/source, mob/user, atom/thing) + SIGNAL_HANDLER + if(!user.research_scanner) + return + to_chat(user, "Scientific data detected.") + to_chat(user, "Scanned: [scanned ? "True" : "False"].") + to_chat(user, "Discovery Value: [point_reward].") + +/datum/component/discoverable/proc/discovery_scan(datum/techweb/linked_techweb, mob/user) + //Already scanned our atom. + var/atom/A = parent + if(scanned) + to_chat(user, "[A] has already been analysed.") + return + //Already scanned another of this type. + if(linked_techweb.scanned_atoms[A.type] && !unique) + to_chat(user, "Datapoints about [A] already in system.") + return + scanned = TRUE + linked_techweb.add_point_type(TECHWEB_POINT_TYPE_DISCOVERY, point_reward) + linked_techweb.scanned_atoms[A.type] = TRUE + playsound(user, 'sound/machines/terminal_success.ogg', 60) + to_chat(user, "New datapoint scanned, [point_reward] discovery points gained.") + pulse_effect(get_turf(A), 4) diff --git a/code/modules/exploration_crew/discovery_research/discovery_scanner.dm b/code/modules/exploration_crew/discovery_research/discovery_scanner.dm new file mode 100644 index 0000000000000..fd0b2e5afd5ce --- /dev/null +++ b/code/modules/exploration_crew/discovery_research/discovery_scanner.dm @@ -0,0 +1,38 @@ +/obj/item/discovery_scanner + name = "discovery scanner" + desc = "A scanner used by scientists to collect research data about unknown artifacts and specimins." + icon = 'icons/obj/device.dmi' + icon_state = "discovery" + item_state = "healthanalyzer" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + var/datum/techweb/linked_techweb + +/obj/item/discovery_scanner/Initialize() + . = ..() + if(!linked_techweb) + linked_techweb = SSresearch.science_tech + +/obj/item/discovery_scanner/Destroy() + linked_techweb = null //Note: Shouldn't hard del anyway since techwebs don't get deleted, however if they do then troubles will arise and this will need to be changed. + . = ..() + +/obj/item/discovery_scanner/examine(mob/user) + . = ..() + . += "Left-Click on any mob or researchable specimin to scan and gain discovery research points." + . += "[src] has unlimited range." + . += "Science goggles can help detect researchable items." + +/obj/item/discovery_scanner/attack_obj(obj/O, mob/living/user) + if(istype(O, /obj/machinery/computer/rdconsole)) + to_chat(user, "You link [src] to [O].") + var/obj/machinery/computer/rdconsole/rdconsole = O + linked_techweb = rdconsole.stored_research + return + . = ..() + +/obj/item/discovery_scanner/proc/begin_scanning(mob/user, datum/component/discoverable/discoverable) + to_chat(user, "You begin scanning [discoverable.parent]...") + if(do_after(user, 50, target=get_turf(user))) + discoverable.discovery_scan(linked_techweb, user) diff --git a/code/modules/exploration_crew/exploration_explosives.dm b/code/modules/exploration_crew/exploration_explosives.dm new file mode 100644 index 0000000000000..d2a205a2b8eb9 --- /dev/null +++ b/code/modules/exploration_crew/exploration_explosives.dm @@ -0,0 +1,129 @@ +/obj/item/grenade/exploration + name = "breaching charges" + desc = "Used by exploration crews to breach and demolish unknown places. Requires a nearby detonator to be triggered." + icon_state = "plastic-explosive0" + item_state = "plastic-explosive" + lefthand_file = 'icons/mob/inhands/weapons/bombs_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/bombs_righthand.dmi' + item_flags = NOBLUDGEON + flags_1 = NONE + w_class = WEIGHT_CLASS_SMALL + var/atom/target = null + var/mutable_appearance/plastic_overlay + var/light_exp_range = 3 + var/heavy_range = 0 + var/devastation_range = 0 + var/list/attached_detonators = list() + +/obj/item/grenade/exploration/Initialize() + . = ..() + plastic_overlay = mutable_appearance(icon, "[item_state]2", HIGH_OBJ_LAYER) + +/obj/item/grenade/exploration/Destroy() + for(var/obj/item/exploration_detonator/detonator in attached_detonators) + detonator.linked_explosives -= src + attached_detonators = null + . = ..() + +/obj/item/grenade/exploration/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/exploration_detonator)) + var/obj/item/exploration_detonator/detonator = W + detonator.linked_explosives |= src + attached_detonators |= detonator + to_chat(user, "You link [src] to [W].") + return + . = ..() + +/obj/item/grenade/exploration/afterattack(atom/movable/AM, mob/user, flag) + . = ..() + + if(!length(attached_detonators)) + to_chat(user, "[src] needs to be linked to a detonator first!") + return + + if(!flag) + return + if(ismob(AM)) + return + + to_chat(user, "You start planting [src].") + + if(do_after(user, 30, target = AM)) + if(!user.temporarilyRemoveItemFromInventory(src)) + return + target = AM + + message_admins("[ADMIN_LOOKUPFLW(user)] planted [name] on [target.name] at [ADMIN_VERBOSEJMP(target)]") + log_game("[key_name(user)] planted [name] on [target.name] at [AREACOORD(user)]") + + notify_ghosts("[user] has planted \a [src] on [target]!", source = target, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Explosive Planted") + + moveToNullspace() //Yep + + if(istype(AM, /obj/item)) //your crappy throwing star can't fly so good with a giant brick of c4 on it. + var/obj/item/I = AM + I.throw_speed = max(1, (I.throw_speed - 3)) + I.throw_range = max(1, (I.throw_range - 3)) + if(I.embedding) + I.embedding["embed_chance"] = 0 + I.updateEmbedding() + else if(istype(AM, /mob/living)) + plastic_overlay.layer = FLOAT_LAYER + + target.add_overlay(plastic_overlay, TRUE) + to_chat(user, "You plant the bomb.") + +/obj/item/grenade/exploration/prime(mob/living/lanced_by) + . = ..() + var/turf/location + if(target) + if(!QDELETED(target)) + location = get_turf(target) + target.cut_overlay(plastic_overlay, TRUE) + target.ex_act(EXPLODE_HEAVY, target) + else + location = get_turf(src) + if(location) + explosion(location, devastation_range, heavy_range, light_exp_range) + if(ismob(target)) + var/mob/M = target + M.gib() + qdel(src) + +/obj/item/exploration_detonator + name = "detonator" + desc = "A detonator that can be linked to explosives and then... you know, detonate them." + icon = 'icons/obj/grenade.dmi' + icon_state = "detonator" + w_class = WEIGHT_CLASS_SMALL + var/range = 16 + var/list/linked_explosives = list() + +/obj/item/exploration_detonator/Destroy() + . = ..() + for(var/obj/item/grenade/exploration/explosive in linked_explosives) + explosive.attached_detonators -= src + linked_explosives = null + +/obj/item/exploration_detonator/attack_self(mob/user) + . = ..() + if(.) + return + var/turf/T = get_turf(user) + if(is_station_level(T.z) && !(obj_flags & EMAGGED)) + to_chat(user, "STATION SAFETY ENABLED.") + return + var/explosives_trigged = 0 + for(var/obj/item/grenade/exploration/exploration in linked_explosives) + var/turf/T2 = get_turf(exploration.target) + if(T2.get_virtual_z_level() == T.get_virtual_z_level() && get_dist(exploration.target, user) <= range) + addtimer(CALLBACK(exploration, /obj/item/grenade/exploration.proc/prime), 10) + explosives_trigged ++ + to_chat(user, "[explosives_trigged] explosives triggered.") + +/obj/item/exploration_detonator/emag_act(mob/user) + . = ..() + if(obj_flags & EMAGGED) + return + obj_flags |= EMAGGED + to_chat(user, "You override the safety controls of [src]. You can now trigger explosives on the station.") diff --git a/code/modules/exploration_crew/exploration_laser_gun.dm b/code/modules/exploration_crew/exploration_laser_gun.dm new file mode 100644 index 0000000000000..5b3d102b16499 --- /dev/null +++ b/code/modules/exploration_crew/exploration_laser_gun.dm @@ -0,0 +1,107 @@ +/obj/item/gun/energy/e_gun/mini/exploration + name = "handheld multi-purpose energy gun" + desc = "A pistol-sized energy gun with a built-in flashlight designed for exploration crews. It serves a duel purpose and has modes for anti-creature lasers and cutting lasers." + pin = /obj/item/firing_pin/off_station + ammo_type = list(/obj/item/ammo_casing/energy/laser/anti_creature, /obj/item/ammo_casing/energy/laser/cutting) + +/obj/item/gun/energy/e_gun/mini/exploration/emag_act(mob/user) + . = ..() + //Emag the pin too + if(pin) + pin.emag_act(user) + if(obj_flags & EMAGGED) + return + to_chat(user, "You override the safety of the energy gun, it will now fire higher powered projectiles at a greater cost.") + ammo_type = list(/obj/item/ammo_casing/energy/laser/exploration_kill, /obj/item/ammo_casing/energy/laser/exploration_destroy) + update_ammo_types() + obj_flags |= EMAGGED + +//Anti-creature - Extra damage against simplemobs + +/obj/item/ammo_casing/energy/laser/anti_creature + projectile_type = /obj/item/projectile/beam/laser/anti_creature + select_name = "anti-creature" + e_cost = 40 + +/obj/item/projectile/beam/laser/anti_creature + damage = 15 + tracer_type = /obj/effect/projectile/tracer/laser + muzzle_type = /obj/effect/projectile/muzzle/laser + impact_type = /obj/effect/projectile/impact/laser + +/obj/item/projectile/beam/laser/anti_creature/on_hit(atom/target, blocked) + damage = initial(damage) + if(!iscarbon(target) && !issilicon(target)) + damage = 30 + . = ..() + +//Cutting projectile - Damage against objects + +/obj/item/ammo_casing/energy/laser/cutting + projectile_type = /obj/item/projectile/beam/laser/cutting + select_name = "cutting laser" + e_cost = 30 + +/obj/item/projectile/beam/laser/cutting + damage = 5 + icon_state = "heavylaser" + tracer_type = /obj/effect/projectile/tracer/heavy_laser + muzzle_type = /obj/effect/projectile/muzzle/heavy_laser + impact_type = /obj/effect/projectile/impact/heavy_laser + +/obj/item/projectile/beam/laser/cutting/on_hit(atom/target, blocked) + damage = initial(damage) + if(isobj(target)) + damage = 70 + else if(istype(target, /turf/closed/mineral)) + var/turf/closed/mineral/T = target + T.gets_drilled() + . = ..() + +//Emagged ammo types + +/obj/item/ammo_casing/energy/laser/exploration_kill + projectile_type = /obj/item/projectile/beam/laser/exploration_kill + select_name = "KILL" + e_cost = 80 + +/obj/item/projectile/beam/laser/exploration_kill + damage = 30 + tracer_type = /obj/effect/projectile/tracer/laser + muzzle_type = /obj/effect/projectile/muzzle/laser + impact_type = /obj/effect/projectile/impact/laser + +/obj/item/projectile/beam/laser/exploration_kill/on_hit(atom/target, blocked) + damage = initial(damage) + if(!iscarbon(target) && !issilicon(target)) + damage = 50 + //If you somehow hit yourself you get fried. + if(target == firer) + to_chat(firer, "The laser accelerates violently towards your gun's magnetic field, tearing its way through your body!") + damage = 200 + . = ..() + +//destroy + +/obj/item/ammo_casing/energy/laser/exploration_destroy + projectile_type = /obj/item/projectile/beam/laser/exploration_destroy + select_name = "DESTROY" + e_cost = 120 + +/obj/item/projectile/beam/laser/exploration_destroy + damage = 20 + icon_state = "heavylaser" + tracer_type = /obj/effect/projectile/tracer/heavy_laser + muzzle_type = /obj/effect/projectile/muzzle/heavy_laser + impact_type = /obj/effect/projectile/impact/heavy_laser + +/obj/item/projectile/beam/laser/exploration_destroy/on_hit(atom/target, blocked) + damage = initial(damage) + if(isobj(target)) + damage = 150 + else if(istype(target, /turf/closed/mineral)) + var/turf/closed/mineral/T = target + T.gets_drilled() + else if(isturf(target)) + SSexplosions.medturf += target + . = ..() diff --git a/code/modules/exploration_crew/exploration_shuttle.dm b/code/modules/exploration_crew/exploration_shuttle.dm new file mode 100644 index 0000000000000..7fc44209c7cc0 --- /dev/null +++ b/code/modules/exploration_crew/exploration_shuttle.dm @@ -0,0 +1,13 @@ +/obj/machinery/computer/shuttle_flight/custom_shuttle/exploration + name = "exploration shuttle console" + desc = "Used to pilot the exploration shuttle." + circuit = /obj/item/circuitboard/computer/exploration_shuttle + shuttleId = "exploration" + possible_destinations = "exploration_home" + req_access = list(ACCESS_EXPLORATION) + +/obj/machinery/computer/shuttle_flight/custom_shuttle/exploration/linkShuttle(new_id) + return + +/obj/machinery/computer/shuttle_flight/custom_shuttle/exploration/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override) + return diff --git a/code/modules/exploration_crew/exploration_vendor.dm b/code/modules/exploration_crew/exploration_vendor.dm new file mode 100644 index 0000000000000..63b177377e3b6 --- /dev/null +++ b/code/modules/exploration_crew/exploration_vendor.dm @@ -0,0 +1,41 @@ +GLOBAL_VAR_INIT(exploration_points, 0) + +/obj/machinery/vendor/exploration + name = "exploration equipment vendor" + desc = "An equipment vendor for exploration teams. Points are acquired by completing missions and shared between team members." + icon = 'icons/obj/machines/mining_machines.dmi' + icon_state = "mining" + density = TRUE + circuit = /obj/item/circuitboard/machine/exploration_equipment_vendor + + icon_deny = "mining-deny" + prize_list = list( + new /datum/data/vendor_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 50), + new /datum/data/vendor_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 300), + new /datum/data/vendor_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 500), + new /datum/data/vendor_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/medipen/survival, 2000), + new /datum/data/vendor_equipment("Brute Healing Kit", /obj/item/storage/firstaid/brute, 3000), + new /datum/data/vendor_equipment("Burn Healing Kit", /obj/item/storage/firstaid/fire, 3000), + new /datum/data/vendor_equipment("Advanced Healing Kit", /obj/item/storage/firstaid/advanced, 5000), + new /datum/data/vendor_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 2000), + new /datum/data/vendor_equipment("Breaching Charge", /obj/item/grenade/exploration, 1000), + new /datum/data/vendor_equipment("Charge Detonator", /obj/item/exploration_detonator, 10000), + new /datum/data/vendor_equipment("Multi-Purpose Energy Gun", /obj/item/gun/energy/e_gun/mini/exploration, 20000), + new /datum/data/vendor_equipment("Expanded E. Oxygen Tank", /obj/item/tank/internals/emergency_oxygen/engi, 1000), + new /datum/data/vendor_equipment("Survival Knife", /obj/item/kitchen/knife/combat/survival, 1000), + new /datum/data/vendor_equipment("Pizza", /obj/item/pizzabox/margherita, 200), + new /datum/data/vendor_equipment("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 1000), + new /datum/data/vendor_equipment("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 1000), + new /datum/data/vendor_equipment("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 1500), + new /datum/data/vendor_equipment("Soap", /obj/item/soap/nanotrasen, 2000), + new /datum/data/vendor_equipment("Laser Pointer", /obj/item/laser_pointer, 3000), + new /datum/data/vendor_equipment("Toy Alien", /obj/item/clothing/mask/facehugger/toy, 3000), + ) + +/obj/machinery/vendor/exploration/subtract_points(obj/item/card/id/I, amount) + GLOB.exploration_points -= amount + +/obj/machinery/vendor/exploration/get_points(obj/item/card/id/I) + if(!(ACCESS_EXPLORATION in I.access)) + return 0 + return GLOB.exploration_points diff --git a/code/modules/exploration_crew/nostation_firing_pin.dm b/code/modules/exploration_crew/nostation_firing_pin.dm new file mode 100644 index 0000000000000..76893325dcafd --- /dev/null +++ b/code/modules/exploration_crew/nostation_firing_pin.dm @@ -0,0 +1,15 @@ +/obj/item/firing_pin/off_station + name = "off-station firing pin" + desc = "Allows the firing of weapons while not on the station." + fail_message = "STATION SAFETY ENABLED." + pin_removeable = TRUE + +/obj/item/firing_pin/off_station/pin_auth(mob/living/user) + if(!istype(user)) + return FALSE + var/turf/T = get_turf(user) + if(!T) + return FALSE + if(is_station_level(T.z)) + return FALSE + return TRUE diff --git a/code/modules/exploration_crew/research_locator.dm b/code/modules/exploration_crew/research_locator.dm new file mode 100644 index 0000000000000..77a2ab13d0061 --- /dev/null +++ b/code/modules/exploration_crew/research_locator.dm @@ -0,0 +1,23 @@ + +/obj/item/research_disk_pinpointer + name = "research disk locator" + desc = "A small handheld device that detects nearby research disks. Despite its extremely high sensitivity, the returned signal from research disks is so weak that it only has a short range." + icon = 'icons/obj/device.dmi' + icon_state = "researchlocator" + var/next_use_time = 0 + var/range = 30 + +/obj/item/research_disk_pinpointer/attack_self(mob/user) + if(world.time < next_use_time) + to_chat(user, "Internal capacitors recharging...") + return + to_chat(user, "You pulse for nearby research disks.") + pulse_effect(get_turf(src), 6) + next_use_time = world.time + 10 SECONDS + for(var/obj/item/disk/tech_disk/research/research_disk in SSorbits.research_disks) + var/dist = get_dist(user, research_disk) + if(dist <= range && isturf(research_disk.loc) && research_disk.get_virtual_z_level() == get_virtual_z_level()) + var/direction = get_dir(user, research_disk) + dir = direction + say("Weak signal detected [dir2text(direction)] of current location, [dist] meters away.") + return diff --git a/code/modules/fields/fields.dm b/code/modules/fields/fields.dm index 5b0b4bc1f8940..38cc9f8e66037 100644 --- a/code/modules/fields/fields.dm +++ b/code/modules/fields/fields.dm @@ -154,7 +154,7 @@ var/atom/_host = host var/atom/new_host_loc = _host.loc if(last_host_loc != new_host_loc) - recalculate_field() + INVOKE_ASYNC(src, .proc/recalculate_field) /datum/proximity_monitor/advanced/proc/post_setup_field() @@ -319,6 +319,8 @@ listeningTo = null /obj/item/multitool/field_debug/proc/on_mob_move() + SIGNAL_HANDLER + check_turf(get_turf(src)) /obj/item/multitool/field_debug/process() diff --git a/code/modules/fields/peaceborg_dampener.dm b/code/modules/fields/peaceborg_dampener.dm index 47d7f096c968a..ad62066dcda07 100644 --- a/code/modules/fields/peaceborg_dampener.dm +++ b/code/modules/fields/peaceborg_dampener.dm @@ -38,7 +38,7 @@ for(var/obj/item/projectile/P in tracked) if(!(P in ranged) || !P.loc) release_projectile(P) - for(var/mob/living/silicon/robot/R in range(current_range, get_turf(host))) + for(var/mob/living/silicon/robot/R in ohearers(current_range, get_turf(host))) if(R.has_buckled_mobs()) for(var/mob/living/L in R.buckled_mobs) L.visible_message("[L] is knocked off of [R] by the charge in [R]'s chassis induced by [name]!") //I know it's bad. diff --git a/code/modules/fields/timestop.dm b/code/modules/fields/timestop.dm index bc5502438f9b2..ff4402b0a90c9 100644 --- a/code/modules/fields/timestop.dm +++ b/code/modules/fields/timestop.dm @@ -42,7 +42,7 @@ timestop() /obj/effect/timestop/Destroy() - qdel(chronofield) + QDEL_NULL(chronofield) playsound(src, 'sound/magic/timeparadox2.ogg', 75, TRUE, frequency = -1) //reverse! return ..() @@ -108,6 +108,8 @@ unfreeze_atom(i) /datum/proximity_monitor/advanced/timestop/proc/unfreeze_atom(atom/movable/A) + SIGNAL_HANDLER + if(A.throwing) unfreeze_throwing(A) if(isliving(A)) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 11fd61c09bf76..fcea44fcfee15 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -11,7 +11,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( /datum/hallucination/weird_sounds = 8, /datum/hallucination/stationmessage = 7, /datum/hallucination/fake_flood = 7, - /datum/hallucination/stray_bullet = 7, + /datum/hallucination/stray_bullet = 3, /datum/hallucination/bolts = 7, /datum/hallucination/items_other = 7, /datum/hallucination/husks = 7, @@ -31,6 +31,11 @@ GLOBAL_LIST_INIT(hallucination_list, list( hallucination-- + //This system used to shit bricks with decimal values. Fuck it. + if(hallucination <= 0) + hallucination = 0 + return + if(world.time < next_hallucination) return @@ -282,10 +287,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( /datum/hallucination/oh_yeah/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE . = ..() - var/turf/closed/wall/wall - for(var/turf/closed/wall/W in range(7,target)) - wall = W - break + var/turf/closed/wall/wall = locate() in spiral_range_turfs(7, target) if(!wall) return INITIALIZE_HINT_QDEL feedback_details += "Source: [wall.x],[wall.y],[wall.z]" @@ -420,9 +422,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( var/image/A = null var/list/mob_pool = list() - for(var/mob/living/carbon/human/M in view(7,target)) - if(M != target) - mob_pool += M + for(var/mob/living/carbon/human/M in ohearers(7,target)) + mob_pool += M if(!mob_pool.len) return @@ -518,7 +519,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( feedback_details += "Type: [kind]" var/list/nearby if(skip_nearby) - nearby = get_hearers_in_view(7, target) + nearby = hearers(7, target) for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) if(H == target) continue @@ -689,31 +690,29 @@ GLOBAL_LIST_INIT(hallucination_list, list( else if(get_dist(target,H) Biohazard Alert") to_chat(target, "Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak. ") - SEND_SOUND(target, 'sound/ai/outbreak5.ogg') + SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_OUTBREAK5]) if("ratvar") target.playsound_local(target, 'sound/machines/clockcult/ark_deathrattle.ogg', 50, FALSE, pressure_affected = FALSE) target.playsound_local(target, 'sound/effects/clockcult_gateway_disrupted.ogg', 50, FALSE, pressure_affected = FALSE) @@ -891,15 +890,15 @@ GLOBAL_LIST_INIT(hallucination_list, list( if("shuttle dock") to_chat(target, " Priority Announcement") to_chat(target, "The Emergency Shuttle has docked with the station. You have 3 minutes to board the Emergency Shuttle. ") - SEND_SOUND(target, 'sound/ai/shuttledock.ogg') + SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_SHUTTLEDOCK]) if("malf ai") //AI is doomsdaying! to_chat(target, " Anomaly Alert") to_chat(target, "Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core. ") - SEND_SOUND(target, 'sound/ai/aimalf.ogg') + SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_AIMALF]) if("meteors") //Meteors inbound! to_chat(target, " Meteor Alert") to_chat(target, "Meteors have been detected on collision course with the station. ") - SEND_SOUND(target, 'sound/ai/meteors.ogg') + SEND_SOUND(target, SSstation.announcer.event_sounds[ANNOUNCER_METEORS]) if("supermatter") SEND_SOUND(target, 'sound/magic/charge.ogg') to_chat(target, "You feel reality distort for a moment...") @@ -930,46 +929,46 @@ GLOBAL_LIST_INIT(hallucination_list, list( feedback_details += "Type: [alert_type]" switch(alert_type) if("not_enough_oxy") - target.throw_alert(alert_type, /obj/screen/alert/not_enough_oxy, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_oxy, override = TRUE) if("not_enough_tox") - target.throw_alert(alert_type, /obj/screen/alert/not_enough_tox, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_tox, override = TRUE) if("not_enough_co2") - target.throw_alert(alert_type, /obj/screen/alert/not_enough_co2, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/not_enough_co2, override = TRUE) if("too_much_oxy") - target.throw_alert(alert_type, /obj/screen/alert/too_much_oxy, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_oxy, override = TRUE) if("too_much_co2") - target.throw_alert(alert_type, /obj/screen/alert/too_much_co2, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_co2, override = TRUE) if("too_much_tox") - target.throw_alert(alert_type, /obj/screen/alert/too_much_tox, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/too_much_tox, override = TRUE) if("nutrition") if(prob(50)) - target.throw_alert(alert_type, /obj/screen/alert/fat, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/fat, override = TRUE) else - target.throw_alert(alert_type, /obj/screen/alert/starving, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/starving, override = TRUE) if("gravity") - target.throw_alert(alert_type, /obj/screen/alert/weightless, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/weightless, override = TRUE) if("fire") - target.throw_alert(alert_type, /obj/screen/alert/fire, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/fire, override = TRUE) if("temphot") alert_type = "temp" - target.throw_alert(alert_type, /obj/screen/alert/hot, 3, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/hot, 3, override = TRUE) if("tempcold") alert_type = "temp" - target.throw_alert(alert_type, /obj/screen/alert/cold, 3, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/cold, 3, override = TRUE) if("pressure") if(prob(50)) - target.throw_alert(alert_type, /obj/screen/alert/highpressure, 2, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/highpressure, 2, override = TRUE) else - target.throw_alert(alert_type, /obj/screen/alert/lowpressure, 2, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/lowpressure, 2, override = TRUE) //BEEP BOOP I AM A ROBOT if("newlaw") - target.throw_alert(alert_type, /obj/screen/alert/newlaw, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/newlaw, override = TRUE) if("locked") - target.throw_alert(alert_type, /obj/screen/alert/locked, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/locked, override = TRUE) if("hacked") - target.throw_alert(alert_type, /obj/screen/alert/hacked, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/hacked, override = TRUE) if("charge") - target.throw_alert(alert_type, /obj/screen/alert/emptycell, override = TRUE) + target.throw_alert(alert_type, /atom/movable/screen/alert/emptycell, override = TRUE) sleep(duration) target.clear_alert(alert_type, clear_override = TRUE) qdel(src) @@ -1044,7 +1043,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( //Flashes of danger if(!target.halimage) var/list/possible_points = list() - for(var/turf/open/floor/F in view(target,world.view)) + for(var/turf/open/floor/F in view(world.view, target)) possible_points += F if(possible_points.len) var/turf/open/floor/danger_point = pick(possible_points) @@ -1116,8 +1115,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( . = ..() START_PROCESSING(SSobj, src) -/obj/effect/hallucination/danger/anomaly/process() - if(prob(70)) +/obj/effect/hallucination/danger/anomaly/process(delta_time) + if(DT_PROB(45, delta_time)) step(src,pick(GLOB.alldirs)) /obj/effect/hallucination/danger/anomaly/Destroy() @@ -1174,7 +1173,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( if(target.client) target.client.images += fire_overlay to_chat(target, "You're set on fire!") - target.throw_alert("fire", /obj/screen/alert/fire, override = TRUE) + target.throw_alert("fire", /atom/movable/screen/alert/fire, override = TRUE) sleep(20) for(var/i in 1 to 3) if(target.fire_stacks <= 0) @@ -1196,7 +1195,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( target.clear_alert("temp", clear_override = TRUE) else target.clear_alert("temp", clear_override = TRUE) - target.throw_alert("temp", /obj/screen/alert/hot, stage, override = TRUE) + target.throw_alert("temp", /atom/movable/screen/alert/hot, stage, override = TRUE) /datum/hallucination/fire/proc/clear_fire() if(!active) @@ -1253,7 +1252,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( ..() if(!target.halbody) var/list/possible_points = list() - for(var/turf/open/floor/F in view(target,world.view)) + for(var/turf/open/floor/F in view(world.view, target)) possible_points += F if(possible_points.len) var/turf/open/floor/husk_point = pick(possible_points) @@ -1284,8 +1283,10 @@ GLOBAL_LIST_INIT(hallucination_list, list( set waitfor = FALSE ..() var/list/turf/startlocs = list() - for(var/turf/open/T in view(world.view+1,target)-view(world.view,target)) + for(var/turf/open/T in view(getexpandedview(world.view, 1, 1),target)) startlocs += T + for(var/turf/open/T in view(world.view,target)) // God this is bad + startlocs -= T if(!startlocs.len) qdel(src) return diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index c8e837a1245da..e8c274a1992ec 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -257,7 +257,7 @@ /obj/item/reagent_containers/food/drinks/mug/cocoa name = "Dutch hot cocoa" desc = "Made in Space South America." - list_reagents = list(/datum/reagent/consumable/hot_cocoa = 15, /datum/reagent/consumable/sugar = 5) + list_reagents = list(/datum/reagent/consumable/cocoa/hot_cocoa = 15, /datum/reagent/consumable/sugar = 5) foodtype = SUGAR resistance_flags = FREEZE_PROOF custom_price = 42 @@ -279,6 +279,15 @@ list_reagents = list(/datum/reagent/consumable/ethanol/beer = 30) foodtype = GRAIN | ALCOHOL +/obj/item/reagent_containers/food/drinks/beer/almost_empty + var/amount + list_reagents = null + +/obj/item/reagent_containers/food/drinks/beer/almost_empty/Initialize() + . = ..() + amount = rand(1,4) + reagents.add_reagent(/datum/reagent/consumable/ethanol/beer, amount) + /obj/item/reagent_containers/food/drinks/syndicatebeer name = "syndicate beer" desc = "Consumed only by the finest syndicate agents. There is a round warning label stating 'Don't drink more than one in quick succession!'" @@ -382,7 +391,21 @@ name = "small carton" desc = "A small carton, intended for holding drinks." +/obj/item/reagent_containers/food/drinks/honeycomb + name = "Honeycomb" + desc = "A honeycomb made by an apid. It seems to be made out of beeswax and fairly weak." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "honeycomb" + list_reagents = list(/datum/reagent/consumable/honey = 25) +/obj/item/reagent_containers/food/drinks/honeycomb/attack_self(mob/user) + if(!reagents.total_volume) + user.visible_message("[user] snaps the [src] into 2 pieces!", + "You snap [src] in half.") + new /obj/item/stack/sheet/mineral/wax(user.loc, 2) + qdel(src) + return + return ..() //////////////////////////drinkingglass and shaker// //Note by Darem: This code handles the mixing of drinks. New drinks go in three places: In Chemistry-Reagents.dm (for the drink @@ -426,6 +449,7 @@ volume = 30 spillable = TRUE + //////////////////////////soda_cans// //These are in their own group to be used as IED's in /obj/item/grenade/ghettobomb.dm @@ -449,7 +473,7 @@ playsound(H,'sound/items/drink.ogg', 80, 1) reagents.trans_to(H, src.reagents.total_volume, transfered_by = H) //a big sip sleep(5) - H.say(pick("Now, Outbomb Cuban Pete, THAT was a game.", "All these new fangled arcade games are too slow. I prefer the classics.", "They don't make 'em like Orion Trail anymore.", "You know what they say. Worst day of spess carp fishing is better than the best day at work.", "They don't make 'em like good old fashioned singularity engines anymore.")) + H.say(pick("Now, Outbomb Cuban Pete, THAT was a game.", "All these new fangled arcade games are too slow. I prefer the classics.", "They don't make 'em like Orion Trail anymore.", "You know what they say. Worst day of spess carp fishing is better than the best day at work.", "They don't make 'em like good old-fashioned singularity engines anymore.")) if(H.age >= 30) H.Stun(50) sleep(50) diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm index e485f693413d7..5194f1a5c1da8 100644 --- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm +++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm @@ -21,10 +21,10 @@ if(isliving(hitby)) var/mob/living/L = hitby smash(L) - else + else smash() return TRUE - + /obj/item/reagent_containers/food/drinks/bottle/smash(mob/living/target, mob/thrower, ranged = FALSE) //Creates a shattering noise and replaces the bottle with a broken_bottle @@ -336,6 +336,24 @@ icon_state = "fernetbottle" list_reagents = list(/datum/reagent/consumable/ethanol/fernet = 100) +/obj/item/reagent_containers/food/drinks/bottle/beer + name = "Space Beer" + desc = "Beer. In space. In a bigger bottle." + icon_state = "beer" + list_reagents = list(/datum/reagent/consumable/ethanol/beer = 100) + +/obj/item/reagent_containers/food/drinks/bottle/ale + name = "Magm-Ale" + desc = "A true dorf's drink of choice, now in a MANLY bottle." + icon_state = "alebottle" + list_reagents = list(/datum/reagent/consumable/ethanol/ale = 100) + +/obj/item/reagent_containers/food/drinks/bottle/homemaderum + name = "Cookie's Homemade Rum" + desc = "Brewed all the way back on Space Station 3. Might tell you where those basket-hats of fruit keep coming from." + icon_state = "moonshinebottle" + list_reagents = list(/datum/reagent/consumable/ethanol/rum = 95, /datum/reagent/drug/mushroomhallucinogen = 5) + //////////////////////////JUICES AND STUFF /////////////////////// /obj/item/reagent_containers/food/drinks/bottle/orangejuice diff --git a/code/modules/food_and_drinks/food/customizables.dm b/code/modules/food_and_drinks/food/customizables.dm index 184b391203eac..759ba38d44889 100644 --- a/code/modules/food_and_drinks/food/customizables.dm +++ b/code/modules/food_and_drinks/food/customizables.dm @@ -53,7 +53,7 @@ mix_filling_color(S) S.reagents.trans_to(src,min(S.reagents.total_volume, 15), transfered_by = user) //limit of 15, we don't want our custom food to be completely filled by just one ingredient with large reagent volume. foodtype |= S.foodtype - update_overlays(S) + update_customizable_overlays(S) to_chat(user, "You add the [I.name] to the [name].") update_name(S) else @@ -101,7 +101,7 @@ rgbcolor[4] = (customcolor[4]+ingcolor[4])/2 filling_color = rgb(rgbcolor[1], rgbcolor[2], rgbcolor[3], rgbcolor[4]) -/obj/item/reagent_containers/food/snacks/customizable/update_overlays(obj/item/reagent_containers/food/snacks/S) +/obj/item/reagent_containers/food/snacks/customizable/update_customizable_overlays(obj/item/reagent_containers/food/snacks/S) var/mutable_appearance/filling = mutable_appearance(icon, "[initial(icon_state)]_filling") if(S.filling_color == "#FFFFFF") filling.color = pick("#FF0000","#0000FF","#008000","#FFFF00") @@ -137,7 +137,7 @@ /obj/item/reagent_containers/food/snacks/customizable/initialize_slice(obj/item/reagent_containers/food/snacks/slice, reagents_per_slice) ..() slice.filling_color = filling_color - slice.update_overlays(src) + slice.update_customizable_overlays(src) /obj/item/reagent_containers/food/snacks/customizable/Destroy() diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index a9dfd126c6c6a..b2d4b2cc14797 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -270,7 +270,8 @@ All foods are distributed among various categories. Use common sense. trash = null return -/obj/item/reagent_containers/food/snacks/proc/update_overlays(obj/item/reagent_containers/food/snacks/S) +// We need to refactor this someday, like, in 3 years +/obj/item/reagent_containers/food/snacks/proc/update_customizable_overlays(obj/item/reagent_containers/food/snacks/S) cut_overlays() var/mutable_appearance/filling = mutable_appearance(icon, "[initial(icon_state)]_filling") if(S.filling_color == "#FFFFFF") @@ -312,6 +313,14 @@ All foods are distributed among various categories. Use common sense. return result +/obj/item/reagent_containers/food/snacks/burn() + if(prob(25)) + microwave_act() + else + var/turf/T = get_turf(src) + new /obj/item/reagent_containers/food/snacks/badrecipe(T) + qdel(src) + /obj/item/reagent_containers/food/snacks/Destroy() if(contents) for(var/atom/movable/something in contents) @@ -323,13 +332,13 @@ All foods are distributed among various categories. Use common sense. if(iscorgi(M)) var/mob/living/L = M if(bitecount == 0 || prob(50)) - M.emote("me", 1, "nibbles away at \the [src]") + M.emote("me", 1, "nibbles away at \the [src].") bitecount++ L.taste(reagents) // why should carbons get all the fun? if(bitecount >= 5) - var/sattisfaction_text = pick("burps from enjoyment", "yaps for more", "woofs twice", "looks at the area where \the [src] was") - if(sattisfaction_text) - M.emote("me", 1, "[sattisfaction_text]") + var/satisfaction_text = pick("burps from enjoyment.", "yaps for more.", "woofs twice.", "looks at the area where \the [src] was.") + if(satisfaction_text) + M.emote("me", 1, "[satisfaction_text]") qdel(src) /obj/item/reagent_containers/food/snacks/afterattack(obj/item/reagent_containers/M, mob/user, proximity) diff --git a/code/modules/food_and_drinks/food/snacks_bread.dm b/code/modules/food_and_drinks/food/snacks_bread.dm index 807e4f655160f..26966dea14e97 100644 --- a/code/modules/food_and_drinks/food/snacks_bread.dm +++ b/code/modules/food_and_drinks/food/snacks_bread.dm @@ -199,6 +199,14 @@ tastes = list("bread" = 1) foodtype = GRAIN +/obj/item/reagent_containers/food/snacks/baguette/mime + name = "French Baguette" + desc = "It would be a shame if it was consumed by someone unworthy..." + bonus_reagents = list(/datum/reagent/consumable/nutriment = 2, /datum/reagent/consumable/nutriment/vitamin = 2, /datum/reagent/consumable/nothing = 1) + list_reagents = list(/datum/reagent/consumable/nutriment = 6, /datum/reagent/consumable/nutriment/vitamin = 1, /datum/reagent/consumable/nothing = 2) + bitesize = -1 + + /obj/item/reagent_containers/food/snacks/garlicbread name = "garlic bread" desc = "Alas, it is limited." diff --git a/code/modules/food_and_drinks/food/snacks_burgers.dm b/code/modules/food_and_drinks/food/snacks_burgers.dm index f5e16c3c93e4d..5cde358161ed3 100644 --- a/code/modules/food_and_drinks/food/snacks_burgers.dm +++ b/code/modules/food_and_drinks/food/snacks_burgers.dm @@ -383,3 +383,11 @@ if(prob(33)) icon_state = "cheeseburgeralt" +/obj/item/reagent_containers/food/snacks/burger/crazy + name = "crazy hamburger" + desc = "Crazy hamburger! It is horrible!" + icon_state = "crazyhamburger" + tastes = list("bread made in turkey" = 2, "horse meat" = 4, "cheese from sao paulo from brazil" = 3, "red hot chili peppers" = 3, "oil from iraq" = 2, "grass of death" = 3) + bonus_reagents = list(/datum/reagent/consumable/nutriment = 1) + foodtype = GRAIN | MEAT | DAIRY | TOXIC | GROSS | FRUIT + w_class = WEIGHT_CLASS_NORMAL // The crazy hamburger in the video was bigger than joker's hand therefore i think this weight class is adequate. diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index ffc26e5b25fbe..1383b491a834c 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -33,6 +33,12 @@ name = "imitation carp fillet" desc = "Almost just like the real thing, kinda." +/obj/item/reagent_containers/food/snacks/carpmeat/icantbeliveitsnotcarp + name = "fish fillet" + desc = "A fillet of unspecified fish meat." + list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 2) //No carpotoxin + + /obj/item/reagent_containers/food/snacks/fishfingers name = "fish fingers" desc = "A finger of fish." @@ -123,7 +129,7 @@ /obj/item/reagent_containers/food/snacks/sausage/Initialize() . = ..() - eatverb = pick("bite","chew","nibble","deep throat","gobble","chomp") + eatverb = pick("bite","chew","nibble","gobble","chomp") /obj/item/reagent_containers/food/snacks/rawkhinkali name = "raw khinkali" @@ -160,7 +166,7 @@ if(GLOB.total_cube_monkeys >= CONFIG_GET(number/max_cube_monkeys)) visible_message("[src] refuses to expand!") return - var/mob/spammer = get_mob_by_key(fingerprintslast) + var/mob/spammer = get_mob_by_ckey(fingerprintslast) var/mob/living/bananas = new spawned_mob(drop_location(), TRUE, spammer) if(faction) bananas.faction = faction @@ -377,4 +383,4 @@ list_reagents = list(/datum/reagent/consumable/nutriment = 8, /datum/reagent/consumable/nutriment/vitamin = 2, /datum/reagent/consumable/bbqsauce = 5) bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 1) tastes = list("meat" = 3, "smokey sauce" = 1) - foodtype = MEAT \ No newline at end of file + foodtype = MEAT diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm index 5afeda625f28c..1596c542db543 100644 --- a/code/modules/food_and_drinks/food/snacks_other.dm +++ b/code/modules/food_and_drinks/food/snacks_other.dm @@ -647,3 +647,13 @@ filling_color = "#ECA735" tastes = list("fried corn" = 1) foodtype = JUNKFOOD | FRIED + +/obj/item/reagent_containers/food/snacks/canned/beefbroth + name = "canned beef broth" + desc = "Why does this exist?" + icon_state = "beefcan" + trash = /obj/item/trash/can/food/beefbroth + list_reagents = list(/datum/reagent/consumable/beefbroth = 50) + filling_color = "#100800" + tastes = list("disgust" = 7, "tin" = 1) + foodtype = MEAT | GROSS | JUNKFOOD \ No newline at end of file diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm index 8e104dcdd52c6..2ec595df8abb5 100644 --- a/code/modules/food_and_drinks/food/snacks_pastry.dm +++ b/code/modules/food_and_drinks/food/snacks_pastry.dm @@ -111,7 +111,7 @@ name = "chocolate donut" desc = "Goes great with a glass of warm milk." icon_state = "donut_choc" - bonus_reagents = list(/datum/reagent/consumable/hot_cocoa = 3, /datum/reagent/consumable/sprinkles = 1) //the cocoa reagent is just bitter. + bonus_reagents = list(/datum/reagent/consumable/cocoa/hot_cocoa = 3, /datum/reagent/consumable/sprinkles = 1) //the cocoa reagent is just bitter. tastes = list("donut" = 4, "bitterness" = 1) decorated_icon = "donut_choc_sprinkles" filling_color = "#4F230D" @@ -201,7 +201,7 @@ name = "chocolate jelly donut" desc = "Goes great with a glass of warm milk." icon_state = "jelly_choc" - bonus_reagents = list(/datum/reagent/consumable/hot_cocoa = 3, /datum/reagent/consumable/sprinkles = 1, /datum/reagent/consumable/nutriment/vitamin = 1) //the cocoa reagent is just bitter. + bonus_reagents = list(/datum/reagent/consumable/cocoa/hot_cocoa = 3, /datum/reagent/consumable/sprinkles = 1, /datum/reagent/consumable/nutriment/vitamin = 1) //the cocoa reagent is just bitter. tastes = list("jelly" = 1, "donut" = 4, "bitterness" = 1) decorated_icon = "jelly_choc_sprinkles" filling_color = "#4F230D" @@ -282,7 +282,7 @@ name = "chocolate jelly donut" desc = "Goes great with a glass of warm milk." icon_state = "jelly_choc" - bonus_reagents = list(/datum/reagent/consumable/hot_cocoa = 3, /datum/reagent/consumable/sprinkles = 1, /datum/reagent/consumable/nutriment/vitamin = 1) //the cocoa reagent is just bitter. + bonus_reagents = list(/datum/reagent/consumable/cocoa/hot_cocoa = 3, /datum/reagent/consumable/sprinkles = 1, /datum/reagent/consumable/nutriment/vitamin = 1) //the cocoa reagent is just bitter. tastes = list("jelly" = 1, "donut" = 4, "bitterness" = 1) decorated_icon = "jelly_choc_sprinkles" filling_color = "#4F230D" @@ -601,7 +601,7 @@ /obj/item/reagent_containers/food/snacks/hotdog name = "hotdog" - desc = "Fresh footlong ready to go down on." + desc = "Hot and steamy hotdog weenie." icon_state = "hotdog" bitesize = 3 bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 3) @@ -782,22 +782,22 @@ to_chat(user, "You add the [I] to the [name].") P.name = initial(P.name) contents += P - update_overlays(P) + update_customizable_overlays(P) if (P.contents.len) for(var/V in P.contents) P = V P.name = initial(P.name) contents += P - update_overlays(P) + update_customizable_overlays(P) P = I - clearlist(P.contents) + P.contents.Cut() return else if(contents.len) var/obj/O = contents[contents.len] return O.attackby(I, user, params) ..() -/obj/item/reagent_containers/food/snacks/pancakes/update_overlays(obj/item/reagent_containers/food/snacks/P) +/obj/item/reagent_containers/food/snacks/pancakes/update_customizable_overlays(obj/item/reagent_containers/food/snacks/P) var/mutable_appearance/pancake = mutable_appearance(icon, "[P.item_state]_[rand(1,3)]") pancake.pixel_x = rand(-1,1) pancake.pixel_y = 3 * contents.len - 1 diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm index c5e0b7651dcf2..76f2db7e638d8 100644 --- a/code/modules/food_and_drinks/food/snacks_pie.dm +++ b/code/modules/food_and_drinks/food/snacks_pie.dm @@ -52,7 +52,7 @@ H.Paralyze(20) //splat! H.adjust_blurriness(1) H.visible_message("[H] is creamed by [src]!", "You've been creamed by [src]!") - playsound(H, "desceration", 50, TRUE) + playsound(H, "desecration", 50, TRUE) if(!H.creamed) // one layer at a time H.add_overlay(creamoverlay) H.creamed = TRUE diff --git a/code/modules/food_and_drinks/food/snacks_pizza.dm b/code/modules/food_and_drinks/food/snacks_pizza.dm index cb2ea093ca3d3..91cb43b0006e3 100644 --- a/code/modules/food_and_drinks/food/snacks_pizza.dm +++ b/code/modules/food_and_drinks/food/snacks_pizza.dm @@ -177,7 +177,7 @@ l_arm.dismember() else r_arm.dismember() - playsound(user,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, TRUE, -1) + playsound(user,pick('sound/misc/desecration-01.ogg','sound/misc/desecration-02.ogg','sound/misc/desecration-01.ogg') ,50, TRUE, -1) /obj/item/reagent_containers/food/snacks/proc/i_kill_you(obj/item/I, mob/user) if(istype(I, /obj/item/reagent_containers/food/snacks/pineappleslice)) diff --git a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm index 727b17d8e783d..28b69a5a85266 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm @@ -20,6 +20,9 @@ God bless America. - */ +#define DEEPFRYER_COOKTIME 60 +#define DEEPFRYER_BURNTIME 120 + /obj/machinery/deepfryer name = "deep fryer" desc = "Deep fried everything." @@ -31,7 +34,7 @@ God bless America. layer = BELOW_OBJ_LAYER var/obj/item/reagent_containers/food/snacks/deepfryholder/frying //What's being fried RIGHT NOW? var/cook_time = 0 - var/oil_use = 0.05 //How much cooking oil is used per tick + var/oil_use = 0.025 //How much cooking oil is used per second var/fry_speed = 1 //How quickly we fry food var/frying_fried //If the object has been fried; used for messages var/frying_burnt //If the object has been burnt @@ -42,6 +45,7 @@ God bless America. /obj/item/wirecutters, /obj/item/multitool, /obj/item/weldingtool, + /obj/item/powertool, /obj/item/reagent_containers/glass, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/food/condiment, @@ -60,11 +64,15 @@ God bless America. RefreshParts() fry_loop = new(list(src), FALSE) +/obj/machinery/deepfryer/Destroy() + QDEL_NULL(fry_loop) + return ..() + /obj/machinery/deepfryer/RefreshParts() var/oil_efficiency for(var/obj/item/stock_parts/micro_laser/M in component_parts) oil_efficiency += M.rating - oil_use = initial(oil_use) - (oil_efficiency * 0.0095) + oil_use = initial(oil_use) - (oil_efficiency * 0.00475) fry_speed = oil_efficiency /obj/machinery/deepfryer/examine(mob/user) @@ -72,7 +80,7 @@ God bless America. if(frying) . += "You can make out \a [frying] in the oil." if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Frying at [fry_speed*100]% speed. Using [oil_use*10] units of oil per second." + . += "The status display reads: Frying at [fry_speed*100]% speed. Using [oil_use] units of oil per second." /obj/machinery/deepfryer/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/reagent_containers/pill)) @@ -110,20 +118,20 @@ God bless America. icon_state = "fryer_on" fry_loop.start() -/obj/machinery/deepfryer/process() +/obj/machinery/deepfryer/process(delta_time) ..() var/datum/reagent/consumable/cooking_oil/C = reagents.has_reagent(/datum/reagent/consumable/cooking_oil) if(!C) return reagents.chem_temp = C.fry_temperature if(frying) - reagents.trans_to(frying, oil_use, multiplier = fry_speed * 3) //Fried foods gain more of the reagent thanks to space magic - cook_time += fry_speed - if(cook_time >= 30 && !frying_fried) + reagents.trans_to(frying, oil_use * delta_time, multiplier = fry_speed * 3) //Fried foods gain more of the reagent thanks to space magic + cook_time += fry_speed * delta_time + if(cook_time >= DEEPFRYER_COOKTIME && !frying_fried) frying_fried = TRUE //frying... frying... fried playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) audible_message("[src] dings!") - else if (cook_time >= 60 && !frying_burnt) + else if (cook_time >= DEEPFRYER_BURNTIME && !frying_burnt) frying_burnt = TRUE visible_message("[src] emits an acrid smell!") @@ -158,3 +166,6 @@ God bless America. C.Paralyze(60) user.changeNext_move(CLICK_CD_MELEE) return ..() + +#undef DEEPFRYER_COOKTIME +#undef DEEPFRYER_BURNTIME diff --git a/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm b/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm index 893d40819bb9f..22a76ff6962c3 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm @@ -27,35 +27,6 @@ QDEL_NULL(mixer) return ..() -/obj/machinery/food_cart/ui_interact(mob/user) - . = ..() - var/dat - dat += " STORED INGREDIENTS AND DRINKS "
- dat += "Remaining glasses: [glasses] " - dat += "Portion: [portion] " - for(var/datum/reagent/R in reagents.reagent_list) - dat += "[R.name]: [R.volume] " - dat += "Purge" - if (glasses > 0) - dat += "Pour in a glass" - dat += "Add to the mixer " - dat += " MIXER CONTENTS "
- for(var/datum/reagent/R in mixer.reagents.reagent_list)
- dat += "[R.name]: [R.volume] "
- dat += "Transfer back"
- if (glasses > 0)
- dat += "Pour in a glass"
- dat += " " - dat += " STORED FOOD "
- for(var/V in stored_food)
- if(stored_food[V] > 0)
- dat += "[V]: [stored_food[V]] Dispense " - dat += " Refresh Close" - - var/datum/browser/popup = new(user, "foodcart","Food Cart", 500, 350, src) - popup.set_content(dat) - popup.open() - /obj/machinery/food_cart/proc/isFull() return food_stored >= STORAGE_CAPACITY @@ -104,12 +75,43 @@ . = ..() updateDialog() +/obj/machinery/food_cart/ui_interact(mob/user) + . = ..() + var/dat + dat += " STORED INGREDIENTS AND DRINKS "
+ dat += "Remaining glasses: [glasses] " + dat += "Portion: [portion] " + for(var/i in 1 to LAZYLEN(reagents.reagent_list)) + var/datum/reagent/R = reagents.reagent_list[i] + dat += "[R.name]: [R.volume] " + dat += "Purge" + if (glasses > 0) + dat += "Pour in a glass" + dat += "Add to the mixer " + dat += " MIXER CONTENTS "
+ for(var/i in 1 to LAZYLEN(mixer.reagents.reagent_list))
+ var/datum/reagent/R = mixer.reagents.reagent_list[i]
+ dat += "[R.name]: [R.volume] "
+ dat += "Transfer back"
+ if (glasses > 0)
+ dat += "Pour in a glass"
+ dat += " " + dat += " STORED FOOD "
+ for(var/V in stored_food)
+ if(stored_food[V] > 0)
+ dat += "[V]: [stored_food[V]] Dispense " + dat += " Refresh Close" + + var/datum/browser/popup = new(user, "foodcart","Food Cart", 500, 350, src) + popup.set_content(dat) + popup.open() + /obj/machinery/food_cart/Topic(href, href_list) if(..()) return if(href_list["disposeI"]) - reagents.del_reagent(href_list["disposeI"]) + reagents.del_reagent(reagents.reagent_list[text2num(href_list["disposeI"])]?.type) if(href_list["dispense"]) if(stored_food[href_list["dispense"]]-- <= 0) @@ -130,16 +132,16 @@ else var/obj/item/reagent_containers/food/drinks/drinkingglass/DG = new(loc) if(href_list["pour"]) - reagents.trans_id_to(DG, href_list["pour"], portion) + reagents.trans_id_to(DG, reagents.reagent_list[text2num(href_list["pour"])]?.type, portion) if(href_list["m_pour"]) - mixer.reagents.trans_id_to(DG, href_list["m_pour"], portion) + mixer.reagents.trans_id_to(DG, mixer.reagents.reagent_list[text2num(href_list["m_pour"])]?.type, portion) if(href_list["mix"]) - if(reagents.trans_id_to(mixer, href_list["mix"], portion) == 0) + if(!reagents.trans_id_to(mixer, reagents.reagent_list[text2num(href_list["mix"])]?.type, portion)) to_chat(usr, "[mixer] is full!") if(href_list["transfer"]) - if(mixer.reagents.trans_id_to(src, href_list["transfer"], portion) == 0) + if(!mixer.reagents.trans_id_to(src, mixer.reagents.reagent_list[text2num(href_list["transfer"])]?.type, portion)) to_chat(usr, "[src] is full!") updateDialog() @@ -154,6 +156,25 @@ new /obj/item/stack/sheet/iron(loc, 4) qdel(src) +/obj/machinery/food_cart/coffee + name = "coffee cart" + desc = "Ah! The bitter drink of the Gods." + icon_state = "icecream_vat" + glasses = 10 + portion = 20 + +/obj/machinery/food_cart/coffee/Initialize() + ..() + var/A = rand(0,3) + var/B = rand(0,3) + var/C = rand(0,3) + var/D = rand(0,1) + reagents.add_reagent(/datum/reagent/consumable/cafe_latte, A*20) + reagents.add_reagent(/datum/reagent/consumable/icecoffee, B*20) + reagents.add_reagent(/datum/reagent/consumable/soy_latte, C*20) + reagents.add_reagent(/datum/reagent/consumable/pumpkin_latte, D*20) + reagents.add_reagent(/datum/reagent/consumable/coffee, (10-A-B-C-D)*20) + #undef STORAGE_CAPACITY #undef LIQUID_CAPACIY #undef MIXER_CAPACITY diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm index 8756ab4fbfcf5..067169c2cbe8e 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm @@ -33,10 +33,10 @@ /obj/machinery/gibber/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Outputting [meat_produced] meat slab(s) after [gibtime*0.1] seconds of processing." + . += "The status display reads: Outputting [meat_produced] meat slab(s) after [gibtime*0.1] seconds of processing." for(var/obj/item/stock_parts/manipulator/M in component_parts) if(M.rating >= 2) - . += "Gibber has been upgraded to process inorganic materials." + . += "Gibber has been upgraded to process inorganic materials." /obj/machinery/gibber/update_icon() cut_overlays() diff --git a/code/modules/food_and_drinks/kitchen_machinery/grill.dm b/code/modules/food_and_drinks/kitchen_machinery/grill.dm index e2ec15c1854c4..7d7ab765dc02c 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/grill.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/grill.dm @@ -1,5 +1,8 @@ //I JUST WANNA GRILL FOR GOD'S SAKE +#define GRILL_FUELUSAGE_IDLE 0.5 +#define GRILL_FUELUSAGE_ACTIVE 5 + /obj/machinery/grill name = "grill" desc = "Just like the old days." @@ -20,7 +23,7 @@ /obj/machinery/grill/update_icon() if(grilled_item) icon_state = "grill" - else if(grill_fuel) + else if(grill_fuel > 0) icon_state = "grill_on" else icon_state = "grill_open" @@ -62,21 +65,21 @@ return ..() -/obj/machinery/grill/process() +/obj/machinery/grill/process(delta_time) ..() update_icon() - if(!grill_fuel) + if(grill_fuel <= 0) return else - grill_fuel -= 1 - if(prob(1)) + grill_fuel -= GRILL_FUELUSAGE_IDLE * delta_time + if(DT_PROB(0.5, delta_time)) var/datum/effect_system/smoke_spread/bad/smoke = new smoke.set_up(1, loc) smoke.start() if(grilled_item) - grill_time += 1 - grilled_item.reagents.add_reagent(/datum/reagent/consumable/char, 1) - grill_fuel -= 10 + grill_time += delta_time + grilled_item.reagents.add_reagent(/datum/reagent/consumable/char, 0.5 * delta_time) + grill_fuel -= GRILL_FUELUSAGE_ACTIVE * delta_time grilled_item.AddComponent(/datum/component/sizzle) /obj/machinery/grill/Exited(atom/movable/AM) @@ -86,6 +89,7 @@ ..() /obj/machinery/grill/Destroy() + QDEL_NULL(grill_loop) grilled_item = null . = ..() @@ -118,19 +122,19 @@ return ..() /obj/machinery/grill/proc/finish_grill() - switch(grill_time) //no 0-9 to prevent spam - if(10 to 15) + switch(grill_time) //no 0-20 to prevent spam + if(20 to 30) grilled_item.name = "lightly-grilled [grilled_item.name]" grilled_item.desc = "[grilled_item.desc] It's been lightly grilled." - if(16 to 39) + if(30 to 80) grilled_item.name = "grilled [grilled_item.name]" grilled_item.desc = "[grilled_item.desc] It's been grilled." grilled_item.foodtype |= FRIED - if(40 to 50) + if(80 to 100) grilled_item.name = "heavily grilled [grilled_item.name]" grilled_item.desc = "[grilled_item.desc] It's been heavily grilled." grilled_item.foodtype |= FRIED - if(51 to INFINITY) //grill marks reach max alpha + if(100 to INFINITY) //grill marks reach max alpha grilled_item.name = "Powerfully Grilled [grilled_item.name]" grilled_item.desc = "A [grilled_item.name]. Reminds you of your wife, wait, no, it's prettier!" grilled_item.foodtype |= FRIED @@ -139,3 +143,6 @@ /obj/machinery/grill/unwrenched anchored = FALSE + +#undef GRILL_FUELUSAGE_IDLE +#undef GRILL_FUELUSAGE_ACTIVE diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm index 4125b21494c06..4729824db5d13 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm @@ -40,6 +40,7 @@ /obj/machinery/microwave/Destroy() eject() + QDEL_NULL(soundloop) if(wires) QDEL_NULL(wires) . = ..() @@ -83,9 +84,9 @@ . += "\The [src] is empty." if(!(stat & (NOPOWER|BROKEN))) - . += {"The status display reads:\n - - Capacity: [max_n_of_items] items.\n - - Cook time reduced by [(efficiency - 1) * 25]%."} + . += "The status display reads:\n"+\ + "- Capacity: [max_n_of_items] items.\n"+\ + "- Cook time reduced by [(efficiency - 1) * 25]%." /obj/machinery/microwave/update_icon() if(broken) @@ -146,10 +147,13 @@ to_chat(user, "You need more space cleaner!") return TRUE - if(istype(O, /obj/item/soap)) - var/obj/item/soap/P = O + if(istype(O, /obj/item/soap) || istype(O, /obj/item/reagent_containers/glass/rag)) + var/cleanspeed = 50 + if(istype(O, /obj/item/soap)) + var/obj/item/soap/used_soap = O + cleanspeed = used_soap.cleanspeed user.visible_message("[user] starts to clean \the [src].", "You start to clean \the [src]...") - if(do_after(user, P.cleanspeed, target = src)) + if(do_after(user, cleanspeed, target = src)) user.visible_message("[user] has cleaned \the [src].", "You clean \the [src].") dirty = 0 update_icon() @@ -252,8 +256,8 @@ break start() -/obj/machinery/microwave/proc/turn_on() - visible_message("\The [src] turns on.", "You hear a microwave humming.") +/obj/machinery/microwave/proc/wzhzhzh() + visible_message("\The [src] turns on.", null, "You hear a microwave humming.") operating = TRUE set_light(1.5) @@ -271,15 +275,15 @@ #define MICROWAVE_PRE 2 /obj/machinery/microwave/proc/start() - turn_on() + wzhzhzh() loop(MICROWAVE_NORMAL, 10) /obj/machinery/microwave/proc/start_can_fail() - turn_on() + wzhzhzh() loop(MICROWAVE_PRE, 4) /obj/machinery/microwave/proc/muck() - turn_on() + wzhzhzh() playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) dirty_anim_playing = TRUE update_icon() diff --git a/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm b/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm index 5722601ad5522..bf8ae8047e56b 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm @@ -37,7 +37,7 @@ GLOBAL_LIST_EMPTY(monkey_recyclers) /obj/machinery/monkey_recycler/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Producing [cube_production] cubes for every monkey inserted." + . += "The status display reads: Producing [cube_production] cubes for every monkey inserted." /obj/machinery/monkey_recycler/attackby(obj/item/O, mob/user, params) if(default_deconstruction_screwdriver(user, "grinder_open", "grinder", O)) diff --git a/code/modules/food_and_drinks/kitchen_machinery/processor.dm b/code/modules/food_and_drinks/kitchen_machinery/processor.dm index e31b9e6eddc4c..479416615af80 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/processor.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/processor.dm @@ -14,6 +14,7 @@ var/processing = FALSE var/rating_speed = 1 var/rating_amount = 1 + processing_flags = NONE /obj/machinery/processor/RefreshParts() for(var/obj/item/stock_parts/matter_bin/B in component_parts) @@ -24,7 +25,7 @@ /obj/machinery/processor/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Outputting [rating_amount] item(s) at [rating_speed*100]% speed." + . += "The status display reads: Outputting [rating_amount] item(s) at [rating_speed*100]% speed." /obj/machinery/processor/proc/process_food(datum/food_processor_process/recipe, atom/movable/what) if (recipe.output && loc && !QDELETED(src)) @@ -153,11 +154,13 @@ /obj/machinery/processor/slime name = "slime processor" desc = "An industrial grinder with a sticker saying appropriated for science department. Keep hands clear of intake area while operating." + var/sbacklogged = FALSE /obj/machinery/processor/slime/Initialize() . = ..() var/obj/item/circuitboard/machine/B = new /obj/item/circuitboard/machine/processor/slime(null) B.apply_default_parts(src) + proximity_monitor = new(src, 1) /obj/machinery/processor/slime/adjust_item_drop_location(atom/movable/AM) var/static/list/slimecores = subtypesof(/obj/item/slime_extract) @@ -173,36 +176,31 @@ AM.pixel_y = -8 + (round(ii/3)*8) return i -/obj/machinery/processor/slime/process() - if(processing) - return - var/mob/living/simple_animal/slime/picked_slime - for(var/mob/living/simple_animal/slime/slime in range(1,src)) - if(slime.loc == src) - continue - if(istype(slime, /mob/living/simple_animal/slime)) - if(slime.stat) - picked_slime = slime - break - if(!picked_slime) - return - var/datum/food_processor_process/P = select_recipe(picked_slime) - if (!P) - return - - visible_message("[picked_slime] is sucked into [src].") - picked_slime.forceMove(src) +/obj/machinery/processor/slime/interact(mob/user) + . = ..() + if(sbacklogged) + for(var/mob/living/simple_animal/slime/AM in ohearers(1,src)) //fallback in case slimes got placed while processor was active triggers only after processing!!!! + if(AM.stat == DEAD) + visible_message("[AM] is sucked into [src].") + AM.forceMove(src) + sbacklogged = FALSE + +/obj/machinery/processor/slime/HasProximity(mob/AM) + if(!sbacklogged && istype(AM, /mob/living/simple_animal/slime) && AM.stat == DEAD) + if(processing) + sbacklogged = TRUE + else + visible_message("[AM] is sucked into [src].") + AM.forceMove(src) /obj/machinery/processor/slime/process_food(datum/food_processor_process/recipe, atom/movable/what) var/mob/living/simple_animal/slime/S = what if (istype(S)) var/C = S.cores - if(S.stat != DEAD) - S.forceMove(drop_location()) - S.visible_message("[C] crawls free of the processor!") - return for(var/i in 1 to (C+rating_amount-1)) - var/atom/movable/item = new S.coretype(drop_location()) + var/obj/item/slime_extract/item = new S.coretype(drop_location()) + if(S.transformeffects & SLIME_EFFECT_GOLD) + item.sparkly = TRUE adjust_item_drop_location(item) SSblackbox.record_feedback("tally", "slime_core_harvested", 1, S.colour) ..() diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index c6608e416b60b..ad8f98caea03e 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -39,7 +39,7 @@ /obj/machinery/smartfridge/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += "The status display reads: This unit can hold a maximum of [max_n_of_items] items." + . += "The status display reads: This unit can hold a maximum of [max_n_of_items] items." /obj/machinery/smartfridge/power_change() ..() @@ -73,7 +73,7 @@ cut_overlays() if(panel_open) add_overlay("[initial(icon_state)]-panel") - updateUsrDialog() + ui_update() return if(default_pry_open(O)) @@ -84,7 +84,6 @@ return if(default_deconstruction_crowbar(O)) - updateUsrDialog() return if(!stat) @@ -96,7 +95,6 @@ if(accept_check(O)) load(O) user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") - updateUsrDialog() if (visible_contents) update_icon() return TRUE @@ -110,7 +108,6 @@ if(accept_check(G)) load(G) loaded++ - updateUsrDialog() if(loaded) if(contents.len >= max_n_of_items) @@ -128,21 +125,40 @@ to_chat(user, "There is nothing in [O] to put in [src]!") return FALSE + if(istype(O, /obj/item/organ_storage)) + var/obj/item/organ_storage/S = O + if(S.contents.len) + var/obj/item/I = S.contents[1] + if(accept_check(I)) + load(I) + user.visible_message("[user] inserts \the [I] into \the [src].", \ + "You insert \the [I] into \the [src].") + O.cut_overlays() + O.icon_state = "evidenceobj" + O.desc = "A container for holding body parts." + if(visible_contents) + update_icon() + return TRUE + else + to_chat(user, "[src] does not accept [I]!") + return FALSE + else + to_chat(user, "There is nothing in [O] to put into [src]!") + return FALSE + if(user.a_intent != INTENT_HARM) to_chat(user, "\The [src] smartly refuses [O].") - updateUsrDialog() return FALSE else return ..() -/obj/machinery/smartfridge/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) +/obj/machinery/smartfridge/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) if(!stat) - if (istype(AM, /obj/item)) + if (istype(AM, /obj/item)) var/obj/item/O = AM if(contents.len < max_n_of_items && accept_check(O)) load(O) - updateUsrDialog() if (visible_contents) update_icon() return TRUE @@ -161,13 +177,16 @@ to_chat(usr, "\the [O] is stuck to your hand, you cannot put it in \the [src]!") return FALSE else - return TRUE + . = TRUE else if(SEND_SIGNAL(O.loc, COMSIG_CONTAINS_STORAGE)) - return SEND_SIGNAL(O.loc, COMSIG_TRY_STORAGE_TAKE, O, src) + . = SEND_SIGNAL(O.loc, COMSIG_TRY_STORAGE_TAKE, O, src) else O.forceMove(src) - return TRUE + . = TRUE + + if(.) + ui_update() ///Really simple proc, just moves the object "O" into the hands of mob "M" if able, done so I could modify the proc a little for the organ fridge /obj/machinery/smartfridge/proc/dispense(obj/item/O, var/mob/M) @@ -226,28 +245,21 @@ else desired = input("How many items?", "How many items would you like to take out?", 1) as null|num - if(QDELETED(src) || QDELETED(usr) || !usr.Adjacent(src)) // Sanity checkin' in case stupid stuff happens while we wait for input() - return FALSE + if(!isnum_safe(desired) || desired <= 0) + return - if(desired == 1 && Adjacent(usr) && !issilicon(usr)) - for(var/obj/item/O in src) - if(O.name == params["name"]) - dispense(O, usr) - break - if (visible_contents) - update_icon() - return TRUE + if(QDELETED(src) || QDELETED(usr) || !usr.Adjacent(src)) // Sanity checkin' in case stupid stuff happens while we wait for input() + return for(var/obj/item/O in src) - if(desired <= 0) - break if(O.name == params["name"]) dispense(O, usr) desired-- - if (visible_contents) + . = TRUE + if(desired <= 0) + break + if (visible_contents && .) update_icon() - return TRUE - return FALSE // ---------------------------- @@ -341,7 +353,11 @@ S.forceMove(drop_location()) else var/dried = S.dried_type - new dried(drop_location()) + dried = new dried(drop_location()) + if(istype(dried, /obj/item/reagent_containers)) // If the product is a reagent container, transfer reagents + var/obj/item/reagent_containers/R = dried + R.reagents.clear_reagents() + S.reagents.copy_to(R) qdel(S) return TRUE for(var/obj/item/stack/sheet/wetleather/WL in src) @@ -422,14 +438,14 @@ /obj/machinery/smartfridge/organ/RefreshParts() for(var/obj/item/stock_parts/matter_bin/B in component_parts) max_n_of_items = 20 * B.rating - repair_rate = max(0, STANDARD_ORGAN_HEALING * (B.rating - 1)) + repair_rate = max(0, STANDARD_ORGAN_HEALING * (B.rating - 1) * 0.5) -/obj/machinery/smartfridge/organ/process() +/obj/machinery/smartfridge/organ/process(delta_time) for(var/organ in contents) var/obj/item/organ/O = organ if(!istype(O)) return - O.applyOrganDamage(-repair_rate) + O.applyOrganDamage(-repair_rate * delta_time) /obj/machinery/smartfridge/organ/Exited(obj/item/organ/AM, atom/newLoc) . = ..() diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm index ceb58da11375b..0ee7479a0bba5 100644 --- a/code/modules/food_and_drinks/pizzabox.dm +++ b/code/modules/food_and_drinks/pizzabox.dm @@ -25,11 +25,11 @@ var/obj/item/bombcore/miniature/pizza/bomb var/bomb_active = FALSE // If the bomb is counting down. var/bomb_defused = TRUE // If the bomb is inert. - var/bomb_timer = 1 // How long before blowing the bomb. - /// Min bomb timer allowed + var/bomb_timer = 1 // How long before blowing the bomb, in seconds. + /// Min bomb timer allowed in seconds var/bomb_timer_min = 1 - /// Max bomb timer allower - var/bomb_timer_max = 10 + /// Max bomb timer allower in seconds + var/bomb_timer_max = 20 /obj/item/pizzabox/Initialize() . = ..() @@ -105,6 +105,7 @@ if(open && !bomb_defused) audible_message("[icon2html(src, hearers(src))] *beep*") bomb_active = TRUE + wires.ui_update() START_PROCESSING(SSobj, src) update_icon() @@ -131,10 +132,11 @@ if (isnull(bomb_timer)) return - bomb_timer = clamp(CEILING(bomb_timer / 2, 1), bomb_timer_min, bomb_timer_max) + bomb_timer = clamp(CEILING(bomb_timer, 1), bomb_timer_min, bomb_timer_max) bomb_defused = FALSE + wires.ui_update() - log_bomber(user, "has trapped a", src, "with [bomb] set to [bomb_timer * 2] seconds") + log_bomber(user, "has trapped a", src, "with [bomb] set to [bomb_timer] seconds") bomb.adminlog = "The [bomb.name] in [src.name] that [key_name(user)] activated has detonated!" to_chat(user, "You trap [src] with [bomb].") @@ -213,10 +215,10 @@ to_chat(user, "That's not a pizza!") ..() -/obj/item/pizzabox/process() +/obj/item/pizzabox/process(delta_time) if(bomb_active && !bomb_defused && (bomb_timer > 0)) - playsound(loc, 'sound/items/timer.ogg', 50, 0) - bomb_timer-- + playsound(loc, 'sound/items/timer.ogg', 50, FALSE) + bomb_timer -= delta_time if(bomb_active && !bomb_defused && (bomb_timer <= 0)) if(bomb in src) bomb.detonate() @@ -226,6 +228,7 @@ if(bomb_defused && (bomb in src)) bomb.defuse() bomb_active = FALSE + wires.ui_update() unprocess() return diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm index 09d3225e4e708..cc36bad640428 100644 --- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm +++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm @@ -606,7 +606,7 @@ id = /datum/reagent/consumable/ethanol/quintuple_sec results = list(/datum/reagent/consumable/ethanol/quintuple_sec = 15) required_reagents = list(/datum/reagent/consumable/ethanol/quadruple_sec = 5, /datum/reagent/consumable/clownstears = 5, /datum/reagent/consumable/ethanol/syndicatebomb = 5) - mix_message = "Judgement is upon you." + mix_message = "Judgment is upon you." mix_sound = 'sound/items/airhorn2.ogg' /datum/chemical_reaction/bastion_bourbon @@ -656,7 +656,7 @@ name = "Peppermint Patty" id = /datum/reagent/consumable/ethanol/peppermint_patty results = list(/datum/reagent/consumable/ethanol/peppermint_patty = 10) - required_reagents = list(/datum/reagent/consumable/hot_cocoa = 6, /datum/reagent/consumable/ethanol/creme_de_cacao = 1, /datum/reagent/consumable/ethanol/creme_de_menthe = 1, /datum/reagent/consumable/ethanol/vodka = 1, /datum/reagent/consumable/menthol = 1) + required_reagents = list(/datum/reagent/consumable/cocoa/hot_cocoa = 6, /datum/reagent/consumable/ethanol/creme_de_cacao = 1, /datum/reagent/consumable/ethanol/creme_de_menthe = 1, /datum/reagent/consumable/ethanol/vodka = 1, /datum/reagent/consumable/menthol = 1) mix_message = "The cocoa turns mint green just as the strong scent hits your nose." /datum/chemical_reaction/alexander @@ -840,3 +840,9 @@ id = /datum/reagent/consumable/ethanol/beesknees results = list(/datum/reagent/consumable/ethanol/beesknees = 4) required_reagents = list(/datum/reagent/consumable/ethanol/mead = 1, /datum/reagent/consumable/honey = 1, /datum/reagent/consumable/ethanol/whiskey = 1, /datum/reagent/consumable/lemonjuice = 1) + +/datum/chemical_reaction/beeffizz + name = "Beef Fizz" + id = /datum/reagent/consumable/ethanol/beeffizz + results = list(/datum/reagent/consumable/ethanol/beeffizz = 10) + required_reagents = list(/datum/reagent/consumable/beefbroth = 7, /datum/reagent/consumable/ice = 2, /datum/reagent/consumable/lemonjuice = 1 ) diff --git a/code/modules/food_and_drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm index 5503ecc0b25a0..3a126cf8bee5a 100644 --- a/code/modules/food_and_drinks/recipes/food_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/food_mixtures.dm @@ -47,8 +47,8 @@ /datum/chemical_reaction/hot_cocoa name = "Hot Cocoa" - id = /datum/reagent/consumable/hot_cocoa - results = list(/datum/reagent/consumable/hot_cocoa = 5) + id = /datum/reagent/consumable/cocoa/hot_cocoa + results = list(/datum/reagent/consumable/cocoa/hot_cocoa = 5) required_reagents = list(/datum/reagent/water = 5, /datum/reagent/consumable/cocoa = 1) /datum/chemical_reaction/coffee diff --git a/code/modules/food_and_drinks/recipes/processor_recipes.dm b/code/modules/food_and_drinks/recipes/processor_recipes.dm index eab3cc0707da3..8a53b95cb8690 100644 --- a/code/modules/food_and_drinks/recipes/processor_recipes.dm +++ b/code/modules/food_and_drinks/recipes/processor_recipes.dm @@ -51,4 +51,8 @@ /datum/food_processor_process/mob/slime input = /mob/living/simple_animal/slime output = null - required_machine = /obj/machinery/processor/slime \ No newline at end of file + required_machine = /obj/machinery/processor/slime + +/datum/food_processor_process/fish + input = /obj/item/fish + output = /obj/item/reagent_containers/food/snacks/carpmeat/icantbeliveitsnotcarp \ No newline at end of file diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_bread.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_bread.dm index 6d08137d49342..6c315a2747a76 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_bread.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_bread.dm @@ -115,3 +115,13 @@ ) result = /mob/living/simple_animal/pet/cat/breadcat subcategory = CAT_BREAD + +/datum/crafting_recipe/food/hotdog + name = "Hot dog" + reqs = list( + /datum/reagent/consumable/ketchup = 5, + /obj/item/reagent_containers/food/snacks/bun = 1, + /obj/item/reagent_containers/food/snacks/sausage = 1 + ) + result = /obj/item/reagent_containers/food/snacks/hotdog + subcategory = CAT_BREAD diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm index 0bb2b970ca2a3..a35a6025988fb 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm @@ -368,3 +368,17 @@ ) result = /obj/item/reagent_containers/food/snacks/burger/chicken subcategory = CAT_BURGER + +/datum/crafting_recipe/food/crazyhamburger + name = "Crazy Hamburger" + reqs = list( + /obj/item/reagent_containers/food/snacks/meat/steak/plain = 1, // we have no horse meat sadly + /obj/item/reagent_containers/food/snacks/grown/chili = 2, + /datum/reagent/consumable/cooking_oil = 20, + /obj/item/reagent_containers/food/snacks/grown/nettle/death = 2, // closest thing to "grass of death" + /obj/item/reagent_containers/food/snacks/cheesewedge = 4, + /obj/item/reagent_containers/food/snacks/bun = 1 + ) + result = /obj/item/reagent_containers/food/snacks/burger/crazy + subcategory = CAT_BURGER + diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm index 1cb57f78f748d..e2b13d4b0f2bc 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm @@ -114,3 +114,12 @@ time = 10 reqs = list(/obj/item/stack/sheet/cardboard = 1) category = CAT_DRINK + +/datum/crafting_recipe/honeycomb + name = "Honeycomb" + result = /obj/item/reagent_containers/food/drinks/honeycomb + always_available = FALSE + time = 30 + reqs = list(/datum/reagent/consumable/sugar = 50) + category = CAT_DRINK + diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm index 40f24280943cf..0f248e3afcc03 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm @@ -23,7 +23,7 @@ ) result = /obj/item/reagent_containers/food/snacks/donut/chaos -datum/crafting_recipe/food/donut/meat +/datum/crafting_recipe/food/donut/meat time = 15 name = "Meat donut" reqs = list( @@ -444,16 +444,6 @@ datum/crafting_recipe/food/donut/meat ////////////////////////////////////////////OTHER//////////////////////////////////////////// -/datum/crafting_recipe/food/hotdog - name = "Hot dog" - reqs = list( - /datum/reagent/consumable/ketchup = 5, - /obj/item/reagent_containers/food/snacks/bun = 1, - /obj/item/reagent_containers/food/snacks/sausage = 1 - ) - result = /obj/item/reagent_containers/food/snacks/hotdog - subcategory = CAT_PASTRY - /datum/crafting_recipe/food/meatbun name = "Meat bun" reqs = list( diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm index 0cc5f67f94b31..41a3756673803 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm @@ -140,7 +140,7 @@ /datum/reagent/water = 10, /obj/item/reagent_containers/glass/bowl = 1, /obj/item/reagent_containers/food/snacks/grown/banana = 1, - /obj/item/stack/ore/bananium = 1 + /obj/item/stack/sheet/mineral/bananium = 1 ) result = /obj/item/reagent_containers/food/snacks/soup/clownstears subcategory = CAT_SOUP @@ -269,4 +269,4 @@ /obj/item/reagent_containers/food/snacks/grown/mushroom/jupitercup = 1 ) result = /obj/item/reagent_containers/food/snacks/soup/electron - subcategory = CAT_SOUP \ No newline at end of file + subcategory = CAT_SOUP diff --git a/code/modules/guardian/abilities/major/assassin.dm b/code/modules/guardian/abilities/major/assassin.dm index 3569ba88e59ad..27994c798344b 100644 --- a/code/modules/guardian/abilities/major/assassin.dm +++ b/code/modules/guardian/abilities/major/assassin.dm @@ -10,8 +10,8 @@ arrow_weight = 0.9 var/next_stealth = 0 var/stealthcooldown = 0 - var/obj/screen/alert/canstealthalert - var/obj/screen/alert/instealthalert + var/atom/movable/screen/alert/canstealthalert + var/atom/movable/screen/alert/instealthalert /datum/guardian_ability/major/assassin/Apply() @@ -68,12 +68,12 @@ if(next_stealth <= world.time) if(mode) if(!instealthalert) - instealthalert = guardian.throw_alert("instealth", /obj/screen/alert/instealth) + instealthalert = guardian.throw_alert("instealth", /atom/movable/screen/alert/instealth) guardian.clear_alert("canstealth") canstealthalert = null else if(!canstealthalert) - canstealthalert = guardian.throw_alert("canstealth", /obj/screen/alert/canstealth) + canstealthalert = guardian.throw_alert("canstealth", /atom/movable/screen/alert/canstealth) guardian.clear_alert("instealth") instealthalert = null else diff --git a/code/modules/guardian/abilities/major/explosive.dm b/code/modules/guardian/abilities/major/explosive.dm index 4bc3d7963c83f..b015db7dad833 100644 --- a/code/modules/guardian/abilities/major/explosive.dm +++ b/code/modules/guardian/abilities/major/explosive.dm @@ -14,11 +14,11 @@ /datum/guardian_ability/major/explosive/Apply() . = ..() - guardian.verbs += /mob/living/simple_animal/hostile/guardian/proc/DetonateBomb + guardian.add_verb(/mob/living/simple_animal/hostile/guardian/proc/DetonateBomb) /datum/guardian_ability/major/explosive/Remove() . = ..() - guardian.verbs -= /mob/living/simple_animal/hostile/guardian/proc/DetonateBomb + guardian.remove_verb(/mob/living/simple_animal/hostile/guardian/proc/DetonateBomb) /datum/guardian_ability/major/explosive/Attack(atom/target) if(prob(40) && isliving(target)) @@ -26,7 +26,7 @@ if(!M.anchored && M != guardian.summoner?.current && !guardian.hasmatchingsummoner(M)) new /obj/effect/temp_visual/guardian/phase/out(get_turf(M)) do_teleport(M, M, 10, channel = TELEPORT_CHANNEL_BLUESPACE) - for(var/mob/living/L in range(1, M)) + for(var/mob/living/L in hearers(1, M)) if(guardian.hasmatchingsummoner(L)) //if the summoner matches don't hurt them continue if(L != guardian && L != guardian.summoner?.current) @@ -51,6 +51,8 @@ to_chat(guardian, "Your powers are on cooldown! You must wait 20 seconds between bombs.") /datum/guardian_ability/major/explosive/proc/kaboom(atom/source, mob/living/explodee) + SIGNAL_HANDLER + if(!istype(explodee)) return if(explodee == guardian || explodee == guardian.summoner?.current || guardian.hasmatchingsummoner(explodee)) @@ -70,6 +72,8 @@ UNREGISTER_BOMB_SIGNALS(A) /datum/guardian_ability/major/explosive/proc/display_examine(datum/source, mob/user, text) + SIGNAL_HANDLER + text += "It glows with a strange light!" diff --git a/code/modules/guardian/abilities/major/frenzy.dm b/code/modules/guardian/abilities/major/frenzy.dm index 51ee4d68383b0..800b1dd9d93e5 100644 --- a/code/modules/guardian/abilities/major/frenzy.dm +++ b/code/modules/guardian/abilities/major/frenzy.dm @@ -41,7 +41,7 @@ next_rush = world.time + ((0.2 SECONDS * (5 - master_stats.potential)) + 2) //2 to 3 seconds /datum/guardian_ability/major/frenzy/Stat() - . = ..() - if(statpanel("Status")) - if(next_rush > world.time) - stat(null, "Frenzy Charge Cooldown Remaining: [DisplayTimeText(next_rush - world.time)]") + var/list/tab_data = list() + if(next_rush > world.time) + tab_data["Frenzy Charge Cooldown Remaining"] = GENERATE_STAT_TEXT("[DisplayTimeText(next_rush - world.time)]") + return tab_data diff --git a/code/modules/guardian/abilities/major/gravity.dm b/code/modules/guardian/abilities/major/gravity.dm index 16ec3978eb53c..8f57370c4fc54 100644 --- a/code/modules/guardian/abilities/major/gravity.dm +++ b/code/modules/guardian/abilities/major/gravity.dm @@ -23,6 +23,8 @@ remove_gravity(C) /datum/guardian_ability/major/gravity/proc/recheck_distances() + SIGNAL_HANDLER + for(var/datum/component/C in gravito_targets) if(get_dist(src, C.parent) > (master_stats.potential * 2)) remove_gravity(C) @@ -49,5 +51,7 @@ qdel(C) /datum/guardian_ability/major/gravity/proc/__distance_check(atom/movable/AM, OldLoc, Dir, Forced) + SIGNAL_HANDLER + if(get_dist(src, AM) > (master_stats.potential * 2)) remove_gravity(AM.GetComponent(/datum/component/forced_gravity)) diff --git a/code/modules/guardian/abilities/major/hand.dm b/code/modules/guardian/abilities/major/hand.dm index 1c107cf22d6dc..2173d6408a763 100644 --- a/code/modules/guardian/abilities/major/hand.dm +++ b/code/modules/guardian/abilities/major/hand.dm @@ -14,12 +14,12 @@ for(var/atom/movable/AM in get_turf(target)) if(AM.anchored) continue - AM.forceMove(hand_turf) + do_teleport(AM, hand_turf, no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC) guardian.face_atom(hand_turf) return ..() /datum/guardian_ability/major/hand/Stat() - . = ..() - if(statpanel("Status")) - if(next_hand > world.time) - stat(null, "THE HAND Cooldown Remaining: [DisplayTimeText(next_hand - world.time)]") + var/list/tab_data = list() + if(next_hand > world.time) + tab_data["THE HAND Cooldown Remaining"] = GENERATE_STAT_TEXT("[DisplayTimeText(next_hand - world.time)]") + return tab_data diff --git a/code/modules/guardian/abilities/major/predator.dm b/code/modules/guardian/abilities/major/predator.dm index f6f4e58fff6b2..f2e2ca14d7121 100644 --- a/code/modules/guardian/abilities/major/predator.dm +++ b/code/modules/guardian/abilities/major/predator.dm @@ -83,7 +83,7 @@ status.scan_target = prey status.point_to_target() -/obj/screen/alert/status_effect/agent_pinpointer/predator +/atom/movable/screen/alert/status_effect/agent_pinpointer/predator name = "Predator's All-Seeing Eyes" /datum/status_effect/agent_pinpointer/predator @@ -91,7 +91,7 @@ minimum_range = 1 range_fuzz_factor = 0 tick_interval = 10 - alert_type = /obj/screen/alert/status_effect/agent_pinpointer/predator + alert_type = /atom/movable/screen/alert/status_effect/agent_pinpointer/predator /datum/status_effect/agent_pinpointer/predator/scan_for_target() return diff --git a/code/modules/guardian/abilities/major/scout.dm b/code/modules/guardian/abilities/major/scout.dm index 714accc693d88..bba045dc3b4ed 100644 --- a/code/modules/guardian/abilities/major/scout.dm +++ b/code/modules/guardian/abilities/major/scout.dm @@ -17,6 +17,7 @@ guardian.alpha = 45 guardian.range = 255 guardian.do_the_cool_invisible_thing = FALSE + guardian.can_use_abilities = FALSE else guardian.ranged = initial(guardian.ranged) guardian.melee_damage = initial(guardian.melee_damage) @@ -26,6 +27,7 @@ guardian.range = initial(guardian.range) guardian.do_the_cool_invisible_thing = initial(guardian.do_the_cool_invisible_thing) guardian.stats.Apply(guardian) + guardian.can_use_abilities = initial(guardian.can_use_abilities) /datum/guardian_ability/major/scout/Manifest() if(mode) diff --git a/code/modules/guardian/abilities/minor/snares.dm b/code/modules/guardian/abilities/minor/snares.dm index 2b616ecd24ac0..0b1a1e3344293 100644 --- a/code/modules/guardian/abilities/minor/snares.dm +++ b/code/modules/guardian/abilities/minor/snares.dm @@ -5,17 +5,20 @@ cost = 1 /datum/guardian_ability/minor/snare/Apply() - guardian.verbs += /mob/living/simple_animal/hostile/guardian/proc/Snare - guardian.verbs += /mob/living/simple_animal/hostile/guardian/proc/DisarmSnare + guardian.add_verb(/mob/living/simple_animal/hostile/guardian/proc/Snare) + guardian.add_verb(/mob/living/simple_animal/hostile/guardian/proc/DisarmSnare) /datum/guardian_ability/minor/snare/Remove() - guardian.verbs -= /mob/living/simple_animal/hostile/guardian/proc/Snare - guardian.verbs -= /mob/living/simple_animal/hostile/guardian/proc/DisarmSnare + guardian.remove_verb(/mob/living/simple_animal/hostile/guardian/proc/Snare) + guardian.remove_verb(/mob/living/simple_animal/hostile/guardian/proc/DisarmSnare) /mob/living/simple_animal/hostile/guardian/proc/Snare() set name = "Set Surveillance Snare" set category = "Guardian" set desc = "Set an invisible snare that will alert you when living creatures walk over it. Max of 5" + if(!can_use_abilities) + to_chat(src, "You can't do that right now!") + return if(snares.len <6) var/turf/snare_loc = get_turf(src.loc) var/obj/effect/snare/S = new /obj/effect/snare(snare_loc) diff --git a/code/modules/guardian/abilities/minor/teleport.dm b/code/modules/guardian/abilities/minor/teleport.dm index d155ed50f00f0..d6b9bcc5fb996 100644 --- a/code/modules/guardian/abilities/minor/teleport.dm +++ b/code/modules/guardian/abilities/minor/teleport.dm @@ -7,15 +7,15 @@ /datum/guardian_ability/minor/teleport/Apply() ..() - guardian.verbs += /mob/living/simple_animal/hostile/guardian/proc/Beacon + guardian.add_verb(/mob/living/simple_animal/hostile/guardian/proc/Beacon) /datum/guardian_ability/minor/teleport/Remove() ..() - guardian.verbs -= /mob/living/simple_animal/hostile/guardian/proc/Beacon + guardian.remove_verb(/mob/living/simple_animal/hostile/guardian/proc/Beacon) /obj/effect/proc_holder/spell/targeted/guardian/teleport name = "Teleport" - desc = "Teleport someone to your recieving pad." + desc = "Teleport someone to your receiving pad." /obj/effect/proc_holder/spell/targeted/guardian/teleport/InterceptClickOn(mob/living/caller, params, atom/movable/A) if(!istype(A)) @@ -27,6 +27,9 @@ if(!G.is_deployed()) to_chat(G, "You must be manifested to warp a target!") return + if(!G.can_use_abilities) + to_chat(G, "You can't do that right now!") + return if(!G.beacon) to_chat(G, "You need a beacon placed to warp things!") return @@ -38,7 +41,7 @@ return var/turf/T = get_turf(A) - if(G.beacon.z != T.z) + if(G.beacon.get_virtual_z_level() != T.get_virtual_z_level()) to_chat(G, "The beacon is too far away to warp to!") return remove_ranged_ability() diff --git a/code/modules/guardian/abilities/special/absolution.dm b/code/modules/guardian/abilities/special/absolution.dm index 5bd3a8af31d8b..91ba7eb2dcf26 100644 --- a/code/modules/guardian/abilities/special/absolution.dm +++ b/code/modules/guardian/abilities/special/absolution.dm @@ -50,19 +50,6 @@ // STUFF -/obj/item/melee_attack_chain(mob/user, atom/target, params) - if(!tool_attack_chain(user, target) && pre_attack(target, user, params)) - var/resolved - if(HAS_TRAIT(target, TRAIT_ONEWAYROAD)) - resolved = user.attackby(src, user, params) // you just hit yourself - else - resolved = target.attackby(src, user, params) - if(!resolved && target && !QDELETED(src)) - if(HAS_TRAIT(target, TRAIT_ONEWAYROAD)) - afterattack(user, user, 1, params) - else - afterattack(target, user, 1, params) - /mob/living/carbon/human/bullet_act(obj/item/projectile/P, def_zone) if(HAS_TRAIT(src, TRAIT_ONEWAYROAD)) var/atom/movable/oldfirer = P.firer diff --git a/code/modules/guardian/guardian.dm b/code/modules/guardian/guardian.dm index c65469e451b33..2e1b43a3315c2 100644 --- a/code/modules/guardian/guardian.dm +++ b/code/modules/guardian/guardian.dm @@ -38,7 +38,8 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians melee_damage = 15 AIStatus = AI_OFF hud_type = /datum/hud/guardian - mobsay_color = "#ffffff" + chat_color = "#ffffff" + mobchatspan = "blob" var/next_reset = 0 var/guardiancolor = "#ffffff" var/mutable_appearance/cooloverlay @@ -49,7 +50,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/range = 10 var/cooldown = 0 var/datum/mind/summoner - var/toggle_button_type = /obj/screen/guardian/ToggleMode + var/toggle_button_type = /atom/movable/screen/guardian/ToggleMode var/datum/guardian_stats/stats var/summoner_visible = TRUE var/battlecry = "AT" @@ -63,12 +64,14 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/beacon_cooldown = 0 var/list/pocket_dim var/transforming = FALSE + var/can_use_abilities = TRUE + discovery_points = 5000 /mob/living/simple_animal/hostile/guardian/Initialize(mapload, theme, guardiancolor) GLOB.parasites += src if(guardiancolor) src.guardiancolor = guardiancolor - src.mobsay_color = guardiancolor + src.chat_color = guardiancolor updatetheme(theme) battlecry = pick("ORA", "MUDA", "DORA", "ARRI", "VOLA", "AT") return ..() @@ -169,7 +172,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians speak_emote = list("telepathically cries") desc = "A truly alien creature, it is a mass of unknown organic material, standing by its' owner's side." attack_sound = 'sound/weapons/pierce.ogg' - if(!recolorentiresprite) //we want this to proc before stand logs in, so the overlay isnt gone for some reason + if(!recolorentiresprite) //we want this to proc before stand logs in, so the overlay isn't gone for some reason cooloverlay = mutable_appearance(icon, theme) cooloverlay.color = guardiancolor add_overlay(cooloverlay) @@ -216,6 +219,8 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians snapback() /mob/living/simple_animal/hostile/guardian/proc/OnMoved() + SIGNAL_HANDLER + snapback() setup_barriers() @@ -241,20 +246,20 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians if(stats.ability) stats.ability.Berserk() -/mob/living/simple_animal/hostile/guardian/Stat() - ..() - if(statpanel("Status")) - if(summoner?.current) - var/resulthealth - if(iscarbon(summoner.current)) - resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.current.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.current.maxHealth)) * 100) - else - resulthealth = round((summoner.current.health / summoner.current.maxHealth) * 100, 0.5) - stat(null, "Summoner Health: [resulthealth]%") - if(cooldown >= world.time) - stat(null, "Manifest/Recall Cooldown Remaining: [DisplayTimeText(cooldown - world.time)]") - if(stats.ability) - stats.ability.Stat() +/mob/living/simple_animal/hostile/guardian/get_stat_tab_status() + var/list/tab_data = ..() + if(summoner?.current) + var/resulthealth + if(iscarbon(summoner.current)) + resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.current.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.current.maxHealth)) * 100) + else + resulthealth = round((summoner.current.health / summoner.current.maxHealth) * 100, 0.5) + tab_data["Summoner Health"] = GENERATE_STAT_TEXT("[resulthealth]%") + if(cooldown >= world.time) + tab_data["Manifest/Recall Cooldown Remaining"] = GENERATE_STAT_TEXT(" [DisplayTimeText(cooldown - world.time)]") + if(stats.ability) + tab_data += stats.ability.Stat() + return tab_data /mob/living/simple_animal/hostile/guardian/Move() //Returns to summoner if they move out of range pixel_x = initial(pixel_x) @@ -340,9 +345,10 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return loc != summoner?.current /mob/living/simple_animal/hostile/guardian/Shoot(atom/targeted_atom) - if( QDELETED(targeted_atom) || targeted_atom == targets_from.loc || targeted_atom == targets_from ) + var/atom/target_from = GET_TARGETS_FROM(src) + if( QDELETED(targeted_atom) || targeted_atom == target_from.loc || targeted_atom == target_from ) return - var/turf/startloc = get_turf(targets_from) + var/turf/startloc = get_turf(target_from) var/obj/item/projectile/P = new /obj/item/projectile/guardian(startloc) playsound(src, projectilesound, 100, 1) P.color = guardiancolor @@ -353,7 +359,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians P.yo = targeted_atom.y - startloc.y P.xo = targeted_atom.x - startloc.x if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space - newtonian_move(get_dir(targeted_atom, targets_from)) + newtonian_move(get_dir(targeted_atom, target_from)) P.original = targeted_atom P.preparePixelProjectile(targeted_atom, src) P.fire() @@ -391,7 +397,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians if(stats.ability) stats.ability.AfterAttack(target) -/mob/living/simple_animal/hostile/guardian/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash) +/mob/living/simple_animal/hostile/guardian/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash) return FALSE /mob/living/simple_animal/hostile/guardian/death() @@ -409,7 +415,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.current.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.current.maxHealth)) * 100) else resulthealth = round((summoner.current.health / summoner.current.maxHealth) * 100, 0.5) - hud_used.healths.maptext = " [resulthealth]% "
+ hud_used.healths.maptext = MAPTEXT("[resulthealth]% ")
/mob/living/simple_animal/hostile/guardian/adjustHealth(amount, updating_health = TRUE, forced = FALSE) //The spirit is invincible, but passes on damage to the summoner
if(berserk)
@@ -580,16 +586,20 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
key = C.key
/mob/living/simple_animal/hostile/guardian/proc/Reviveify()
+ SIGNAL_HANDLER
+
revive()
var/mob/gost = grab_ghost(TRUE)
if(!QDELETED(gost) && gost.ckey)
ckey = gost.ckey
/mob/living/simple_animal/hostile/guardian/proc/OnMindTransfer(datum/_source, mob/old_body, mob/new_body)
+ SIGNAL_HANDLER
+
if(!QDELETED(old_body))
- old_body.verbs -= /mob/living/proc/guardian_comm
- old_body.verbs -= /mob/living/proc/guardian_recall
- old_body.verbs -= /mob/living/proc/guardian_reset
+ old_body.remove_verb(/mob/living/proc/guardian_comm)
+ old_body.remove_verb(/mob/living/proc/guardian_recall)
+ old_body.remove_verb(/mob/living/proc/guardian_reset)
UnregisterSignal(old_body, COMSIG_MOVABLE_MOVED)
UnregisterSignal(old_body, COMSIG_LIVING_REVIVE)
if(isliving(new_body))
@@ -600,9 +610,9 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
RegisterSignal(new_body, COMSIG_MOVABLE_MOVED, /mob/living/simple_animal/hostile/guardian.proc/OnMoved)
RegisterSignal(new_body, COMSIG_LIVING_REVIVE, /mob/living/simple_animal/hostile/guardian.proc/Reviveify)
to_chat(src, "You manifest into existence, as your master's soul appears in a new body!")
- new_body.verbs |= /mob/living/proc/guardian_comm
- new_body.verbs |= /mob/living/proc/guardian_recall
- new_body.verbs |= /mob/living/proc/guardian_reset
+ new_body.add_verb(/mob/living/proc/guardian_comm)
+ new_body.add_verb(/mob/living/proc/guardian_recall)
+ new_body.add_verb(/mob/living/proc/guardian_reset)
/mob/living/proc/guardian_comm()
set name = "Communicate"
@@ -682,7 +692,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
else
to_chat(src, "You decide not to reset [guardians.len > 1 ? "any of your guardians":"your guardian"].")
else
- verbs -= /mob/living/proc/guardian_reset
+ remove_verb(/mob/living/proc/guardian_reset)
////////parasite tracking/finding procs
diff --git a/code/modules/guardian/guardianbuilder.dm b/code/modules/guardian/guardianbuilder.dm
index 5452ea37026a8..36b9023edf615 100644
--- a/code/modules/guardian/guardianbuilder.dm
+++ b/code/modules/guardian/guardianbuilder.dm
@@ -34,7 +34,6 @@
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Guardian")
- ui.set_autoupdate(TRUE)
ui.open()
/datum/guardianbuilder/ui_data(mob/user)
@@ -83,7 +82,7 @@
cost = GA.cost,
icon = GA.ui_icon,
selected = istype(saved_stats.ability, ability),
- available = (points >= GA.cost) && GA.CanBuy(),
+ available = (points+saved_stats.ability?.cost >= GA.cost) && GA.CanBuy(),
path = "[ability]",
requiem = istype(GA, /datum/guardian_ability/major/special)
))
@@ -104,60 +103,73 @@
/datum/guardianbuilder/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
if(..() || used)
return
- calc_points()
switch(action)
if("name")
guardian_name = params["name"]
+ . = TRUE
if("set")
switch(params["name"])
if("Damage")
var/lvl = CLAMP(text2num(params["level"]), 1, 5)
if((points + (saved_stats.damage > 1 ? saved_stats.damage - 1 : 0)) >= lvl - 1 || lvl == 1)
saved_stats.damage = lvl
- . = TRUE
+ . = TRUE
if("Defense")
var/lvl = CLAMP(text2num(params["level"]), 1, 5)
if((points + (saved_stats.defense > 1 ? saved_stats.defense - 1 : 0)) >= lvl - 1 || lvl == 1)
saved_stats.defense = lvl
- . = TRUE
+ . = TRUE
if("Speed")
var/lvl = CLAMP(text2num(params["level"]), 1, 5)
if((points + (saved_stats.speed > 1 ? saved_stats.speed - 1 : 0)) >= lvl - 1 || lvl == 1)
saved_stats.speed = lvl
- . = TRUE
+ . = TRUE
if("Potential")
var/lvl = CLAMP(text2num(params["level"]), 1, 5)
if((points + (saved_stats.potential > 1 ? saved_stats.potential - 1 : 0)) >= lvl - 1 || lvl == 1)
saved_stats.potential = lvl
- . = TRUE
+ . = TRUE
if("Range")
var/lvl = CLAMP(text2num(params["level"]), 1, 5)
if((points + (saved_stats.range > 1 ? saved_stats.range - 1 : 0)) >= lvl - 1 || lvl == 1)
saved_stats.range = lvl
- . = TRUE
+ . = TRUE
if("color")
var/color = input(usr, "What would you like your guardian's color to be?", "Choose Your Color", "#ffffff") as color|null
if(color)
guardian_color = color
+ . = TRUE
if("clear_ability_major")
QDEL_NULL(saved_stats.ability)
+ . = TRUE
if("ability_major")
- var/ability = text2path(params["path"])
+ var/datum/guardian_ability/ability = text2path(params["path"])
var/list/types = allow_special ? (subtypesof(/datum/guardian_ability/major) - /datum/guardian_ability/major/special) : (subtypesof(/datum/guardian_ability/major) - typesof(/datum/guardian_ability/major/special))
if(ispath(ability))
if(saved_stats.ability && saved_stats.ability.type == ability)
QDEL_NULL(saved_stats.ability)
- else if(ability in types) // no nullspace narsie for you!
- QDEL_NULL(saved_stats.ability)
- saved_stats.ability = new ability
- saved_stats.ability.master_stats = saved_stats
+ . = TRUE
+ else if((ability in types) && (points + (saved_stats.ability?.cost || 0)) >= initial(ability.cost)) // no nullspace narsie for you!
+ var/datum/guardian_ability/new_ability = new ability
+ new_ability.master_stats = saved_stats
+ var/datum/guardian_ability/old_ability = saved_stats.ability
+ saved_stats.ability = null
+ if(new_ability.CanBuy(FALSE))
+ qdel(old_ability)
+ saved_stats.ability = new_ability
+ . = TRUE
+ else
+ qdel(new_ability)
+ saved_stats.ability = old_ability
if("ability_minor")
- var/ability = text2path(params["path"])
+ var/datum/guardian_ability/ability = text2path(params["path"])
if(ispath(ability) && (ability in subtypesof(/datum/guardian_ability/minor))) // no nullspace narsie for you!
if(saved_stats.HasMinorAbility(ability))
saved_stats.TakeMinorAbility(ability)
- else
+ . = TRUE
+ else if(points >= initial(ability.cost))
saved_stats.AddMinorAbility(ability)
+ . = TRUE
if("spawn")
. = spawn_guardian(usr)
if("reset")
@@ -167,8 +179,21 @@
if("ranged")
if(points >= 3)
saved_stats.ranged = TRUE
+ . = TRUE
if("melee")
saved_stats.ranged = FALSE
+ . = TRUE
+ if(.)
+ if(saved_stats.ability && !saved_stats.ability.CanBuy(FALSE)) // In case stat changes made some abilities invalid to have. Right now only Frenzy.
+ QDEL_NULL(saved_stats.ability)
+ var/list/datum/guardian_ability/minor/abilities_to_remove = list()
+ for(var/datum/guardian_ability/minor/minor in saved_stats.minor_abilities)
+ if(!minor.CanBuy(FALSE))
+ abilities_to_remove += minor
+ for(var/datum/guardian_ability/minor/minor in abilities_to_remove)
+ saved_stats.minor_abilities -= minor
+ qdel(minor)
+ calc_points()
/datum/guardianbuilder/proc/calc_points()
points = max_points
@@ -230,9 +255,9 @@
to_chat(user, "[G.real_name] has been caught!")
if(GUARDIAN_HIVE)
to_chat(user, "[G.real_name] has been created from the core!")
- user.verbs += /mob/living/proc/guardian_comm
- user.verbs += /mob/living/proc/guardian_recall
- user.verbs += /mob/living/proc/guardian_reset
+ user.add_verb(/mob/living/proc/guardian_comm)
+ user.add_verb(/mob/living/proc/guardian_recall)
+ user.add_verb(/mob/living/proc/guardian_reset)
return TRUE
else
to_chat(user, "[failure_message]")
diff --git a/code/modules/guardian/standarrow.dm b/code/modules/guardian/standarrow.dm
index d690bec334831..fe996818b9af3 100644
--- a/code/modules/guardian/standarrow.dm
+++ b/code/modules/guardian/standarrow.dm
@@ -45,17 +45,9 @@
user.dropItemToGround(src, TRUE)
forceMove(H)
if(iscarbon(M))
- sleep(15 SECONDS)
- if(prob(kill_chance))
- H.visible_message("[H] stares ahead, eyes full of fear, before collapsing lifelessly into ash, \the [src] falling out...")
- log_game("[key_name(H)] was killed by a stand arrow.")
- forceMove(H.drop_location())
- H.mind.no_cloning_at_all = TRUE
- H.adjustCloneLoss(500)
- H.dust(TRUE)
- in_use = FALSE
- else
- INVOKE_ASYNC(src, .proc/generate_stand, H)
+ in_use = TRUE
+ addtimer(CALLBACK(src, .proc/after_arrow_attack, H, kill_chance), 15 SECONDS)
+ in_use = FALSE
else if(isguardian(M))
INVOKE_ASYNC(src, .proc/requiem, M)
@@ -63,6 +55,17 @@
visible_message("[src] falls apart!")
qdel(src)
+/obj/item/stand_arrow/proc/after_arrow_attack(mob/living/carbon/H, var/kill_chance)
+ if(prob(kill_chance))
+ H.visible_message("[H] stares ahead, eyes full of fear, before collapsing lifelessly into ash, \the [src] falling out...")
+ log_game("[key_name(H)] was killed by a stand arrow.")
+ forceMove(H.drop_location())
+ H.mind.no_cloning_at_all = TRUE
+ H.adjustCloneLoss(500)
+ H.dust(TRUE)
+ else
+ INVOKE_ASYNC(src, .proc/generate_stand, H)
+
/obj/item/stand_arrow/proc/requiem(mob/living/simple_animal/hostile/guardian/G)
G.range = 255
G.transforming = TRUE
@@ -178,9 +181,9 @@
users[G] = TRUE
log_game("[key_name(H)] has summoned [key_name(G)], a holoparasite, via the stand arrow.")
to_chat(H, "[G.real_name] has been summoned!")
- H.verbs += /mob/living/proc/guardian_comm
- H.verbs += /mob/living/proc/guardian_recall
- H.verbs += /mob/living/proc/guardian_reset
+ H.add_verb(/mob/living/proc/guardian_comm)
+ H.add_verb(/mob/living/proc/guardian_recall)
+ H.add_verb(/mob/living/proc/guardian_reset)
uses--
in_use = FALSE
H.visible_message("\The [src] falls out of [H]!")
diff --git a/code/modules/holiday/easter.dm b/code/modules/holiday/easter.dm
index 9b21cde77f48f..6dbd6e92e38c0 100644
--- a/code/modules/holiday/easter.dm
+++ b/code/modules/holiday/easter.dm
@@ -7,7 +7,7 @@
earliest_start = 0 MINUTES
/datum/round_event/easter/announce(fake)
- priority_announce(pick("Hip-hop into Easter!","Find some Bunny's stash!","Today is National 'Hunt a Wabbit' Day.","Be kind, give Chocolate Eggs!"))
+ priority_announce(pick("Hip-hop into Easter!","Find some Bunny's stash!","Today is National 'Hunt a Wabbit' Day.","Be kind, give Chocolate Eggs!"), sound = SSstation.announcer.get_rand_alert_sound())
/datum/round_event_control/rabbitrelease
@@ -18,7 +18,7 @@
max_occurrences = 10
/datum/round_event/rabbitrelease/announce(fake)
- priority_announce("Unidentified furry objects detected coming aboard [station_name()]. Beware of Adorable-ness.", "Fluffy Alert", 'sound/ai/aliens.ogg')
+ priority_announce("Unidentified furry objects detected coming aboard [station_name()]. Beware of Adorable-ness.", "Fluffy Alert", ANNOUNCER_ALIENS)
/datum/round_event/rabbitrelease/start()
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index 098972034b1c6..6eb02740e6540 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -93,6 +93,19 @@
/datum/holiday/valentines/getStationPrefix()
return pick("Love","Amore","Single","Smootch","Hug")
+/// Garbage DAYYYYY
+/// Huh?.... NOOOO
+/// *GUNSHOT*
+/// AHHHGHHHHHHH
+/datum/holiday/garbageday
+ name = GARBAGEDAY
+ begin_day = 17
+ end_day = 17
+ begin_month = JUNE
+
+/datum/holiday/garbageday/getStationPrefix()
+ return pick("Trash","Janitor","Rubish","Bin")
+
/datum/holiday/birthday
name = "Birthday of Space Station 13"
begin_day = 16
@@ -281,6 +294,14 @@
/datum/holiday/friendship/greet()
return "Have a magical [name]!"
+/datum/holiday/anz
+ name = "ANZAC Day"
+ begin_day = 25
+ begin_month = APRIL
+
+/datum/holiday/anz/getStationPrefix()
+ return pick("Australian", "New Zealand", "Poppy", "Southern Cross")
+
/datum/holiday/beer
name = "Beer Day"
@@ -498,9 +519,9 @@ Since Ramadan is an entire month that lasts 29.5 days on average, the start and
/datum/holiday/xmas
name = CHRISTMAS
- begin_day = 22
+ begin_day = 24
begin_month = DECEMBER
- end_day = 27
+ end_day = 26
drone_hat = /obj/item/clothing/head/santa
/datum/holiday/xmas/greet()
diff --git a/code/modules/holodeck/area_copy.dm b/code/modules/holodeck/area_copy.dm
index dca4681137894..c94700b45e162 100644
--- a/code/modules/holodeck/area_copy.dm
+++ b/code/modules/holodeck/area_copy.dm
@@ -1,7 +1,10 @@
//Vars that will not be copied when using /DuplicateObject
GLOBAL_LIST_INIT(duplicate_forbidden_vars,list(
"tag", "datum_components", "area", "type", "loc", "locs", "vars", "parent", "parent_type", "verbs", "ckey", "key",
- "power_supply", "contents", "reagents", "stat", "x", "y", "z", "group", "atmos_adjacent_turfs", "comp_lookup"
+ "power_supply", "contents", "reagents", "stat", "x", "y", "z", "group", "atmos_adjacent_turfs", "comp_lookup",
+ "client_mobs_in_contents", "bodyparts", "internal_organs", "hand_bodyparts", "overlays_standing", "hud_list",
+ "actions", "AIStatus", "appearance", "managed_overlays", "managed_vis_overlays", "computer_id", "lastKnownIP", "implants",
+ "tgui_shared_states"
))
/proc/DuplicateObject(atom/original, perfectcopy = TRUE, sameloc, atom/newloc = null, nerf, holoitem)
@@ -20,7 +23,7 @@ GLOBAL_LIST_INIT(duplicate_forbidden_vars,list(
if(islist(original.vars[V]))
var/list/L = original.vars[V]
O.vars[V] = L.Copy()
- else if(istype(original.vars[V], /datum))
+ else if(istype(original.vars[V], /datum) || ismob(original.vars[V]))
continue // this would reference the original's object, that will break when it is used or deleted.
else
O.vars[V] = original.vars[V]
@@ -52,6 +55,11 @@ GLOBAL_LIST_INIT(duplicate_forbidden_vars,list(
contained_atom.flags_1 |= HOLOGRAM_1
if(M.circuit)
M.circuit.flags_1 |= HOLOGRAM_1
+
+ if(ismob(O)) //Overlays are carried over despite disallowing them, if a fix is found remove this.
+ var/mob/M = O
+ M.cut_overlays()
+ M.regenerate_icons()
return O
@@ -135,7 +143,5 @@ GLOBAL_LIST_INIT(duplicate_forbidden_vars,list(
if(toupdate.len)
for(var/turf/T1 in toupdate)
CALCULATE_ADJACENT_TURFS(T1)
- SSair.add_to_active(T1,1)
-
return copiedobjs
diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm
index e9803d7e575f0..eb6dfc51f1dd5 100644
--- a/code/modules/holodeck/computer.dm
+++ b/code/modules/holodeck/computer.dm
@@ -1,22 +1,30 @@
/*
- Holodeck Update
-
- The on-station holodeck area is of type [holodeck_type].
- All subtypes of [program_type] are loaded into the program cache or emag programs list.
- If init_program is null, a random program will be loaded on startup.
- If you don't wish this, set it to the offline program or another of your choosing.
-
- You can use this to add holodecks with minimal code:
- 1) Define new areas for the holodeck programs
- 2) Map them
- 3) Create a new control console that uses those areas
-
- Non-mapped areas should be skipped but you should probably comment them out anyway.
- The base of program_type will always be ignored; only subtypes will be loaded.
+Map Template Holodeck
+
+Holodeck finds the location of mapped_start_area and loads offline_program in it on LateInitialize. It then passes its program templates to Holodeck.js in the form of program_cache and emag_programs. when a user selects a program the
+ui calls load_program() with the id of the selected program.
+load_program() -> map_template/load() on map_template/holodeck.
+
+holodeck map templates:
+1. have an update_blacklist that doesnt allow placing on non holofloors (except for engine floors so you can repair it)
+2. has should_place_on_top = FALSE, so that the baseturfs list doesnt pull a kilostation oom crash
+3. has returns_created = TRUE, so that SSatoms gives the map template a list of spawned atoms
+all the fancy flags and shit are added to holodeck objects in finish_spawn()
+
+Easiest way to add new holodeck programs:
+1. Define new map template datums in code/modules/holodeck/holodeck_map_templates, make sure they have the access flags
+of the holodeck you want them to be able to load, for the onstation holodeck the flag is STATION_HOLODECK.
+2. Create the new map templates in _maps/templates (remember theyre 9x10, and make sure they have area/noop or else it will fuck with linked)
+all turfs in holodeck programs MUST be of type /turf/open/floor/holofloor, OR /turf/open/floor/engine, or they will block future programs!
+
+Note: if youre looking at holodeck code because you want to see how returns_created is handled so that templates return a list of atoms
+created from them: make sure you handle that list correctly! Either copy them by value and delete them or reference it and handle qdel'ing
+and clear when youre done! if you dont i will use :newspaper2: on you
*/
-#define HOLODECK_CD 25
-#define HOLODECK_DMG_CD 500
+#define HOLODECK_CD 2 SECONDS
+#define HOLODECK_DMG_CD 5 SECONDS
+
/obj/machinery/computer/holodeck
name = "holodeck control console"
@@ -25,71 +33,97 @@
idle_power_usage = 10
active_power_usage = 50
+ //new vars
+
+ ///what area type this holodeck loads into. linked turns into the nearest instance of this area
+ var/area/mapped_start_area = /area/holodeck/rec_center
+ ///the currently used map template
+ var/datum/map_template/holodeck/template
+ ///bottom left corner of the loading room, used for placing
+ var/turf/bottom_left
+
+ ///if TRUE the holodeck is busy spawning another simulation and should immediately stop loading the newest one
+ var/spawning_simulation = FALSE
+
+ //old vars
+
+ ///the area that this holodeck loads templates into, used for power and deleting holo objects that leave it
var/area/holodeck/linked
- var/area/holodeck/program
- var/area/holodeck/last_program
- var/area/offline_program = /area/holodeck/rec_center/offline
+ ///what program is loaded right now or is about to be loaded
+ var/program = "offline"
+ var/last_program
+
+ ///the default program loaded by this holodeck when spawned and when deactivated
+ var/offline_program = "offline"
+
+ ///stores all of the unrestricted holodeck map templates that this computer has access to
var/list/program_cache
+ ///stores all of the restricted holodeck map templates that this computer has access to
var/list/emag_programs
- // Splitting this up allows two holodecks of the same size
- // to use the same source patterns. Y'know, if you want to.
- var/holodeck_type = /area/holodeck/rec_center // locate(this) to get the target holodeck
- var/program_type = /area/holodeck/rec_center // subtypes of this (but not this itself) are loadable programs
+ ///subtypes of this (but not this itself) are loadable programs
+ var/program_type = /datum/map_template/holodeck
+ ///every holo object created by the holodeck goes in here to track it
+ var/list/spawned = list()
+ var/list/effects = list() //like above, but for holo effects
+
+ ///TRUE if the holodeck is using extra power because of a program, FALSE otherwise
var/active = FALSE
+ ///increases the holodeck cooldown if TRUE, causing the holodeck to take longer to allow loading new programs
var/damaged = FALSE
- var/list/spawned = list()
- var/list/effects = list()
- var/current_cd = 0
+
+ //creates the timer that determines if another program can be manually loaded
+ COOLDOWN_DECLARE(holodeck_cooldown)
/obj/machinery/computer/holodeck/Initialize(mapload)
..()
return INITIALIZE_HINT_LATELOAD
-/obj/machinery/computer/holodeck/LateInitialize()
- if(ispath(holodeck_type, /area))
- linked = pop(get_areas(holodeck_type, FALSE))
- if(ispath(offline_program, /area))
- offline_program = pop(get_areas(offline_program), FALSE)
+/obj/machinery/computer/holodeck/LateInitialize()//from here linked is populated and the program list is generated. its also set to load the offline program
+ linked = GLOB.areas_by_type[mapped_start_area]
+ bottom_left = locate(linked.x, linked.y, src.z)
+
+ var/area/computer_area = get_area(src)
+ if(istype(computer_area, /area/holodeck))
+ log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.")
+ qdel(src)
+ return
+
// the following is necessary for power reasons
- if(!linked || !offline_program)
+ if(!linked)
log_world("No matching holodeck area found")
qdel(src)
return
- var/area/AS = get_area(src)
- if(istype(AS, /area/holodeck))
- log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.")
+ else if (!offline_program)
+ stack_trace("Holodeck console created without an offline program")
qdel(src)
return
+
else
linked.linked = src
var/area/my_area = get_area(src)
if(my_area)
linked.power_usage = my_area.power_usage
else
- linked.power_usage = new /list(AREA_USAGE_LEN)
+ linked.power_usage = list(AREA_USAGE_LEN)
+ COOLDOWN_START(src, holodeck_cooldown, HOLODECK_CD)
generate_program_list()
- load_program(offline_program, FALSE, FALSE)
-
-/obj/machinery/computer/holodeck/Destroy()
- emergency_shutdown()
- if(linked)
- linked.linked = null
- linked.power_usage = new /list(AREA_USAGE_LEN)
- return ..()
-
-/obj/machinery/computer/holodeck/power_change()
- . = ..()
- toggle_power(!stat)
+ load_program(offline_program,TRUE)
-
-/obj/machinery/computer/holodeck/ui_state(mob/user)
- return GLOB.default_state
+///adds all programs that this holodeck has access to, and separates the restricted and unrestricted ones
+/obj/machinery/computer/holodeck/proc/generate_program_list()
+ for(var/typekey in subtypesof(program_type))
+ var/datum/map_template/holodeck/program = typekey
+ var/list/info_this = list("id" = initial(program.template_id), "name" = initial(program.name))
+ if(initial(program.restricted))
+ LAZYADD(emag_programs, list(info_this))
+ else
+ LAZYADD(program_cache, list(info_this))
/obj/machinery/computer/holodeck/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -104,220 +138,273 @@
if(obj_flags & EMAGGED)
data["emagged"] = TRUE
data["emag_programs"] = emag_programs
+ else
+ data["emagged"] = null
+ data["emag_programs"] = null
data["program"] = program
data["can_toggle_safety"] = issilicon(user) || IsAdminGhost(user)
-
return data
/obj/machinery/computer/holodeck/ui_act(action, params)
if(..())
return
- . = TRUE
switch(action)
if("load_program")
- var/program_to_load = text2path(params["type"])
- if(!ispath(program_to_load))
- return FALSE
- var/valid = FALSE
- var/list/checked = program_cache.Copy()
- if(obj_flags & EMAGGED)
- checked |= emag_programs
- for(var/prog in checked)
- var/list/P = prog
- if(P["type"] == program_to_load)
+ var/program_to_load = params["id"]
+
+ var/valid = FALSE //dont tell security about this
+
+ //checks if program_to_load is any one of the loadable programs, if it isnt then it rejects it
+ for(var/list/check_list as anything in program_cache)
+ if(check_list["id"] == program_to_load)
valid = TRUE
break
+ if(!valid && (obj_flags & EMAGGED))
+ for(var/list/check_list as anything in emag_programs)
+ if(check_list["id"] == program_to_load)
+ valid = TRUE
+ break
if(!valid)
- return FALSE
-
- var/area/A = locate(program_to_load) in GLOB.sortedAreas
- if(A)
- if(istype(A, /area/holodeck)) //Admins could technically load a non-holodeck area with some varediting
- var/area/holodeck/H = A
- if(H.restricted)
- message_admins("[key_name(usr)] is loading a restricted (and potentially dangerous) holodeck area: [H.name]")
- log_game("[key_name(usr)] is loading a restricted (and potentially dangerous) holodeck area: [H.name]")
- load_program(A)
+ return
+ //load the map_template that program_to_load represents
+ load_program(program_to_load)
+ . = TRUE
if("safety")
if((obj_flags & EMAGGED) && program)
emergency_shutdown()
- nerf(obj_flags & EMAGGED)
+ nerf(obj_flags & EMAGGED,FALSE)
obj_flags ^= EMAGGED
- say("Safeties restored. Restarting...")
+ say("Safeties reset. Restarting...")
+ . = TRUE
-/obj/machinery/computer/holodeck/process()
- if(damaged && prob(10))
- for(var/turf/T in linked)
- if(prob(5))
- do_sparks(2, 1, T)
- return
+///this is what makes the holodeck not spawn anything on broken tiles (space and non engine plating / non holofloors)
+/datum/map_template/holodeck/update_blacklist(turf/placement, list/input_blacklist)
+ for(var/turf/possible_blacklist as anything in get_affected_turfs(placement))
+ if (possible_blacklist.holodeck_compatible)
+ continue
+ input_blacklist += possible_blacklist
- if(!..() || !active)
+///loads the template whose id string it was given ("offline_program" loads datum/map_template/holodeck/offline)
+/obj/machinery/computer/holodeck/proc/load_program(map_id, force = FALSE, add_delay = TRUE)
+ if (program == map_id)
return
- if(!floorcheck())
- emergency_shutdown()
- damaged = TRUE
- for(var/mob/M in urange(10,src))
- M.show_message("The holodeck overloads!")
+ if (!is_operational())//load_program is called once with a timer (in toggle_power) we dont want this to load anything if its off
+ map_id = offline_program
+ force = TRUE
- for(var/turf/T in linked)
- if(prob(30))
- do_sparks(2, 1, T)
- T.ex_act(EXPLODE_LIGHT)
- T.hotspot_expose(1000,500,1)
+ if (!force && (!COOLDOWN_FINISHED(src, holodeck_cooldown) || spawning_simulation))
+ say("ERROR. Recalibrating projection apparatus.")
+ return
- if(!(obj_flags & EMAGGED))
- for(var/item in spawned)
- if(!(get_turf(item) in linked))
- derez(item, 0)
- for(var/e in effects)
- var/obj/effect/holodeck_effect/HE = e
- HE.tick()
+ if(spawning_simulation)
+ return
- active_power_usage = 50 + spawned.len * 3 + effects.len * 5
+ if (add_delay)
+ COOLDOWN_START(src, holodeck_cooldown, (damaged ? HOLODECK_CD + HOLODECK_DMG_CD : HOLODECK_CD))
+ if (damaged && floorcheck())
+ damaged = FALSE
+
+ spawning_simulation = TRUE
+ active = (map_id != offline_program)
+ use_power = active + IDLE_POWER_USE
+ program = map_id
+
+ //clear the items from the previous program
+ for(var/holo_atom in spawned)
+ derez(holo_atom)
+
+ for(var/obj/effect/holodeck_effect/holo_effect as anything in effects)
+ effects -= holo_effect
+ holo_effect.deactivate(src)
+
+ //makes sure that any time a holoturf is inside a baseturf list (e.g. if someone put a wall over it) its set to the OFFLINE turf
+ //so that you cant bring turfs from previous programs into other ones (like putting the plasma burn turf into lounge for example)
+ for(var/turf/closed/holo_turf in linked)
+ for(var/baseturf in holo_turf.baseturfs)
+ if(ispath(baseturf, /turf/open/floor/holofloor))
+ holo_turf.baseturfs -= baseturf
+ holo_turf.baseturfs += /turf/open/floor/holofloor/plating
+
+ template = SSmapping.holodeck_templates[map_id]
+ template.load(bottom_left) //this is what actually loads the holodeck simulation into the map
+
+ spawned = template.created_atoms //populate the spawned list with the atoms belonging to the holodeck
+
+ if(istype(template, /datum/map_template/holodeck/thunderdome1218) && !SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_MEDISIM])
+ say("Special note from \"1218 AD\" developer: I see you too are interested in the REAL dark ages of humanity! I've made this program also unlock some interesting shuttle designs on any communication console around. Have fun!")
+ SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_MEDISIM] = TRUE
-/obj/machinery/computer/holodeck/emag_act(mob/user)
- if(obj_flags & EMAGGED)
- return
- if(!LAZYLEN(emag_programs))
- to_chat(user, "[src] does not seem to have a card swipe port. It must be an inferior model.")
- return
- playsound(src, "sparks", 75, TRUE)
- obj_flags |= EMAGGED
- to_chat(user, "You vastly increase projector power and override the safety and security protocols.")
- say("Warning. Automatic shutoff and derezzing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.")
- log_game("[key_name(user)] emagged the Holodeck Control Console")
nerf(!(obj_flags & EMAGGED))
+ finish_spawn()
-/obj/machinery/computer/holodeck/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
+///finalizes objects in the spawned list
+/obj/machinery/computer/holodeck/proc/finish_spawn()
+ for(var/atom/holo_atom as anything in spawned)
+ if(QDELETED(holo_atom))
+ spawned -= holo_atom
+ continue
+
+ RegisterSignal(holo_atom, COMSIG_PARENT_PREQDELETED, .proc/remove_from_holo_lists)
+ holo_atom.flags_1 |= HOLOGRAM_1
+
+ if(isholoeffect(holo_atom))//activates holo effects and transfers them from the spawned list into the effects list
+ var/obj/effect/holodeck_effect/holo_effect = holo_atom
+ effects += holo_effect
+ spawned -= holo_effect
+ var/atom/holo_effect_product = holo_effect.activate(src)//change name
+ if(istype(holo_effect_product))
+ spawned += holo_effect_product // we want mobs or objects spawned via holoeffects to be tracked as objects
+ RegisterSignal(holo_effect_product, COMSIG_PARENT_PREQDELETED, .proc/remove_from_holo_lists)
+ if(islist(holo_effect_product))
+ for(var/atom/atom_product as anything in holo_effect_product)
+ RegisterSignal(atom_product, COMSIG_PARENT_PREQDELETED, .proc/remove_from_holo_lists)
+ continue
+
+ if(isobj(holo_atom))
+ var/obj/holo_object = holo_atom
+ holo_object.resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+
+ if(isstructure(holo_object))
+ holo_object.flags_1 |= NODECONSTRUCT_1
+ continue
+
+ if(ismachinery(holo_object))
+ var/obj/machinery/holo_machine = holo_object
+ holo_machine.flags_1 |= NODECONSTRUCT_1
+ holo_machine.power_change()
+
+ if(istype(holo_machine, /obj/machinery/button))
+ var/obj/machinery/button/holo_button = holo_machine
+ holo_button.setup_device()
+
+ spawning_simulation = FALSE
+
+///this qdels holoitems that should no longer exist for whatever reason
+/obj/machinery/computer/holodeck/proc/derez(atom/movable/holo_atom, silent = TRUE, forced = FALSE)
+ spawned -= holo_atom
+ if(!holo_atom)
return
- emergency_shutdown()
+ UnregisterSignal(holo_atom, COMSIG_PARENT_PREQDELETED)
+ var/turf/target_turf = get_turf(holo_atom)
+ for(var/atom/movable/atom_contents as anything in holo_atom) //make sure that things inside of a holoitem are moved outside before destroying it
+ atom_contents.forceMove(target_turf)
-/obj/machinery/computer/holodeck/ex_act(severity, target)
- emergency_shutdown()
- return ..()
+ if(!silent)
+ visible_message("[holo_atom] fades away!")
-/obj/machinery/computer/holodeck/blob_act(obj/structure/blob/B)
- emergency_shutdown()
- return ..()
+ qdel(holo_atom)
-/obj/machinery/computer/holodeck/proc/generate_program_list()
- for(var/typekey in subtypesof(program_type))
- var/area/holodeck/A = GLOB.areas_by_type[typekey]
- if(!A || !A.contents.len)
- continue
- var/list/info_this = list()
- info_this["name"] = A.name
- info_this["type"] = A.type
- if(A.restricted)
- LAZYADD(emag_programs, list(info_this))
- else
- LAZYADD(program_cache, list(info_this))
+/obj/machinery/computer/holodeck/proc/remove_from_holo_lists(datum/to_remove, _forced)
+ SIGNAL_HANDLER
+
+ spawned -= to_remove
+ UnregisterSignal(to_remove, COMSIG_PARENT_PREQDELETED)
+
+/obj/machinery/computer/holodeck/process(delta_time=2)
+ if(damaged && DT_PROB(10, delta_time))
+ for(var/turf/holo_turf in linked)
+ if(DT_PROB(5, delta_time))
+ do_sparks(2, 1, holo_turf)
+ return
+ . = ..()
+ if(!. || program == offline_program)//we dont need to scan the holodeck if the holodeck is offline
+ return
+
+ if(!floorcheck()) //if any turfs in the floor of the holodeck are broken
+ emergency_shutdown()
+ damaged = TRUE
+ visible_message("The holodeck overloads!")
+ for(var/turf/holo_turf in linked)
+ if(DT_PROB(30, delta_time))
+ do_sparks(2, 1, holo_turf)
+ SSexplosions.lowturf += holo_turf
+ holo_turf.hotspot_expose(1000,500,1)
+
+ if(!(obj_flags & EMAGGED))
+ for(var/item in spawned)
+ if(!(get_turf(item) in linked))
+ derez(item)
+ for(var/obj/effect/holodeck_effect/holo_effect as anything in effects)
+ holo_effect.tick()
+ active_power_usage = 50 + spawned.len * 3 + effects.len * 5
/obj/machinery/computer/holodeck/proc/toggle_power(toggleOn = FALSE)
if(active == toggleOn)
return
if(toggleOn)
- if(last_program && last_program != offline_program)
- addtimer(CALLBACK(src, .proc/load_program, last_program, TRUE), 25)
+ if(last_program && (last_program != offline_program))
+ addtimer(CALLBACK(src,.proc/load_program, last_program, TRUE), 25)
active = TRUE
else
last_program = program
load_program(offline_program, TRUE)
active = FALSE
+/obj/machinery/computer/holodeck/power_change()
+ . = ..()
+ INVOKE_ASYNC(src, .proc/toggle_power, !stat)
+
+///shuts down the holodeck and force loads the offline_program
/obj/machinery/computer/holodeck/proc/emergency_shutdown()
last_program = program
- load_program(offline_program, TRUE)
active = FALSE
+ load_program(offline_program, TRUE)
+ ui_update()
+///returns TRUE if the entire floor of the holodeck is intact, returns FALSE if any are broken
/obj/machinery/computer/holodeck/proc/floorcheck()
- for(var/turf/T in linked)
- if(!T.intact || isspaceturf(T))
+ for(var/turf/holo_floor in linked)
+ if(isspaceturf(holo_floor))
+ return FALSE
+ if(!holo_floor.intact)
return FALSE
return TRUE
-/obj/machinery/computer/holodeck/proc/nerf(active)
- for(var/obj/item/I in spawned)
- I.damtype = active ? STAMINA : initial(I.damtype)
- for(var/e in effects)
- var/obj/effect/holodeck_effect/HE = e
- HE.safety(active)
-
-/obj/machinery/computer/holodeck/proc/load_program(area/A, force = FALSE, add_delay = TRUE)
- if(!is_operational())
- A = offline_program
- force = TRUE
+///changes all weapons in the holodeck to do stamina damage if set
+/obj/machinery/computer/holodeck/proc/nerf(nerf_this, is_loading = TRUE)
+ if (!nerf_this && is_loading)
+ return
+ for(var/obj/item/to_be_nerfed in spawned)
+ to_be_nerfed.damtype = nerf_this ? STAMINA : initial(to_be_nerfed.damtype)
+ for(var/obj/effect/holodeck_effect/holo_effect as anything in effects)
+ holo_effect.safety(nerf_this)
- if(program == A)
+/obj/machinery/computer/holodeck/emag_act(mob/user)
+ if(obj_flags & EMAGGED)
return
- if(current_cd > world.time && !force)
- say("ERROR. Recalibrating projection apparatus.")
+ if(!LAZYLEN(emag_programs))
+ to_chat(user, "[src] does not seem to have a card swipe port. It must be an inferior model.")
return
- if(add_delay)
- current_cd = world.time + HOLODECK_CD
- if(damaged)
- current_cd += HOLODECK_DMG_CD
- active = (A != offline_program)
- use_power = active + IDLE_POWER_USE
-
- for(var/e in effects)
- var/obj/effect/holodeck_effect/HE = e
- HE.deactivate(src)
-
- for(var/item in spawned)
- derez(item, !force)
+ playsound(src, "sparks", 75, TRUE)
+ obj_flags |= EMAGGED
+ to_chat(user, "You vastly increase projector power and override the safety and security protocols.")
+ say("Warning. Automatic shutoff and derezzing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.")
+ log_game("[key_name(user)] emagged the Holodeck Control Console")
+ nerf(!(obj_flags & EMAGGED),FALSE)
+ ui_update()
- program = A
- // note nerfing does not yet work on guns, should
- // should also remove/limit/filter reagents?
- // this is an exercise left to others I'm afraid. -Sayu
- spawned = A.copy_contents_to(linked, 1, nerf_weapons = !(obj_flags & EMAGGED))
- for(var/obj/machinery/M in spawned)
- M.flags_1 |= NODECONSTRUCT_1
- for(var/obj/structure/S in spawned)
- S.flags_1 |= NODECONSTRUCT_1
- effects = list()
+/obj/machinery/computer/holodeck/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ emergency_shutdown()
- addtimer(CALLBACK(src, .proc/finish_spawn), 30)
+/obj/machinery/computer/holodeck/ex_act(severity, target)
+ emergency_shutdown()
+ return ..()
-/obj/machinery/computer/holodeck/proc/finish_spawn()
- var/list/added = list()
- for(var/obj/effect/holodeck_effect/HE in spawned)
- effects += HE
- spawned -= HE
- var/atom/x = HE.activate(src)
- if(istype(x) || islist(x))
- spawned += x // holocarp are not forever
- added += x
- for(var/obj/machinery/M in added)
- M.flags_1 |= NODECONSTRUCT_1
- for(var/obj/structure/S in added)
- S.flags_1 |= NODECONSTRUCT_1
-
-/obj/machinery/computer/holodeck/proc/derez(obj/O, silent = TRUE, forced = FALSE)
- // Emagging a machine creates an anomaly in the derez systems.
- if(O && (obj_flags & EMAGGED) && !stat && !forced)
- if((ismob(O) || ismob(O.loc)) && prob(50))
- addtimer(CALLBACK(src, .proc/derez, O, silent), 50) // may last a disturbingly long time
- return
-
- spawned -= O
- if(!O)
- return
- var/turf/T = get_turf(O)
- for(var/atom/movable/AM in O) // these should be derezed if they were generated
- AM.forceMove(T)
- if(ismob(AM))
- silent = FALSE // otherwise make sure they are dropped
+/obj/machinery/computer/holodeck/Destroy()
+ emergency_shutdown()
+ if(linked)
+ linked.linked = null
+ linked.power_usage = list(AREA_USAGE_LEN)
+ return ..()
- if(!silent)
- visible_message("[O] fades away!")
- qdel(O)
+/obj/machinery/computer/holodeck/blob_act(obj/structure/blob/B)
+ emergency_shutdown()
+ return ..()
#undef HOLODECK_CD
#undef HOLODECK_DMG_CD
diff --git a/code/modules/holodeck/holo_effect.dm b/code/modules/holodeck/holo_effect.dm
index 7d39ad8fa96d3..adfe9c96b2f1a 100644
--- a/code/modules/holodeck/holo_effect.dm
+++ b/code/modules/holodeck/holo_effect.dm
@@ -28,31 +28,36 @@
/obj/effect/holodeck_effect/cards
icon = 'icons/obj/toy.dmi'
icon_state = "deck_nanotrasen_full"
- var/obj/item/toy/cards/deck/D
+ var/obj/item/toy/cards/deck/deck
/obj/effect/holodeck_effect/cards/activate(var/obj/machinery/computer/holodeck/HC)
- D = new(loc)
+ deck = new(loc)
safety(!(HC.obj_flags & EMAGGED))
- D.holo = HC
- return D
+ deck.holo = HC
+ RegisterSignal(deck, COMSIG_PARENT_QDELETING, .proc/handle_card_delete)
+ return deck
+
+/obj/effect/holodeck_effect/cards/proc/handle_card_delete(datum/source)
+ SIGNAL_HANDLER
+ deck = null
/obj/effect/holodeck_effect/cards/safety(active)
- if(!D)
+ if(!deck)
return
if(active)
- D.card_hitsound = null
- D.card_force = 0
- D.card_throwforce = 0
- D.card_throw_speed = 3
- D.card_throw_range = 7
- D.card_attack_verb = list("attacked")
+ deck.card_hitsound = null
+ deck.card_force = 0
+ deck.card_throwforce = 0
+ deck.card_throw_speed = 3
+ deck.card_throw_range = 7
+ deck.card_attack_verb = list("attacked")
else
- D.card_hitsound = 'sound/weapons/bladeslice.ogg'
- D.card_force = 5
- D.card_throwforce = 10
- D.card_throw_speed = 3
- D.card_throw_range = 7
- D.card_attack_verb = list("attacked", "sliced", "diced", "slashed", "cut")
+ deck.card_hitsound = 'sound/weapons/bladeslice.ogg'
+ deck.card_force = 5
+ deck.card_throwforce = 10
+ deck.card_throw_speed = 3
+ deck.card_throw_range = 7
+ deck.card_attack_verb = list("attacked", "sliced", "diced", "slashed", "cut")
/obj/effect/holodeck_effect/sparks/activate(var/obj/machinery/computer/holodeck/HC)
@@ -61,31 +66,44 @@
var/datum/effect_system/spark_spread/s = new
s.set_up(3, 1, T)
s.start()
- T.temperature = 5000
+ T.set_temperature(5000)
T.hotspot_expose(50000,50000,1)
+/obj/effect/holodeck_effect/random_book
+
+/obj/effect/holodeck_effect/random_book/activate(obj/machinery/computer/holodeck/father_holodeck)
+ var/static/banned_books = list(/obj/item/book/manual/random, /obj/item/book/manual/nuclear, /obj/item/book/manual/wiki)
+ var/newtype = pick(subtypesof(/obj/item/book/manual) - banned_books)
+ var/obj/item/book/manual/to_spawn = new newtype(loc)
+ to_spawn.flags_1 |= (HOLOGRAM_1 | NODECONSTRUCT_1)
+ return to_spawn
/obj/effect/holodeck_effect/mobspawner
var/mobtype = /mob/living/simple_animal/hostile/carp/holocarp
- var/mob/mob = null
+ var/mob/our_mob = null
/obj/effect/holodeck_effect/mobspawner/activate(var/obj/machinery/computer/holodeck/HC)
if(islist(mobtype))
mobtype = pick(mobtype)
- mob = new mobtype(loc)
- mob.flags_1 |= HOLOGRAM_1
+ our_mob = new mobtype(loc)
+ our_mob.flags_1 |= HOLOGRAM_1
// these vars are not really standardized but all would theoretically create stuff on death
- for(var/v in list("butcher_results","corpse","weapon1","weapon2","blood_volume") & mob.vars)
- mob.vars[v] = null
- return mob
+ for(var/v in list("butcher_results","corpse","weapon1","weapon2","blood_volume") & our_mob.vars)
+ our_mob.vars[v] = null
+ RegisterSignal(our_mob, COMSIG_PARENT_QDELETING, .proc/handle_mob_delete)
+ return our_mob
/obj/effect/holodeck_effect/mobspawner/deactivate(var/obj/machinery/computer/holodeck/HC)
- if(mob)
- HC.derez(mob)
+ if(our_mob)
+ HC.derez(our_mob)
qdel(src)
+/obj/effect/holodeck_effect/mobspawner/proc/handle_mob_delete(datum/source)
+ SIGNAL_HANDLER
+ our_mob = null
+
/obj/effect/holodeck_effect/mobspawner/pet
mobtype = list(
/mob/living/simple_animal/butterfly, /mob/living/simple_animal/chick/holo,
@@ -101,7 +119,7 @@
/obj/effect/holodeck_effect/mobspawner/penguin
mobtype = /mob/living/simple_animal/pet/penguin/emperor
-
+
/obj/effect/holodeck_effect/mobspawner/penguin/Initialize()
if(prob(1))
mobtype = /mob/living/simple_animal/pet/penguin/emperor/shamebrero
@@ -109,3 +127,21 @@
/obj/effect/holodeck_effect/mobspawner/penguin_baby
mobtype = /mob/living/simple_animal/pet/penguin/baby
+
+/obj/effect/holodeck_effect/mobspawner/cat
+ mobtype = /mob/living/simple_animal/pet/cat
+
+/obj/effect/holodeck_effect/mobspawner/butterfly
+ mobtype = /mob/living/simple_animal/butterfly
+
+/obj/effect/holodeck_effect/mobspawner/clown
+ mobtype = list (/mob/living/simple_animal/hostile/retaliate/clown = 10,
+ /mob/living/simple_animal/hostile/retaliate/clown/banana = 6, /mob/living/simple_animal/hostile/retaliate/clown/honkling = 6,
+ /mob/living/simple_animal/hostile/retaliate/clown/fleshclown = 3, /mob/living/simple_animal/hostile/retaliate/clown/longface = 3,
+ /mob/living/simple_animal/hostile/retaliate/clown/mutant = 1, /mob/living/simple_animal/hostile/retaliate/clown/mutant/blob = 1)
+
+/obj/effect/holodeck_effect/mobspawner/psycho
+ mobtype = list (/mob/living/simple_animal/hostile/psycho/regular = 9,
+ /mob/living/simple_animal/hostile/psycho/muzzle = 3,
+ /mob/living/simple_animal/hostile/psycho/fast = 3,
+ /mob/living/simple_animal/hostile/psycho/trap = 1)
diff --git a/code/modules/holodeck/holodeck_map_templates.dm b/code/modules/holodeck/holodeck_map_templates.dm
new file mode 100644
index 0000000000000..f687d3e2fd6ba
--- /dev/null
+++ b/code/modules/holodeck/holodeck_map_templates.dm
@@ -0,0 +1,165 @@
+// --------------------
+// -- HOLODECK TEMPLATES --
+// --------------------
+
+/datum/map_template/holodeck
+ var/template_id = "id"
+ var/restricted = FALSE
+ var/datum/parsed_map/lastparsed
+
+ should_place_on_top = FALSE
+ returns_created_atoms = TRUE
+ keep_cached_map = TRUE
+
+ var/obj/machinery/computer/holodeck/linked
+
+/datum/map_template/holodeck/offline
+ name = "Holodeck - Offline"
+ template_id = "offline"
+ mappath = "_maps/holodeck/offline.dmm"
+
+/datum/map_template/holodeck/emptycourt
+ name = "Holodeck - Empty Court"
+ template_id = "emptycourt"
+ mappath = "_maps/holodeck/emptycourt.dmm"
+
+/datum/map_template/holodeck/dodgeball
+ name = "Holodeck - Dodgeball Court"
+ template_id = "dodgeball"
+ mappath = "_maps/holodeck/dodgeball.dmm"
+
+/datum/map_template/holodeck/basketball
+ name = "Holodeck - Basketball Court"
+ template_id = "basketball"
+ mappath = "_maps/holodeck/basketball.dmm"
+
+/datum/map_template/holodeck/thunderdome
+ name = "Holodeck - Thunderdome Arena"
+ template_id = "thunderdome"
+ mappath = "_maps/holodeck/thunderdome.dmm"
+
+/datum/map_template/holodeck/beach
+ name = "Holodeck - Beach"
+ template_id = "beach"
+ mappath = "_maps/holodeck/beach.dmm"
+
+/datum/map_template/holodeck/lounge
+ name = "Holodeck - Lounge"
+ template_id = "lounge"
+ mappath = "_maps/holodeck/lounge.dmm"
+
+/datum/map_template/holodeck/petpark
+ name = "Holodeck - Pet Park"
+ template_id = "petpark"
+ mappath = "_maps/holodeck/petpark.dmm"
+
+/datum/map_template/holodeck/firingrange
+ name = "Holodeck - Firing Range"
+ template_id = "firingrange"
+ mappath = "_maps/holodeck/firingrange.dmm"
+
+/datum/map_template/holodeck/anime_school
+ name = "Holodeck - Anime School"
+ template_id = "animeschool"
+ mappath = "_maps/holodeck/animeschool.dmm"
+
+/datum/map_template/holodeck/chapelcourt
+ name = "Holodeck - Chapel Courtroom"
+ template_id = "chapelcourt"
+ mappath = "_maps/holodeck/chapelcourt.dmm"
+
+/datum/map_template/holodeck/spacechess
+ name = "Holodeck - Space Chess"
+ template_id = "spacechess"
+ mappath = "_maps/holodeck/spacechess.dmm"
+
+/datum/map_template/holodeck/spacecheckers
+ name = "Holodeck - Space Checkers"
+ template_id = "spacecheckers"
+ mappath = "_maps/holodeck/spacecheckers.dmm"
+
+/datum/map_template/holodeck/kobayashi
+ name = "Holodeck - Kobayashi Maru"
+ template_id = "kobayashi"
+ mappath = "_maps/holodeck/kobayashi.dmm"
+
+/datum/map_template/holodeck/winterwonderland
+ name = "Holodeck - Winter Wonderland"
+ template_id = "winterwonderland"
+ mappath = "_maps/holodeck/winterwonderland.dmm"
+
+/datum/map_template/holodeck/photobooth
+ name = "Holodeck - Photobooth"
+ template_id = "photobooth"
+ mappath = "_maps/holodeck/photobooth.dmm"
+
+/datum/map_template/holodeck/skatepark
+ name = "Holodeck - Skatepark"
+ template_id = "skatepark"
+ mappath = "_maps/holodeck/skatepark.dmm"
+
+/datum/map_template/holodeck/teahouse
+ name = "Holodeck - Japanese Tea House"
+ template_id = "holodeck_teahouse"
+ mappath = "_maps/templates/holodeck_teahouse.dmm"
+
+/datum/map_template/holodeck/kitchen
+ name = "Holodeck - Holo-Kitchen"
+ template_id = "holodeck_kitchen"
+ mappath = "_maps/templates/holodeck_kitchen.dmm"
+
+//bad evil no good programs
+
+/datum/map_template/holodeck/medicalsim
+ name = "Holodeck - Emergency Medical"
+ template_id = "medicalsim"
+ mappath = "_maps/holodeck/medicalsim.dmm"
+ restricted = TRUE
+
+/datum/map_template/holodeck/thunderdome1218
+ name = "Holodeck - 1218 AD"
+ template_id = "thunderdome1218"
+ mappath = "_maps/holodeck/thunderdome1218.dmm"
+ restricted = TRUE
+
+/datum/map_template/holodeck/burntest
+ name = "Holodeck - Atmospheric Burn Test"
+ template_id = "burntest"
+ mappath = "_maps/holodeck/burntest.dmm"
+ restricted = TRUE
+
+/datum/map_template/holodeck/wildlifesim
+ name = "Holodeck - Wildlife Simulation"
+ template_id = "wildlifesim"
+ mappath = "_maps/holodeck/wildlifesim.dmm"
+ restricted = TRUE
+
+/datum/map_template/holodeck/holdoutbunker
+ name = "Holodeck - Holdout Bunker"
+ template_id = "holdoutbunker"
+ mappath = "_maps/holodeck/holdoutbunker.dmm"
+ restricted = TRUE
+
+/datum/map_template/holodeck/anthophillia
+ name = "Holodeck - Anthophillia"
+ template_id = "anthophillia"
+ mappath = "_maps/holodeck/anthophillia.dmm"
+ restricted = TRUE
+
+/datum/map_template/holodeck/refuelingstation
+ name = "Holodeck - Refueling Station"
+ template_id = "refuelingstation"
+ mappath = "_maps/holodeck/refuelingstation.dmm"
+ restricted = TRUE
+
+/datum/map_template/holodeck/asylum
+ name = "Holodeck - Asylum"
+ template_id = "holodeck_asylum"
+ mappath = "_maps/templates/holodeck_asylum.dmm"
+ restricted = TRUE
+
+/datum/map_template/holodeck/clownworld
+ name = "Holodeck - Clown World"
+ template_id = "holodeck_clownworld"
+ mappath = "_maps/templates/holodeck_clownworld.dmm"
+ restricted = TRUE
diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm
index 27685f242b385..9bcf8513db529 100644
--- a/code/modules/holodeck/items.dm
+++ b/code/modules/holodeck/items.dm
@@ -227,3 +227,20 @@
/obj/item/paper/fluff/holodeck/disclaimer
name = "Holodeck Disclaimer"
info = "Bruises sustained in the holodeck can be healed simply by sleeping."
+
+/obj/vehicle/ridden/scooter/skateboard/pro/holodeck
+ name = "holographic skateboard"
+ desc = "A holographic copy of the EightO brand professional skateboard."
+ instability = 6
+
+/obj/vehicle/ridden/scooter/skateboard/pro/holodeck/screwdriver_act(mob/living/user, obj/item/I)
+ return FALSE
+
+/obj/vehicle/ridden/scooter/skateboard/pro/holodeck/pick_up_board() //picking up normal skateboards spawned in the holodeck gets rid of the holo flag, now you cant pick them up.
+ return
+
+/obj/vehicle/ridden/scooter/skateboard/pro/holodeck/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/stack/rods))
+ return
+ else
+ return ..()
diff --git a/code/modules/holodeck/turfs.dm b/code/modules/holodeck/turfs.dm
index f8b9e3587e52e..a482ed877febf 100644
--- a/code/modules/holodeck/turfs.dm
+++ b/code/modules/holodeck/turfs.dm
@@ -1,7 +1,9 @@
/turf/open/floor/holofloor
icon_state = "floor"
+ holodeck_compatible = TRUE
thermal_conductivity = 0
flags_1 = NONE
+ var/direction = SOUTH
/turf/open/floor/holofloor/attackby(obj/item/I, mob/living/user)
return // HOLOFLOOR DOES NOT GIVE A FUCK
@@ -19,6 +21,30 @@
name = "holodeck projector floor"
icon_state = "engine"
+/turf/open/floor/holofloor/chapel
+ name = "chapel floor"
+ icon_state = "chapel"
+
+/turf/open/floor/holofloor/chapel/bottom_left
+ direction = WEST
+
+/turf/open/floor/holofloor/chapel/top_right
+ direction = EAST
+
+/turf/open/floor/holofloor/chapel/bottom_right
+
+/turf/open/floor/holofloor/chapel/top_left
+ direction = NORTH
+
+/turf/open/floor/holofloor/chapel/Initialize(mapload)
+ . = ..()
+ if (direction != SOUTH)
+ setDir(direction)
+
+/turf/open/floor/holofloor/white
+ name = "white floor"
+ icon_state = "white"
+
/turf/open/floor/holofloor/plating/burnmix
name = "burn-mix floor"
initial_gas_mix = BURNMIX_ATMOS
@@ -142,3 +168,14 @@
icon = 'icons/turf/floors.dmi'
icon_state = "asteroid"
tiled_dirt = FALSE
+
+/turf/open/floor/holofloor/dark
+ icon_state = "darkfull"
+
+/turf/open/floor/holofloor/clown
+ icon_state = "bananium"
+
+/turf/open/floor/holofloor/white
+ name = "white floor"
+ desc = "A tile in a pure white color."
+ icon_state = "pure_white"
diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm
index 38dd8b48e0029..8dbdc997c482a 100644
--- a/code/modules/hydroponics/beekeeping/beebox.dm
+++ b/code/modules/hydroponics/beekeeping/beebox.dm
@@ -16,9 +16,7 @@
/mob/living/carbon/human/bee_friendly()
- if(dna?.species?.id == "pod") //bees pollinate plants, duh.
- return 1
- if (wear_suit && head && istype(wear_suit, /obj/item/clothing) && istype(head, /obj/item/clothing))
+ if (wear_suit && head && isclothing(wear_suit) && isclothing(head))
var/obj/item/clothing/CS = wear_suit
var/obj/item/clothing/CH = head
if (CS.clothing_flags & CH.clothing_flags & THICKMATERIAL)
@@ -208,7 +206,7 @@
continue
if(B.loc == src)
B.forceMove(drop_location())
- B.target = user
+ B.GiveTarget(user)
bees = TRUE
if(bees)
visible_message("[user] disturbs the bees!")
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index f0342c7c847b3..e4b2483355803 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -32,13 +32,20 @@
/obj/machinery/biogenerator/contents_explosion(severity, target)
..()
if(beaker)
- beaker.ex_act(severity, target)
+ switch(severity)
+ if(EXPLODE_DEVASTATE)
+ SSexplosions.high_mov_atom += beaker
+ if(EXPLODE_HEAVY)
+ SSexplosions.med_mov_atom += beaker
+ if(EXPLODE_LIGHT)
+ SSexplosions.low_mov_atom += beaker
/obj/machinery/biogenerator/handle_atom_del(atom/A)
..()
if(A == beaker)
beaker = null
update_icon()
+ ui_update()
/obj/machinery/biogenerator/RefreshParts()
var/E = 0
@@ -56,7 +63,7 @@
/obj/machinery/biogenerator/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
- . += "The status display reads: Productivity at [productivity*100]%.Matter consumption reduced by [(efficiency*25)-25]%. Machine can hold up to [max_items] pieces of produce." + . += "The status display reads: Productivity at [productivity*100]%. Matter consumption reduced by [(efficiency*25)-25]%. Machine can hold up to [max_items] pieces of produce." /obj/machinery/biogenerator/on_reagent_change(changetype) //When the reagents change, change the icon as well. update_icon() @@ -85,6 +92,7 @@ var/obj/item/reagent_containers/glass/B = beaker B.forceMove(drop_location()) beaker = null + ui_update() update_icon() return @@ -102,6 +110,7 @@ beaker = O to_chat(user, "You add the container to the machine.") update_icon() + ui_update() else to_chat(user, "Close the maintenance panel first.") return @@ -125,6 +134,7 @@ to_chat(user, "You empty the plant bag into the biogenerator, filling it to its capacity.") else to_chat(user, "You fill the biogenerator to its capacity.") + ui_update() return TRUE //no afterattack else if(istype(O, /obj/item/reagent_containers/food/snacks/grown)) @@ -136,24 +146,26 @@ else if(user.transferItemToLoc(O, src)) to_chat(user, "You put [O.name] in [src.name]") + ui_update() return TRUE //no afterattack else if (istype(O, /obj/item/disk/design_disk)) user.visible_message("[user] begins to load \the [O] in \the [src]...", "You begin to load a design from \the [O]...", "You hear the chatter of a floppy drive.") processing = TRUE + ui_update() var/obj/item/disk/design_disk/D = O if(do_after(user, 10, target = src)) for(var/B in D.blueprints) if(B) stored_research.add_design(B) processing = FALSE + ui_update() return TRUE else to_chat(user, "You cannot put this in [src.name]!") /obj/machinery/biogenerator/AltClick(mob/living/user) - . = ..() if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && can_interact(user)) detach(user) @@ -176,16 +188,20 @@ S += 5 if(I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) < 0.1) points += 1 * productivity + ui_update() else points += I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) * 10 * productivity + ui_update() qdel(I) if(S) processing = TRUE + ui_update() update_icon() playsound(loc, 'sound/machines/blender.ogg', 50, TRUE) use_power(S * 30) sleep(S + 15 / productivity) processing = FALSE + ui_update() update_icon() /obj/machinery/biogenerator/proc/check_cost(list/materials, multiplier = 1, remove_points = TRUE) @@ -196,6 +212,7 @@ else if(remove_points) points -= materials[getmaterialref(/datum/material/biomass)]*multiplier/efficiency + ui_update() update_icon() return TRUE @@ -245,6 +262,7 @@ beaker.forceMove(drop_location()) beaker = null update_icon() + ui_update() /obj/machinery/biogenerator/ui_status(mob/user) if(stat & BROKEN || panel_open) diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm index cdcca86bd5e3e..058a65e57dba7 100644 --- a/code/modules/hydroponics/gene_modder.dm +++ b/code/modules/hydroponics/gene_modder.dm @@ -15,7 +15,7 @@ var/list/trait_genes = list() var/datum/plant_gene/target - var/operation = "" + var/operation = null var/max_potency = 50 // See RefreshParts() for how these work var/max_yield = 2 var/min_production = 12 @@ -23,6 +23,8 @@ var/min_wchance = 67 var/min_wrate = 10 + var/skip_confirmation = FALSE + /obj/machinery/plantgenes/RefreshParts() // Comments represent the max you can set per tier, respectively. seeds.dm [219] clamps these for us but we don't want to mislead the viewer. for(var/obj/item/stock_parts/manipulator/M in component_parts) if(M.rating > 3) @@ -52,6 +54,7 @@ max_endurance = 100 min_wchance = 0 min_wrate = 0 + ui_update() /obj/machinery/plantgenes/update_icon() ..() @@ -97,163 +100,148 @@ else ..() -/obj/machinery/plantgenes/ui_interact(mob/user) - . = ..() - if(!user) +/obj/machinery/plantgenes/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PlantDNAManipulator") + ui.open() + +/obj/machinery/plantgenes/ui_data(mob/user) + var/list/data = list() + . = data + + data["seed"] = seed?.name + data["disk"] = null + data["disk_gene"] = null + data["disk_readonly"] = FALSE + data["disk_canadd"] = TRUE + + data["operation"] = operation + data["operation_target"] = build_gene(target) + + if(disk) + data["disk_readonly"] = disk.read_only + if(disk.gene) + data["disk_gene"] = build_gene(disk.gene) + data["disk"] = disk.gene.get_name() + if(seed) + data["disk_canadd"] = disk.gene.can_add(seed) + else + data["disk"] = "Empty disk" + if(disk.read_only) + data["disk"] += " (RO)" + + data["core_genes"] = build_gene_list(core_genes, /datum/plant_gene/core) + data["reagent_genes"] = build_gene_list(reagent_genes, /datum/plant_gene/reagent) + data["trait_genes"] = build_gene_list(trait_genes, /datum/plant_gene/trait) + + data["machine_stats"] = build_machine_stats() + data["skip_confirmation"] = skip_confirmation + +/obj/machinery/plantgenes/proc/build_machine_stats() + var/list/L = list() + . = L + + L["potency"] = list("max", max_potency) + L["yield"] = list("max", max_yield) + L["production speed"] = list("min", min_production) + L["endurance"] = list("max", max_endurance) + L["lifespan"] = list("max", max_endurance) + L["weed growth rate"] = list("min", min_wrate) + L["weed vulnerability"] = list("min", min_wchance) + +/obj/machinery/plantgenes/proc/build_gene_list(list/genes, filter_type) + var/list/L = list() + . = L + + for(var/datum/plant_gene/gene in genes) + L += list(build_gene(gene, filter_type)) + +/obj/machinery/plantgenes/proc/get_gene_id(datum/plant_gene/gene) + if(istype(gene, /datum/plant_gene/core)) + var/datum/plant_gene/core/core_gene = gene + return "core[core_gene.type]" + if(istype(gene, /datum/plant_gene/reagent)) + var/datum/plant_gene/reagent/reagent_gene = gene + return "reagent[reagent_gene.reagent_id]" + if(istype(gene, /datum/plant_gene/trait)) + var/datum/plant_gene/trait/trait_gene = gene + return "trait[trait_gene.type]" + + return "unknown/[gene.name]" + +/obj/machinery/plantgenes/proc/build_gene(datum/plant_gene/gene, filter_type) + if(!gene) return - var/datum/browser/popup = new(user, "plantdna", "Plant DNA Manipulator", 450, 600) - if(!(in_range(src, user) || issilicon(user))) - popup.close() + if(filter_type && !istype(gene, filter_type)) return - var/dat = "" + var/list/L = list() + . = L - if(operation) - if(!seed || (!target && operation != "insert")) - operation = "" - target = null - interact(user) - return - if((operation == "replace" || operation == "insert") && (!disk || !disk.gene)) - operation = "" - target = null - interact(user) - return + L["name"] = gene.get_name() - dat += " Confirm OperationAre you sure you want to [operation] "
- switch(operation)
- if("remove")
- dat += "[target.get_name()] gene from \the [seed]? "
- popup.set_content(dat)
- popup.open()
- return
+ L["extractable"] = gene.mutability_flags & PLANT_GENE_EXTRACTABLE
+ L["removable"] = gene.mutability_flags & PLANT_GENE_REMOVABLE
- dat+= "" - if("extract") - dat += "[target.get_name()] gene from \the [seed]? " - dat += "The sample will be destroyed in process!" - if(istype(target, /datum/plant_gene/core)) - var/datum/plant_gene/core/gene = target - if(istype(target, /datum/plant_gene/core/potency)) - if(gene.value > max_potency) - dat += " This device's extraction capabilities are currently limited to [max_potency] potency. " - dat += "Target gene will be degraded to [max_potency] potency on extraction." - else if(istype(target, /datum/plant_gene/core/lifespan)) - if(gene.value > max_endurance) - dat += " This device's extraction capabilities are currently limited to [max_endurance] lifespan. " - dat += "Target gene will be degraded to [max_endurance] Lifespan on extraction." - else if(istype(target, /datum/plant_gene/core/endurance)) - if(gene.value > max_endurance) - dat += " This device's extraction capabilities are currently limited to [max_endurance] endurance. " - dat += "Target gene will be degraded to [max_endurance] endurance on extraction." - else if(istype(target, /datum/plant_gene/core/yield)) - if(gene.value > max_yield) - dat += " This device's extraction capabilities are currently limited to [max_yield] yield. " - dat += "Target gene will be degraded to [max_yield] yield on extraction." - else if(istype(target, /datum/plant_gene/core/production)) - if(gene.value < min_production) - dat += " This device's extraction capabilities are currently limited to [min_production] production. " - dat += "Target gene will be degraded to [min_production] production on extraction." - else if(istype(target, /datum/plant_gene/core/weed_rate)) - if(gene.value < min_wrate) - dat += " This device's extraction capabilities are currently limited to [min_wrate] weed rate. " - dat += "Target gene will be degraded to [min_wrate] weed rate on extraction." - else if(istype(target, /datum/plant_gene/core/weed_chance)) - if(gene.value < min_wchance) - dat += " This device's extraction capabilities are currently limited to [min_wchance] weed chance. " - dat += "Target gene will be degraded to [min_wchance] weed chance on extraction." - - if("replace") - dat += "[target.get_name()] gene with [disk.gene.get_name()]? " - if("insert") - dat += "[disk.gene.get_name()] gene into \the [seed]? " - dat += " "
+ if(istype(gene, /datum/plant_gene/core))
+ var/datum/plant_gene/core/core_gene = gene
- dat += " "
+ L["type"] = "reagent"
+ L["id"] = get_gene_id(gene)
+ L["rate"] = reagent_gene.rate
- if(seed)
- var/can_insert = disk && disk.gene && disk.gene.can_add(seed)
- var/can_extract = disk && !disk.read_only
+ if(istype(gene, /datum/plant_gene/trait))
+ var/datum/plant_gene/trait/trait_gene = gene
- dat += "Plant Sample: Core Genes
Content Genes"
- if(reagent_genes.len)
- dat += " "
- if(can_insert && istype(disk.gene, /datum/plant_gene/reagent))
- dat += "Insert: [disk.gene.get_name()]"
-
- dat += "
" - dat += " Trait Genes"
- if(trait_genes.len)
- dat += " "
- else
- dat += "
" - if(can_insert && istype(disk.gene, /datum/plant_gene/trait)) - dat += "Insert: [disk.gene.get_name()]" - dat += " No sample found. Please, insert a plant sample to use this device." - popup.set_content(dat) - popup.open() + L["type"] = "trait" + L["id"] = get_gene_id(gene) + L["trait_id"] = trait_gene.trait_id + +/obj/machinery/plantgenes/ui_static_data(mob/user) + var/list/data = list() + + data["stat_tooltips"] = list( + potency = "The 'power' of a plant. Generally effects the amount of reagent in a plant.", + yield = "The amount of crop yielded from a harvest", + "production speed" = "The speed at which a plant grows. Lower is better.", + endurance = "The amount of health the plant has", + lifespan = "The time it takes before the plant starts dying of old age", + "weed vulnerability" = "The vulnerability of the plant to weeds growing", + "weed growth rate" = "The speed at which weeds can grow around the plant. The higher the faster they grow.", + ) + return data -/obj/machinery/plantgenes/Topic(var/href, var/list/href_list) +/obj/machinery/plantgenes/proc/find_gene_by_id(var/gene_id) + for(var/datum/plant_gene/gene in core_genes) + if(get_gene_id(gene) == gene_id) + return gene + for(var/datum/plant_gene/gene in reagent_genes) + if(get_gene_id(gene) == gene_id) + return gene + for(var/datum/plant_gene/gene in trait_genes) + if(get_gene_id(gene) == gene_id) + return gene + +/obj/machinery/plantgenes/ui_act(action, params) if(..()) return - usr.set_machine(src) - if(href_list["eject_seed"] && !operation) + if(action == "toggle_skip_confirmation") + skip_confirmation = !skip_confirmation + . = TRUE + + if(action == "eject_insert_seed") var/obj/item/I = usr.get_active_held_item() if(istype(I, /obj/item/seeds)) if(!usr.transferItemToLoc(I, src)) @@ -261,9 +249,11 @@ eject_seed() insert_seed(I) to_chat(usr, "You add [I] to the machine.") + . = TRUE else - eject_seed() - else if(href_list["eject_disk"] && !operation) + . = eject_seed() + + if(action == "eject_insert_disk") var/obj/item/I = usr.get_active_held_item() if(istype(I, /obj/item/disk/plantgene)) if(!usr.transferItemToLoc(I, src)) @@ -271,86 +261,104 @@ eject_disk() disk = I to_chat(usr, "You add [I] to the machine.") + . = TRUE else - eject_disk() - else if(href_list["op"] == "insert" && disk && disk.gene && seed) - if(!operation) // Wait for confirmation - operation = "insert" - else - if(!istype(disk.gene, /datum/plant_gene/core) && disk.gene.can_add(seed)) - seed.genes += disk.gene.Copy() - if(istype(disk.gene, /datum/plant_gene/reagent)) - seed.reagents_from_genes() - update_genes() - repaint_seed() - operation = "" - target = null + . = eject_disk() - else if(href_list["gene"] && seed) - var/datum/plant_gene/G = seed.get_gene(href_list["gene"]) - if(!G || !href_list["op"] || !(href_list["op"] in list("remove", "extract", "replace"))) - interact(usr) - return - - if(!operation || target != G) // Wait for confirmation + if(seed) + if(action == "remove") + var/datum/plant_gene/G = find_gene_by_id(params["gene_id"]) + if(!G) + return FALSE + operation = action target = G - operation = href_list["op"] - - else if(operation == href_list["op"] && target == G) - switch(href_list["op"]) - if("remove") - if(!istype(G, /datum/plant_gene/core)) - seed.genes -= G - if(istype(G, /datum/plant_gene/reagent)) - seed.reagents_from_genes() - repaint_seed() - if("extract") - if(disk && !disk.read_only) - disk.gene = G - if(istype(G, /datum/plant_gene/core)) - var/datum/plant_gene/core/gene = G - if(istype(G, /datum/plant_gene/core/potency)) - gene.value = min(gene.value, max_potency) - else if(istype(G, /datum/plant_gene/core/lifespan)) - gene.value = min(gene.value, max_endurance) //INTENDED - else if(istype(G, /datum/plant_gene/core/endurance)) - gene.value = min(gene.value, max_endurance) - else if(istype(G, /datum/plant_gene/core/production)) - gene.value = max(gene.value, min_production) - else if(istype(G, /datum/plant_gene/core/yield)) - gene.value = min(gene.value, max_yield) - else if(istype(G, /datum/plant_gene/core/weed_rate)) - gene.value = max(gene.value, min_wrate) - else if(istype(G, /datum/plant_gene/core/weed_chance)) - gene.value = max(gene.value, min_wchance) - disk.update_name() - qdel(seed) - seed = null - update_icon() - if("replace") - if(disk && disk.gene && istype(disk.gene, G.type) && istype(G, /datum/plant_gene/core)) - seed.genes -= G - var/datum/plant_gene/core/C = disk.gene.Copy() - seed.genes += C - C.apply_stat(seed) - repaint_seed() - if("insert") - if(disk && disk.gene && !istype(disk.gene, /datum/plant_gene/core) && disk.gene.can_add(seed)) - seed.genes += disk.gene.Copy() - if(istype(disk.gene, /datum/plant_gene/reagent)) - seed.reagents_from_genes() - disk.gene.apply_vars(seed) - repaint_seed() - - - update_genes() - operation = "" + . = TRUE + + if(action == "extract") + var/datum/plant_gene/G = find_gene_by_id(params["gene_id"]) + if(!G) + return FALSE + if(disk && !disk.read_only) + operation = action + target = G + . = TRUE + + if(action == "replace") + var/datum/plant_gene/G = find_gene_by_id(params["gene_id"]) + if(!G) + return FALSE + if(disk && disk.gene && istype(disk.gene, G.type) && istype(G, /datum/plant_gene/core)) + operation = action + target = G + . = TRUE + + if(action == "insert" && !istype(disk.gene, /datum/plant_gene/core) && disk.gene.can_add(seed)) + operation = action target = null - else if(href_list["abort"]) - operation = "" + . = TRUE + + if((action == "confirm" || (. && skip_confirmation)) && operation) + if(operation == "remove") + var/datum/plant_gene/G = target + if(G) + if(!istype(G, /datum/plant_gene/core)) + seed.genes -= G + if(istype(G, /datum/plant_gene/reagent)) + seed.reagents_from_genes() + repaint_seed() + + if(operation == "extract") + var/datum/plant_gene/G = target + if(G && disk && !disk.read_only) + disk.gene = G + if(istype(G, /datum/plant_gene/core)) + var/datum/plant_gene/core/gene = G + if(istype(G, /datum/plant_gene/core/potency)) + gene.value = min(gene.value, max_potency) + else if(istype(G, /datum/plant_gene/core/lifespan)) + gene.value = min(gene.value, max_endurance) //INTENDED + else if(istype(G, /datum/plant_gene/core/endurance)) + gene.value = min(gene.value, max_endurance) + else if(istype(G, /datum/plant_gene/core/production)) + gene.value = max(gene.value, min_production) + else if(istype(G, /datum/plant_gene/core/yield)) + gene.value = min(gene.value, max_yield) + else if(istype(G, /datum/plant_gene/core/weed_rate)) + gene.value = max(gene.value, min_wrate) + else if(istype(G, /datum/plant_gene/core/weed_chance)) + gene.value = max(gene.value, min_wchance) + disk.update_name() + qdel(seed) + seed = null + + if(operation == "replace") + var/datum/plant_gene/G = target + if(G && disk && disk.gene && istype(disk.gene, G.type) && istype(G, /datum/plant_gene/core)) + seed.genes -= G + var/datum/plant_gene/core/C = disk.gene.Copy() + seed.genes += C + C.apply_stat(seed) + repaint_seed() + + if(operation == "insert" && !istype(disk.gene, /datum/plant_gene/core) && disk.gene.can_add(seed)) + seed.genes += disk.gene.Copy() + if(istype(disk.gene, /datum/plant_gene/reagent)) + seed.reagents_from_genes() + repaint_seed() + + operation = null target = null + . = TRUE - interact(usr) + if(action == "abort" && operation) + operation = null + target = null + . = TRUE + + + if(.) + update_genes() + update_icon() /obj/machinery/plantgenes/proc/insert_seed(obj/item/seeds/S) if(!istype(S) || seed) @@ -359,6 +367,7 @@ seed = S update_genes() update_icon() + ui_update() /obj/machinery/plantgenes/proc/eject_disk() if (disk && !operation) @@ -369,6 +378,8 @@ disk.forceMove(drop_location()) disk = null update_genes() + ui_update() + . = TRUE /obj/machinery/plantgenes/proc/eject_seed() if (seed && !operation) @@ -379,6 +390,8 @@ seed.forceMove(drop_location()) seed = null update_genes() + ui_update() + . = TRUE /obj/machinery/plantgenes/proc/update_genes() core_genes = list() @@ -411,6 +424,7 @@ return // Already modded name and icon seed.name = "experimental " + seed.name seed.icon_state = "seed-x" + ui_update() // Gene modder for seed vault ship, built with high tech alien parts. /obj/machinery/plantgenes/seedvault diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 8ca883af88941..1adba2432014a 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -117,15 +117,11 @@ if(seed) for(var/datum/plant_gene/trait/trait in seed.genes) trait.on_squash(src, target) - if(!seed.get_gene(/datum/plant_gene/trait/noreact)) - reagents.reaction(T) - for(var/A in T) - reagents.reaction(A) - qdel(src) - if(seed.get_gene(/datum/plant_gene/trait/noreact)) - visible_message("[src] crumples, and bubbles ominously as its contents mix.") - addtimer(CALLBACK(src, .proc/squashreact), 20) - + reagents.reaction(T) + for(var/A in T) + reagents.reaction(A) + qdel(src) + /obj/item/reagent_containers/food/snacks/grown/proc/squashreact() for(var/datum/plant_gene/trait/trait in seed.genes) trait.on_squashreact(src) @@ -180,6 +176,7 @@ var/obj/item/T if(trash) T = generate_trash() + T.remove_item_from_storage(get_turf(T)) qdel(src) - user.putItemFromInventoryInHandIfPossible(T, user.active_hand_index, TRUE) + user.put_in_hands(T, FALSE) to_chat(user, "You open [src]\'s shell, revealing \a [T].") diff --git a/code/modules/hydroponics/grown/berries.dm b/code/modules/hydroponics/grown/berries.dm index 886386a69fb46..c1c9d5563fa93 100644 --- a/code/modules/hydroponics/grown/berries.dm +++ b/code/modules/hydroponics/grown/berries.dm @@ -90,7 +90,7 @@ lifespan = 30 endurance = 25 mutatelist = list() - genes = list(/datum/plant_gene/trait/glow/berry, /datum/plant_gene/trait/noreact, /datum/plant_gene/trait/repeated_harvest) + genes = list(/datum/plant_gene/trait/glow/white, /datum/plant_gene/trait/repeated_harvest) reagents_add = list(/datum/reagent/uranium = 0.25, /datum/reagent/iodine = 0.2, /datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/consumable/nutriment = 0.1) rarity = 20 @@ -121,8 +121,9 @@ growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' icon_grow = "cherry-grow" icon_dead = "cherry-dead" + icon_harvest = "cherry-harvest" genes = list(/datum/plant_gene/trait/repeated_harvest) - mutatelist = list(/obj/item/seeds/cherry/blue) + mutatelist = list(/obj/item/seeds/cherry/blue, /obj/item/seeds/cherry/bulb) reagents_add = list(/datum/reagent/consumable/nutriment = 0.07, /datum/reagent/consumable/sugar = 0.07) /obj/item/reagent_containers/food/snacks/grown/cherries @@ -162,6 +163,31 @@ tastes = list("blue cherry" = 1) wine_power = 50 +//Cherry Bulbs +/obj/item/seeds/cherry/bulb + name = "pack of cherry bulb pits" + desc = "The glowy kind of cherries." + icon_state = "seed-cherrybulb" + species = "cherrybulb" + plantname = "Cherry Bulb Tree" + product = /obj/item/reagent_containers/food/snacks/grown/cherrybulbs + genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/glow/pink) + mutatelist = list() + reagents_add = list(/datum/reagent/consumable/nutriment = 0.07, /datum/reagent/consumable/sugar = 0.07) + rarity = 10 + +/obj/item/reagent_containers/food/snacks/grown/cherrybulbs + seed = /obj/item/seeds/cherry/bulb + name = "cherry bulbs" + desc = "They're like little Space Christmas lights!" + icon_state = "cherry_bulb" + filling_color = "#FF0000" + bitesize_mod = 2 + foodtype = FRUIT + grind_results = list(/datum/reagent/consumable/cherryjelly = 0) + tastes = list("cherry" = 1) + wine_power = 50 + // Grapes /obj/item/seeds/grape name = "pack of grape seeds" diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm index a7e571d7c1d3f..cadc2d1325fe2 100644 --- a/code/modules/hydroponics/grown/cannabis.dm +++ b/code/modules/hydroponics/grown/cannabis.dm @@ -61,6 +61,7 @@ species = "ocannabis" plantname = "Omega Weed" product = /obj/item/reagent_containers/food/snacks/grown/cannabis/ultimate + genes = list(/datum/plant_gene/trait/glow/green) mutatelist = list() reagents_add = list(/datum/reagent/drug/space_drugs = 0.3, /datum/reagent/toxin/mindbreaker = 0.3, diff --git a/code/modules/hydroponics/grown/chili.dm b/code/modules/hydroponics/grown/chili.dm index 0522b5fd45434..d50a22362128a 100644 --- a/code/modules/hydroponics/grown/chili.dm +++ b/code/modules/hydroponics/grown/chili.dm @@ -88,13 +88,13 @@ held_mob = loc START_PROCESSING(SSobj, src) -/obj/item/reagent_containers/food/snacks/grown/ghost_chili/process() +/obj/item/reagent_containers/food/snacks/grown/ghost_chili/process(delta_time) if(held_mob && loc == held_mob) if(held_mob.is_holding(src)) if(istype(held_mob) && held_mob.gloves) return - held_mob.adjust_bodytemperature(15 * TEMPERATURE_DAMAGE_COEFFICIENT) - if(prob(10)) + held_mob.adjust_bodytemperature(7.5 * TEMPERATURE_DAMAGE_COEFFICIENT * delta_time) + if(DT_PROB(5, delta_time)) to_chat(held_mob, "Your hand holding [src] burns!") else held_mob = null diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm index 99de8b92d668f..d500578af11b2 100644 --- a/code/modules/hydroponics/grown/citrus.dm +++ b/code/modules/hydroponics/grown/citrus.dm @@ -135,7 +135,7 @@ /obj/item/reagent_containers/food/snacks/grown/firelemon/ex_act(severity) qdel(src) //Ensuring that it's deleted by its own explosion -/obj/item/reagent_containers/food/snacks/grown/firelemon/proc/prime() +/obj/item/reagent_containers/food/snacks/grown/firelemon/proc/prime(mob/living/lanced_by) switch(seed.potency) //Combustible lemons are alot like IEDs, lots of flame, very little bang. if(0 to 30) update_mob() diff --git a/code/modules/hydroponics/grown/cotton.dm b/code/modules/hydroponics/grown/cotton.dm index 3fa4247f578a3..a898b76ee884f 100644 --- a/code/modules/hydroponics/grown/cotton.dm +++ b/code/modules/hydroponics/grown/cotton.dm @@ -32,7 +32,7 @@ var/cotton_name = "raw cotton" /obj/item/grown/cotton/attack_self(mob/user) - user.show_message("You pull some [cotton_name] out of the [name]!", 1) + user.show_message("You pull some [cotton_name] out of the [name]!", MSG_VISUAL) var/seed_modifier = 0 if(seed) seed_modifier = round(seed.potency / 25) @@ -76,4 +76,4 @@ throw_range = 3 attack_verb = list("bashed", "battered", "bludgeoned", "whacked") cotton_type = /obj/item/stack/sheet/cotton/durathread - cotton_name = "raw durathread" \ No newline at end of file + cotton_name = "raw durathread" diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm index 2be0cb14a3655..2cb3691e477eb 100644 --- a/code/modules/hydroponics/grown/flowers.dm +++ b/code/modules/hydroponics/grown/flowers.dm @@ -36,19 +36,21 @@ species = "lily" plantname = "Lily Plants" product = /obj/item/reagent_containers/food/snacks/grown/poppy/lily + icon_grow = "lily-grow" + icon_dead = "lily-dead" mutatelist = list(/obj/item/seeds/poppy/lily/trumpet) /obj/item/reagent_containers/food/snacks/grown/poppy/lily seed = /obj/item/seeds/poppy/lily name = "lily" - desc = "A beautiful orange flower." + desc = "A beautiful white flower with rich symbolism. The lily is said to represent love and affection as well as purity and innocence in some cultures." icon_state = "lily" - filling_color = "#FFA500" + filling_color = "#fff8ea" - //Spacemans's Trumpet +//Spacemans's Trumpet /obj/item/seeds/poppy/lily/trumpet name = "pack of spaceman's trumpet seeds" - desc = "A plant sculped by extensive genetic engineering. The spaceman's trumpet is said to bear no resemblance to its wild ancestors. Inside NT AgriSci circles it is better known as NTPW-0372." + desc = "A plant sculpted by extensive genetic engineering. The spaceman's trumpet is said to bear no resemblance to its wild ancestors. Inside NT AgriSci circles it is better known as NTPW-0372." icon_state = "seed-trumpet" species = "spacemanstrumpet" plantname = "Spaceman's Trumpet Plant" @@ -80,7 +82,7 @@ name = "spaceman's trumpet" desc = "A vivid flower that smells faintly of freshly cut grass. Touching the flower seems to stain the skin some time after contact, yet most other surfaces seem to be unaffected by this phenomenon." icon_state = "spacemanstrumpet" - filling_color = "#FF6347" + filling_color = "#8324f0" bitesize_mod = 3 foodtype = VEGETABLES @@ -92,14 +94,41 @@ species = "geranium" plantname = "Geranium Plants" product = /obj/item/reagent_containers/food/snacks/grown/poppy/geranium - mutatelist = list() + icon_grow = "geranium-grow" + icon_dead = "geranium-dead" + mutatelist = list(/obj/item/seeds/poppy/geranium/forgetmenot) /obj/item/reagent_containers/food/snacks/grown/poppy/geranium seed = /obj/item/seeds/poppy/geranium name = "geranium" - desc = "A beautiful blue flower." + desc = "A cluster of small purple geranium flowers. They symbolize happiness, good health, wishes and friendship and are generally associated with positive emotions." icon_state = "geranium" - filling_color = "#008B8B" + filling_color = "#9325ee" + +//Forget-Me-Not +/obj/item/seeds/poppy/geranium/forgetmenot + name = "pack of forget-me-not seeds" + desc = "These seeds grow into forget-me-nots." + icon_state = "seed-forget_me_not" + species = "forget_me_not" + plantname = "Forget-Me-Not Plants" + product = /obj/item/reagent_containers/food/snacks/grown/poppy/geranium/forgetmenot + endurance = 30 + maturation = 5 + yield = 4 + potency = 25 + icon_grow = "forget_me_not-grow" + icon_dead = "forget_me_not-dead" + mutatelist = list() + reagents_add = list(/datum/reagent/medicine/kelotane = 0.2, /datum/reagent/consumable/nutriment = 0.05) + +/obj/item/reagent_containers/food/snacks/grown/poppy/geranium/forgetmenot + seed = /obj/item/seeds/poppy/geranium/forgetmenot + name = "forget-me-not" + desc = "A clump of small blue flowers, they are primarily associated with rememberance, respect and loyalty." + icon_state = "forget_me_not" + filling_color = "#4466ff" + bitesize_mod = 2 // Harebell /obj/item/seeds/harebell @@ -179,6 +208,7 @@ icon_grow = "moonflower-grow" icon_dead = "sunflower-dead" product = /obj/item/reagent_containers/food/snacks/grown/moonflower + genes = list(/datum/plant_gene/trait/glow/purple) mutatelist = list() reagents_add = list(/datum/reagent/consumable/ethanol/moonshine = 0.2,/datum/reagent/medicine/morphine = 0.3, /datum/reagent/consumable/nutriment = 0.02) rarity = 20 @@ -252,5 +282,4 @@ ..() if(!user.gloves) to_chat(user, "The [name] burns your bare hand!") - user.adjustFireLoss(rand(1, 5)) - + user.adjustFireLoss(rand(1, 5)) \ No newline at end of file diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm index bbaccbc437dd0..55dad6bf312b3 100644 --- a/code/modules/hydroponics/grown/grass_carpet.dm +++ b/code/modules/hydroponics/grown/grass_carpet.dm @@ -15,7 +15,7 @@ icon_grow = "grass-grow" icon_dead = "grass-dead" genes = list(/datum/plant_gene/trait/repeated_harvest) - mutatelist = list(/obj/item/seeds/grass/carpet) + mutatelist = list(/obj/item/seeds/grass/carpet, /obj/item/seeds/grass/fairy) reagents_add = list(/datum/reagent/consumable/nutriment = 0.02, /datum/reagent/hydrogen = 0.05) /obj/item/reagent_containers/food/snacks/grown/grass @@ -40,6 +40,54 @@ new stacktype(user.drop_location(), grassAmt) qdel(src) +//Fairygrass +/obj/item/seeds/grass/fairy + name = "pack of fairygrass seeds" + desc = "These seeds grow into a more mystical grass." + icon_state = "seed-fairygrass" + species = "fairygrass" + plantname = "Fairygrass" + product = /obj/item/reagent_containers/food/snacks/grown/grass/fairy + icon_grow = "fairygrass-grow" + icon_dead = "grass-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/glow/blue) + reagents_add = list(/datum/reagent/consumable/nutriment = 0.02, /datum/reagent/hydrogen = 0.05, /datum/reagent/drug/space_drugs = 0.15) + +/obj/item/reagent_containers/food/snacks/grown/grass/fairy + seed = /obj/item/seeds/grass/fairy + name = "fairygrass" + desc = "Glowing, and smells fainly of mushrooms." + icon_state = "fairygrassclump" + filling_color = "#3399ff" + stacktype = /obj/item/stack/tile/fairygrass + +/obj/item/reagent_containers/food/snacks/grown/grass/fairy/attack_self(mob/user) + var/datum/plant_gene/trait/glow/G = null + for(var/datum/plant_gene/trait/glow/gene in seed.genes) + G = gene + break + + stacktype = initial(stacktype) + + if(G) + switch(G.type) + if(/datum/plant_gene/trait/glow/white) + stacktype = /obj/item/stack/tile/fairygrass/white + if(/datum/plant_gene/trait/glow/red) + stacktype = /obj/item/stack/tile/fairygrass/red + if(/datum/plant_gene/trait/glow/yellow) + stacktype = /obj/item/stack/tile/fairygrass/yellow + if(/datum/plant_gene/trait/glow/green) + stacktype = /obj/item/stack/tile/fairygrass/green + if(/datum/plant_gene/trait/glow/blue) + stacktype = /obj/item/stack/tile/fairygrass/blue + if(/datum/plant_gene/trait/glow/purple) + stacktype = /obj/item/stack/tile/fairygrass/purple + if(/datum/plant_gene/trait/glow/pink) + stacktype = /obj/item/stack/tile/fairygrass/pink + + . = ..() + // Carpet /obj/item/seeds/grass/carpet name = "pack of carpet seeds" diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm index 3f42815eac99a..871c31dd5ace6 100644 --- a/code/modules/hydroponics/grown/melon.dm +++ b/code/modules/hydroponics/grown/melon.dm @@ -44,6 +44,7 @@ species = "holymelon" plantname = "Holy Melon Vines" product = /obj/item/reagent_containers/food/snacks/grown/holymelon + genes = list(/datum/plant_gene/trait/glow/yellow) mutatelist = list() reagents_add = list(/datum/reagent/water/holywater = 0.2, /datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/consumable/nutriment = 0.1) rarity = 20 diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm index 3a325729c0bb6..0dfd065ceb1d3 100644 --- a/code/modules/hydroponics/grown/misc.dm +++ b/code/modules/hydroponics/grown/misc.dm @@ -14,7 +14,7 @@ growthstages = 3 growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' genes = list(/datum/plant_gene/trait/plant_type/weed_hardy) - mutatelist = list(/obj/item/seeds/starthistle/corpse_flower) + mutatelist = list(/obj/item/seeds/starthistle/corpse_flower, /obj/item/seeds/galaxythistle) /obj/item/seeds/starthistle/harvest(mob/user) var/obj/machinery/hydroponics/parent = loc @@ -46,7 +46,7 @@ START_PROCESSING(SSobj, src) return ..() -/obj/item/seeds/starthistle/corpse_flower/process() +/obj/item/seeds/starthistle/corpse_flower/process(delta_time) var/obj/machinery/hydroponics/parent = loc if(parent.age < maturation) // Start a little before it blooms return @@ -56,7 +56,7 @@ return var/datum/gas_mixture/stank = new - stank.set_moles(/datum/gas/miasma, (yield + 6)*7*MIASMA_CORPSE_MOLES) // this process is only being called about 2/7 as much as corpses so this is 12-32 times a corpses + stank.set_moles(GAS_MIASMA, (yield + 6)*3.5*MIASMA_CORPSE_MOLES*delta_time) // this process is only being called about 2/7 as much as corpses so this is 12-32 times a corpses stank.set_temperature(T20C) // without this the room would eventually freeze and miasma mining would be easier T.assume_air(stank) T.air_update_turf() @@ -221,7 +221,7 @@ /obj/item/reagent_containers/food/snacks/grown/cherry_bomb/ex_act(severity) qdel(src) //Ensuring that it's deleted by its own explosion. Also prevents mass chain reaction with piles of cherry bombs -/obj/item/reagent_containers/food/snacks/grown/cherry_bomb/proc/prime() +/obj/item/reagent_containers/food/snacks/grown/cherry_bomb/proc/prime(mob/living/lanced_by) icon_state = "cherry_bomb_lit" playsound(src, 'sound/effects/fuse.ogg', seed.potency, 0) reagents.chem_temp = 1000 //Sets off the black powder diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm index 7fe9d52fe50bd..96a2183fb0eec 100644 --- a/code/modules/hydroponics/grown/nettle.dm +++ b/code/modules/hydroponics/grown/nettle.dm @@ -99,13 +99,13 @@ if(..()) if(prob(50)) user.Paralyze(100) - to_chat(user, "You are stunned by the Deathnettle as you try picking it up!") + to_chat(user, "You are stunned by [src] as you try picking it up!") /obj/item/reagent_containers/food/snacks/grown/nettle/death/attack(mob/living/carbon/M, mob/user) if(!..()) return if(isliving(M)) - to_chat(M, "You are stunned by the powerful acid of the Deathnettle!") + to_chat(M, "You are stunned by the powerful acid of [src]!") log_combat(user, M, "attacked", src) M.adjust_blurriness(force/7) diff --git a/code/modules/hydroponics/grown/pumpkin.dm b/code/modules/hydroponics/grown/pumpkin.dm index e26d1c1d2a1ee..dbd240a54d128 100644 --- a/code/modules/hydroponics/grown/pumpkin.dm +++ b/code/modules/hydroponics/grown/pumpkin.dm @@ -29,7 +29,7 @@ /obj/item/reagent_containers/food/snacks/grown/pumpkin/attackby(obj/item/W as obj, mob/user as mob, params) if(W.is_sharp()) - user.show_message("You carve a face into [src]!", 1) + user.show_message("You carve a face into [src]!", MSG_VISUAL) new /obj/item/clothing/head/hardhat/pumpkinhead(user.loc) qdel(src) return diff --git a/code/modules/hydroponics/grown/rainbow_bunch.dm b/code/modules/hydroponics/grown/rainbow_bunch.dm index 9626d2e6c4d44..4ad9490aa99d5 100644 --- a/code/modules/hydroponics/grown/rainbow_bunch.dm +++ b/code/modules/hydroponics/grown/rainbow_bunch.dm @@ -3,7 +3,7 @@ desc = "A pack of seeds that'll grow into a beautiful bush of various colored flowers." icon_state = "seed-rainbowbunch" species = "rainbowbunch" - plantname = "Rainbow Flowers" + plantname = "Rainbow Bunch" icon_harvest = "rainbowbunch-harvest" product = /obj/item/reagent_containers/food/snacks/grown/rainbow_flower lifespan = 25 @@ -38,40 +38,40 @@ if(1) item_color = "red" color = "#DA0000" - list_reagents = list(/datum/reagent/colorful_reagent/powder/red = 3) - desc += " This one is in a bright red color." + reagents.add_reagent(/datum/reagent/colorful_reagent/powder/red, 3) + desc += " This one is in a fiery red color." if(2) item_color = "orange" color = "#FF9300" - list_reagents = list(/datum/reagent/colorful_reagent/powder/orange = 3) + reagents.add_reagent(/datum/reagent/colorful_reagent/powder/orange, 3) desc += " This one is in a citrus orange color." if(3) item_color = "yellow" color = "#FFF200" - list_reagents = list(/datum/reagent/colorful_reagent/powder/yellow = 3) + reagents.add_reagent(/datum/reagent/colorful_reagent/powder/yellow, 3) desc += " This one is in a bright yellow color." if(4) item_color = "green" color = "#A8E61D" - list_reagents = list(/datum/reagent/colorful_reagent/powder/green = 3) + reagents.add_reagent(/datum/reagent/colorful_reagent/powder/green, 3) desc += " This one is in a grassy green color." if(5) item_color = "blue" color = "#00B7EF" - list_reagents = list(/datum/reagent/colorful_reagent/powder/blue = 3) + reagents.add_reagent(/datum/reagent/colorful_reagent/powder/blue, 3) desc += " This one is in a soothing blue color." if(6) item_color = "purple" color = "#DA00FF" - list_reagents = list(/datum/reagent/colorful_reagent/powder/purple = 3) + reagents.add_reagent(/datum/reagent/colorful_reagent/powder/purple, 3) desc += " This one is in a vibrant purple color." if(7) item_color = "black" color = "#1C1C1C" - list_reagents = list(/datum/reagent/colorful_reagent/powder/black = 3) + reagents.add_reagent(/datum/reagent/colorful_reagent/powder/black, 3) desc += " This one is in a midnight black color." if(8) item_color = "white" color = "#FFFFFF" - list_reagents = list(/datum/reagent/colorful_reagent/powder/white = 3) + reagents.add_reagent(/datum/reagent/colorful_reagent/powder/white, 3) desc += " This one is in a pure white color." diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm index d4e59cfbe8c8c..355966444726e 100644 --- a/code/modules/hydroponics/grown/replicapod.dm +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -14,15 +14,17 @@ yield = 1 //seeds if there isn't a dna inside potency = 30 var/volume = 5 - var/ckey = null - var/realName = null - var/datum/mind/mind = null - var/blood_gender = null - var/blood_type = null - var/list/features = null - var/factions = null - var/list/quirks = null - var/contains_sample = 0 + var/ckey + var/realName + var/datum/mind/mind + var/blood_gender + var/blood_type + var/list/features + var/factions + var/list/quirks + var/sampleDNA + var/contains_sample = FALSE + var/being_harvested = FALSE /obj/item/seeds/replicapod/Initialize() . = ..() @@ -42,10 +44,9 @@ features = B.data["features"] factions = B.data["factions"] quirks = B.data["quirks"] + sampleDNA = B.data["blood_DNA"] contains_sample = TRUE visible_message("The [src] is injected with a fresh blood sample.") - mind.grab_ghost() - to_chat(mind, "Consciousness slowly creeps over you as your body sprouts into a new form. Am I being planted?") log_cloning("[key_name(mind)]'s cloning record was added to [src] at [AREACOORD(src)].") else visible_message("The [src] rejects the sample!") @@ -58,18 +59,19 @@ blood_type = null features = null factions = null + sampleDNA = null contains_sample = FALSE /obj/item/seeds/replicapod/get_analyzer_text() var/text = ..() if(contains_sample) - text += "\n It contains a blood sample!" + text += "\n It contains a blood sample with blood DNA ([sampleDNA])." //blood DNA (UE) shows in medical records and is readable by forensics scanners return text /obj/item/seeds/replicapod/harvest(mob/user) //now that one is fun -- Urist var/obj/machinery/hydroponics/parent = loc - var/make_podman = 0 + var/make_podman = FALSE var/ckey_holder = null var/list/result = list() if(CONFIG_GET(flag/revival_pod_plants)) @@ -78,11 +80,12 @@ if(isobserver(M)) var/mob/dead/observer/O = M if(O.ckey == ckey && O.can_reenter_corpse) - make_podman = 1 + make_podman = TRUE break else if(M.ckey == ckey && M.stat == DEAD && !M.suiciding) - make_podman = 1 + make_podman = TRUE + // Devil code if(isliving(M)) var/mob/living/L = M make_podman = !L.hellbound @@ -94,45 +97,67 @@ var/mob/dead/observer/O = M if(!O.can_reenter_corpse) break - make_podman = 1 + make_podman = TRUE + // Devil code if(isliving(M)) var/mob/living/L = M make_podman = !L.hellbound ckey_holder = M.ckey break - if(make_podman) //all conditions met! - var/mob/living/carbon/human/podman = new /mob/living/carbon/human(parent.loc) - if(realName) - podman.real_name = realName - else - podman.real_name = "Pod Person ([rand(1,999)])" - mind.transfer_to(podman) - if(ckey) - podman.ckey = ckey - else - podman.ckey = ckey_holder - podman.gender = blood_gender - podman.faction |= factions - if(!features["mcolor"]) - features["mcolor"] = "#59CE00" - for(var/V in quirks) - new V(podman) - podman.hardset_dna(null,null,podman.real_name,blood_type, new /datum/species/pod,features)//Discard SE's and UI's, podman cloning is inaccurate, and always make them a podman - podman.set_cloned_appearance() - to_chat(podman, "There is a bright flash! You feel like a new being.") - podman.flash_act() - log_cloning("[key_name(mind)] cloned as a podman via [src] in [parent] at [AREACOORD(parent)].") + // No podman player, give one or two seeds. + if(!make_podman) + // Prevent accidental harvesting. Make sure the user REALLY wants to do this if there's a chance of this coming from a living creature. + if(mind || ckey) + if(alert("The pod is currently devoid of soul. There is a possibility that a soul could claim this creature, or you could harvest it for seeds.", "Harvest Seeds?", "Harvest Seeds", "Cancel") == "Cancel") + return result + + // If this plant has already been harvested, return early. + // parent.update_tray() qdels this seed. + if(QDELETED(src)) + to_chat(user, text = "This pod has already had its seeds harvested!", type = MESSAGE_TYPE_INFO) + return result + + // Make sure they can still interact with the parent hydroponics tray. + if(!parent.can_interact(user)) + to_chat(user, text = "You are no longer able to harvets the seeds from [parent]!", type = MESSAGE_TYPE_INFO) + return result - else //else, one packet of seeds. maybe two var/seed_count = 1 if(prob(getYield() * 20)) seed_count++ var/output_loc = parent.Adjacent(user) ? user.loc : parent.loc //needed for TK - for(var/i=0,i You feel like a new being. Machine can store up to [max_seeds]% seeds." + . += "The status display reads: Extracting [seed_multiplier] seed(s) per piece of produce. Machine can store up to [max_seeds]% seeds." /obj/machinery/seed_extractor/attackby(obj/item/O, mob/user, params) @@ -104,78 +119,25 @@ else return ..() -/datum/seed_pile - var/name = "" - var/lifespan = 0 //Saved stats - var/endurance = 0 - var/maturation = 0 - var/production = 0 - var/yield = 0 - var/potency = 0 - var/amount = 0 - -/datum/seed_pile/New(var/name, var/life, var/endur, var/matur, var/prod, var/yie, var/poten, var/am = 1) - src.name = name - src.lifespan = life - src.endurance = endur - src.maturation = matur - src.production = prod - src.yield = yie - src.potency = poten - src.amount = am - -/obj/machinery/seed_extractor/ui_interact(mob/user) - . = ..() - if (stat) - return FALSE - - var/dat = "Stored seeds: " - - if (contents.len == 0) - dat += "No seeds" - else - dat += "
"
+ . += "Current instrument: "
+ if(!using_instrument)
+ . += "No instrument loaded! "
+
+/datum/song/ui_interact(mob/user)
+ var/list/dat = list()
+
+ dat += instrument_status_ui()
+
+ if(lines.len > 0)
+ dat += "" + else + . += "[using_instrument.name] " + . += "Playback Settings: " + if(can_noteshift) + . += "Note Shift/Note Transpose: [note_shift] keys / [round(note_shift / 12, 0.01)] octaves " + var/smt + var/modetext = "" + switch(sustain_mode) + if(SUSTAIN_LINEAR) + smt = "Linear" + modetext = "Linear Sustain Duration: [sustain_linear_duration / 10] seconds " + if(SUSTAIN_EXPONENTIAL) + smt = "Exponential" + modetext = "Exponential Falloff Factor: [sustain_exponential_dropoff]% per decisecond " + . += "Sustain Mode: [smt] " + . += modetext + . += using_instrument?.ready()? "Status: Ready " : "Status: !Instrument Definition Error! " + . += "Instrument Type: [legacy? "Legacy" : "Synthesized"] " + . += "Volume: [volume] " + . += "Volume Dropoff Threshold: [sustain_dropoff_volume] " + . += "Sustain indefinitely last held note: [full_sustain_held_note? "Enabled" : "Disabled"]. " + . += " Playback" + if(!playing) + dat += "Play Stop" + dat += "Repeat Song: " + dat += repeat > 0 ? "--" : "--" + dat += " [repeat] times " + dat += repeat < max_repeats ? "++" : "++" + dat += " " + else + dat += "Play Stop " + dat += "Repeats left: [repeat] " + if(!editing) + dat += " Show Editor " + else + dat += " Editing" + dat += "Hide Editor" + dat += " Start a New Song" + dat += " Import a Song" + var/bpm = round(600 / tempo) + dat += "Tempo: - [bpm] BPM + " + var/linecount = 0 + for(var/line in lines) + linecount += 1 + dat += "Line [linecount]: Edit X [line] " + dat += "Add Line " + if(help) + dat += "Hide Help " + dat += {" + Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-). + Every note in a chord will play together, with chord timed by the tempo. + + Notes are played by the names of the note, and optionally, the accidental, and/or the octave number. + By default, every note is natural and in octave 3. Defining otherwise is remembered for each note. + Example: C,D,E,F,G,A,B will play a C major scale. + After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3 + Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B + A pause may be denoted by an empty chord: C,E,,C,G + To make a chord be a different time, end it with /x, where the chord length will be length + defined by tempo / x: C,G/2,E/4 + Combined, an example is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4 + + Lines may be up to [MUSIC_MAXLINECHARS] characters. + A song may only contain up to [MUSIC_MAXLINES] lines. + "} + else + dat += "Show Help " + + var/datum/browser/popup = new(user, "instrument", parent?.name || "instrument", 700, 500) + popup.set_content(dat.Join("")) + popup.open() + +/** + * Parses a song the user has input into lines and stores them. + */ +/datum/song/proc/ParseSong(text) + set waitfor = FALSE + //split into lines + lines = splittext(text, "\n") + if(lines.len) + var/bpm_string = "BPM: " + if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1)) + var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default + tempo = sanitize_tempo(600 / round(divisor, 1)) + lines.Cut(1, 2) + else + tempo = sanitize_tempo(5) // default 120 BPM + if(lines.len > MUSIC_MAXLINES) + to_chat(usr, "Too many lines!") + lines.Cut(MUSIC_MAXLINES + 1) + var/linenum = 1 + for(var/l in lines) + if(length_char(l) > MUSIC_MAXLINECHARS) + to_chat(usr, "Line [linenum] too long!") + lines.Remove(l) + else + linenum++ + updateDialog(usr) // make sure updates when complete + +/datum/song/Topic(href, href_list) + if(!usr.canUseTopic(parent, TRUE, FALSE, FALSE, FALSE)) + usr << browse(null, "window=instrument") + usr.unset_machine() + return + + parent.add_fingerprint(usr) + + if(href_list["newsong"]) + lines = new() + tempo = sanitize_tempo(5) // default 120 BPM + name = "" + + else if(href_list["import"]) + var/t = "" + do + t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) + if(!in_range(parent, usr)) + return + + if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS) + var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") + if(cont == "no") + break + while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS) + ParseSong(t) + + else if(href_list["help"]) + help = text2num(href_list["help"]) - 1 + + else if(href_list["edit"]) + editing = text2num(href_list["edit"]) - 1 + + if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. + if(playing) + return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. + repeat += round(text2num(href_list["repeat"])) + if(repeat < 0) + repeat = 0 + if(repeat > max_repeats) + repeat = max_repeats + + else if(href_list["tempo"]) + tempo = sanitize_tempo(tempo + text2num(href_list["tempo"])) + + else if(href_list["play"]) + INVOKE_ASYNC(src, .proc/start_playing, usr) + + else if(href_list["newline"]) + var/newline = html_encode(input("Enter your line: ", parent.name) as text|null) + if(!newline || !in_range(parent, usr)) + return + if(lines.len > MUSIC_MAXLINES) + return + if(length(newline) > MUSIC_MAXLINECHARS) + newline = copytext(newline, 1, MUSIC_MAXLINECHARS) + lines.Add(newline) + + else if(href_list["deleteline"]) + var/num = round(text2num(href_list["deleteline"])) + if(num > lines.len || num < 1) + return + lines.Cut(num, num+1) + + else if(href_list["modifyline"]) + var/num = round(text2num(href_list["modifyline"]),1) + var/content = stripped_input(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS) + if(!content || !in_range(parent, usr)) + return + if(num > lines.len || num < 1) + return + lines[num] = content + + else if(href_list["stop"]) + stop_playing() + + else if(href_list["setlinearfalloff"]) + var/amount = input(usr, "Set linear sustain duration in seconds", "Linear Sustain Duration") as null|num + if(!isnull(amount)) + set_linear_falloff_duration(round(amount * 10, world.tick_lag)) + + else if(href_list["setexpfalloff"]) + var/amount = input(usr, "Set exponential sustain factor", "Exponential sustain factor") as null|num + if(!isnull(amount)) + set_exponential_drop_rate(round(amount, 0.00001)) + + else if(href_list["setvolume"]) + var/amount = input(usr, "Set volume", "Volume") as null|num + if(!isnull(amount)) + set_volume(round(amount, 1)) + + else if(href_list["setdropoffvolume"]) + var/amount = input(usr, "Set dropoff threshold", "Dropoff Threshold Volume") as null|num + if(!isnull(amount)) + set_dropoff_volume(round(amount, 0.01)) + + else if(href_list["switchinstrument"]) + if(!length(allowed_instrument_ids)) + return + else if(length(allowed_instrument_ids) == 1) + set_instrument(allowed_instrument_ids[1]) + return + var/list/categories = list() + for(var/i in allowed_instrument_ids) + var/datum/instrument/I = SSinstruments.get_instrument(i) + if(I) + LAZYSET(categories[I.category || "ERROR CATEGORY"], I.name, I.id) + var/cat = input(usr, "Select Category", "Instrument Category") as null|anything in categories + if(!cat) + return + var/list/instruments = categories[cat] + var/choice = input(usr, "Select Instrument", "Instrument Selection") as null|anything in instruments + if(!choice) + return + choice = instruments[choice] //get id + if(choice) + set_instrument(choice) + + else if(href_list["setnoteshift"]) + var/amount = input(usr, "Set note shift", "Note Shift") as null|num + if(!isnull(amount)) + note_shift = clamp(amount, note_shift_min, note_shift_max) + + else if(href_list["setsustainmode"]) + var/choice = input(usr, "Choose a sustain mode", "Sustain Mode") as null|anything in list("Linear", "Exponential") + switch(choice) + if("Linear") + sustain_mode = SUSTAIN_LINEAR + if("Exponential") + sustain_mode = SUSTAIN_EXPONENTIAL + + else if(href_list["togglesustainhold"]) + full_sustain_held_note = !full_sustain_held_note + + updateDialog() \ No newline at end of file diff --git a/code/modules/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm new file mode 100644 index 0000000000000..b95e8f193f548 --- /dev/null +++ b/code/modules/instruments/songs/play_legacy.dm @@ -0,0 +1,91 @@ +/** + * Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag. + */ +/datum/song/proc/compile_legacy() + if(!length(src.lines)) + return + var/list/lines = src.lines //cache for hyepr speed! + compiled_chords = list() + var/list/octaves = list(3, 3, 3, 3, 3, 3, 3) + var/list/accents = list("n", "n", "n", "n", "n", "n", "n") + for(var/line in lines) + var/list/chords = splittext(lowertext(line), ",") + for(var/chord in chords) + var/list/compiled_chord = list() + var/tempodiv = 1 + var/list/notes_tempodiv = splittext(chord, "/") + var/len = length(notes_tempodiv) + if(len >= 2) + tempodiv = text2num(notes_tempodiv[2]) + if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that. + var/list/notes = splittext(notes_tempodiv[1], "-") + for(var/note in notes) + if(length(note) == 0) + continue + // 1-7, A-G + var/key = text2ascii(note) - 96 + if((key < 1) || (key > 7)) + continue + for(var/i in 2 to length(note)) + var/oct_acc = copytext(note, i, i + 1) + var/num = text2num(oct_acc) + if(!num) //it's an accidental + accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks. + else //octave + octaves[key] = clamp(num, octave_min, octave_max) + compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key]) + compiled_chord += tempodiv //this goes last + if(length(compiled_chord)) + compiled_chords[++compiled_chords.len] = compiled_chord + +/** + * Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management. + * + * Arguments: + * * note is a number from 1-7 for A-G + * * acc is either "b", "n", or "#" + * * oct is 1-8 (or 9 for C) + */ +/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user) + // handle accidental -> B<>C of E<>F + if(acc == "b" && (note == 3 || note == 6)) // C or F + if(note == 3) + oct-- + note-- + acc = "n" + else if(acc == "#" && (note == 2 || note == 5)) // B or E + if(note == 2) + oct++ + note++ + acc = "n" + else if(acc == "#" && (note == 7)) //G# + note = 1 + acc = "b" + else if(acc == "#") // mass convert all sharps to flats, octave jump already handled + acc = "b" + note++ + + // check octave, C is allowed to go to 9 + if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) + return + + // now generate name + var/soundfile = "sound/instruments/[cached_legacy_dir]/[ascii2text(note+64)][acc][oct].[cached_legacy_ext]" + soundfile = file(soundfile) + // make sure the note exists + if(!fexists(soundfile)) + return + // and play + var/turf/source = get_turf(parent) + if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) + do_hearcheck() + var/sound/music_played = sound(soundfile) + for(var/i in hearing_mobs) + var/mob/M = i + if(user && HAS_TRAIT(user, TRAIT_MUSICIAN) && isliving(M)) + var/mob/living/L = M + L.apply_status_effect(STATUS_EFFECT_GOOD_MUSIC) + if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS)) + continue + M.playsound_local(source, null, volume * using_instrument.volume_multiplier, S = music_played) + // Could do environment and echo later but not for now \ No newline at end of file diff --git a/code/modules/instruments/songs/play_synthesized.dm b/code/modules/instruments/songs/play_synthesized.dm new file mode 100644 index 0000000000000..b1d6ad500548a --- /dev/null +++ b/code/modules/instruments/songs/play_synthesized.dm @@ -0,0 +1,137 @@ +/** + * Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag. + */ +/datum/song/proc/compile_synthesized() + if(!length(src.lines)) + return + var/list/lines = src.lines //cache for hyepr speed! + compiled_chords = list() + var/list/octaves = list(3, 3, 3, 3, 3, 3, 3) + var/list/accents = list("n", "n", "n", "n", "n", "n", "n") + for(var/line in lines) + var/list/chords = splittext(lowertext(line), ",") + for(var/chord in chords) + var/list/compiled_chord = list() + var/tempodiv = 1 + var/list/notes_tempodiv = splittext(chord, "/") + var/len = length(notes_tempodiv) + if(len >= 2) + tempodiv = text2num(notes_tempodiv[2]) + if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that. + var/list/notes = splittext(notes_tempodiv[1], "-") + for(var/note in notes) + if(length(note) == 0) + continue + // 1-7, A-G + var/key = text2ascii(note) - 96 + if((key < 1) || (key > 7)) + continue + for(var/i in 2 to length(note)) + var/oct_acc = copytext(note, i, i + 1) + var/num = text2num(oct_acc) + if(!num) //it's an accidental + accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks. + else //octave + octaves[key] = clamp(num, octave_min, octave_max) + compiled_chord += clamp((note_offset_lookup[key] + octaves[key] * 12 + accent_lookup[accents[key]]), key_min, key_max) + compiled_chord += tempodiv //this goes last + if(length(compiled_chord)) + compiled_chords[++compiled_chords.len] = compiled_chord + +/** + * Plays a specific numerical key from our instrument to anyone who can hear us. + * Does a hearing check if enough time has passed. + */ +/datum/song/proc/playkey_synth(key, mob/user) + if(can_noteshift) + key = clamp(key + note_shift, key_min, key_max) + if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) + do_hearcheck() + var/datum/instrument_key/K = using_instrument.samples[num2text(key)] //See how fucking easy it is to make a number text? You don't need a complicated 9 line proc! + //Should probably add channel limiters here at some point but I don't care right now. + var/channel = pop_channel() + if(isnull(channel)) + return FALSE + . = TRUE + var/sound/copy = sound(K.sample) + var/volume = src.volume * using_instrument.volume_multiplier + copy.frequency = K.frequency + copy.volume = volume + var/channel_text = num2text(channel) + channels_playing[channel_text] = 100 + last_channel_played = channel_text + for(var/i in hearing_mobs) + var/mob/M = i + if(user && HAS_TRAIT(user, TRAIT_MUSICIAN) && isliving(M)) + var/mob/living/L = M + L.apply_status_effect(STATUS_EFFECT_GOOD_MUSIC) + if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS)) + continue + M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF) + // Could do environment and echo later but not for now + +/** + * Stops all sounds we are "responsible" for. Only works in synthesized mode. + */ +/datum/song/proc/terminate_all_sounds(clear_channels = TRUE) + for(var/i in hearing_mobs) + terminate_sound_mob(i) + if(clear_channels) + channels_playing.len = 0 + channels_idle.len = 0 + SSinstruments.current_instrument_channels -= using_sound_channels + using_sound_channels = 0 + SSsounds.free_datum_channels(src) + +/** + * Stops all sounds we are responsible for in a given person. Only works in synthesized mode. + */ +/datum/song/proc/terminate_sound_mob(mob/M) + for(var/channel in channels_playing) + M.stop_sound_channel(text2num(channel)) + +/** + * Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster. + */ +/datum/song/proc/pop_channel() + if(length(channels_idle)) //just pop one off of here if we have one available + . = text2num(channels_idle[1]) + channels_idle.Cut(1,2) + return + if(using_sound_channels >= max_sound_channels) + return + . = SSinstruments.reserve_instrument_channel(src) + if(!isnull(.)) + using_sound_channels++ + +/** + * Decays our channels and updates their volumes to mobs who can hear us. + * + * Arguments: + * * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation. + */ +/datum/song/proc/process_decay(wait_ds) + var/linear_dropoff = cached_linear_dropoff * wait_ds + var/exponential_dropoff = cached_exponential_dropoff ** wait_ds + for(var/channel in channels_playing) + if(full_sustain_held_note && (channel == last_channel_played)) + continue + var/current_volume = channels_playing[channel] + switch(sustain_mode) + if(SUSTAIN_LINEAR) + current_volume -= linear_dropoff + if(SUSTAIN_EXPONENTIAL) + current_volume /= exponential_dropoff + channels_playing[channel] = current_volume + var/dead = current_volume <= sustain_dropoff_volume + var/channelnumber = text2num(channel) + if(dead) + channels_playing -= channel + channels_idle += channel + for(var/i in hearing_mobs) + var/mob/M = i + M.stop_sound_channel(channelnumber) + else + for(var/i in hearing_mobs) + var/mob/M = i + M.set_sound_channel_volume(channelnumber, (current_volume * 0.01) * volume * using_instrument.volume_multiplier) \ No newline at end of file diff --git a/code/modules/instruments/stationary.dm b/code/modules/instruments/stationary.dm new file mode 100644 index 0000000000000..79228d88a8fd9 --- /dev/null +++ b/code/modules/instruments/stationary.dm @@ -0,0 +1,52 @@ +/obj/structure/musician + name = "Not A Piano" + desc = "Something broke, contact coderbus." + interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT | INTERACT_ATOM_REQUIRES_DEXTERITY + var/can_play_unanchored = FALSE + var/list/allowed_instrument_ids = list("r3grand","r3harpsi","crharpsi","crgrand1","crbright1", "crichugan", "crihamgan","piano") + var/datum/song/song + +/obj/structure/musician/Initialize(mapload) + . = ..() + song = new(src, allowed_instrument_ids) + allowed_instrument_ids = null + +/obj/structure/musician/Destroy() + QDEL_NULL(song) + return ..() + +/obj/structure/musician/proc/should_stop_playing(mob/user) + if(!(anchored || can_play_unanchored)) + return TRUE + if(!user) + return FALSE + return !user.canUseTopic(src, FALSE, TRUE, FALSE, FALSE) //can play with TK and while resting because fun. + +/obj/structure/musician/ui_interact(mob/user) + . = ..() + song.ui_interact(user) + +/obj/structure/musician/wrench_act(mob/living/user, obj/item/I) + default_unfasten_wrench(user, I, 40) + return TRUE + +/obj/structure/musician/piano + name = "space minimoog" + icon = 'icons/obj/musician.dmi' + icon_state = "minimoog" + anchored = TRUE + density = TRUE + +/obj/structure/musician/piano/unanchored + anchored = FALSE + +/obj/structure/musician/piano/Initialize(mapload) + . = ..() + if(prob(50) && icon_state == initial(icon_state)) + name = "space minimoog" + desc = "This is a minimoog, like a space piano, but more spacey!" + icon_state = "minimoog" + else + name = "space piano" + desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." + icon_state = "piano" \ No newline at end of file diff --git a/code/modules/integrated_electronics/core/analyzer.dm b/code/modules/integrated_electronics/core/analyzer.dm deleted file mode 100644 index c3851cdb320a9..0000000000000 --- a/code/modules/integrated_electronics/core/analyzer.dm +++ /dev/null @@ -1,22 +0,0 @@ -/obj/item/integrated_electronics/analyzer - name = "circuit analyzer" - desc = "This tool can scan an assembly and generate code necessary to recreate it in a circuit printer." - icon = 'icons/obj/assemblies/electronic_tools.dmi' - icon_state = "analyzer" - flags_1 = CONDUCT_1 - w_class = WEIGHT_CLASS_SMALL - -/obj/item/integrated_electronics/analyzer/afterattack(var/atom/A, var/mob/living/user) - . = ..() - if(istype(A, /obj/item/electronic_assembly)) - var/obj/item/electronic_assembly/EA = A - if(EA.idlock) - to_chat(user, "[A] is currently identity-locked and can't be analyzed.") - return FALSE - - var/saved = "[A.name] analyzed! On circuit printers with cloning enabled, you may use the code below to clone the circuit: [SScircuit.save_electronic_assembly(A)] "
- if(saved)
- to_chat(user, "You scan [A].")
- user << browse(saved, "window=circuit_scan;size=500x600;border=1;can_resize=1;can_close=1;can_minimize=1")
- else
- to_chat(user, "[A] is not complete enough to be encoded!")
diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm
deleted file mode 100644
index b3f604daf194b..0000000000000
--- a/code/modules/integrated_electronics/core/assemblies.dm
+++ /dev/null
@@ -1,1048 +0,0 @@
-#define IC_MAX_SIZE_BASE 25
-#define IC_COMPLEXITY_BASE 75
-
-/obj/item/electronic_assembly
- name = "electronic assembly"
- obj_flags = CAN_BE_HIT | UNIQUE_RENAME
- desc = "A case designed for building small electronics."
- w_class = WEIGHT_CLASS_SMALL
- icon = 'icons/obj/assemblies/electronic_setups.dmi'
- icon_state = "setup_small"
- item_flags = NOBLUDGEON
- materials = list() // To be filled later
- datum_flags = DF_USE_TAG
- var/list/assembly_components = list()
- var/list/ckeys_allowed_to_scan = list() // Players who built the circuit can scan it as a ghost.
- var/max_components = IC_MAX_SIZE_BASE
- var/max_complexity = IC_COMPLEXITY_BASE
- var/opened = TRUE
- var/obj/item/stock_parts/cell/battery // Internal cell which most circuits need to work.
- var/cell_type = /obj/item/stock_parts/cell
- var/can_charge = TRUE //Can it be charged in a recharger?
- var/can_fire_equipped = FALSE //Can it fire/throw weapons when the assembly is being held?
- var/charge_sections = 4
- var/charge_tick = FALSE
- var/charge_delay = 4
- var/use_cyborg_cell = TRUE
- var/ext_next_use = 0
- var/atom/collw
- var/obj/item/card/id/access_card
- var/allowed_circuit_action_flags = IC_ACTION_COMBAT | IC_ACTION_LONG_RANGE //which circuit flags are allowed
- var/combat_circuits = 0 //number of combat cicuits in the assembly, used for diagnostic hud
- var/long_range_circuits = 0 //number of long range cicuits in the assembly, used for diagnostic hud
- var/prefered_hud_icon = "hudstat" // Used by the AR circuit to change the hud icon.
- var/creator // circuit creator if any
- var/static/next_assembly_id = 0
- var/sealed = FALSE
- var/datum/weakref/idlock = null
-
- hud_possible = list(DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD, DIAG_CIRCUIT_HUD) //diagnostic hud overlays
- max_integrity = 50
- pass_flags = 0
- armor = list("melee" = 50, "bullet" = 70, "laser" = 70, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0)
- anchored = FALSE
- var/can_anchor = TRUE
- var/detail_color = COLOR_ASSEMBLY_BLACK
-
-/obj/item/electronic_assembly/New()
- ..()
- src.max_components = round(max_components)
- src.max_complexity = round(max_complexity)
-
-/obj/item/electronic_assembly/GenerateTag()
- tag = "assembly_[next_assembly_id++]"
-
-/obj/item/electronic_assembly/examine(mob/user)
- . = ..()
- if(can_anchor)
- to_chat(user, "The anchoring bolts [anchored ? "are" : "can be"] wrenched in place and the maintenance panel [opened ? "can be" : "is"] screwed in place.")
- else
- to_chat(user, "The maintenance panel [opened ? "can be" : "is"] screwed in place.")
-
- if((isobserver(user) && ckeys_allowed_to_scan[user.ckey]) || IsAdminGhost(user))
- to_chat(user, "You can scan this circuit.")
-
- for(var/obj/item/integrated_circuit/I in assembly_components)
- I.external_examine(user)
- if(opened)
- interact(user)
-
-/obj/item/electronic_assembly/proc/check_interactivity(mob/user)
- if(!istype(user, /mob))
- return
- return user.canUseTopic(src, BE_CLOSE)
-
-/obj/item/electronic_assembly/Bump(atom/AM)
- collw = AM
- .=..()
- if((istype(collw, /obj/machinery/door/airlock) || istype(collw, /obj/machinery/door/window)) && (!isnull(access_card)))
- var/obj/machinery/door/D = collw
- if(D.check_access(access_card))
- D.open()
-
-/obj/item/electronic_assembly/Initialize()
- .=..()
- START_PROCESSING(SScircuit, src)
- materials[/datum/material/iron] = round((max_complexity + max_components) / 4) * SScircuit.cost_multiplier
-
- //sets up diagnostic hud view
- prepare_huds()
- for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
- diag_hud.add_to_hud(src)
- diag_hud_set_circuithealth()
- diag_hud_set_circuitcell()
- diag_hud_set_circuitstat()
- diag_hud_set_circuittracking()
-
- access_card = new /obj/item/card/id(src)
-
-/obj/item/electronic_assembly/Destroy()
- STOP_PROCESSING(SScircuit, src)
- for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
- diag_hud.remove_from_hud(src)
- QDEL_NULL(access_card)
- return ..()
-
-/obj/item/electronic_assembly/process()
- handle_idle_power()
- check_pulling()
-
- //updates diagnostic hud
- diag_hud_set_circuithealth()
- diag_hud_set_circuitcell()
-
-/obj/item/electronic_assembly/proc/handle_idle_power()
-
- // First we generate power.
- for(var/obj/item/integrated_circuit/passive/power/P in assembly_components)
- P.make_energy()
-
- // Now spend it.
- for(var/obj/item/integrated_circuit/I in assembly_components)
- if(I.power_draw_idle)
- if(!draw_power(I.power_draw_idle))
- I.power_fail()
-
-/obj/item/electronic_assembly/interact(mob/user, circuit)
- ui_interact(user, circuit)
-
-/obj/item/electronic_assembly/ui_interact(mob/user, obj/item/integrated_circuit/circuit_pins)
- . = ..()
- if(!check_interactivity(user))
- return
-
- var/total_part_size = return_total_size()
- var/total_complexity = return_total_complexity()
- var/datum/browser/popup = new(user, "scannernew", name, 800, 630) // Set up the popup browser window
- popup.add_stylesheet("scannernew", 'html/browser/assembly_ui.css')
-
- var/HTML = "
[SScircuit.save_electronic_assembly(src)] "
- usr << browse(saved, "window=circuit_scan;size=500x600;border=1;can_resize=1;can_close=1;can_minimize=1")
- else
- to_chat(usr, "The circuit is empty!")
- return
-
- if(!check_interactivity(usr))
- return
-
- if(href_list["rename"])
- rename(usr)
-
- if(href_list["remove_cell"])
- if(!battery)
- to_chat(usr, "There's no power cell to remove from \the [src].")
- else
- battery.forceMove(drop_location())
- playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
- to_chat(usr, "You pull \the [battery] out of \the [src]'s power supplier.")
- battery = null
- diag_hud_set_circuitstat() //update diagnostic hud
-
- var/obj/item/integrated_circuit/component
-
- if(href_list["component"])
- component = locate(href_list["component"]) in assembly_components
-
- if(!component)
- return
-
-
- if(href_list["scan"])
- var/obj/held_item = usr.get_active_held_item()
- if(istype(held_item, /obj/item/integrated_electronics/debugger))
- var/obj/item/integrated_electronics/debugger/D = held_item
- if(D.accepting_refs)
- D.afterattack(component, usr, TRUE)
- else
- to_chat(usr, "The debugger's 'ref scanner' needs to be on.")
- else
- to_chat(usr, "You need a debugger set to 'ref' mode to do that.")
-
- // Builtin components are not supposed to be removed or rearranged
- if(!component.removable)
- return
-
- add_allowed_scanner(usr.ckey)
-
- // Find the position of a first removable component
- var/first_removable_pos = 0
- for(var/i in assembly_components)
- first_removable_pos++
- var/obj/item/integrated_circuit/temp_component = i
- if(temp_component.removable)
- break
-
- if(href_list["remove"])
- if(try_remove_component(component, usr))
- component = null
-
- if(href_list["rename_component"])
- component.rename_component(usr)
- if(component.assembly)
- component.assembly.add_allowed_scanner(usr.ckey)
-
- if(href_list["interact"])
- var/obj/item/I = usr.get_active_held_item()
- if(istype(I))
- I.melee_attack_chain(usr, component)
- else
- component.attack_hand(usr)
-
- // Adjust the position
- if(href_list["change_pos"])
- var/new_pos = max(input(usr,"Write the new number","New position") as num,1)
-
- if(new_pos > assembly_components.len)
- new_pos = assembly_components.len
-
- if(new_pos < first_removable_pos)
- new_pos = first_removable_pos
-
- assembly_components.Remove(component)
- assembly_components.Insert(new_pos, component)
-
- interact(usr, component) // To refresh the UI.
-
-/obj/item/electronic_assembly/pickup(mob/living/user)
- . = ..()
- //update diagnostic hud when picked up, true is used to force the hud to be hidden
- diag_hud_set_circuithealth(TRUE)
- diag_hud_set_circuitcell(TRUE)
- diag_hud_set_circuitstat(TRUE)
- diag_hud_set_circuittracking(TRUE)
-
-/obj/item/electronic_assembly/dropped(mob/user)
- . = ..()
- //update diagnostic hud when dropped
- diag_hud_set_circuithealth()
- diag_hud_set_circuitcell()
- diag_hud_set_circuitstat()
- diag_hud_set_circuittracking()
-
-/obj/item/electronic_assembly/proc/rename()
- var/mob/M = usr
- if(!check_interactivity(M))
- return
-
- var/input = reject_bad_name(input("What do you want to name this?", "Rename", src.name) as null|text, TRUE)
- if(!check_interactivity(M))
- return
- if(src && input)
- to_chat(M, "The machine now has a label reading '[input]'.")
- name = input
-
-/obj/item/electronic_assembly/proc/add_allowed_scanner(ckey)
- ckeys_allowed_to_scan[ckey] = TRUE
-
-/obj/item/electronic_assembly/proc/can_move()
- return FALSE
-
-/obj/item/electronic_assembly/update_icon()
- if(opened)
- icon_state = initial(icon_state) + "-open"
- else
- icon_state = initial(icon_state)
- cut_overlays()
- if(detail_color == COLOR_ASSEMBLY_BLACK) //Black colored overlay looks almost but not exactly like the base sprite, so just cut the overlay and avoid it looking kinda off.
- return
- var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/assemblies/electronic_setups.dmi', "[icon_state]-color")
- detail_overlay.color = detail_color
- add_overlay(detail_overlay)
-
-/obj/item/electronic_assembly/proc/return_total_complexity()
- var/returnvalue = 0
- for(var/obj/item/integrated_circuit/part in assembly_components)
- returnvalue += part.complexity
- return(returnvalue)
-
-/obj/item/electronic_assembly/proc/return_total_size()
- var/returnvalue = 0
- for(var/obj/item/integrated_circuit/part in assembly_components)
- returnvalue += part.size
- return(returnvalue)
-
-// Returns true if the circuit made it inside.
-/obj/item/electronic_assembly/proc/try_add_component(obj/item/integrated_circuit/IC, mob/user)
- if(!opened)
- to_chat(user, "\The [src]'s hatch is closed, you can't put anything inside.")
- return FALSE
-
- if(IC.w_class > w_class)
- to_chat(user, "\The [IC] is way too big to fit into \the [src].")
- return FALSE
-
- var/total_part_size = return_total_size()
- var/total_complexity = return_total_complexity()
-
- if(IC.max_allowed)
- var/current_components
- for(var/obj/item/integrated_circuit/component as anything in assembly_components)
- if(component.type == IC.type)
- current_components++
- if(current_components >= IC.max_allowed)
- to_chat(user, "You can't seem to add the '[IC]', as there are too many installed already.")
- return FALSE
-
- if((total_part_size + IC.size) > max_components)
- to_chat(user, "You can't seem to add the '[IC]', as there's insufficient space.")
- return FALSE
- if((total_complexity + IC.complexity) > max_complexity)
- to_chat(user, "You can't seem to add the '[IC]', since this setup's too complicated for the case.")
- return FALSE
- if((allowed_circuit_action_flags & IC.action_flags) != IC.action_flags)
- to_chat(user, "You can't seem to add the '[IC]', since the case doesn't support the circuit type.")
- return FALSE
-
- if(!user.transferItemToLoc(IC, src))
- return FALSE
-
- to_chat(user, "You slide [IC] inside [src].")
- playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
- add_allowed_scanner(user.ckey)
- investigate_log("had [IC]([IC.type]) inserted by [key_name(user)].", INVESTIGATE_CIRCUIT)
-
- add_component(IC)
- return TRUE
-
-
-// Actually puts the circuit inside, doesn't perform any checks.
-/obj/item/electronic_assembly/proc/add_component(obj/item/integrated_circuit/component)
- component.forceMove(get_object())
- component.assembly = src
- assembly_components |= component
-
- //increment numbers for diagnostic hud
- if(component.action_flags & IC_ACTION_COMBAT)
- combat_circuits += 1;
- if(component.action_flags & IC_ACTION_LONG_RANGE)
- long_range_circuits += 1;
-
- //diagnostic hud update
- diag_hud_set_circuitstat()
- diag_hud_set_circuittracking()
-
-
-/obj/item/electronic_assembly/proc/try_remove_component(obj/item/integrated_circuit/IC, mob/user, silent)
- if(!opened)
- if(!silent)
- to_chat(user, "[src]'s hatch is closed, so you can't fiddle with the internal components.")
- return FALSE
-
- if(!IC.removable)
- if(!silent)
- to_chat(user, "[src] is permanently attached to the case.")
- return FALSE
-
- remove_component(IC)
- if(!silent)
- to_chat(user, "You pop \the [IC] out of the case, and slide it out.")
- playsound(src, 'sound/items/crowbar.ogg', 50, 1)
- user.put_in_hands(IC)
- add_allowed_scanner(user.ckey)
- investigate_log("had [IC]([IC.type]) removed by [key_name(user)].", INVESTIGATE_CIRCUIT)
-
- return TRUE
-
-// Actually removes the component, doesn't perform any checks.
-/obj/item/electronic_assembly/proc/remove_component(obj/item/integrated_circuit/component)
- component.disconnect_all()
- component.forceMove(drop_location())
- component.assembly = null
-
- assembly_components -= component
-
- //decrement numbers for diagnostic hud
- if(component.action_flags & IC_ACTION_COMBAT)
- combat_circuits -= 1;
- if(component.action_flags & IC_ACTION_LONG_RANGE)
- long_range_circuits -= 1;
-
- //diagnostic hud update
- diag_hud_set_circuitstat()
- diag_hud_set_circuittracking()
-
-
-/obj/item/electronic_assembly/afterattack(atom/target, mob/user, proximity)
- . = ..()
- for(var/obj/item/integrated_circuit/input/S in assembly_components)
- if(S.sense(target,user,proximity))
- visible_message(" [user] waves [src] around [target].")
-
-
-/obj/item/electronic_assembly/screwdriver_act(mob/living/user, obj/item/I)
- if(sealed)
- to_chat(user,"The assembly is sealed. Any attempt to force it open would break it.")
- return FALSE
- if(..())
- return TRUE
- I.play_tool_sound(src)
- opened = !opened
- to_chat(user, "You [opened ? "open" : "close"] the maintenance hatch of [src].")
- update_icon()
- return TRUE
-
-/obj/item/electronic_assembly/welder_act(mob/living/user, obj/item/I)
- var/type_to_use
-
- if(!sealed)
- type_to_use = input("What would you like to do?","[src] type setting") as null|anything in list("repair", "seal")
- else
- type_to_use = input("What would you like to do?","[src] type setting") as null|anything in list("repair", "unseal")
-
- switch(type_to_use)
- if("repair")
- to_chat(world,"Integrity: [obj_integrity] / [max_integrity]")
- if(obj_integrity < max_integrity)
- obj_integrity = min(obj_integrity + 20,max_integrity)
- to_chat(world,"Integrity: [obj_integrity] / [max_integrity]")
- to_chat(user,"You fix the dents and scratches of the assembly.")
- to_chat(world,user)
- return TRUE
-
- else
- to_chat(user,"The assembly is already in impeccable condition.")
- return FALSE
-
- if("seal")
- if(!opened)
- sealed = TRUE
- if(I.use_tool(src, user, 50, volume=100, amount=3))
- to_chat(user,"You seal the assembly, making it impossible to be opened.")
- return TRUE
-
- else
- to_chat(user,"You need to close the assembly first before sealing it indefinitely!")
- return FALSE
-
- if("unseal")
- to_chat(user,"You start unsealing the assembly carefully...")
- if(I.use_tool(src, user, 50, volume=250, amount=3))
- for(var/obj/item/integrated_circuit/IC in assembly_components)
- if(prob(50))
- IC.disconnect_all()
-
- to_chat(user,"You unsealed the assembly.")
- sealed = FALSE
- return TRUE
-
-/obj/item/electronic_assembly/attackby(obj/item/I, mob/living/user)
- if(can_anchor && default_unfasten_wrench(user, I, 20))
- return
-
- // ID-Lock part: check if we have an id-lock and only lock if we're not trying to get values from it, to prevent accidents
- if(istype(I, /obj/item/integrated_electronics/debugger))
- var/obj/item/integrated_electronics/debugger/debugger = I
- if(debugger.idlock)
- // check if unlocked to lock
- if(!idlock)
- idlock = debugger.idlock
- to_chat(user,"You lock \the [src].")
-
- //if locked, unlock if ids match
- else
- if(idlock.resolve() == debugger.idlock.resolve())
- idlock = null
- to_chat(user,"You unlock \the [src].")
-
- else
- to_chat(user,"The scanned ID doesn't match with \the [src]'s lock.")
-
- debugger.idlock = null
- return
-
- if(istype(I, /obj/item/integrated_circuit))
- if(!user.canUnEquip(I))
- return FALSE
- if(try_add_component(I, user))
- return TRUE
- else
- for(var/obj/item/integrated_circuit/input/S in assembly_components)
- S.attackby_react(I,user,user.a_intent)
- return ..()
-
- else if(I.tool_behaviour == TOOL_MULTITOOL || istype(I, /obj/item/integrated_electronics/wirer) || istype(I, /obj/item/integrated_electronics/debugger))
- if(opened)
- interact(user)
- return TRUE
- else
- to_chat(user, "[src]'s hatch is closed, so you can't fiddle with the internal components.")
- for(var/obj/item/integrated_circuit/input/S in assembly_components)
- S.attackby_react(I,user,user.a_intent)
- return ..()
-
- else if(istype(I, /obj/item/stock_parts/cell))
- if(!opened)
- to_chat(user, "[src]'s hatch is closed, so you can't access \the [src]'s power supplier.")
- for(var/obj/item/integrated_circuit/input/S in assembly_components)
- S.attackby_react(I,user,user.a_intent)
- return ..()
- if(battery)
- to_chat(user, "[src] already has \a [battery] installed. Remove it first if you want to replace it.")
- for(var/obj/item/integrated_circuit/input/S in assembly_components)
- S.attackby_react(I,user,user.a_intent)
- return ..()
- I.forceMove(src)
- battery = I
- diag_hud_set_circuitstat() //update diagnostic hud
- playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1)
- to_chat(user, "You slot the [I] inside \the [src]'s power supplier.")
- return TRUE
-
- else if(istype(I, /obj/item/integrated_electronics/detailer))
- var/obj/item/integrated_electronics/detailer/D = I
- detail_color = D.detail_color
- update_icon()
-
- else
- if(user.a_intent != INTENT_HELP)
- return ..()
- var/list/input_selection = list()
- //Check all the components asking for an input
- for(var/obj/item/integrated_circuit/input in assembly_components)
- if((input.demands_object_input && opened) || (input.demands_object_input && input.can_input_object_when_closed))
- var/i = 0
- //Check if there is another component with the same name and append a number for identification
- for(var/s in input_selection)
- var/obj/item/integrated_circuit/s_circuit = input_selection[s] //The for-loop iterates the keys of the associative list.
- if(s_circuit.name == input.name && s_circuit.displayed_name == input.displayed_name && s_circuit != input)
- i++
- var/disp_name= "[input.displayed_name] \[[input]\]"
- if(i)
- disp_name += " ([i+1])"
- //Associative lists prevent me from needing another list and using a Find proc
- input_selection[disp_name] = input
-
- var/obj/item/integrated_circuit/choice
- if(input_selection)
- if(input_selection.len == 1)
- choice = input_selection[input_selection[1]]
- else
- var/selection = input(user, "Where do you want to insert that item?", "Interaction") as null|anything in input_selection
- if(!check_interactivity(user))
- return ..()
- if(selection)
- choice = input_selection[selection]
- if(choice)
- choice.additem(I, user)
- for(var/obj/item/integrated_circuit/input/S in assembly_components)
- S.attackby_react(I,user,user.a_intent)
- return ..()
-
-
-/obj/item/electronic_assembly/attack_self(mob/user)
- if(!check_interactivity(user))
- return
- if(opened)
- interact(user)
-
- var/list/input_selection = list()
- //Check all the components asking for an input
- for(var/obj/item/integrated_circuit/input/input in assembly_components)
- if(input.can_be_asked_input)
- var/i = 0
- //Check if there is another component with the same name and append a number for identification
- for(var/s in input_selection)
- var/obj/item/integrated_circuit/s_circuit = input_selection[s] //The for-loop iterates the keys of an associative list.
- if(s_circuit.name == input.name && s_circuit.displayed_name == input.displayed_name && s_circuit != input)
- i++
- var/disp_name= "[input.displayed_name] \[[input]\]"
- if(i)
- disp_name += " ([i+1])"
- //Associative lists prevent me from needing another list and using a Find proc
- input_selection[disp_name] = input
-
- var/obj/item/integrated_circuit/input/choice
-
-
- if(input_selection)
- if(input_selection.len ==1)
- choice = input_selection[input_selection[1]]
- else
- var/selection = input(user, "What do you want to interact with?", "Interaction") as null|anything in input_selection
- if(!check_interactivity(user))
- return
- if(selection)
- choice = input_selection[selection]
-
- if(choice)
- choice.ask_for_input(user)
-
-/obj/item/electronic_assembly/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_CONTENTS)
- return
- for(var/I in src)
- var/atom/movable/AM = I
- AM.emp_act(severity)
-
-// Returns true if power was successfully drawn.
-/obj/item/electronic_assembly/proc/draw_power(amount)
- if(battery && battery.use(amount * GLOB.CELLRATE))
- return TRUE
- return FALSE
-
-// Ditto for giving.
-/obj/item/electronic_assembly/proc/give_power(amount)
- if(battery && battery.give(amount * GLOB.CELLRATE))
- return TRUE
- return FALSE
-
-/obj/item/electronic_assembly/Moved(oldLoc, dir)
- for(var/I in assembly_components)
- var/obj/item/integrated_circuit/IC = I
- IC.ext_moved(oldLoc, dir)
- if(light) //Update lighting objects (From light circuits).
- update_light()
-
-/obj/item/electronic_assembly/stop_pulling()
- for(var/I in assembly_components)
- var/obj/item/integrated_circuit/IC = I
- IC.stop_pulling()
- ..()
-
-
-// Returns the object that is supposed to be used in attack messages, location checks, etc.
-// Override in children for special behavior.
-/obj/item/electronic_assembly/proc/get_object()
- return src
-
-// Returns the location to be used for dropping items.
-// Same as the regular drop_location(), but with checks being run on acting_object if necessary.
-/obj/item/integrated_circuit/drop_location()
- var/atom/movable/acting_object = get_object()
-
- // plz no infinite loops
- if(acting_object == src)
- return ..()
-
- return acting_object.drop_location()
-
-/obj/item/electronic_assembly/attack_tk(mob/user)
- if(anchored)
- return
- ..()
-
-/obj/item/electronic_assembly/attack_hand(mob/user)
- if(anchored)
- attack_self(user)
- return
- ..()
-
-/obj/item/electronic_assembly/default //The /default electronic_assemblys are to allow the introduction of the new naming scheme without breaking old saves.
- name = "type-a electronic assembly"
-
-/obj/item/electronic_assembly/calc
- name = "type-b electronic assembly"
- icon_state = "setup_small_calc"
- desc = "A case designed for building small electronics. This one resembles a pocket calculator."
-
-/obj/item/electronic_assembly/clam
- name = "type-c electronic assembly"
- icon_state = "setup_small_clam"
- desc = "A case designed for building small electronics. This one has a clamshell design."
-
-/obj/item/electronic_assembly/simple
- name = "type-d electronic assembly"
- icon_state = "setup_small_simple"
- desc = "A case designed for building small electronics. This one has a simple design."
-
-/obj/item/electronic_assembly/hook
- name = "type-e electronic assembly"
- icon_state = "setup_small_hook"
- desc = "A case designed for building small electronics. This one looks like it has a belt clip, but it's purely decorative."
-
-/obj/item/electronic_assembly/pda
- name = "type-f electronic assembly"
- icon_state = "setup_small_pda"
- desc = "A case designed for building small electronics. This one resembles a PDA."
-
-/obj/item/electronic_assembly/small
- name = "electronic device"
- icon_state = "setup_device"
- desc = "A case designed for building tiny-sized electronics."
- w_class = WEIGHT_CLASS_TINY
- max_components = IC_MAX_SIZE_BASE / 2
- max_complexity = IC_COMPLEXITY_BASE / 2
-
-/obj/item/electronic_assembly/small/default
- name = "type-a electronic device"
-
-/obj/item/electronic_assembly/small/cylinder
- name = "type-b electronic device"
- icon_state = "setup_device_cylinder"
- desc = "A case designed for building tiny-sized electronics. This one has a cylindrical design."
-
-/obj/item/electronic_assembly/small/scanner
- name = "type-c electronic device"
- icon_state = "setup_device_scanner"
- desc = "A case designed for building tiny-sized electronics. This one has a scanner-like design."
-
-/obj/item/electronic_assembly/small/hook
- name = "type-d electronic device"
- icon_state = "setup_device_hook"
- desc = "A case designed for building tiny-sized electronics. This one looks like it has a belt clip, but it's purely decorative."
-
-/obj/item/electronic_assembly/small/box
- name = "type-e electronic device"
- icon_state = "setup_device_box"
- desc = "A case designed for building tiny-sized electronics. This one has a box design."
-
-/obj/item/electronic_assembly/medium
- name = "electronic mechanism"
- icon_state = "setup_medium"
- desc = "A case designed for building medium-sized electonics."
- w_class = WEIGHT_CLASS_NORMAL
- max_components = IC_MAX_SIZE_BASE * 2
- max_complexity = IC_COMPLEXITY_BASE * 2
-
-/obj/item/electronic_assembly/medium/default
- name = "type-a electronic mechanism"
-
-/obj/item/electronic_assembly/medium/box
- name = "type-b electronic mechanism"
- icon_state = "setup_medium_box"
- desc = "A case designed for building medium-sized electonics. This one has a box design."
-
-/obj/item/electronic_assembly/medium/clam
- name = "type-c electronic mechanism"
- icon_state = "setup_medium_clam"
- desc = "A case designed for building medium-sized electonics. This one has a clamshell design."
-
-/obj/item/electronic_assembly/medium/medical
- name = "type-d electronic mechanism"
- icon_state = "setup_medium_med"
- desc = "A case designed for building medium-sized electonics. This one resembles medical apparatus."
-
-/obj/item/electronic_assembly/medium/gun
- name = "type-e electronic mechanism"
- icon_state = "setup_medium_gun"
- item_state = "circuitgun"
- desc = "A case designed for building medium-sized electonics. This one resembles a gun, or some type of tool, if you're feeling optimistic. It can fire guns and throw items while the user is holding it."
- lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
- can_fire_equipped = TRUE
-
-/obj/item/electronic_assembly/medium/radio
- name = "type-f electronic mechanism"
- icon_state = "setup_medium_radio"
- desc = "A case designed for building medium-sized electonics. This one resembles an old radio."
-
-/obj/item/electronic_assembly/large
- name = "electronic machine"
- icon_state = "setup_large"
- desc = "A case designed for building large electronics."
- w_class = WEIGHT_CLASS_BULKY
- max_components = IC_MAX_SIZE_BASE * 4
- max_complexity = IC_COMPLEXITY_BASE * 4
-
-/obj/item/electronic_assembly/large/default
- name = "type-a electronic machine"
-
-/obj/item/electronic_assembly/large/scope
- name = "type-b electronic machine"
- icon_state = "setup_large_scope"
- desc = "A case designed for building large electronics. This one resembles an oscilloscope."
-
-/obj/item/electronic_assembly/large/terminal
- name = "type-c electronic machine"
- icon_state = "setup_large_terminal"
- desc = "A case designed for building large electronics. This one resembles a computer terminal."
-
-/obj/item/electronic_assembly/large/arm
- name = "type-d electronic machine"
- icon_state = "setup_large_arm"
- desc = "A case designed for building large electronics. This one resembles a robotic arm."
-
-/obj/item/electronic_assembly/large/tall
- name = "type-e electronic machine"
- icon_state = "setup_large_tall"
- desc = "A case designed for building large electronics. This one has a tall design."
-
-/obj/item/electronic_assembly/large/industrial
- name = "type-f electronic machine"
- icon_state = "setup_large_industrial"
- desc = "A case designed for building mobile electronics. This one resembles industrial machinery."
-
-/obj/item/electronic_assembly/drone
- name = "electronic drone"
- icon_state = "setup_drone"
- desc = "A case designed for building mobile electronics."
- w_class = WEIGHT_CLASS_BULKY
- max_components = IC_MAX_SIZE_BASE * 3
- max_complexity = IC_COMPLEXITY_BASE * 3
- allowed_circuit_action_flags = IC_ACTION_MOVEMENT | IC_ACTION_COMBAT | IC_ACTION_LONG_RANGE
- can_anchor = FALSE
-
-/obj/item/electronic_assembly/drone/can_move()
- return TRUE
-
-/obj/item/electronic_assembly/drone/default
- name = "type-a electronic drone"
-
-/obj/item/electronic_assembly/drone/arms
- name = "type-b electronic drone"
- icon_state = "setup_drone_arms"
- desc = "A case designed for building mobile electronics. This one is armed and dangerous."
-
-/obj/item/electronic_assembly/drone/secbot
- name = "type-c electronic drone"
- icon_state = "setup_drone_secbot"
- desc = "A case designed for building mobile electronics. This one resembles a Securitron."
-
-/obj/item/electronic_assembly/drone/medbot
- name = "type-d electronic drone"
- icon_state = "setup_drone_medbot"
- desc = "A case designed for building mobile electronics. This one resembles a Medibot."
-
-/obj/item/electronic_assembly/drone/genbot
- name = "type-e electronic drone"
- icon_state = "setup_drone_genbot"
- desc = "A case designed for building mobile electronics. This one has a generic bot design."
-
-/obj/item/electronic_assembly/drone/android
- name = "type-f electronic drone"
- icon_state = "setup_drone_android"
- desc = "A case designed for building mobile electronics. This one has a hominoid design."
-
-/obj/item/electronic_assembly/wallmount
- name = "wall-mounted electronic assembly"
- icon_state = "setup_wallmount_medium"
- desc = "A case designed for building medium-sized electronics.. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on."
- w_class = WEIGHT_CLASS_NORMAL
- max_components = IC_MAX_SIZE_BASE * 2
- max_complexity = IC_COMPLEXITY_BASE * 2
-
-/obj/item/electronic_assembly/wallmount/heavy
- name = "heavy wall-mounted electronic assembly"
- icon_state = "setup_wallmount_large"
- desc = "A case designed for building large electronics. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on."
- w_class = WEIGHT_CLASS_BULKY
- max_components = IC_MAX_SIZE_BASE * 4
- max_complexity = IC_COMPLEXITY_BASE * 4
-
-/obj/item/electronic_assembly/wallmount/light
- name = "light wall-mounted electronic assembly"
- icon_state = "setup_wallmount_small"
- desc = "A case designed for building small electronics. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on."
- w_class = WEIGHT_CLASS_SMALL
- max_components = IC_MAX_SIZE_BASE
- max_complexity = IC_COMPLEXITY_BASE
-
-/obj/item/electronic_assembly/wallmount/tiny
- name = "tiny wall-mounted electronic assembly"
- icon_state = "setup_wallmount_tiny"
- desc = "It's a case, for building tiny electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on."
- w_class = WEIGHT_CLASS_TINY
- max_components = IC_MAX_SIZE_BASE / 2
- max_complexity = IC_COMPLEXITY_BASE / 2
-
-/obj/item/electronic_assembly/wallmount/proc/mount_assembly(turf/on_wall, mob/user) //Yeah, this is admittedly just an abridged and kitbashed version of the wallframe attach procs.
- if(get_dist(on_wall,user)>1)
- return
- var/ndir = get_dir(on_wall, user)
- if(!(ndir in GLOB.cardinals))
- return
- var/turf/T = get_turf(user)
- if(!isfloorturf(T))
- to_chat(user, "You cannot place [src] on this spot!")
- return
- if(gotwallitem(T, ndir))
- to_chat(user, "There's already an item on this wall!")
- return
- playsound(src.loc, 'sound/machines/click.ogg', 75, 1)
- user.visible_message("[user.name] attaches [src] to the wall.",
- "You attach [src] to the wall.",
- "You hear clicking.")
- user.dropItemToGround(src)
- switch(ndir)
- if(NORTH)
- pixel_y = -31
- if(SOUTH)
- pixel_y = 31
- if(EAST)
- pixel_x = -31
- if(WEST)
- pixel_x = 31
diff --git a/code/modules/integrated_electronics/core/debugger.dm b/code/modules/integrated_electronics/core/debugger.dm
deleted file mode 100644
index 867ae9ad460bb..0000000000000
--- a/code/modules/integrated_electronics/core/debugger.dm
+++ /dev/null
@@ -1,108 +0,0 @@
-/obj/item/integrated_electronics/debugger
- name = "circuit debugger"
- desc = "This small tool allows one working with custom machinery to directly set data to a specific pin, useful for writing \
- settings to specific circuits, or for debugging purposes. It can also pulse activation pins."
- icon = 'icons/obj/assemblies/electronic_tools.dmi'
- icon_state = "debugger"
- flags_1 = CONDUCT_1
- item_flags = NOBLUDGEON
- w_class = WEIGHT_CLASS_SMALL
- var/data_to_write = null
- var/accepting_refs = FALSE
- var/copy_values = FALSE
- var/copy_id = FALSE
- var/datum/weakref/idlock = null
-
-/obj/item/integrated_electronics/debugger/attack_self(mob/user)
- var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref","copy","null","id lock")
- if(!user.IsAdvancedToolUser())
- return
-
- var/new_data = null
- switch(type_to_use)
- if("string")
- accepting_refs = FALSE
- copy_values = FALSE
- copy_id = FALSE
- new_data = stripped_input(user, "Now type in a string.","[src] string writing", no_trim = TRUE)
- if(istext(new_data) && user.IsAdvancedToolUser())
- data_to_write = new_data
- to_chat(user, "You set \the [src]'s memory to \"[new_data]\".")
- if("number")
- accepting_refs = FALSE
- copy_values = FALSE
- copy_id = FALSE
- new_data = input(user, "Now type in a number.","[src] number writing") as null|num
- if(isnum_safe(new_data) && user.IsAdvancedToolUser())
- data_to_write = new_data
- to_chat(user, "You set \the [src]'s memory to [new_data].")
- if("ref")
- accepting_refs = TRUE
- copy_values = FALSE
- copy_id = FALSE
- to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \
- an object for a ref of that object to save it in memory.")
- if("copy")
- accepting_refs = FALSE
- copy_values = TRUE
- copy_id = FALSE
- to_chat(user, "You turn \the [src]'s value copier on. Use it on a pin \
- to save its current value in memory.")
- if("null")
- data_to_write = null
- copy_values = FALSE
- to_chat(user, "You set \the [src]'s memory to absolutely nothing.")
- if("id lock")
- accepting_refs = FALSE
- copy_values = FALSE
- copy_id = TRUE
- to_chat(user, "You turn \the [src]'s id card scanner on. Use your own card \
- to store the identity and id-lock an assembly.")
-
-/obj/item/integrated_electronics/debugger/afterattack(atom/target, mob/living/user, proximity)
- . = ..()
- if(accepting_refs && proximity)
- data_to_write = WEAKREF(target)
- visible_message("[user] slides \a [src]'s over \the [target].")
- to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \
- now off.")
- accepting_refs = FALSE
-
- else if(copy_id && proximity)
- if(istype(target,/obj/item/card/id))
- idlock = WEAKREF(target)
- to_chat(user, "You set \the [src]'s card memory to [target.name]. The id card scanner is \
- now off.")
-
- else
- to_chat(user, "You turn the id card scanner is off.")
-
- copy_id = FALSE
- return
-
-/obj/item/integrated_electronics/debugger/proc/write_data(var/datum/integrated_io/io, mob/user)
- //If the pin can take data:
- if(io.io_type == DATA_CHANNEL)
- //If the debugger is set to copy, copy the data in the pin onto it
- if(copy_values)
- data_to_write = io.data
- to_chat(user, "You let the debugger copy the data.")
- copy_values = FALSE
- return
-
- //Else, write the data to the pin
- io.write_data_to_pin(data_to_write)
- var/data_to_show = data_to_write
- //This is only to convert a weakref into a name for better output
- if(isweakref(data_to_write))
- var/datum/weakref/w = data_to_write
- var/atom/A = w.resolve()
- data_to_show = A.name
- to_chat(user, "You write '[data_to_write ? data_to_show : "NULL"]' to the '[io]' pin of \the [io.holder].")
-
- //If the pin can only be pulsed
- else if(io.io_type == PULSE_CHANNEL)
- io.holder.check_then_do_work(io.ord,ignore_power = TRUE)
- to_chat(user, "You pulse \the [io.holder]'s [io].")
-
- io.holder.interact(user) // This is to update the UI.
diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm
deleted file mode 100644
index 33f7ef96add03..0000000000000
--- a/code/modules/integrated_electronics/core/detailer.dm
+++ /dev/null
@@ -1,48 +0,0 @@
-/obj/item/integrated_electronics/detailer
- name = "assembly detailer"
- desc = "A combination autopainter and flash anodizer designed to give electronic assemblies a colorful, wear-resistant finish."
- icon = 'icons/obj/assemblies/electronic_tools.dmi'
- icon_state = "detailer"
- flags_1 = CONDUCT_1
- item_flags = NOBLUDGEON
- w_class = WEIGHT_CLASS_SMALL
- var/data_to_write = null
- var/accepting_refs = FALSE
- var/detail_color = COLOR_ASSEMBLY_WHITE
- var/list/color_list = list(
- "black" = COLOR_ASSEMBLY_BLACK,
- "gray" = COLOR_FLOORTILE_GRAY,
- "machine gray" = COLOR_ASSEMBLY_BGRAY,
- "white" = COLOR_ASSEMBLY_WHITE,
- "red" = COLOR_ASSEMBLY_RED,
- "orange" = COLOR_ASSEMBLY_ORANGE,
- "beige" = COLOR_ASSEMBLY_BEIGE,
- "brown" = COLOR_ASSEMBLY_BROWN,
- "gold" = COLOR_ASSEMBLY_GOLD,
- "yellow" = COLOR_ASSEMBLY_YELLOW,
- "gurkha" = COLOR_ASSEMBLY_GURKHA,
- "light green" = COLOR_ASSEMBLY_LGREEN,
- "green" = COLOR_ASSEMBLY_GREEN,
- "light blue" = COLOR_ASSEMBLY_LBLUE,
- "blue" = COLOR_ASSEMBLY_BLUE,
- "purple" = COLOR_ASSEMBLY_PURPLE
- )
-
-/obj/item/integrated_electronics/detailer/Initialize()
- .=..()
- update_icon()
-
-/obj/item/integrated_electronics/detailer/update_icon()
- cut_overlays()
- var/mutable_appearance/detail_overlay = mutable_appearance('icons/obj/assemblies/electronic_tools.dmi', "detailer-color")
- detail_overlay.color = detail_color
- add_overlay(detail_overlay)
-
-/obj/item/integrated_electronics/detailer/attack_self(mob/user)
- var/color_choice = input(user, "Select color.", "Assembly Detailer") as null|anything in color_list
- if(!color_list[color_choice])
- return
- if(!in_range(src, user))
- return
- detail_color = color_list[color_choice]
- update_icon()
diff --git a/code/modules/integrated_electronics/core/helpers.dm b/code/modules/integrated_electronics/core/helpers.dm
deleted file mode 100644
index e9cbb346458d9..0000000000000
--- a/code/modules/integrated_electronics/core/helpers.dm
+++ /dev/null
@@ -1,142 +0,0 @@
-/obj/item/integrated_circuit/proc/setup_io(list/io_list, io_type, list/io_default_list, pin_type)
- var/list/io_list_copy = io_list.Copy()
- io_list.Cut()
- for(var/i in 1 to io_list_copy.len)
- var/io_entry = io_list_copy[i]
- var/default_data = null
- var/io_type_override = null
-
- // Override the default data.
- if(length(io_default_list)) // List containing special pin types that need to be added.
- default_data = io_default_list["[i]"] // This is deliberately text because the index is a number in text form.
-
- // Override the pin type.
- if(io_list_copy[io_entry])
- io_type_override = io_list_copy[io_entry]
-
- if(io_type_override)
- io_list.Add(new io_type_override(src, io_entry, default_data, pin_type,i))
- else
- io_list.Add(new io_type(src, io_entry, default_data, pin_type,i))
-
-
-/obj/item/integrated_circuit/proc/set_pin_data(pin_type, pin_number, datum/new_data)
- if(islist(new_data))
- for(var/i in 1 to length(new_data))
- if (istype(new_data) && !isweakref(new_data))
- new_data[i] = WEAKREF(new_data[i])
- if (istype(new_data) && !isweakref(new_data))
- new_data = WEAKREF(new_data)
- var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
- return pin.write_data_to_pin(new_data)
-
-/obj/item/integrated_circuit/proc/get_pin_data(pin_type, pin_number)
- var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
- return pin.get_data()
-
-/obj/item/integrated_circuit/proc/get_pin_data_as_type(pin_type, pin_number, as_type)
- var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
- return pin.data_as_type(as_type)
-
-/obj/item/integrated_circuit/proc/activate_pin(pin_number)
- var/datum/integrated_io/activate/A = activators[pin_number]
- A.push_data()
-
-/obj/item/integrated_circuit/proc/get_pin_ref(pin_type, pin_number)
- switch(pin_type)
- if(IC_INPUT)
- if(pin_number > inputs.len)
- return
- return inputs[pin_number]
- if(IC_OUTPUT)
- if(pin_number > outputs.len)
- return
- return outputs[pin_number]
- if(IC_ACTIVATOR)
- if(pin_number > activators.len)
- return
- return activators[pin_number]
- return
-
-/datum/integrated_io/proc/get_data()
- if(islist(data))
- for(var/i in 1 to length(data))
- if(isweakref(data[i]))
- data[i] = data[i].resolve()
- if(isweakref(data))
- return data.resolve()
- return data
-
-
-// Returns a list of parameters necessary to locate a pin in the assembly: component number, pin type and pin number
-// Components list can be supplied from the outside, for use in savefiles
-/datum/integrated_io/proc/get_pin_parameters(list/components)
- if(!holder)
- return
-
- if(!components)
- if(!holder.assembly)
- return
- components = holder.assembly.assembly_components
-
- var/component_number = components.Find(holder)
-
- var/list/pin_holder_list
- switch(pin_type)
- if(IC_INPUT)
- pin_holder_list = holder.inputs
- if(IC_OUTPUT)
- pin_holder_list = holder.outputs
- if(IC_ACTIVATOR)
- pin_holder_list = holder.activators
- else
- return
-
- var/pin_number = pin_holder_list.Find(src)
-
- return list(component_number, pin_type, pin_number)
-
-
-// Locates a pin in the assembly when given component number, pin type and pin number
-// Components list can be supplied from the outside, for use in savefiles
-/obj/item/electronic_assembly/proc/get_pin_ref(component_number, pin_type, pin_number, list/components)
- if(!components)
- components = assembly_components
-
- if(component_number > components.len)
- return
-
- var/obj/item/integrated_circuit/component = components[component_number]
- return component.get_pin_ref(pin_type, pin_number)
-
-
-// Same as get_pin_ref, but takes in a list of 3 parameters (same format as get_pin_parameters)
-// and performs extra sanity checks on parameters list and index numbers
-/obj/item/electronic_assembly/proc/get_pin_ref_list(list/parameters, list/components)
- if(!components)
- components = assembly_components
-
- if(!islist(parameters) || parameters.len != 3)
- return
-
- // Those are supposed to be list indexes, check them for sanity
- if(!isnum_safe(parameters[1]) || parameters[1] % 1 || parameters[1] < 1)
- return
-
- if(!isnum_safe(parameters[3]) || parameters[3] % 1 || parameters[3] < 1)
- return
-
- return get_pin_ref(parameters[1], parameters[2], parameters[3], components)
-
-
-
-
-// Used to obfuscate object refs imported/exported as strings.
-// Not very secure, but if someone still finds a way to abuse refs, they deserve it.
-/proc/XorEncrypt(string, key)
- if(!string || !key ||!istext(string)||!istext(key))
- return
- var/r
- for(var/i = 1 to length(string))
- r += ascii2text(text2ascii(string,i) ^ text2ascii(key,((i-1)%length(string))+1))
- return r
diff --git a/code/modules/integrated_electronics/core/integrated_circuit.dm b/code/modules/integrated_electronics/core/integrated_circuit.dm
deleted file mode 100644
index 73f64c98f1b17..0000000000000
--- a/code/modules/integrated_electronics/core/integrated_circuit.dm
+++ /dev/null
@@ -1,414 +0,0 @@
-/obj/item/integrated_circuit
- name = "integrated circuit"
- desc = "It's a tiny chip! This one doesn't seem to do much, however."
- icon = 'icons/obj/assemblies/electronic_components.dmi'
- icon_state = "template"
- w_class = WEIGHT_CLASS_TINY
- materials = list() // To be filled later
- var/obj/item/electronic_assembly/assembly // Reference to the assembly holding this circuit, if any.
- var/extended_desc
- var/list/inputs = list()
- var/list/inputs_default = list()// Assoc list which will fill a pin with data upon creation. e.g. "2" = 0 will set input pin 2 to equal 0 instead of null.
- var/list/outputs = list()
- var/list/outputs_default =list()// Ditto, for output.
- var/list/activators = list()
- var/next_use = 0 // Uses world.time
- var/complexity = 1 // This acts as a limitation on building machines, more resource-intensive components cost more 'space'.
- var/size = 1 // This acts as a limitation on building machines, bigger components cost more 'space'. -1 for size 0
- var/cooldown_per_use = 1 // Circuits are limited in how many times they can be work()'d by this variable.
- var/ext_cooldown = 0 // Circuits are limited in how many times they can be work()'d with external world by this variable.
- var/power_draw_per_use = 0 // How much power is drawn when work()'d.
- var/power_draw_idle = 0 // How much power is drawn when doing nothing.
- var/spawn_flags // Used for world initializing, see the #defines above.
- var/action_flags = NONE // Used for telling circuits that can do certain actions from other circuits.
- var/category_text = "NO CATEGORY THIS IS A BUG" // To show up on circuit printer, and perhaps other places.
- var/removable = TRUE // Determines if a circuit is removable from the assembly.
- var/displayed_name = ""
- var/demands_object_input = FALSE
- var/can_input_object_when_closed = FALSE
- var/max_allowed = 0 // The maximum amount of components allowed inside an integrated circuit.
-
-
-/*
- Integrated circuits are essentially modular machines. Each circuit has a specific function, and combining them inside Electronic Assemblies allows
-a creative player the means to solve many problems. Circuits are held inside an electronic assembly, and are wired using special tools.
-*/
-
-/obj/item/integrated_circuit/examine(mob/user)
- interact(user)
- external_examine(user)
- . = ..()
-
-// Can be called via electronic_assembly/attackby()
-/obj/item/integrated_circuit/proc/additem(var/obj/item/I, var/mob/living/user)
- attackby(I, user)
-
-// This should be used when someone is examining while the case is opened.
-/obj/item/integrated_circuit/proc/internal_examine(mob/user)
- to_chat(user, "This board has [inputs.len] input pin\s, [outputs.len] output pin\s and [activators.len] activation pin\s.")
- for(var/k in inputs)
- var/datum/integrated_io/I = k
- if(I.linked.len)
- to_chat(user, "The '[I]' is connected to [I.get_linked_to_desc()].")
- for(var/k in outputs)
- var/datum/integrated_io/O = k
- if(O.linked.len)
- to_chat(user, "The '[O]' is connected to [O.get_linked_to_desc()].")
- for(var/k in activators)
- var/datum/integrated_io/activate/A = k
- if(A.linked.len)
- to_chat(user, "The '[A]' is connected to [A.get_linked_to_desc()].")
- any_examine(user)
- interact(user)
-
-// This should be used when someone is examining from an 'outside' perspective, e.g. reading a screen or LED.
-/obj/item/integrated_circuit/proc/external_examine(mob/user)
- any_examine(user)
-
-/obj/item/integrated_circuit/proc/any_examine(mob/user)
- return
-
-/obj/item/integrated_circuit/proc/attackby_react(var/atom/movable/A,mob/user)
- return
-
-/obj/item/integrated_circuit/proc/sense(var/atom/movable/A,mob/user,prox)
- return
-
-/obj/item/integrated_circuit/proc/check_interactivity(mob/user)
- if(assembly)
- return assembly.check_interactivity(user)
- else
- return user.canUseTopic(src, BE_CLOSE)
-
-/obj/item/integrated_circuit/Initialize()
- . = ..()
- displayed_name = name
- setup_io(inputs, /datum/integrated_io, inputs_default, IC_INPUT)
- setup_io(outputs, /datum/integrated_io, outputs_default, IC_OUTPUT)
- setup_io(activators, /datum/integrated_io/activate, null, IC_ACTIVATOR)
- materials[/datum/material/iron] = w_class * SScircuit.cost_multiplier
-
-
-/obj/item/integrated_circuit/proc/on_data_written() //Override this for special behaviour when new data gets pushed to the circuit.
- return
-
-/obj/item/integrated_circuit/Destroy()
- . = ..()
- QDEL_LIST(inputs)
- QDEL_LIST(outputs)
- QDEL_LIST(activators)
-
-
-/obj/item/integrated_circuit/emp_act(severity)
- for(var/k in inputs)
- var/datum/integrated_io/I = k
- I.scramble()
- for(var/k in outputs)
- var/datum/integrated_io/O = k
- O.scramble()
- for(var/k in activators)
- var/datum/integrated_io/activate/A = k
- A.scramble()
-
-
-/obj/item/integrated_circuit/verb/rename_component()
- set name = "Rename Circuit"
- set category = "Object"
- set desc = "Rename your circuit, useful to stay organized."
-
- var/mob/M = usr
- if(!check_interactivity(M))
- return
-
- var/input = reject_bad_name(stripped_input(M, "What do you want to name this?", "Rename", name), TRUE)
- if(check_interactivity(M))
- if(!input)
- input = name
- if(CHAT_FILTER_CHECK(input))
- to_chat(M, "The circuit name contains prohibited words!")
- return
- to_chat(M, "The circuit '[name]' is now labeled '[input]'.")
- displayed_name = input
-
-/obj/item/integrated_circuit/interact(mob/user)
- ui_interact(user)
-
-/obj/item/integrated_circuit/ui_interact(mob/user)
- . = ..()
- if(!check_interactivity(user))
- return
-
- if(assembly)
- assembly.ui_interact(user, src)
- return
-
- var/table_edge_width = "30%"
- var/table_middle_width = "40%"
-
- var/datum/browser/popup = new(user, "scannernew", name, 800, 630) // Set up the popup browser window
- popup.add_stylesheet("scannernew", 'html/browser/assembly_ui.css')
-
- var/HTML = " \
- \
-
Complexity: [complexity] \ - Cooldown per use: [cooldown_per_use/10] sec \ - [max_allowed ? " Maximum per circuit: [max_allowed]" : ""]" - - if(ext_cooldown) - HTML += " External manipulation cooldown: [ext_cooldown/10] sec" - if(power_draw_idle) - HTML += " Power Draw: [power_draw_idle] W (Idle)" - if(power_draw_per_use) - HTML += " Power Draw: [power_draw_per_use] W (Active)" // Borgcode says that powercells' checked_use() takes joules as input. - - HTML += " [extended_desc]" - - popup.set_content(HTML) - popup.open() - -/obj/item/integrated_circuit/Topic(href, href_list) - if(!check_interactivity(usr)) - return - if(..()) - return TRUE - - var/update = TRUE - var/update_to_assembly = FALSE - - var/obj/held_item = usr.get_active_held_item() - - if(href_list["rename"]) - rename_component(usr) - if(assembly) - assembly.add_allowed_scanner(usr.ckey) - - if(href_list["pin"]) - var/datum/integrated_io/pin = locate(href_list["pin"]) in inputs + outputs + activators - if(pin) - var/datum/integrated_io/linked - var/success = TRUE - if(href_list["link"]) - linked = locate(href_list["link"]) in pin.linked - - if(istype(held_item, /obj/item/integrated_electronics) || istype(held_item, /obj/item/multitool)) - update_to_assembly = pin.handle_wire(linked, held_item, href_list["act"], usr) - else - to_chat(usr, "You can't do a whole lot without the proper tools.") - success = FALSE - if(success && assembly) - assembly.add_allowed_scanner(usr.ckey) - - if(href_list["scan"]) - if(istype(held_item, /obj/item/integrated_electronics/debugger)) - var/obj/item/integrated_electronics/debugger/D = held_item - if(D.accepting_refs) - D.afterattack(src, usr, TRUE) - else - to_chat(usr, "The debugger's 'ref scanner' needs to be on.") - else - to_chat(usr, "You need a debugger set to 'ref' mode to do that.") - - if(href_list["return"]) - update_to_assembly = TRUE - - - if(update) - if(assembly && update_to_assembly) - assembly.interact(usr, src) - else - interact(usr) // To refresh the UI. - -/obj/item/integrated_circuit/proc/push_data() - for(var/k in outputs) - var/datum/integrated_io/O = k - O.push_data() - -/obj/item/integrated_circuit/proc/pull_data() - for(var/k in inputs) - var/datum/integrated_io/I = k - I.push_data() - -/obj/item/integrated_circuit/proc/draw_idle_power() - if(assembly) - return assembly.draw_power(power_draw_idle) - -// Override this for special behaviour when there's no power left. -/obj/item/integrated_circuit/proc/power_fail() - return - -// Returns true if there's enough power to work(). -/obj/item/integrated_circuit/proc/check_power() - if(!assembly) - return FALSE // Not in an assembly, therefore no power. - if(assembly.draw_power(power_draw_per_use)) - return TRUE // Battery has enough. - return FALSE // Not enough power. - -/obj/item/integrated_circuit/proc/check_then_do_work(ord,var/ignore_power = FALSE) - if(world.time < next_use) // All intergrated circuits have an internal cooldown, to protect from spam. - return FALSE - if(assembly && ext_cooldown && (world.time < assembly.ext_next_use)) // Some circuits have external cooldown, to protect from spam. - return FALSE - if(power_draw_per_use && !ignore_power) - if(!check_power()) - power_fail() - return FALSE - next_use = world.time + cooldown_per_use - if(assembly) - assembly.ext_next_use = world.time + ext_cooldown - do_work(ord) - return TRUE - -/obj/item/integrated_circuit/proc/do_work(ord) - return - -/obj/item/integrated_circuit/proc/disconnect_all() - var/datum/integrated_io/I - - for(var/i in inputs) - I = i - I.disconnect_all() - - for(var/i in outputs) - I = i - I.disconnect_all() - - for(var/i in activators) - I = i - I.disconnect_all() - -/obj/item/integrated_circuit/proc/ext_moved(oldLoc, dir) - return - - -// Returns the object that is supposed to be used in attack messages, location checks, etc. -/obj/item/integrated_circuit/proc/get_object() - // If the component is located in an assembly, let assembly determine it. - if(assembly) - return assembly.get_object() - else - return src // If not, the component is acting on its own. - - -// Returns the location to be used for dropping items. -// Same as the regular drop_location(), but with proc being run on assembly if there is any. -/obj/item/integrated_circuit/drop_location() - // If the component is located in an assembly, let the assembly figure that one out. - if(assembly) - return assembly.drop_location() - else - return ..() // If not, the component is acting on its own. - - -// Checks if the target object is reachable. Useful for various manipulators and manipulator-like objects. -/obj/item/integrated_circuit/proc/check_target(atom/target, exclude_contents = FALSE, exclude_components = FALSE, exclude_self = FALSE) - if(!target) - return FALSE - - var/atom/movable/acting_object = get_object() - - if(exclude_self && target == acting_object) - return FALSE - - if(exclude_components && assembly) - if(target in assembly.assembly_components) - return FALSE - - if(target == assembly.battery) - return FALSE - - if(target.Adjacent(acting_object) && isturf(target.loc)) - return TRUE - - if(!exclude_contents && (target in acting_object.GetAllContents())) - return TRUE - - if(target in acting_object.loc) - return TRUE - - return FALSE diff --git a/code/modules/integrated_electronics/core/pins.dm b/code/modules/integrated_electronics/core/pins.dm deleted file mode 100644 index ad0f800b91631..0000000000000 --- a/code/modules/integrated_electronics/core/pins.dm +++ /dev/null @@ -1,216 +0,0 @@ -/* - Pins both hold data for circuits, as well move data between them. Some also cause circuits to do their function. DATA_CHANNEL pins are the data holding/moving kind, -where as PULSE_CHANNEL causes circuits to work() when their pulse hits them. -A visualization of how pins work is below. Imagine the below image involves an addition circuit. -When the bottom pin, the activator, receives a pulse, all the numbers on the left (input) get added, and the answer goes on the right side (output). -Inputs Outputs -A [2]\ /[8] result -B [1]-\|++|/ -C [4]-/|++| -D [1]/ || - || - Activator -*/ -/datum/integrated_io - var/name = "input/output" - var/obj/item/integrated_circuit/holder - var/datum/weakref/data // This is a weakref, to reduce typecasts. Note that oftentimes numbers and text may also occupy this. - var/list/linked = list() - var/io_type = DATA_CHANNEL - var/pin_type // IC_INPUT, IC_OUTPUT, IC_ACTIVATOR - used in saving assembly wiring - var/ord - -/datum/integrated_io/New(loc, _name, _data, _pin_type,_ord) - name = _name - if(!isnull(_data)) - data = _data - if(_pin_type) - pin_type = _pin_type - if(_ord) - ord = _ord - - holder = loc - - if(!istype(holder)) - message_admins("ERROR: An integrated_io ([name]) spawned without a valid holder! This is a bug.") - -/datum/integrated_io/Destroy() - disconnect_all() - data = null - holder = null - return ..() - -/datum/integrated_io/proc/data_as_type(var/as_type) - if(!isweakref(data)) - return - var/datum/weakref/w = data - var/output = w.resolve() - return istype(output, as_type) ? output : null - -/datum/integrated_io/proc/display_data(var/input) - if(isnull(input)) - return "(null)" // Empty data means nothing to show. - - if(istext(input)) - return "(\"[input]\")" // Wraps the 'string' in escaped quotes, so that people know it's a 'string'. - - if(islist(input)) - var/list/my_list = input - var/result = "list\[[my_list.len]\](" - if(my_list.len) - result += " " - var/pos = 0 - for(var/line in my_list) - result += "[display_data(line)]" - pos++ - if(pos != my_list.len) - result += ", " - result += " " - result += ")" - return result - - if(isweakref(input)) - var/datum/weakref/w = input - var/atom/A = w.resolve() - return A ? "([A.name] \[Ref\])" : "(null)" // For refs, we want just the name displayed. - - return "([input])" // Nothing special needed for numbers or other stuff. - -/datum/integrated_io/activate/display_data() - return "(\[pulse\])" - -/datum/integrated_io/proc/display_pin_type() - return IC_FORMAT_ANY - -/datum/integrated_io/activate/display_pin_type() - return IC_FORMAT_PULSE - -/datum/integrated_io/proc/scramble() - if(isnull(data)) - return - if(isnum_safe(data)) - write_data_to_pin(rand(-10000, 10000)) - if(istext(data)) - write_data_to_pin("ERROR") - push_data() - -/datum/integrated_io/activate/scramble() - push_data() - -/datum/integrated_io/proc/handle_wire(datum/integrated_io/linked_pin, obj/item/tool, action, mob/living/user) - if(tool.tool_behaviour == TOOL_MULTITOOL) - if(!tool.multitool_check_buffer(user, tool)) - return - var/obj/item/multitool/multitool = tool - switch(action) - if("wire") - multitool.wire(src, user) - return TRUE - if("unwire") - if(linked_pin) - multitool.unwire(src, linked_pin, user) - return TRUE - if("data") - ask_for_pin_data(user) - return TRUE - - else if(istype(tool, /obj/item/integrated_electronics/wirer)) - var/obj/item/integrated_electronics/wirer/wirer = tool - if(linked_pin) - wirer.wire(linked_pin, user) - else - wirer.wire(src, user) - - else if(istype(tool, /obj/item/integrated_electronics/debugger)) - var/obj/item/integrated_electronics/debugger/debugger = tool - debugger.write_data(src, user) - return TRUE - - return FALSE - -/datum/integrated_io/proc/write_data_to_pin(new_data) - if(isnull(new_data) || isnum_safe(new_data) || istext(new_data) || isweakref(new_data)) - data = new_data - holder.on_data_written() - else if(islist(new_data)) - var/list/new_list = new_data - data = new_list.Copy(max(1,new_list.len - IC_MAX_LIST_LENGTH+1),0) - holder.on_data_written() - -/datum/integrated_io/proc/push_data() - for(var/k in 1 to linked.len) - var/datum/integrated_io/io = linked[k] - io.write_data_to_pin(data) - -/datum/integrated_io/activate/push_data() - for(var/k in 1 to linked.len) - var/datum/integrated_io/io = linked[k] - io.holder.check_then_do_work(io.ord) - -/datum/integrated_io/proc/pull_data() - for(var/k in 1 to linked.len) - var/datum/integrated_io/io = linked[k] - write_data_to_pin(io.data) - -/datum/integrated_io/proc/get_linked_to_desc() - if(linked.len) - return "the [english_list(linked)]" - return "nothing" - - -/datum/integrated_io/proc/connect_pin(datum/integrated_io/pin) - pin.linked |= src - linked |= pin - -// Iterates over every linked pin and disconnects them. -/datum/integrated_io/proc/disconnect_all() - for(var/pin in linked) - disconnect_pin(pin) - -/datum/integrated_io/proc/disconnect_pin(datum/integrated_io/pin) - pin.linked.Remove(src) - linked.Remove(pin) - - -/datum/integrated_io/proc/ask_for_data_type(mob/user, var/default, var/list/allowed_data_types = list("string","number","null")) - var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in allowed_data_types - if(!holder.check_interactivity(user)) - return - - var/new_data = null - switch(type_to_use) - if("string") - new_data = stripped_multiline_input(user, "Now type in a string.","[src] string writing", istext(default) ? default : null, no_trim = TRUE) - if(istext(new_data) && holder.check_interactivity(user) ) - to_chat(user, "You input "+new_data+" into the pin.") - return new_data - if("number") - new_data = input("Now type in a number.","[src] number writing", isnum_safe(default) ? default : null) as null|num - if(isnum_safe(new_data) && holder.check_interactivity(user) ) - to_chat(user, "You input [new_data] into the pin.") - return new_data - if("null") - if(holder.check_interactivity(user)) - to_chat(user, "You clear the pin's memory.") - return new_data - -// Basically a null check -/datum/integrated_io/proc/is_valid() - return !isnull(data) - -// This proc asks for the data to write, then writes it. -/datum/integrated_io/proc/ask_for_pin_data(mob/user) - var/new_data = ask_for_data_type(user) - write_data_to_pin(new_data) - -/datum/integrated_io/activate/ask_for_pin_data(mob/user) // This just pulses the pin. - holder.investigate_log(" was manually pulsed by [key_name(user)].", INVESTIGATE_CIRCUIT) - holder.check_then_do_work(ord,ignore_power = TRUE) - to_chat(user, "You pulse \the [holder]'s [src] pin.") - -/datum/integrated_io/activate - name = "activation pin" - io_type = PULSE_CHANNEL - -/datum/integrated_io/activate/out // All this does is just make the UI say 'out' instead of 'in' - data = 1 diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm deleted file mode 100644 index 9d22cf8cc3805..0000000000000 --- a/code/modules/integrated_electronics/core/printer.dm +++ /dev/null @@ -1,351 +0,0 @@ -#define MAX_CIRCUIT_CLONE_TIME 3 MINUTES //circuit slow-clones can only take up this amount of time to complete - -/obj/item/integrated_circuit_printer - name = "integrated circuit printer" - desc = "A portable(ish) machine made to print tiny modular circuitry out of iron." - icon = 'icons/obj/assemblies/electronic_tools.dmi' - icon_state = "circuit_printer" - w_class = WEIGHT_CLASS_BULKY - var/upgraded = FALSE // When hit with an upgrade disk, will turn true, allowing it to print the higher tier circuits. - var/can_clone = TRUE // Allows the printer to clone circuits, either instantly or over time depending on upgrade. Set to FALSE to disable entirely. - var/fast_clone = FALSE // If this is false, then cloning will take an amount of deciseconds equal to the iron cost divided by 100. - var/debug = FALSE // If it's upgraded and can clone, even without config settings. - var/current_category = null - var/cloning = FALSE // If the printer is currently creating a circuit - var/recycling = FALSE // If an assembly is being emptied into this printer - var/list/program // Currently loaded save, in form of list - var/datum/weakref/idlock = null - -/obj/item/integrated_circuit_printer/proc/check_interactivity(mob/user) - return user.canUseTopic(src, BE_CLOSE) - -/obj/item/integrated_circuit_printer/upgraded - upgraded = TRUE - can_clone = TRUE - fast_clone = TRUE - -/obj/item/integrated_circuit_printer/debug //translation: "integrated_circuit_printer/local_server" - name = "debug circuit printer" - debug = TRUE - upgraded = TRUE - can_clone = TRUE - fast_clone = TRUE - w_class = WEIGHT_CLASS_TINY - -/obj/item/integrated_circuit_printer/Initialize() - . = ..() - AddComponent(/datum/component/material_container, list(/datum/material/iron), MINERAL_MATERIAL_AMOUNT * 25, TRUE, list(/obj/item/stack, /obj/item/integrated_circuit, /obj/item/electronic_assembly)) - -/obj/item/integrated_circuit_printer/proc/print_program(mob/user) - if(!cloning) - return - - visible_message("[src] has finished printing its assembly!") - playsound(src, 'sound/items/poster_being_created.ogg', 50, TRUE) - var/obj/item/electronic_assembly/assembly = SScircuit.load_electronic_assembly(get_turf(src), program) - if(idlock) - assembly.idlock = idlock - assembly.creator = key_name(user) - assembly.investigate_log("was printed by [assembly.creator].", INVESTIGATE_CIRCUIT) - cloning = FALSE - -/obj/item/integrated_circuit_printer/attackby(obj/item/O, mob/user) - if(istype(O, /obj/item/disk/integrated_circuit/upgrade/advanced)) - if(upgraded) - to_chat(user, "[src] already has this upgrade. ") - return TRUE - to_chat(user, "You install [O] into [src]. ") - upgraded = TRUE - return TRUE - - if(istype(O, /obj/item/disk/integrated_circuit/upgrade/clone)) - if(fast_clone) - to_chat(user, "[src] already has this upgrade. ") - return TRUE - to_chat(user, "You install [O] into [src]. Circuit cloning will now be instant. ") - fast_clone = TRUE - return TRUE - - if(istype(O, /obj/item/electronic_assembly)) - var/obj/item/electronic_assembly/EA = O //microtransactions not included - if(EA.assembly_components.len) - if(recycling) - return - if(!EA.opened) - to_chat(user, "You can't reach [EA]'s components to remove them!") - return - if(EA.battery) - to_chat(user, "Remove [EA]'s power cell first!") - return - for(var/V in EA.assembly_components) - var/obj/item/integrated_circuit/IC = V - if(!IC.removable) - to_chat(user, "[EA] has irremovable components in the casing, preventing you from emptying it.") - return - to_chat(user, "You begin recycling [EA]'s components...") - playsound(src, 'sound/items/electronic_assembly_emptying.ogg', 50, TRUE) - if(!do_after(user, 30, target = src) || recycling) //short channel so you don't accidentally start emptying out a complex assembly - return - recycling = TRUE - var/datum/component/material_container/mats = GetComponent(/datum/component/material_container) - for(var/V in EA.assembly_components) - var/obj/item/integrated_circuit/IC = V - if(!mats.has_space(mats.get_item_material_amount(IC))) - to_chat(user, "[src] can't hold any more materials!") - break - if(!do_after(user, 5, target = user)) - recycling = FALSE - return - playsound(src, 'sound/items/crowbar.ogg', 50, TRUE) - if(EA.try_remove_component(IC, user, TRUE)) - mats.user_insert(IC, user) - to_chat(user, "You recycle all the components[EA.assembly_components.len ? " you could " : " "]from [EA]!") - playsound(src, 'sound/items/electronic_assembly_empty.ogg', 50, TRUE) - recycling = FALSE - return TRUE - - if(istype(O, /obj/item/integrated_electronics/debugger)) - var/obj/item/integrated_electronics/debugger/debugger = O - if(!debugger.idlock) - return - - if(!idlock) - idlock = debugger.idlock - debugger.idlock = null - to_chat(user, "You set \the [src] to print out id-locked assemblies only.") - return - - if(debugger.idlock.resolve() == idlock.resolve()) - idlock = null - debugger.idlock = null - to_chat(user, "You reset \the [src]'s protection settings.") - return - - return ..() - -/obj/item/integrated_circuit_printer/attack_self(mob/user) - interact(user) - -/obj/item/integrated_circuit_printer/interact(mob/user) - if(!(in_range(src, user) || issilicon(user))) - return - - if(isnull(current_category)) - current_category = SScircuit.circuit_fabricator_recipe_list[1] - - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - - //Preparing the browser - var/datum/browser/popup = new(user, "printernew", "Integrated Circuit Printer", 800, 630) // Set up the popup browser window - - var/HTML = " Integrated Circuit Printer" - if(debug) - HTML += " DEBUG PRINTER -- Infinite materials. Cloning available." - - HTML += "Identity-lock: " - if(idlock) - var/obj/item/card/id = idlock.resolve() - HTML+= "[id.name] | Reset " - else - HTML += "None | Reset " - - if(CONFIG_GET(flag/ic_printing) || debug) - HTML += "Assembly cloning: [can_clone ? (fast_clone ? "Instant" : "Available") : "Unavailable"]. " - - HTML += "Circuits available: [upgraded || debug ? "Advanced":"Regular"]." - if(!upgraded) - HTML += " Crossed out circuits mean that the printer is not sufficiently upgraded to create that circuit." - - HTML += " " - if((can_clone && CONFIG_GET(flag/ic_printing)) || debug) - HTML += "Here you can load script for your assembly. " - if(!cloning) - HTML += " Load Program " - else - HTML += " Load Program" - if(!program) - HTML += " [fast_clone ? "Print" : "Begin Printing"] Assembly" - else if(cloning) - HTML += " Cancel Print" - else - HTML += " [fast_clone ? "Print" : "Begin Printing"] Assembly" - - HTML += " " - HTML += "Categories:" - for(var/category in SScircuit.circuit_fabricator_recipe_list) - if(category != current_category) - HTML += " [category] " - else // Bold the button if it's already selected. - HTML += " [category] " - HTML += " " - HTML += " [current_category]" - else - HTML += " " - - popup.set_content(HTML) - popup.open() - -/obj/item/integrated_circuit_printer/Topic(href, href_list) - if(!check_interactivity(usr)) - return - if(..()) - return TRUE - add_fingerprint(usr) - - if(href_list["id-lock"]) - idlock = null - - if(href_list["category"]) - current_category = href_list["category"] - - if(href_list["build"]) - var/build_type = text2path(href_list["build"]) - if(!build_type || !ispath(build_type)) - return TRUE - - var/cost = 400 - if(ispath(build_type, /obj/item/electronic_assembly)) - var/obj/item/electronic_assembly/E = SScircuit.cached_assemblies[build_type] - cost = E.materials[/datum/material/iron] - else if(ispath(build_type, /obj/item/integrated_circuit)) - var/obj/item/integrated_circuit/IC = SScircuit.cached_components[build_type] - cost = IC.materials[/datum/material/iron] - else if(!(build_type in SScircuit.circuit_fabricator_recipe_list["Tools"])) - log_href_exploit(usr) - return - - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - - if(!debug && !materials.use_amount_mat(cost, /datum/material/iron)) - to_chat(usr, "You need [cost] iron to build that!") - return TRUE - - var/obj/item/built = new build_type(drop_location()) - usr.put_in_hands(built) - - if(istype(built, /obj/item/electronic_assembly)) - var/obj/item/electronic_assembly/E = built - E.creator = key_name(usr) - E.opened = TRUE - E.update_icon() - //reupdate diagnostic hud because it was put_in_hands() and not pickup()'ed - E.diag_hud_set_circuithealth() - E.diag_hud_set_circuitcell() - E.diag_hud_set_circuitstat() - E.diag_hud_set_circuittracking() - E.investigate_log("was printed by [E.creator].", INVESTIGATE_CIRCUIT) - - to_chat(usr, "[capitalize(built.name)] printed.") - playsound(src, 'sound/items/jaws_pry.ogg', 50, TRUE) - - if(href_list["print"]) - if(!CONFIG_GET(flag/ic_printing) && !debug) - to_chat(usr, "CentCom has disabled printing of custom circuitry due to recent allegations of copyright infringement.") - return - if(!can_clone) // Copying and printing ICs is cloning - to_chat(usr, "This printer does not have the cloning upgrade.") - return - switch(href_list["print"]) - if("load") - if(cloning) - return - var/input = capped_multiline_input(usr, "Put your code there:", "loading", max_length = MAX_SIZE_CIRCUIT) - if(!check_interactivity(usr) || cloning) - return - if(!input) - program = null - return - - var/validation = SScircuit.validate_electronic_assembly(input) - - // Validation error codes are returned as text. - if(istext(validation)) - to_chat(usr, "Error: [validation]") - return - else if(islist(validation)) - program = validation - to_chat(usr, "This is a valid program for [program["assembly"]["type"]].") - if(program["requires_upgrades"]) - if(upgraded) - to_chat(usr, "It uses advanced component designs.") - else - to_chat(usr, "It uses unknown component designs. Printer upgrade is required to proceed.") - if(program["unsupported_circuit"]) - to_chat(usr, "This program uses components not supported by the specified assembly. Please change the assembly type in the save file to a supported one.") - to_chat(usr, "Used space: [program["used_space"]]/[program["max_space"]].") - to_chat(usr, "Complexity: [program["complexity"]]/[program["max_complexity"]].") - to_chat(usr, "Iron cost: [program["iron_cost"]].") - - if("print") - if(!program || cloning) - return - - if(program["requires_upgrades"] && !upgraded && !debug) - to_chat(usr, "This program uses unknown component designs. Printer upgrade is required to proceed.") - return - if(program["unsupported_circuit"] && !debug) - to_chat(usr, "This program uses components not supported by the specified assembly. Please change the assembly type in the save file to a supported one.") - return - else if(fast_clone) - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - if(debug || materials.use_amount_mat(program["iron_cost"], /datum/material/iron)) - cloning = TRUE - print_program(usr) - else - to_chat(usr, "You need [program["iron_cost"]] iron to build that!") - else - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - if(!materials.use_amount_mat(program["iron_cost"], /datum/material/iron)) - to_chat(usr, "You need [program["iron_cost"]] iron to build that!") - return - var/cloning_time = round(program["iron_cost"] / 15) - cloning_time = min(cloning_time, MAX_CIRCUIT_CLONE_TIME) - cloning = TRUE - to_chat(usr, "You begin printing a custom assembly. This will take approximately [DisplayTimeText(cloning_time)]. You can still print \ - off normal parts during this time.") - playsound(src, 'sound/items/poster_being_created.ogg', 50, TRUE) - addtimer(CALLBACK(src, .proc/print_program, usr), cloning_time) - - if("cancel") - if(!cloning || !program) - return - - to_chat(usr, "Cloning has been canceled. Iron cost has been refunded.") - cloning = FALSE - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.use_amount_mat(-program["iron_cost"], /datum/material/iron) //use negative amount to regain the cost - - - interact(usr) - - -// FUKKEN UPGRADE DISKS -/obj/item/disk/integrated_circuit/upgrade - name = "integrated circuit printer upgrade disk" - desc = "Install this into your integrated circuit printer to enhance it." - icon = 'icons/obj/assemblies/electronic_tools.dmi' - icon_state = "upgrade_disk" - item_state = "card-id" - w_class = WEIGHT_CLASS_SMALL - -/obj/item/disk/integrated_circuit/upgrade/advanced - name = "integrated circuit printer upgrade disk - advanced designs" - desc = "Install this into your integrated circuit printer to enhance it. This one adds new, advanced designs to the printer." - -/obj/item/disk/integrated_circuit/upgrade/clone - name = "integrated circuit printer upgrade disk - instant cloner" - desc = "Install this into your integrated circuit printer to enhance it. This one allows the printer to duplicate assemblies instantaneously." - icon_state = "upgrade_disk_clone" diff --git a/code/modules/integrated_electronics/core/saved_circuits.dm b/code/modules/integrated_electronics/core/saved_circuits.dm deleted file mode 100644 index ab9fbcacd2c78..0000000000000 --- a/code/modules/integrated_electronics/core/saved_circuits.dm +++ /dev/null @@ -1,361 +0,0 @@ -// Helpers for saving/loading integrated circuits. - - -// Saves type, modified name and modified inputs (if any) to a list -// The list is converted to JSON down the line. -//"Special" is not verified at any point except for by the circuit itself. -/obj/item/integrated_circuit/proc/save() - var/list/component_params = list() - var/init_name = initial(name) - - // Save initial name used for differentiating assemblies - component_params["type"] = init_name - - // Save the modified name. - if(init_name != displayed_name) - component_params["name"] = displayed_name - - // Saving input values - if(length(inputs)) - var/list/saved_inputs = list() - - for(var/index in 1 to inputs.len) - var/datum/integrated_io/input = inputs[index] - - // Don't waste space saving the default values - if(input.data == inputs_default["[index]"]) - continue - if(input.data == initial(input.data)) - continue - - var/list/input_value = list(index, FALSE, input.data) - // Index, Type, Value - // FALSE is default type used for num/text/list/null - // TODO: support for special input types, such as internal refs and maybe typepaths - - if(islist(input.data) || isnum_safe(input.data) || istext(input.data) || isnull(input.data)) - saved_inputs.Add(list(input_value)) - - if(saved_inputs.len) - component_params["inputs"] = saved_inputs - - var/special = save_special() - if(!isnull(special)) - component_params["special"] = special - - return component_params - -/obj/item/integrated_circuit/proc/save_special() - return - -// Verifies a list of component parameters -// Returns null on success, error name on failure -/obj/item/integrated_circuit/proc/verify_save(list/component_params) - var/init_name = initial(name) - // Validate name - if(component_params["name"] && !reject_bad_name(component_params["name"], TRUE)) - return "Bad component name at [init_name]." - - // Validate input values - if(component_params["inputs"]) - var/list/loaded_inputs = component_params["inputs"] - if(!islist(loaded_inputs)) - return "Malformed input values list at [init_name]." - - var/inputs_amt = length(inputs) - - // Too many inputs? Inputs for input-less component? This is not good. - if(!inputs_amt || inputs_amt < length(loaded_inputs)) - return "Input values list out of bounds at [init_name]." - - for(var/list/input in loaded_inputs) - if(input.len != 3) - return "Malformed input data at [init_name]." - - var/input_id = input[1] - var/input_type = input[2] - //var/input_value = input[3] - - // No special type support yet. - if(input_type) - return "Unidentified input type at [init_name]!" - // TODO: support for special input types, such as typepaths and internal refs - - // Input ID is a list index, make sure it's sane. - if(!isnum_safe(input_id) || input_id % 1 || input_id > inputs_amt || input_id < 1) - return "Invalid input index at [init_name]." - - -// Loads component parameters from a list -// Doesn't verify any of the parameters it loads, this is the job of verify_save() -/obj/item/integrated_circuit/proc/load(list/component_params) - // Load name - if(component_params["name"]) - displayed_name = component_params["name"] - - // Load input values - if(component_params["inputs"]) - var/list/loaded_inputs = component_params["inputs"] - - for(var/list/input in loaded_inputs) - var/index = input[1] - //var/input_type = input[2] - var/input_value = input[3] - - var/datum/integrated_io/pin = inputs[index] - // The pins themselves validate the data. - pin.write_data_to_pin(input_value) - // TODO: support for special input types, such as internal refs and maybe typepaths - - if(!isnull(component_params["special"])) - load_special(component_params["special"]) - -/obj/item/integrated_circuit/proc/load_special(special_data) - return - -// Saves type and modified name (if any) to a list -// The list is converted to JSON down the line. -/obj/item/electronic_assembly/proc/save() - var/list/assembly_params = list() - - // Save initial name used for differentiating assemblies - assembly_params["type"] = initial(name) - - // Save modified name - if(initial(name) != name) - assembly_params["name"] = name - - // Save modified description - if(initial(desc) != desc) - assembly_params["desc"] = desc - - // Save modified color - if(initial(detail_color) != detail_color) - assembly_params["detail_color"] = detail_color - - return assembly_params - - -// Verifies a list of assembly parameters -// Returns null on success, error name on failure -/obj/item/electronic_assembly/proc/verify_save(list/assembly_params) - // Validate name and color - if(assembly_params["name"] && !reject_bad_name(assembly_params["name"], TRUE)) - return "Bad assembly name." - if(assembly_params["desc"] && !reject_bad_text(assembly_params["desc"])) - return "Bad assembly description." - -// Loads assembly parameters from a list -// Doesn't verify any of the parameters it loads, this is the job of verify_save() -/obj/item/electronic_assembly/proc/load(list/assembly_params) - // Load modified name, if any. - if(assembly_params["name"]) - name = assembly_params["name"] - - // Load modified description, if any. - if(assembly_params["desc"]) - desc = assembly_params["desc"] - - if(assembly_params["detail_color"]) - detail_color = assembly_params["detail_color"] - - update_icon() - - - -// Attempts to save an assembly into a save file format. -// Returns null if assembly is not complete enough to be saved. -/datum/controller/subsystem/processing/circuit/proc/save_electronic_assembly(obj/item/electronic_assembly/assembly) - // No components? Don't even try to save it. - if(!length(assembly.assembly_components)) - return - - - var/list/blocks = list() - - // Block 1. Assembly. - blocks["assembly"] = assembly.save() - // (implant assemblies are not yet supported) - - - // Block 2. Components. - var/list/components = list() - for(var/c in assembly.assembly_components) - var/obj/item/integrated_circuit/component = c - components.Add(list(component.save())) - blocks["components"] = components - - - // Block 3. Wires. - var/list/wires = list() - var/list/saved_wires = list() - - for(var/c in assembly.assembly_components) - var/obj/item/integrated_circuit/component = c - var/list/all_pins = component.inputs + component.outputs + component.activators - - for(var/p in all_pins) - var/datum/integrated_io/pin = p - var/list/params = pin.get_pin_parameters() - var/text_params = params.Join() - - for(var/p2 in pin.linked) - var/datum/integrated_io/pin2 = p2 - var/list/params2 = pin2.get_pin_parameters() - var/text_params2 = params2.Join() - - // Check if we already saved an opposite version of this wire - // (do not save the same wire twice) - if((text_params2 + "=" + text_params) in saved_wires) - continue - - // If not, add a wire "hash" for future checks and save it - saved_wires.Add(text_params + "=" + text_params2) - wires.Add(list(list(params, params2))) - - if(wires.len) - blocks["wires"] = wires - - return json_encode(blocks) - - - -// Checks assembly save and calculates some of the parameters. -// Returns assembly (type: list) if the save is valid. -// Returns error code (type: text) if loading has failed. -// The following parameters area calculated during validation and added to the returned save list: -// "requires_upgrades", "unsupported_circuit", "iron_cost", "complexity", "max_complexity", "used_space", "max_space" -/datum/controller/subsystem/processing/circuit/proc/validate_electronic_assembly(program) - var/list/blocks = json_decode(program) - if(!blocks) - return - - var/error - - - // Block 1. Assembly. - var/list/assembly_params = blocks["assembly"] - - if(!islist(assembly_params) || !length(assembly_params)) - return "Invalid assembly data." // No assembly, damaged assembly or empty assembly - - // Validate type, get a temporary component - var/assembly_path = all_assemblies[assembly_params["type"]] - var/obj/item/electronic_assembly/assembly = cached_assemblies[assembly_path] - if(!assembly) - return "Invalid assembly type." - - // Check assembly save data for errors - error = assembly.verify_save(assembly_params) - if(error) - return error - - - // Read space & complexity limits and start keeping track of them - blocks["complexity"] = 0 - blocks["max_complexity"] = assembly.max_complexity - blocks["used_space"] = 0 - blocks["max_space"] = assembly.max_components - - // Start keeping track of total iron cost - blocks["iron_cost"] = assembly.materials[/datum/material/iron] - - - // Block 2. Components. - if(!islist(blocks["components"]) || !length(blocks["components"])) - return "Invalid components list." // No components or damaged components list - - var/list/assembly_components = list() - for(var/C in blocks["components"]) - var/list/component_params = C - - if(!islist(component_params) || !length(component_params)) - return "Invalid component data." - - // Validate type, get a temporary component - var/component_path = all_components[component_params["type"]] - var/obj/item/integrated_circuit/component = cached_components[component_path] - if(!component) - return "Invalid component type." - - // Add temporary component to assembly_components list, to be used later when verifying the wires - assembly_components.Add(component) - - // Check component save data for errors - error = component.verify_save(component_params) - if(error) - return error - - // Update estimated assembly complexity, taken space and material cost - blocks["complexity"] += component.complexity - blocks["used_space"] += component.size - blocks["iron_cost"] += component.materials[/datum/material/iron] - - // Check if the assembly requires printer upgrades - if(!(component.spawn_flags & IC_SPAWN_DEFAULT)) - blocks["requires_upgrades"] = TRUE - - // Check if the assembly supports the circucit - if((component.action_flags & assembly.allowed_circuit_action_flags) != component.action_flags) - blocks["unsupported_circuit"] = TRUE - - - // Check complexity and space limitations - if(blocks["used_space"] > blocks["max_space"]) - return "Used space overflow." - if(blocks["complexity"] > blocks["max_complexity"]) - return "Complexity overflow." - - - // Block 3. Wires. - if(blocks["wires"]) - if(!islist(blocks["wires"])) - return "Invalid wiring list." // Damaged wires list - - for(var/w in blocks["wires"]) - var/list/wire = w - - if(!islist(wire) || wire.len != 2) - return "Invalid wire data." - - var/datum/integrated_io/IO = assembly.get_pin_ref_list(wire[1], assembly_components) - var/datum/integrated_io/IO2 = assembly.get_pin_ref_list(wire[2], assembly_components) - if(!IO || !IO2) - return "Invalid wire data." - - if(initial(IO.io_type) != initial(IO2.io_type)) - return "Wire type mismatch." - - return blocks - - -// Loads assembly (in form of list) into an object and returns it. -// No sanity checks are performed, save file is expected to be validated by validate_electronic_assembly -/datum/controller/subsystem/processing/circuit/proc/load_electronic_assembly(loc, list/blocks) - - // Block 1. Assembly. - var/list/assembly_params = blocks["assembly"] - var/obj/item/electronic_assembly/assembly_path = all_assemblies[assembly_params["type"]] - var/obj/item/electronic_assembly/assembly = new assembly_path(null) - assembly.load(assembly_params) - - - - // Block 2. Components. - for(var/component_params in blocks["components"]) - var/obj/item/integrated_circuit/component_path = all_components[component_params["type"]] - var/obj/item/integrated_circuit/component = new component_path(assembly) - assembly.add_component(component) - component.load(component_params) - - - // Block 3. Wires. - if(blocks["wires"]) - for(var/w in blocks["wires"]) - var/list/wire = w - var/datum/integrated_io/IO = assembly.get_pin_ref_list(wire[1]) - var/datum/integrated_io/IO2 = assembly.get_pin_ref_list(wire[2]) - IO.connect_pin(IO2) - - assembly.forceMove(loc) - return assembly diff --git a/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm b/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm deleted file mode 100644 index da59434b65349..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm +++ /dev/null @@ -1,26 +0,0 @@ -// These pins only contain 0 or 1. Null is not allowed. -/datum/integrated_io/boolean - name = "boolean pin" - data = FALSE - -/datum/integrated_io/boolean/ask_for_pin_data(mob/user) // 'Ask' is a bit misleading, acts more like a toggle. - var/new_data = !data - to_chat(user, "You switch the data bit to [new_data ? "TRUE" : "FALSE"].") - write_data_to_pin(new_data) - -/datum/integrated_io/boolean/write_data_to_pin(var/new_data) - if(new_data == FALSE || new_data == TRUE) - data = new_data - holder.on_data_written() - -/datum/integrated_io/boolean/scramble() - write_data_to_pin(rand(FALSE,TRUE)) - push_data() - -/datum/integrated_io/boolean/display_pin_type() - return IC_FORMAT_BOOLEAN - -/datum/integrated_io/boolean/display_data(var/input) - if(data) - return "(TRUE)" - return "(FALSE)" diff --git a/code/modules/integrated_electronics/core/special_pins/char_pin.dm b/code/modules/integrated_electronics/core/special_pins/char_pin.dm deleted file mode 100644 index 632ef695218f9..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/char_pin.dm +++ /dev/null @@ -1,27 +0,0 @@ -// These pins can only contain a 1 character string or null. -/datum/integrated_io/char - name = "char pin" - -/datum/integrated_io/char/ask_for_pin_data(mob/user) - var/new_data = stripped_input(user, "Please type in one character.","[src] char writing", no_trim = TRUE) - if(holder.check_interactivity(user) ) - to_chat(user, "You input [new_data ? "new_data" : "NULL"] into the pin.") - write_data_to_pin(new_data) - -/datum/integrated_io/char/write_data_to_pin(var/new_data) - if(isnull(new_data) || istext(new_data)) - if(length(new_data) > 1) - return - data = new_data - holder.on_data_written() - -// This makes the text go from "A" to "%". -/datum/integrated_io/char/scramble() - if(!is_valid()) - return - var/list/options = list("!","@","#","$","%","^","&","*","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") - data = pick(options) - push_data() - -/datum/integrated_io/char/display_pin_type() - return IC_FORMAT_CHAR diff --git a/code/modules/integrated_electronics/core/special_pins/color_pin.dm b/code/modules/integrated_electronics/core/special_pins/color_pin.dm deleted file mode 100644 index e2a09657dff64..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/color_pin.dm +++ /dev/null @@ -1,49 +0,0 @@ -// These pins can only contain a color (in the form of #FFFFFF) or null. -/datum/integrated_io/color - name = "color pin" - -/datum/integrated_io/color/ask_for_pin_data(mob/user) - var/new_data = input("Please select a color.","[src] color writing") as color|null - if(holder.check_interactivity(user) ) - to_chat(user, "You input a new color into the pin.") - write_data_to_pin(new_data) - -/datum/integrated_io/color/write_data_to_pin(var/new_data) - // Since this is storing the color as a string hex color code, we need to make sure it's actually one. - if(isnull(new_data) || istext(new_data)) - if(istext(new_data)) - new_data = uppertext(new_data) - if(length(new_data) != 7) // We can hex if we want to, we can leave your strings behind - return // Cause your strings don't hex and if they don't hex - var/friends = copytext(new_data, 2, 8) // Well they're are no strings of mine - // I say, we can go where we want to, a place where they will never find - var/safety_dance = 1 - while(safety_dance >= 7) // And we can act like we come from out of this world.log - var/hex = copytext(friends, safety_dance, safety_dance+1) - if(!(hex in list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"))) - return // Leave the fake one far behind, - safety_dance++ - - data = new_data // And we can hex - holder.on_data_written() - -// This randomizes the color. -/datum/integrated_io/color/scramble() - if(!is_valid()) - return - var/new_data - for(var/i=1;i<=3;i++) - var/temp_col = "[num2hex(rand(0,255), 2)]" - if(length(temp_col )<2) - temp_col = "0[temp_col]" - new_data += temp_col - data = new_data - push_data() - -/datum/integrated_io/color/display_pin_type() - return IC_FORMAT_COLOR - -/datum/integrated_io/color/display_data(var/input) - if(!isnull(data)) - return "([data])" - return ..() diff --git a/code/modules/integrated_electronics/core/special_pins/dir_pin.dm b/code/modules/integrated_electronics/core/special_pins/dir_pin.dm deleted file mode 100644 index 8a2fb61a84f76..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/dir_pin.dm +++ /dev/null @@ -1,31 +0,0 @@ -// These pins can only contain directions (1,2,4,8...) or null. -/datum/integrated_io/dir - name = "dir pin" - -/datum/integrated_io/dir/ask_for_pin_data(mob/user) - var/new_data = input("Please type in a valid dir number. \ - Valid dirs are;\n\ - North/Fore = [NORTH],\n\ - South/Aft = [SOUTH],\n\ - East/Starboard = [EAST],\n\ - West/Port = [WEST],\n\ - Northeast = [NORTHEAST],\n\ - Northwest = [NORTHWEST],\n\ - Southeast = [SOUTHEAST],\n\ - Southwest = [SOUTHWEST]","[src] dir writing") as null|num - if(isnum_safe(new_data) && holder.check_interactivity(user) ) - to_chat(user, "You input [new_data] into the pin.") - write_data_to_pin(new_data) - -/datum/integrated_io/dir/write_data_to_pin(var/new_data) - if(isnull(new_data) || (new_data in list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)/* + list(UP, DOWN)*/)) - data = new_data - holder.on_data_written() - -/datum/integrated_io/dir/display_pin_type() - return IC_FORMAT_DIR - -/datum/integrated_io/dir/display_data(var/input) - if(!isnull(data)) - return "([dir2text(data)])" - return ..() diff --git a/code/modules/integrated_electronics/core/special_pins/index_pin.dm b/code/modules/integrated_electronics/core/special_pins/index_pin.dm deleted file mode 100644 index 91fd3e7aaebb4..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/index_pin.dm +++ /dev/null @@ -1,21 +0,0 @@ -// These pins can only contain integer numbers between 0 and IC_MAX_LIST_LENGTH. Null is allowed. -/datum/integrated_io/index - name = "index pin" - data = 1 - -/datum/integrated_io/index/ask_for_pin_data(mob/user) - var/new_data = input("Please type in an index.","[src] index writing") as num - if(isnum_safe(new_data) && holder.check_interactivity(user)) - to_chat(user, "You input [new_data] into the pin.") - write_data_to_pin(new_data) - -/datum/integrated_io/index/write_data_to_pin(new_data) - if(isnull(new_data)) - new_data = 0 - - if(isnum_safe(new_data)) - data = CLAMP(round(new_data), 0, IC_MAX_LIST_LENGTH) - holder.on_data_written() - -/datum/integrated_io/index/display_pin_type() - return IC_FORMAT_INDEX diff --git a/code/modules/integrated_electronics/core/special_pins/list_pin.dm b/code/modules/integrated_electronics/core/special_pins/list_pin.dm deleted file mode 100644 index 6437d0869e756..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/list_pin.dm +++ /dev/null @@ -1,153 +0,0 @@ -// These pins contain a list. Null is not allowed. -/datum/integrated_io/lists - name = "list pin" - data = list() - -/datum/integrated_io/lists/ask_for_pin_data(mob/user) - interact(user) - -/datum/integrated_io/lists/proc/interact(mob/user) - var/list/my_list = data - var/t = " [src]" - t += "List length: [my_list.len] " - t += "\[Refresh\] | " - t += "\[Add\] | " - t += "\[Remove\] | " - t += "\[Edit\] | " - t += "\[Swap\] | " - t += "\[Clear\] " - t += " " - var/i = 0 - for(var/line in my_list) - i++ - t += "#[i] | [display_data(line)] | " - t += "\[Edit\] | " - t += "\[Remove\] " - user << browse(t, "window=list_pin_[REF(src)];size=500x400") - -/datum/integrated_io/lists/proc/add_to_list(mob/user, var/new_entry) - if(!new_entry && user) - new_entry = ask_for_data_type(user) - if(is_valid(new_entry)) - Add(new_entry) - -/datum/integrated_io/lists/proc/Add(var/new_entry) - var/list/my_list = data - if(my_list.len > IC_MAX_LIST_LENGTH) - my_list.Cut(Start=1,End=2) - my_list.Add(new_entry) - -/datum/integrated_io/lists/proc/remove_from_list_by_position(mob/user, var/position) - var/list/my_list = data - if(!my_list.len) - to_chat(user, "The list is empty, there's nothing to remove.") - return - if(!position) - return - var/target_entry = my_list[position] - if(target_entry) - my_list.Remove(target_entry) - -/datum/integrated_io/lists/proc/remove_from_list(mob/user, var/target_entry) - var/list/my_list = data - if(!my_list.len) - to_chat(user, "The list is empty, there's nothing to remove.") - return - if(!target_entry) - target_entry = input(user, "Which piece of data do you want to remove?", "Remove") as null|anything in my_list - if(holder.check_interactivity(user) && target_entry) - my_list.Remove(target_entry) - -/datum/integrated_io/lists/proc/edit_in_list(mob/user, var/target_entry) - var/list/my_list = data - if(!my_list.len) - to_chat(user, "The list is empty, there's nothing to modify.") - return - if(!target_entry) - target_entry = input(user, "Which piece of data do you want to edit?", "Edit") as null|anything in my_list - if(holder.check_interactivity(user) && target_entry) - var/edited_entry = ask_for_data_type(user, target_entry) - if(edited_entry) - my_list[my_list.Find(target_entry)] = edited_entry - -/datum/integrated_io/lists/proc/edit_in_list_by_position(mob/user, var/position) - var/list/my_list = data - if(!my_list.len) - to_chat(user, "The list is empty, there's nothing to modify.") - return - if(!position) - return - var/target_entry = my_list[position] - if(target_entry) - var/edited_entry = ask_for_data_type(user, target_entry) - if(edited_entry) - my_list[position] = edited_entry - -/datum/integrated_io/lists/proc/swap_inside_list(mob/user, var/first_target, var/second_target) - var/list/my_list = data - if(my_list.len <= 1) - to_chat(user, "The list is empty, or too small to do any meaningful swapping.") - return - if(!first_target) - first_target = input(user, "Which piece of data do you want to swap? (1)", "Swap") as null|anything in my_list - - if(holder.check_interactivity(user) && first_target) - if(!second_target) - second_target = input(user, "Which piece of data do you want to swap? (2)", "Swap") as null|anything in my_list - first_target - - if(holder.check_interactivity(user) && second_target) - var/first_pos = my_list.Find(first_target) - var/second_pos = my_list.Find(second_target) - my_list.Swap(first_pos, second_pos) - -/datum/integrated_io/lists/proc/clear_list(mob/user) - var/list/my_list = data - my_list.Cut() - -/datum/integrated_io/lists/scramble() - var/list/my_list = data - my_list = shuffle(my_list) - push_data() - -/datum/integrated_io/lists/write_data_to_pin(var/new_data) - if(islist(new_data)) - var/list/new_list = new_data - data = new_list.Copy(max(1,new_list.len - IC_MAX_LIST_LENGTH+1),0) - holder.on_data_written() - else if(isnull(new_data)) // Clear the list - var/list/my_list = data - my_list.Cut() - holder.on_data_written() - -/datum/integrated_io/lists/display_pin_type() - return IC_FORMAT_LIST - -/datum/integrated_io/lists/Topic(href, href_list) - if(!holder.check_interactivity(usr)) - return - if(..()) - return TRUE - - if(href_list["add"]) - add_to_list(usr) - - if(href_list["swap"]) - swap_inside_list(usr) - - if(href_list["clear"]) - clear_list(usr) - - if(href_list["remove"]) - if(href_list["pos"]) - remove_from_list_by_position(usr, text2num(href_list["pos"])) - else - remove_from_list(usr) - - if(href_list["edit"]) - if(href_list["pos"]) - edit_in_list_by_position(usr, text2num(href_list["pos"])) - else - edit_in_list(usr) - - holder.interact(usr) // Refresh the main UI, - interact(usr) // and the list UI. diff --git a/code/modules/integrated_electronics/core/special_pins/number_pin.dm b/code/modules/integrated_electronics/core/special_pins/number_pin.dm deleted file mode 100644 index e4e02d33a105a..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/number_pin.dm +++ /dev/null @@ -1,17 +0,0 @@ -// These pins can only contain numbers (int and floating point) or null. -/datum/integrated_io/number - name = "number pin" - -/datum/integrated_io/number/ask_for_pin_data(mob/user) - var/new_data = input("Please type in a number.","[src] number writing") as null|num - if(isnum_safe(new_data) && holder.check_interactivity(user) ) - to_chat(user, "You input [new_data] into the pin.") - write_data_to_pin(new_data) - -/datum/integrated_io/number/write_data_to_pin(var/new_data) - if(isnull(new_data) || isnum_safe(new_data)) - data = new_data - holder.on_data_written() - -/datum/integrated_io/number/display_pin_type() - return IC_FORMAT_NUMBER diff --git a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm deleted file mode 100644 index f64e15f225547..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm +++ /dev/null @@ -1,24 +0,0 @@ -// These pins only contain weakrefs or null. -/datum/integrated_io/ref - name = "ref pin" - -/datum/integrated_io/ref/ask_for_pin_data(mob/user) // This clears the pin. - write_data_to_pin(null) - -/datum/integrated_io/ref/write_data_to_pin(var/new_data) - if(isnull(new_data) || isweakref(new_data)) - data = new_data - holder.on_data_written() - -/datum/integrated_io/ref/display_pin_type() - return IC_FORMAT_REF - -/datum/integrated_io/ref/connect_pin(datum/integrated_io/pin) - ..(pin) - if(istype(pin,/datum/integrated_io/selfref)) - write_data_to_pin(pin.data) - -/datum/integrated_io/ref/disconnect_pin(datum/integrated_io/pin) - ..(pin) - if(istype(pin,/datum/integrated_io/selfref)) - write_data_to_pin(null) diff --git a/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm b/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm deleted file mode 100644 index 79dcf9e6c8c9f..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm +++ /dev/null @@ -1,27 +0,0 @@ -// This pin only contains its own weakref and can't be changed -/datum/integrated_io/selfref - name = "selfref pin" - -/datum/integrated_io/selfref/New() - ..() - write_data_to_pin(src) - -/datum/integrated_io/selfref/ask_for_pin_data(mob/user) // You can't clear it, it's self reference. - - -/datum/integrated_io/selfref/write_data_to_pin(var/new_data) // You can't write anything else but itself onto it - if(data) - return - data = WEAKREF(holder) - holder.on_data_written() - -/datum/integrated_io/selfref/display_pin_type() - return IC_FORMAT_REF - -/datum/integrated_io/selfref/connect_pin(datum/integrated_io/pin) - pin.write_data_to_pin(data) - ..(pin) - -/datum/integrated_io/selfref/disconnect_pin(datum/integrated_io/pin) - ..(pin) - pin.write_data_to_pin(null) diff --git a/code/modules/integrated_electronics/core/special_pins/string_pin.dm b/code/modules/integrated_electronics/core/special_pins/string_pin.dm deleted file mode 100644 index 554ebf5948799..0000000000000 --- a/code/modules/integrated_electronics/core/special_pins/string_pin.dm +++ /dev/null @@ -1,30 +0,0 @@ -// These pins can only contain text and null. -/datum/integrated_io/string - name = "string pin" - -/datum/integrated_io/string/ask_for_pin_data(mob/user) - var/new_data = stripped_multiline_input(user, "Please type in a string.","[src] string writing", no_trim = TRUE) - if(holder.check_interactivity(user) ) - if(!isnull(new_data) && istext(new_data) && CHAT_FILTER_CHECK(new_data)) - to_chat(user, "Your input contains prohibited words.") - return - to_chat(user, "You input [new_data ? "[new_data]" : "NULL"] into the pin.") - write_data_to_pin(new_data) - -/datum/integrated_io/string/write_data_to_pin(var/new_data) - if(isnull(new_data) || istext(new_data)) - data = new_data - holder.on_data_written() - -// This makes the text go "from this" to "#G&*!HD$%L" -/datum/integrated_io/string/scramble() - if(!is_valid()) - return - var/list/options = list("!","@","#","$","%","^","&","*","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") - var/new_data = "" - for(var/i in 1 to length(data)) - new_data += pick(options) - push_data() - -/datum/integrated_io/string/display_pin_type() - return IC_FORMAT_STRING diff --git a/code/modules/integrated_electronics/core/wirer.dm b/code/modules/integrated_electronics/core/wirer.dm deleted file mode 100644 index 9550f9b4aefcd..0000000000000 --- a/code/modules/integrated_electronics/core/wirer.dm +++ /dev/null @@ -1,101 +0,0 @@ -#define WIRE "wire" -#define WIRING "wiring" -#define UNWIRE "unwire" -#define UNWIRING "unwiring" - -/obj/item/integrated_electronics/wirer - name = "circuit wirer" - desc = "It's a small wiring tool, with a wire roll, electric soldering iron, wire cutter, and more in one package. \ - The wires used are generally useful for small electronics, such as circuitboards and breadboards, as opposed to larger wires \ - used for power or data transmission." - icon = 'icons/obj/assemblies/electronic_tools.dmi' - icon_state = "wirer-wire" - flags_1 = CONDUCT_1 - w_class = WEIGHT_CLASS_SMALL - var/datum/integrated_io/selected_io = null - var/mode = WIRE - -/obj/item/integrated_electronics/wirer/update_icon() - icon_state = "wirer-[mode]" - -/obj/item/integrated_electronics/wirer/proc/wire(var/datum/integrated_io/io, mob/user) - if(!io.holder.assembly) - to_chat(user, "\The [io.holder] needs to be secured inside an assembly first.") - return - switch(mode) - if(WIRE) - selected_io = io - to_chat(user, "You attach a data wire to \the [selected_io.holder]'s [selected_io.name] data channel.") - mode = WIRING - update_icon() - if(WIRING) - if(io == selected_io) - to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.") - return - if(io.io_type != selected_io.io_type) - to_chat(user, "Those two types of channels are incompatible. The first is a [selected_io.io_type], \ - while the second is a [io.io_type].") - return - if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly) - to_chat(user, "Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.") - return - selected_io.connect_pin(io) - - to_chat(user, "You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].") - mode = WIRE - update_icon() - selected_io.holder.interact(user) // This is to update the UI. - selected_io = null - - if(UNWIRE) - selected_io = io - if(!io.linked.len) - to_chat(user, "There is nothing connected to \the [selected_io] data channel.") - selected_io = null - return - to_chat(user, "You prepare to detach a data wire from \the [selected_io.holder]'s [selected_io.name] data channel.") - mode = UNWIRING - update_icon() - return - - if(UNWIRING) - if(io == selected_io) - to_chat(user, "You can't wire a pin into each other, so unwiring \the [selected_io.holder] from \ - the same pin is rather moot.") - return - if(selected_io in io.linked) - selected_io.disconnect_pin(io) - to_chat(user, "You disconnect \the [selected_io.holder]'s [selected_io.name] from \ - \the [io.holder]'s [io.name].") - selected_io.holder.interact(user) // This is to update the UI. - selected_io = null - mode = UNWIRE - update_icon() - else - to_chat(user, "\The [selected_io.holder]'s [selected_io.name] and \the [io.holder]'s \ - [io.name] are not connected.") - return - -/obj/item/integrated_electronics/wirer/attack_self(mob/user) - switch(mode) - if(WIRE) - mode = UNWIRE - if(WIRING) - if(selected_io) - to_chat(user, "You decide not to wire the data channel.") - selected_io = null - mode = WIRE - if(UNWIRE) - mode = WIRE - if(UNWIRING) - if(selected_io) - to_chat(user, "You decide not to disconnect the data channel.") - selected_io = null - mode = UNWIRE - update_icon() - to_chat(user, "You set \the [src] to [mode].") - -#undef WIRE -#undef WIRING -#undef UNWIRE -#undef UNWIRING diff --git a/code/modules/integrated_electronics/passive/passive.dm b/code/modules/integrated_electronics/passive/passive.dm deleted file mode 100644 index 7678d02ad5fe7..0000000000000 --- a/code/modules/integrated_electronics/passive/passive.dm +++ /dev/null @@ -1,7 +0,0 @@ -// 'Passive' components do not have any pins, and instead contribute in some form to the assembly holding them. -/obj/item/integrated_circuit/passive - inputs = list() - outputs = list() - activators = list() - power_draw_idle = 0 - power_draw_per_use = 0 diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm deleted file mode 100644 index 67677d7d4b35c..0000000000000 --- a/code/modules/integrated_electronics/passive/power.dm +++ /dev/null @@ -1,139 +0,0 @@ - -/obj/item/integrated_circuit/passive/power - name = "power thingy" - desc = "Does power stuff." - complexity = 5 - category_text = "Power - Passive" - -/obj/item/integrated_circuit/passive/power/proc/make_energy() - return - -// For calculators. -/obj/item/integrated_circuit/passive/power/solar_cell - name = "tiny photovoltaic cell" - desc = "It's a very tiny solar cell, generally used in calculators." - extended_desc = "This cell generates 1 W of power in optimal lighting conditions. Less light will result in less power being generated." - icon_state = "solar_cell" - complexity = 8 - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/max_power = 30 - -/obj/item/integrated_circuit/passive/power/solar_cell/make_energy() - var/turf/T = get_turf(src) - var/light_amount = T ? T.get_lumcount() : 0 - var/adjusted_power = max(max_power * light_amount, 0) - adjusted_power = round(adjusted_power, 0.1) - if(adjusted_power) - if(assembly) - assembly.give_power(adjusted_power) - -/obj/item/integrated_circuit/passive/power/starter - name = "starter" - desc = "This tiny circuit will send a pulse right after the device is turned on, or when power is restored to it." - icon_state = "led" - complexity = 1 - activators = list("pulse out" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/is_charge = FALSE - -/obj/item/integrated_circuit/passive/power/starter/make_energy() - if(assembly.battery) - if(assembly.battery.charge) - if(!is_charge) - activate_pin(1) - is_charge = TRUE - else - is_charge = FALSE - else - is_charge=FALSE - return FALSE - -// For fat machines that need fat power, like drones. -/obj/item/integrated_circuit/passive/power/relay - name = "tesla power relay" - desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them." - w_class = WEIGHT_CLASS_SMALL - extended_desc = "The siphon drains 50 W of power from an APC in the same room as it as long as it has charge remaining. It will always drain \ - from the 'equipment' power channel." - icon_state = "power_relay" - complexity = 7 - spawn_flags = IC_SPAWN_RESEARCH - var/power_amount = 50 - - -/obj/item/integrated_circuit/passive/power/relay/make_energy() - if(!assembly) - return - var/area/A = get_area(src) - if(A && A.powered(AREA_USAGE_EQUIP) && assembly.give_power(power_amount)) - A.use_power(power_amount, AREA_USAGE_EQUIP) - // give_power() handles CELLRATE on its own. - - -// For really fat machines. -/obj/item/integrated_circuit/passive/power/relay/large - name = "large tesla power relay" - desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them, now in industrial size!" - w_class = WEIGHT_CLASS_BULKY - extended_desc = "The siphon drains 2 kW of power from an APC in the same room as it as long as it has charge remaining. It will always drain \ - from the 'equipment' power channel." - icon_state = "power_relay" - complexity = 15 - spawn_flags = IC_SPAWN_RESEARCH - power_amount = 1000 - - -//fuel cell -/obj/item/integrated_circuit/passive/power/chemical_cell - name = "fuel cell" - desc = "Produces electricity from chemicals." - icon_state = "chemical_cell" - extended_desc = "This is effectively an internal beaker. It will consume and produce power from plasma, slime jelly, welding fuel, carbon,\ - ethanol, nutriment, and blood in order of decreasing efficiency. It will consume fuel only if the battery can take more energy." - complexity = 4 - inputs = list() - outputs = list("volume used" = IC_PINTYPE_NUMBER, "self reference" = IC_PINTYPE_SELFREF) - activators = list("push ref" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/volume = 60 - var/list/fuel = list("plasma" = 50000, "slimejelly" = 22500, "welding_fuel" = 15000, "carbon" = 10000, "ethanol" = 10000, "nutriment" = 8000) - var/multi = 1 - var/lfwb =TRUE - -/obj/item/integrated_circuit/passive/power/chemical_cell/New() - ..() - create_reagents(volume, OPENCONTAINER) - extended_desc +="But no fuel can be compared with blood of living human." - - -/obj/item/integrated_circuit/passive/power/chemical_cell/interact(mob/user) - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - ..() - -/obj/item/integrated_circuit/passive/power/chemical_cell/on_reagent_change(changetype) - set_pin_data(IC_OUTPUT, 1, reagents.total_volume) - push_data() - -/obj/item/integrated_circuit/passive/power/chemical_cell/make_energy() - if(assembly) - if(assembly.battery) - var/bp = 5000 - if(reagents.get_reagent_amount("blood")) //only blood is powerful enough to power the station(c) - var/datum/reagent/blood/B = locate() in reagents.reagent_list - if(lfwb) - if(B && B.data["cloneable"]) - var/mob/M = B.data["donor"] - if(M && (M.stat != DEAD) && (M.client)) - bp = 500000 - if((assembly.battery.maxcharge-assembly.battery.charge) / GLOB.CELLRATE > bp) - if(reagents.remove_reagent("blood", 1)) - assembly.give_power(bp) - for(var/I in fuel) - if((assembly.battery.maxcharge-assembly.battery.charge) / GLOB.CELLRATE > fuel[I]) - if(reagents.remove_reagent(I, 1)) - assembly.give_power(fuel[I]*multi) - -/obj/item/integrated_circuit/passive/power/chemical_cell/do_work() - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() diff --git a/code/modules/integrated_electronics/subtypes/access.dm b/code/modules/integrated_electronics/subtypes/access.dm deleted file mode 100644 index 10138c4491874..0000000000000 --- a/code/modules/integrated_electronics/subtypes/access.dm +++ /dev/null @@ -1,41 +0,0 @@ -// - card reader - // -/obj/item/integrated_circuit/input/card_reader - name = "ID card reader" //To differentiate it from the data card reader - desc = "A circuit that can read the registred name, assignment, and PassKey string from an ID card." - icon_state = "card_reader" - - complexity = 4 - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - outputs = list( - "registered name" = IC_PINTYPE_STRING, - "assignment" = IC_PINTYPE_STRING, - "passkey" = IC_PINTYPE_STRING - ) - activators = list( - "on read" = IC_PINTYPE_PULSE_OUT - ) - -/obj/item/integrated_circuit/input/card_reader/attackby_react(obj/item/I, mob/living/user, intent) - var/obj/item/card/id/card = I.GetID() - var/list/access = I.GetAccess() - var/passkey = strtohex(XorEncrypt(json_encode(access), SScircuit.cipherkey)) - - if(assembly) - assembly.access_card.access |= access - - if(card) // An ID card. - set_pin_data(IC_OUTPUT, 1, card.registered_name) - set_pin_data(IC_OUTPUT, 2, card.assignment) - - else if(length(access)) // A non-card object that has access levels. - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - - else - return FALSE - - set_pin_data(IC_OUTPUT, 3, passkey) - - push_data() - activate_pin(1) - return TRUE diff --git a/code/modules/integrated_electronics/subtypes/arithmetic.dm b/code/modules/integrated_electronics/subtypes/arithmetic.dm deleted file mode 100644 index 0799b4d2df7a2..0000000000000 --- a/code/modules/integrated_electronics/subtypes/arithmetic.dm +++ /dev/null @@ -1,344 +0,0 @@ -//These circuits do simple math. -/obj/item/integrated_circuit/arithmetic - complexity = 1 - inputs = list( - "A" = IC_PINTYPE_NUMBER, - "B" = IC_PINTYPE_NUMBER, - "C" = IC_PINTYPE_NUMBER, - "D" = IC_PINTYPE_NUMBER, - "E" = IC_PINTYPE_NUMBER, - "F" = IC_PINTYPE_NUMBER, - "G" = IC_PINTYPE_NUMBER, - "H" = IC_PINTYPE_NUMBER - ) - outputs = list("result" = IC_PINTYPE_NUMBER) - activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT) - category_text = "Arithmetic" - power_draw_per_use = 5 // Math is pretty cheap. - -// +Adding+ // - -/obj/item/integrated_circuit/arithmetic/addition - name = "addition circuit" - desc = "This circuit can add numbers together." - extended_desc = "The order that the calculation goes is; \ - result = ((((A + B) + C) + D) ... ) and so on, until all pins have been added. \ - Null pins are ignored." - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/addition/do_work() - var/result = 0 - for(var/k in 1 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(isnum_safe(I)) - result += I - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// -Subtracting- // - -/obj/item/integrated_circuit/arithmetic/subtraction - name = "subtraction circuit" - desc = "This circuit can subtract numbers." - extended_desc = "The order that the calculation goes is; \ - result = ((((A - B) - C) - D) ... ) and so on, until all pins have been subtracted. \ - Null pins are ignored. Pin A must be a number, or the circuit will not function." - icon_state = "subtraction" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/subtraction/do_work() - var/datum/integrated_io/A = inputs[1] - if(!isnum_safe(A.data)) - return - var/result = A.data - - for(var/k in 2 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(isnum_safe(I)) - result -= I - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// *Multiply* // - -/obj/item/integrated_circuit/arithmetic/multiplication - name = "multiplication circuit" - desc = "This circuit can multiply numbers." - extended_desc = "The order that the calculation goes is; \ - result = ((((A * B) * C) * D) ... ) and so on, until all pins have been multiplied. \ - Null pins are ignored. Pin A must be a number, or the circuit will not function." - icon_state = "multiplication" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - - -/obj/item/integrated_circuit/arithmetic/multiplication/do_work() - var/datum/integrated_io/A = inputs[1] - if(!isnum_safe(A.data)) - return - var/result = A.data - for(var/k in 2 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(isnum_safe(I)) - result *= I - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// /Division/ // - -/obj/item/integrated_circuit/arithmetic/division - name = "division circuit" - desc = "This circuit can divide numbers. Don't even think about trying to divide by zero!" - extended_desc = "The order that the calculation goes is; \ - result = ((((A / B) / C) / D) ... ) and so on, until all pins have been divided. \ - Null pins, and pins containing 0, are ignored. Pin A must be a number or the circuit will not function." - icon_state = "division" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/division/do_work() - var/datum/integrated_io/A = inputs[1] - if(!isnum_safe(A.data)) - return - var/result = A.data - - - for(var/k in 2 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(isnum_safe(I) && (I != 0)) - result /= I - - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -//^ Exponent ^// - -/obj/item/integrated_circuit/arithmetic/exponent - name = "exponent circuit" - desc = "Outputs A to the power of B." - icon_state = "exponent" - inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/exponent/do_work() - var/result = 0 - var/datum/integrated_io/A = inputs[1] - var/datum/integrated_io/B = inputs[2] - if(isnum_safe(A.data) && isnum_safe(B.data)) - result = A.data ** B.data - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// +-Sign-+ // - -/obj/item/integrated_circuit/arithmetic/sign - name = "sign circuit" - desc = "This circuit can tell if a number is positive, negative, or zero." - extended_desc = "Will output 1, -1, or 0, depending on if A is a positive number, a negative number, or zero, respectively." - icon_state = "sign" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/sign/do_work() - var/result = 0 - var/datum/integrated_io/A = inputs[1] - if(isnum_safe(A.data)) - if(A.data > 0) - result = 1 - else if (A.data < 0) - result = -1 - else - result = 0 - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Round // - -/obj/item/integrated_circuit/arithmetic/round - name = "round circuit" - desc = "Rounds A to the nearest B multiple of A." - extended_desc = "If B is not given a number, it will output the floor of A instead." - icon_state = "round" - inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/round/do_work() - var/result = 0 - var/datum/integrated_io/A = inputs[1] - var/datum/integrated_io/B = inputs[2] - if(isnum_safe(A.data)) - if(isnum_safe(B.data) && B.data != 0) - result = round(A.data, B.data) - else - result = round(A.data) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Absolute // - -/obj/item/integrated_circuit/arithmetic/absolute - name = "absolute circuit" - desc = "This outputs a non-negative version of the number you put in. This may also be thought of as its distance from zero." - icon_state = "absolute" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/absolute/do_work() - var/result = 0 - for(var/k in 1 to inputs.len) - var/datum/integrated_io/I = inputs[k] - I.pull_data() - if(isnum_safe(I.data)) - result = abs(I.data) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Averaging // - -/obj/item/integrated_circuit/arithmetic/average - name = "average circuit" - desc = "This circuit is of average quality. It will compute the average of the numbers you give it." - extended_desc = "Note that null pins are ignored, whereas a pin containing 0 is included in the averaging calculation." - icon_state = "average" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/average/do_work() - var/result = 0 - var/inputs_used = 0 - for(var/k in 2 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(isnum_safe(I)) - inputs_used++ - result += I - - if(inputs_used) - result = result / inputs_used - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Pi, because why the hell not? // -/obj/item/integrated_circuit/arithmetic/pi - name = "pi constant circuit" - desc = "Not recommended for cooking. Outputs '3.14159' when it receives a pulse." - icon_state = "pi" - inputs = list() - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/pi/Initialize() - . = ..() - desc = "Not recommended for cooking. Outputs '[PI]' when it receives a pulse." - -/obj/item/integrated_circuit/arithmetic/pi/do_work() - set_pin_data(IC_OUTPUT, 1, PI) - push_data() - activate_pin(2) - -// Random // -/obj/item/integrated_circuit/arithmetic/random - name = "random number generator circuit" - desc = "This gives a random (integer) number between values A and B inclusive." - extended_desc = "'Inclusive' means that the upper bound is included in the range of numbers, e.g. L = 1 and H = 3 will allow \ - for outputs of 1, 2, or 3. H being the higher number is not strictly required." - icon_state = "random" - inputs = list("L" = IC_PINTYPE_NUMBER,"H" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/random/do_work() - var/result = 0 - var/L = get_pin_data(IC_INPUT, 1) - var/H = get_pin_data(IC_INPUT, 2) - - if(isnum_safe(L) && isnum_safe(H)) - result = rand(L, H) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Square Root // - -/obj/item/integrated_circuit/arithmetic/square_root - name = "square root circuit" - desc = "This outputs the square root of the number you input." - icon_state = "square_root" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/square_root/do_work() - var/result = 0 - for(var/k in 2 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(isnum_safe(I)) - result += sqrt(I) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// % Modulo % // - -/obj/item/integrated_circuit/arithmetic/modulo - name = "modulo circuit" - desc = "Gets the remainder of A / B." - icon_state = "modulo" - inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/arithmetic/modulo/do_work() - var/result = 0 - var/A = get_pin_data(IC_INPUT, 1) - var/B = get_pin_data(IC_INPUT, 2) - if(isnum_safe(A) && isnum_safe(B) && B != 0) - result = A % B - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// -Max- // -/obj/item/integrated_circuit/arithmetic/max - name = "max circuit" - desc = "This circuit sends out the highest number." - extended_desc = "The highest number is put out. Null is ignored." - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/min_comparision = FALSE - -/obj/item/integrated_circuit/arithmetic/max/do_work() - var/result - for(var/k in 1 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(!isnum_safe(I)) - continue - if(!isnum_safe(result) || (!min_comparision && I > result) || (min_comparision && I < result)) - result = I - if(!isnum_safe(result)) - result = 0 - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - - -// -Min- // -/obj/item/integrated_circuit/arithmetic/max/min - name = "min circuit" - desc = "This circuit sends out the smallest number." - extended_desc = "The smallest number is put out. Null is ignored. In case no number is found, 0 is given out." - min_comparision = TRUE diff --git a/code/modules/integrated_electronics/subtypes/atmospherics.dm b/code/modules/integrated_electronics/subtypes/atmospherics.dm deleted file mode 100644 index 6b1c5fee77685..0000000000000 --- a/code/modules/integrated_electronics/subtypes/atmospherics.dm +++ /dev/null @@ -1,783 +0,0 @@ -#define SOURCE_TO_TARGET 0 -#define TARGET_TO_SOURCE 1 -#define PUMP_EFFICIENCY 0.6 -#define TANK_FAILURE_PRESSURE (ONE_ATMOSPHERE*25) -#define PUMP_MAX_VOLUME 100 - - -/obj/item/integrated_circuit/atmospherics - category_text = "Atmospherics" - cooldown_per_use = 2 SECONDS - complexity = 10 - size = 7 - outputs = list( - "self reference" = IC_PINTYPE_SELFREF, - "pressure" = IC_PINTYPE_NUMBER - ) - var/datum/gas_mixture/air_contents - var/volume = 2 //Pretty small, I know - -/obj/item/integrated_circuit/atmospherics/Initialize() - air_contents = new(volume) - ..() - -/obj/item/integrated_circuit/atmospherics/return_air() - return air_contents - -//Check if the gas container is adjacent and of the right type -/obj/item/integrated_circuit/atmospherics/proc/check_gassource(atom/gasholder) - if(!gasholder) - return FALSE - if(!gasholder.Adjacent(get_object())) - return FALSE - if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics)) - return FALSE - return TRUE - -//Needed in circuits where source and target types differ -/obj/item/integrated_circuit/atmospherics/proc/check_gastarget(atom/gasholder) - return check_gassource(gasholder) - - -// - gas pump - // **works** -/obj/item/integrated_circuit/atmospherics/pump - name = "gas pump" - desc = "Somehow moves gases between two tanks, canisters, and other gas containers." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "source" = IC_PINTYPE_REF, - "target" = IC_PINTYPE_REF, - "target pressure" = IC_PINTYPE_NUMBER - ) - activators = list( - "transfer" = IC_PINTYPE_PULSE_IN, - "on transfer" = IC_PINTYPE_PULSE_OUT - ) - var/direction = SOURCE_TO_TARGET - var/target_pressure = PUMP_MAX_PRESSURE - power_draw_per_use = 20 - -/obj/item/integrated_circuit/atmospherics/pump/Initialize() - air_contents = new(volume) - extended_desc += " Use negative pressure to move air from target to source. \ - Note that only part of the gas is moved on each transfer, \ - so multiple activations will be necessary to achieve target pressure. \ - The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa." - . = ..() - -// This proc gets the direction of the gas flow depending on its value, by calling update target -/obj/item/integrated_circuit/atmospherics/pump/on_data_written() - var/amt = get_pin_data(IC_INPUT, 3) - update_target(amt) - -/obj/item/integrated_circuit/atmospherics/pump/proc/update_target(new_amount) - if(!isnum_safe(new_amount)) - new_amount = 0 - // See in which direction the gas moves - if(new_amount < 0) - direction = TARGET_TO_SOURCE - else - direction = SOURCE_TO_TARGET - target_pressure = min(round(PUMP_MAX_PRESSURE),abs(new_amount)) - -/obj/item/integrated_circuit/atmospherics/pump/do_work() - var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj) - var/obj/target = get_pin_data_as_type(IC_INPUT, 2, /obj) - perform_magic(source, target) - activate_pin(2) - -/obj/item/integrated_circuit/atmospherics/pump/proc/perform_magic(atom/source, atom/target) - //Check if both atoms are of the right type: atmos circuits/gas tanks/canisters. If one is the same, use the circuit var - if(!check_gassource(source)) - source = src - - if(!check_gastarget(target)) - target = src - - // If both are the same, this whole proc would do nothing and just waste performance - if(source == target) - return - - var/datum/gas_mixture/source_air = source.return_air() - var/datum/gas_mixture/target_air = target.return_air() - - if(!source_air || !target_air) - return - - // Swapping both source and target - if(direction == TARGET_TO_SOURCE) - var/temp = source_air - source_air = target_air - target_air = temp - - // If what you are pumping is empty, use the circuit's storage - if(source_air.total_moles() <= 0) - source_air = air_contents - - // Move gas from one place to another - move_gas(source_air, target_air, (istype(target, /obj/item/tank) ? target : null)) - air_update_turf() - -/obj/item/integrated_circuit/atmospherics/pump/proc/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air, obj/item/tank/snowflake) - - // No moles = nothing to pump - if(source_air.total_moles() <= 0 || target_air.return_pressure() >= PUMP_MAX_PRESSURE) - return - - // Negative Kelvin temperatures should never happen and if they do, normalize them - if(source_air.return_temperature() < TCMB) - source_air.set_temperature(TCMB) - - var/pressure_delta = target_pressure - target_air.return_pressure() - if(pressure_delta > 0.1) - var/transfer_moles = (pressure_delta*target_air.return_volume()/(source_air.return_temperature() * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY - var/datum/gas_mixture/removed = source_air.remove(transfer_moles) - if(istype(snowflake)) //Snowflake check for tanks specifically, because tank ruptures are handled in a very snowflakey way that expects all tank interactions to be handled via the tank's procs - snowflake.assume_air(removed) - else - target_air.merge(removed) - - -// - volume pump - // **Works** -/obj/item/integrated_circuit/atmospherics/pump/volume - name = "volume pump" - desc = "Moves gases between two tanks, canisters, and other gas containers by using their volume, up to 200 L/s." - extended_desc = " Use negative volume to move air from target to source. Note that only part of the gas is moved on each transfer. Its maximum pumping volume is capped at 1000kPa." - - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "source" = IC_PINTYPE_REF, - "target" = IC_PINTYPE_REF, - "transfer volume" = IC_PINTYPE_NUMBER - ) - activators = list( - "transfer" = IC_PINTYPE_PULSE_IN, - "on transfer" = IC_PINTYPE_PULSE_OUT - ) - direction = SOURCE_TO_TARGET - var/transfer_rate = PUMP_MAX_VOLUME - power_draw_per_use = 20 - -/obj/item/integrated_circuit/atmospherics/pump/volume/update_target(new_amount) - if(!isnum_safe(new_amount)) - new_amount = 0 - // See in which direction the gas moves - if(new_amount < 0) - direction = TARGET_TO_SOURCE - else - direction = SOURCE_TO_TARGET - target_pressure = min(PUMP_MAX_VOLUME,abs(new_amount)) - -/obj/item/integrated_circuit/atmospherics/pump/volume/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air, obj/item/tank/snowflake) - // No moles = nothing to pump - if(source_air.total_moles() <= 0) - return - - // Negative Kelvin temperatures should never happen and if they do, normalize them - if(source_air.return_temperature() < TCMB) - source_air.set_temperature(TCMB) - - if((source_air.return_pressure() < 0.01) || (target_air.return_pressure() >= PUMP_MAX_PRESSURE)) - return - - //The second part of the min caps the pressure built by the volume pumps to the max pump pressure - var/transfer_ratio = min(transfer_rate,target_air.return_volume()*PUMP_MAX_PRESSURE/source_air.return_pressure())/source_air.return_volume() - - var/datum/gas_mixture/removed = source_air.remove_ratio(transfer_ratio * PUMP_EFFICIENCY) - - if(istype(snowflake)) - snowflake.assume_air(removed) - else - target_air.merge(removed) - -// - gas vent - // **works** -/obj/item/integrated_circuit/atmospherics/pump/vent - name = "gas vent" - extended_desc = "Use negative volume to move air from target to environment. Note that only part of the gas is moved on each transfer. Unlike the gas pump, this one keeps pumping even further to pressures of 9000 pKa and it is not advised to use it on tank circuits." - desc = "Moves gases between the environment and adjacent gas containers." - inputs = list( - "container" = IC_PINTYPE_REF, - "target pressure" = IC_PINTYPE_NUMBER - ) - -/obj/item/integrated_circuit/atmospherics/pump/vent/on_data_written() - var/amt = get_pin_data(IC_INPUT, 2) - update_target(amt) - -/obj/item/integrated_circuit/atmospherics/pump/vent/do_work() - var/turf/source = get_turf(src) - var/obj/target = get_pin_data_as_type(IC_INPUT, 1, /obj) - perform_magic(source, target) - activate_pin(2) - -/obj/item/integrated_circuit/atmospherics/pump/vent/check_gastarget(atom/gasholder) - if(!gasholder) - return FALSE - if(!gasholder.Adjacent(get_object())) - return FALSE - if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics)) - return FALSE - return TRUE - - -/obj/item/integrated_circuit/atmospherics/pump/vent/check_gassource(atom/target) - if(!target) - return FALSE - if(!istype(target, /turf)) - return FALSE - return TRUE - - -// - integrated connector - // Can connect and disconnect properly -/obj/item/integrated_circuit/atmospherics/connector - name = "integrated connector" - desc = "Creates an airtight seal with standard connectors found on the floor, \ - allowing the assembly to exchange gases with a pipe network." - extended_desc = "This circuit will automatically attempt to locate and connect to ports on the floor beneath it when activated. \ - You must set a target before connecting." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "target" = IC_PINTYPE_REF - ) - activators = list( - "toggle connection" = IC_PINTYPE_PULSE_IN, - "on connected" = IC_PINTYPE_PULSE_OUT, - "on connection failed" = IC_PINTYPE_PULSE_OUT, - "on disconnected" = IC_PINTYPE_PULSE_OUT - ) - - var/obj/machinery/atmospherics/components/unary/portables_connector/connector - -/obj/item/integrated_circuit/atmospherics/connector/Initialize() - air_contents = new(volume) - START_PROCESSING(SSobj, src) - . = ..() - -//Sucks up the gas from the connector -/obj/item/integrated_circuit/atmospherics/connector/process() - set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) - -/obj/item/integrated_circuit/atmospherics/connector/check_gassource(atom/gasholder) - if(!gasholder) - return FALSE - if(!istype(gasholder,/obj/machinery/atmospherics/components/unary/portables_connector)) - return FALSE - return TRUE - -//If the assembly containing this is moved from the tile the connector pipe is in, the connection breaks -/obj/item/integrated_circuit/atmospherics/connector/ext_moved() - if(connector) - if(get_dist(get_object(), connector) > 0) - // The assembly is set as connected device and the connector handles the rest - connector.connected_device = null - connector = null - activate_pin(4) - -/obj/item/integrated_circuit/atmospherics/connector/do_work() - // If there is a connection, disconnect - if(connector) - connector.connected_device = null - connector = null - activate_pin(4) - return - - var/obj/machinery/atmospherics/components/unary/portables_connector/PC = locate() in get_turf(src) - // If no connector can't connect - if(!PC) - activate_pin(3) - return - connector = PC - connector.connected_device = src - activate_pin(2) - -// Required for making the connector port script work -obj/item/integrated_circuit/atmospherics/connector/portableConnectorReturnAir() - return air_contents - - -// - gas filter - // **works** -/obj/item/integrated_circuit/atmospherics/pump/filter - name = "gas filter" - desc = "Filters one gas out of a mixture." - complexity = 20 - size = 8 - spawn_flags = IC_SPAWN_RESEARCH - inputs = list( - "source" = IC_PINTYPE_REF, - "filtered output" = IC_PINTYPE_REF, - "contaminants output" = IC_PINTYPE_REF, - "wanted gases" = IC_PINTYPE_LIST, - "target pressure" = IC_PINTYPE_NUMBER - ) - power_draw_per_use = 30 - -/obj/item/integrated_circuit/atmospherics/pump/filter/on_data_written() - var/amt = get_pin_data(IC_INPUT, 5) - target_pressure = CLAMP(amt, 0, PUMP_MAX_PRESSURE) - -/obj/item/integrated_circuit/atmospherics/pump/filter/do_work() - activate_pin(2) - var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj) - var/obj/filtered = get_pin_data_as_type(IC_INPUT, 2, /obj) - var/obj/contaminants = get_pin_data_as_type(IC_INPUT, 3, /obj) - - var/wanted = get_pin_data(IC_INPUT, 4) - - // If there is no filtered output, this whole thing makes no sense - if(!check_gassource(filtered)) - return - - var/datum/gas_mixture/filtered_air = filtered.return_air() - if(!filtered_air) - return - - // If no source is set, the source is possibly this circuit's content - if(!check_gassource(source)) - source = src - var/datum/gas_mixture/source_air = source.return_air() - - //No source air: source is this circuit - if(!source_air) - source_air = air_contents - - // If no filtering tank is set, filter through itself - if(!check_gassource(contaminants)) - contaminants = src - var/datum/gas_mixture/contaminated_air = contaminants.return_air() - - //If there is no gas mixture datum for unfiltered, pump the contaminants back into the circuit - if(!contaminated_air) - contaminated_air = air_contents - - if(contaminated_air.return_pressure() >= PUMP_MAX_PRESSURE || filtered_air.return_pressure() >= PUMP_MAX_PRESSURE) - return - - var/pressure_delta = target_pressure - contaminated_air.return_pressure() - var/transfer_moles - - //Negative Kelvins are an anomaly and should be normalized if encountered - if(source_air.return_temperature() < TCMB) - source_air.set_temperature(TCMB) - - transfer_moles = (pressure_delta*contaminated_air.return_volume()/(source_air.return_temperature() * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY - - //If there is nothing to transfer, just return - if(transfer_moles <= 0) - return - - //This is the var that holds the currently filtered part of the gas - var/datum/gas_mixture/removed = source_air.remove(transfer_moles) - if(!removed) - return - - //This is the gas that will be moved from source to filtered - var/datum/gas_mixture/filtered_out = new - - for(var/filtered_gas in removed.get_gases()) - //Get the name of the gas and see if it is in the list - if(GLOB.meta_gas_info[filtered_gas][META_GAS_NAME] in wanted) - //The gas that is put in all the filtered out gases - filtered_out.set_temperature(removed.return_temperature()) - filtered_out.set_moles(filtered_gas, removed.get_moles(filtered_gas)) - - //The filtered out gas is entirely removed from the currently filtered gases - removed.set_moles(filtered_gas, 0) - - //Check if the pressure is high enough to put stuff in filtered, or else just put it back in the source - if(filtered_air.return_pressure() < target_pressure) - if(istype(filtered, /obj/item/tank)) - filtered.assume_air(filtered_out) - else - filtered_air.merge(filtered_out) - else - if(istype(source, /obj/item/tank)) - source.assume_air(filtered_out) - else - source_air.merge(filtered_out) - if(istype(contaminants, /obj/item/tank)) - contaminants.assume_air(removed) - else - contaminated_air.merge(removed) - - -/obj/item/integrated_circuit/atmospherics/pump/filter/Initialize() - air_contents = new(volume) - . = ..() - extended_desc = "Remember to properly spell and capitalize the filtered gas name. \ - Note that only part of the gas is moved on each transfer, \ - so multiple activations will be necessary to achieve target pressure. \ - The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa." - - -// - gas mixer - // **works** -/obj/item/integrated_circuit/atmospherics/pump/mixer - name = "gas mixer" - desc = "Mixes 2 different types of gases." - complexity = 20 - size = 8 - spawn_flags = IC_SPAWN_RESEARCH - inputs = list( - "first source" = IC_PINTYPE_REF, - "second source" = IC_PINTYPE_REF, - "output" = IC_PINTYPE_REF, - "first source percentage" = IC_PINTYPE_NUMBER, - "target pressure" = IC_PINTYPE_NUMBER - ) - power_draw_per_use = 30 - -/obj/item/integrated_circuit/atmospherics/pump/mixer/do_work() - activate_pin(2) - var/obj/source_1 = get_pin_data(IC_INPUT, 1) - var/obj/source_2 = get_pin_data(IC_INPUT, 2) - var/obj/gas_output = get_pin_data(IC_INPUT, 3) - if(!check_gassource(source_1)) - source_1 = src - - if(!check_gassource(source_2)) - source_2 = src - - if(!check_gassource(gas_output)) - gas_output = src - - if(source_1 == gas_output || source_2 == gas_output) - return - - var/datum/gas_mixture/source_1_gases = source_1.return_air() - var/datum/gas_mixture/source_2_gases = source_2.return_air() - var/datum/gas_mixture/output_gases = gas_output.return_air() - - if(!source_1_gases || !source_2_gases || !output_gases) - return - - if(output_gases.return_pressure() >= PUMP_MAX_PRESSURE) - return - - if(source_1_gases.return_pressure() <= 0 || source_2_gases.return_pressure() <= 0) - return - - //This calculates how much should be sent - var/gas_percentage = round(max(min(get_pin_data(IC_INPUT, 4),100),0) / 100) - - //Basically: number of moles = percentage of pressure filled up * efficiency coefficient * (pressure from both gases * volume of output) / (R * Temperature) - var/transfer_moles = (get_pin_data(IC_INPUT, 5) / max(1,output_gases.return_pressure())) * PUMP_EFFICIENCY * (source_1_gases.return_pressure() * gas_percentage + source_2_gases.return_pressure() * (1 - gas_percentage)) * output_gases.return_volume()/ (R_IDEAL_GAS_EQUATION * max(output_gases.return_temperature(),TCMB)) - - - if(transfer_moles <= 0) - return - - var/snowflakecheck = istype(gas_output, /obj/item/tank) - - var/datum/gas_mixture/mix = source_1_gases.remove(transfer_moles * gas_percentage) - if(snowflakecheck) - gas_output.assume_air(mix) - else - output_gases.merge(mix) - mix = source_2_gases.remove(transfer_moles * (1-gas_percentage)) - if(snowflakecheck) - gas_output.assume_air(mix) - else - output_gases.merge(mix) - - -// - integrated tank - // **works** -/obj/item/integrated_circuit/atmospherics/tank - name = "integrated tank" - desc = "A small tank for the storage of gases." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - size = 4 - activators = list( - "push ref" = IC_PINTYPE_PULSE_IN - ) - volume = 3 //emergency tank sized - var/broken = FALSE - -/obj/item/integrated_circuit/atmospherics/tank/Initialize() - air_contents = new(volume) - START_PROCESSING(SSobj, src) - extended_desc = "Take care not to pressurize it above [round(TANK_FAILURE_PRESSURE)] kPa, or else it will break." - . = ..() - -/obj/item/integrated_circuit/atmospherics/tank/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/integrated_circuit/atmospherics/tank/do_work() - set_pin_data(IC_OUTPUT, 1, WEAKREF(src)) - push_data() - -/obj/item/integrated_circuit/atmospherics/tank/process() - var/tank_pressure = air_contents.return_pressure() - set_pin_data(IC_OUTPUT, 2, tank_pressure) - push_data() - - //Check if tank broken - if(!broken && tank_pressure > TANK_FAILURE_PRESSURE) - broken = TRUE - to_chat(view(2),"The [name] ruptures, releasing its gases!") - if(broken) - release() - -/obj/item/integrated_circuit/atmospherics/tank/proc/release() - if(air_contents.total_moles() > 0) - playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3) - var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) - var/turf/current_turf = get_turf(src) - var/datum/gas_mixture/exterior_gas - if(!current_turf) - return - - exterior_gas = current_turf.return_air() - exterior_gas.merge(expelled_gas) - - -// - large integrated tank - // **works** -/obj/item/integrated_circuit/atmospherics/tank/large - name = "large integrated tank" - desc = "A less small tank for the storage of gases." - volume = 9 - size = 12 - spawn_flags = IC_SPAWN_RESEARCH - - -// - freezer tank - // **works** -/obj/item/integrated_circuit/atmospherics/tank/freezer - name = "freezer tank" - desc = "Cools the gas it contains to a preset temperature." - volume = 6 - size = 8 - inputs = list( - "target temperature" = IC_PINTYPE_NUMBER, - "on" = IC_PINTYPE_BOOLEAN - ) - inputs_default = list("1" = 300) - spawn_flags = IC_SPAWN_RESEARCH - var/temperature = 293.15 - var/heater_coefficient = 0.1 - -/obj/item/integrated_circuit/atmospherics/tank/freezer/on_data_written() - temperature = max(73.15,min(293.15,get_pin_data(IC_INPUT, 1))) - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/atmospherics/tank/freezer/process() - var/tank_pressure = air_contents.return_pressure() - set_pin_data(IC_OUTPUT, 2, tank_pressure) - push_data() - - //Cool the tank if the power is on and the temp is above - if(!power_draw_idle || air_contents.return_temperature() < temperature) - return - - air_contents.set_temperature(max(73.15,air_contents.return_temperature() - (air_contents.return_temperature() - temperature) * heater_coefficient)) - - -// - heater tank - // **works** -/obj/item/integrated_circuit/atmospherics/tank/freezer/heater - name = "heater tank" - desc = "Heats the gas it contains to a preset temperature." - volume = 6 - inputs = list( - "target temperature" = IC_PINTYPE_NUMBER, - "on" = IC_PINTYPE_BOOLEAN - ) - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/on_data_written() - temperature = max(293.15,min(573.15,get_pin_data(IC_INPUT, 1))) - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/process() - var/tank_pressure = air_contents.return_pressure() - set_pin_data(IC_OUTPUT, 2, tank_pressure) - push_data() - - //Heat the tank if the power is on or its temperature is below what is set - if(!power_draw_idle || air_contents.return_temperature() > temperature) - return - - air_contents.set_temperature(min(573.15,air_contents.return_temperature() + (temperature - air_contents.return_temperature()) * heater_coefficient)) - - -// - atmospheric cooler - // **works** -/obj/item/integrated_circuit/atmospherics/cooler - name = "atmospheric cooler circuit" - desc = "Cools the air around it." - volume = 6 - size = 13 - spawn_flags = IC_SPAWN_RESEARCH - inputs = list( - "target temperature" = IC_PINTYPE_NUMBER, - "on" = IC_PINTYPE_BOOLEAN - ) - var/temperature = 293.15 - var/heater_coefficient = 0.1 - -/obj/item/integrated_circuit/atmospherics/cooler/Initialize() - air_contents = new(volume) - START_PROCESSING(SSobj, src) - . = ..() - -/obj/item/integrated_circuit/atmospherics/cooler/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/integrated_circuit/atmospherics/cooler/on_data_written() - temperature = max(243.15,min(293.15,get_pin_data(IC_INPUT, 1))) - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/atmospherics/cooler/process() - set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) - push_data() - - - //Get the turf you're on and its gas mixture - var/turf/current_turf = get_turf(src) - if(!current_turf) - return - - var/datum/gas_mixture/turf_air = current_turf.return_air() - if(!power_draw_idle || turf_air.return_temperature() < temperature) - return - - //Cool the gas - turf_air.set_temperature(max(243.15,turf_air.return_temperature() - (turf_air.return_temperature() - temperature) * heater_coefficient)) - - -// - atmospheric heater - // **works** -/obj/item/integrated_circuit/atmospherics/cooler/heater - name = "atmospheric heater circuit" - desc = "Heats the air around it." - -/obj/item/integrated_circuit/atmospherics/cooler/heater/on_data_written() - temperature = max(293.15,min(323.15,get_pin_data(IC_INPUT, 1))) - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/atmospherics/cooler/heater/process() - set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure()) - push_data() - - //Get the turf and its air mixture - var/turf/current_turf = get_turf(src) - if(!current_turf) - return - - var/datum/gas_mixture/turf_air = current_turf.return_air() - if(!power_draw_idle || turf_air.return_temperature() > temperature) - return - - //Heat the gas - turf_air.set_temperature(min(323.15,turf_air.return_temperature() + (temperature - turf_air.return_temperature()) * heater_coefficient)) - - -// - tank slot - // **works** -/obj/item/integrated_circuit/input/tank_slot - category_text = "Atmospherics" - cooldown_per_use = 1 - name = "tank slot" - desc = "Lets you add a tank to your assembly and remove it even when the assembly is closed." - extended_desc = "It can help you extract gases easier." - complexity = 25 - size = 30 - inputs = list() - outputs = list( - "pressure used" = IC_PINTYPE_NUMBER, - "current tank" = IC_PINTYPE_REF - ) - activators = list( - "push ref" = IC_PINTYPE_PULSE_IN, - "on insert" = IC_PINTYPE_PULSE_OUT, - "on remove" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - - can_be_asked_input = TRUE - demands_object_input = TRUE - can_input_object_when_closed = TRUE - - var/obj/item/tank/internals/current_tank - -/obj/item/integrated_circuit/input/tank_slot/Initialize() - START_PROCESSING(SSobj, src) - . = ..() - -/obj/item/integrated_circuit/input/tank_slot/process() - push_pressure() - -/obj/item/integrated_circuit/input/tank_slot/attackby(var/obj/item/tank/internals/I, var/mob/living/user) - //Check if it truly is a tank - if(!istype(I,/obj/item/tank/internals)) - to_chat(user,"The [I.name] doesn't seem to fit in here.") - return - - //Check if there is no other tank already inside - if(current_tank) - to_chat(user,"There is already a gas tank inside.") - return - - //The current tank is the one we just attached, its location is inside the circuit - current_tank = I - user.transferItemToLoc(I,src) - to_chat(user,"You put the [I.name] inside the tank slot.") - - //Set the pin to a weak reference of the current tank - push_pressure() - set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank)) - push_data() - do_work(1) - - -/obj/item/integrated_circuit/input/tank_slot/ask_for_input(mob/user) - attack_self(user) - -/obj/item/integrated_circuit/input/tank_slot/attack_self(mob/user) - //Check if no tank attached - if(!current_tank) - to_chat(user, "There is currently no tank attached.") - return - - //Remove tank and put in user's hands/location - to_chat(user, "You take [current_tank] out of the tank slot.") - user.put_in_hands(current_tank) - current_tank = null - - //Remove tank reference - push_pressure() - set_pin_data(IC_OUTPUT, 2, null) - push_data() - do_work(2) - -/obj/item/integrated_circuit/input/tank_slot/do_work() - set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank)) - push_data() - -/obj/item/integrated_circuit/input/tank_slot/proc/push_pressure() - if(!current_tank) - set_pin_data(IC_OUTPUT, 1, 0) - return - - var/datum/gas_mixture/tank_air = current_tank.return_air() - if(!tank_air) - set_pin_data(IC_OUTPUT, 1, 0) - return - - set_pin_data(IC_OUTPUT, 1, tank_air.return_pressure()) - push_data() - - -#undef SOURCE_TO_TARGET -#undef TARGET_TO_SOURCE -#undef PUMP_EFFICIENCY -#undef TANK_FAILURE_PRESSURE -#undef PUMP_MAX_PRESSURE -#undef PUMP_MAX_VOLUME diff --git a/code/modules/integrated_electronics/subtypes/converters.dm b/code/modules/integrated_electronics/subtypes/converters.dm deleted file mode 100644 index ad2599f8cf4a5..0000000000000 --- a/code/modules/integrated_electronics/subtypes/converters.dm +++ /dev/null @@ -1,425 +0,0 @@ -//These circuits convert one variable to another. -/obj/item/integrated_circuit/converter - complexity = 2 - inputs = list("input") - outputs = list("output") - activators = list("convert" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) - category_text = "Converter" - power_draw_per_use = 10 - -/obj/item/integrated_circuit/converter/num2text - name = "number to string" - desc = "This circuit can convert a number variable into a string." - extended_desc = "Because of circuit limitations, null/false variables will output a '0' string." - icon_state = "num-string" - inputs = list("input" = IC_PINTYPE_NUMBER) - outputs = list("output" = IC_PINTYPE_STRING) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/num2text/do_work() - var/result = null - pull_data() - var/incoming = get_pin_data(IC_INPUT, 1) - if(!isnull(incoming)) - result = num2text(incoming) - else if(!incoming) - result = "0" - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/converter/text2num - name = "string to number" - desc = "This circuit can convert a string variable into a number." - icon_state = "string-num" - inputs = list("input" = IC_PINTYPE_STRING) - outputs = list("output" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/text2num/do_work() - var/result = null - pull_data() - var/incoming = get_pin_data(IC_INPUT, 1) - if(!isnull(incoming)) - result = text2num(incoming) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/converter/ref2text - name = "reference to string" - desc = "This circuit can convert a reference to something else to a string, specifically the name of that reference." - icon_state = "ref-string" - inputs = list("input" = IC_PINTYPE_REF) - outputs = list("output" = IC_PINTYPE_STRING) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/ref2text/do_work() - var/result = null - pull_data() - var/atom/A = get_pin_data(IC_INPUT, 1) - if(A && istype(A)) - result = A.name - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/converter/refcode - name = "reference encoder" - desc = "This circuit can encode a reference into a string, which can then be read by a reference decoder circuit." - icon_state = "ref-string" - inputs = list("input" = IC_PINTYPE_REF) - outputs = list("output" = IC_PINTYPE_STRING) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/refcode/do_work() - var/result = null - pull_data() - var/atom/A = get_pin_data(IC_INPUT, 1) - if(A && istype(A)) - result = strtohex(XorEncrypt(REF(A), SScircuit.cipherkey)) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/converter/refdecode - name = "reference decoder" - desc = "This circuit can convert an encoded reference to an actual reference." - icon_state = "ref-string" - inputs = list("input" = IC_PINTYPE_STRING) - outputs = list("output" = IC_PINTYPE_REF) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/dec - -/obj/item/integrated_circuit/converter/refdecode/do_work() - pull_data() - dec = XorEncrypt(hextostr(get_pin_data(IC_INPUT, 1), TRUE), SScircuit.cipherkey) - set_pin_data(IC_OUTPUT, 1, WEAKREF(locate(dec))) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/converter/radians2degrees - name = "radians to degrees converter" - desc = "Converts radians to degrees." - inputs = list("radian" = IC_PINTYPE_NUMBER) - outputs = list("degrees" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/radians2degrees/do_work() - var/result = null - pull_data() - var/incoming = get_pin_data(IC_INPUT, 1) - if(!isnull(incoming)) - result = TODEGREES(incoming) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/converter/degrees2radians - name = "degrees to radians converter" - desc = "Converts degrees to radians." - inputs = list("degrees" = IC_PINTYPE_NUMBER) - outputs = list("radians" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/degrees2radians/do_work() - var/result = null - pull_data() - var/incoming = get_pin_data(IC_INPUT, 1) - if(!isnull(incoming)) - result = TORADIANS(incoming) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/converter/abs_to_rel_coords - name = "abs to rel coordinate converter" - desc = "Easily convert absolute coordinates to relative coordinates with this." - extended_desc = "Keep in mind that both sets of input coordinates should be absolute." - complexity = 1 - inputs = list( - "X1" = IC_PINTYPE_NUMBER, - "Y1" = IC_PINTYPE_NUMBER, - "X2" = IC_PINTYPE_NUMBER, - "Y2" = IC_PINTYPE_NUMBER - ) - outputs = list( - "X" = IC_PINTYPE_NUMBER, - "Y" = IC_PINTYPE_NUMBER - ) - activators = list("compute rel coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/abs_to_rel_coords/do_work() - var/x1 = get_pin_data(IC_INPUT, 1) - var/y1 = get_pin_data(IC_INPUT, 2) - - var/x2 = get_pin_data(IC_INPUT, 3) - var/y2 = get_pin_data(IC_INPUT, 4) - - if(!isnull(x1) && !isnull(y1) && !isnull(x2) && !isnull(y2)) - set_pin_data(IC_OUTPUT, 1, x1 - x2) - set_pin_data(IC_OUTPUT, 2, y1 - y2) - - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/converter/rel_to_abs_coords - name = "rel to abs coordinate converter" - desc = "Convert relative coordinates to absolute coordinates with this." - extended_desc = "Keep in mind that only one set of input coordinates should be absolute, and the other relative. \ - The output coordinates will be the absolute form of the input relative coordinates." - complexity = 1 - inputs = list( - "X1" = IC_PINTYPE_NUMBER, - "Y1" = IC_PINTYPE_NUMBER, - "X2" = IC_PINTYPE_NUMBER, - "Y2" = IC_PINTYPE_NUMBER - ) - outputs = list( - "X" = IC_PINTYPE_NUMBER, - "Y" = IC_PINTYPE_NUMBER - ) - activators = list("compute abs coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/rel_to_abs_coords/do_work() - var/x1 = get_pin_data(IC_INPUT, 1) - var/y1 = get_pin_data(IC_INPUT, 2) - - var/x2 = get_pin_data(IC_INPUT, 3) - var/y2 = get_pin_data(IC_INPUT, 4) - - if(!isnull(x1) && !isnull(y1) && !isnull(x2) && !isnull(y2)) - set_pin_data(IC_OUTPUT, 1, x1 + x2) - set_pin_data(IC_OUTPUT, 2, y1 + y2) - - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/converter/adv_rel_to_abs_coords - name = "advanced rel to abs coordinate converter" - desc = "Easily convert relative coordinates to absolute coordinates with this." - extended_desc = "This circuit only requires a single set of relative inputs to output absolute coordinates." - complexity = 2 - inputs = list( - "X" = IC_PINTYPE_NUMBER, - "Y" = IC_PINTYPE_NUMBER, - ) - outputs = list( - "X" = IC_PINTYPE_NUMBER, - "Y" = IC_PINTYPE_NUMBER - ) - activators = list("compute abs coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/adv_rel_to_abs_coords/do_work() - var/turf/T = get_turf(src) - - if(!T) - return - - var/x1 = get_pin_data(IC_INPUT, 1) - var/y1 = get_pin_data(IC_INPUT, 2) - - if(!isnull(x1) && !isnull(y1)) - set_pin_data(IC_OUTPUT, 1, T.x + x1) - set_pin_data(IC_OUTPUT, 2, T.y + y1) - - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/converter/hsv2hex - name = "hsv to hexadecimal" - desc = "This circuit can convert a HSV (Hue, Saturation, and Value) color to a Hexadecimal RGB color." - extended_desc = "The first pin controls tint (0-359), the second pin controls how intense the tint is (0-255), and the third controls how bright the tint is (0 for black, 127 for normal, 255 for white)." - icon_state = "hsv-hex" - inputs = list( - "hue" = IC_PINTYPE_NUMBER, - "saturation" = IC_PINTYPE_NUMBER, - "value" = IC_PINTYPE_NUMBER - ) - outputs = list("hexadecimal rgb" = IC_PINTYPE_COLOR) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/hsv2hex/do_work() - var/result = null - pull_data() - var/hue = get_pin_data(IC_INPUT, 1) - var/saturation = get_pin_data(IC_INPUT, 2) - var/value = get_pin_data(IC_INPUT, 3) - if(isnum_safe(hue)&&isnum_safe(saturation)&&isnum_safe(value)) - result = HSVtoRGB(hsv(AngleToHue(hue),saturation,value)) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/converter/rgb2hex - name = "rgb to hexadecimal" - desc = "This circuit can convert a RGB (Red, Green, Blue) color to a Hexadecimal RGB color." - extended_desc = "The first pin controls red amount, the second pin controls green amount, and the third controls blue amount. They all go from 0-255." - icon_state = "rgb-hex" - inputs = list( - "red" = IC_PINTYPE_NUMBER, - "green" = IC_PINTYPE_NUMBER, - "blue" = IC_PINTYPE_NUMBER - ) - outputs = list("hexadecimal rgb" = IC_PINTYPE_COLOR) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/rgb2hex/do_work() - var/result = null - pull_data() - var/red = get_pin_data(IC_INPUT, 1) - var/green = get_pin_data(IC_INPUT, 2) - var/blue = get_pin_data(IC_INPUT, 3) - if(isnum_safe(red)&&isnum_safe(green)&&isnum_safe(blue)) - result = rgb(red,green,blue) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - - -// - hsv to rgb - // -/obj/item/integrated_circuit/converter/hsv2rgb - name = "hsv to rgb" - desc = "This circuit can convert a HSV (Hue, Saturation, and Value) color to a RGB (red, blue and green) color." - extended_desc = "The first pin controls tint (0-359), the second pin controls how intense the tint is (0-255), and the third controls how bright the tint is (0 for black, 127 for normal, 255 for white)." - icon_state = "hsv-hex" - inputs = list( - "hue" = IC_PINTYPE_NUMBER, - "saturation" = IC_PINTYPE_NUMBER, - "value" = IC_PINTYPE_NUMBER - ) - outputs = list( - "red" = IC_PINTYPE_NUMBER, - "green" = IC_PINTYPE_NUMBER, - "blue" = IC_PINTYPE_NUMBER - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH -/obj/item/integrated_circuit/converter/hsv2rgb/do_work() - var/hue = get_pin_data(IC_INPUT, 1) - var/sat = get_pin_data(IC_INPUT, 2) - var/val = get_pin_data(IC_INPUT, 3) - if(!hue || !sat || !val) - set_pin_data(IC_OUTPUT, 1, 0) - set_pin_data(IC_OUTPUT, 2, 0) - set_pin_data(IC_OUTPUT, 3, 0) - else - var/list/RGB = ReadRGB(HSVtoRGB(hsv(hue,sat,val))) - - set_pin_data(IC_OUTPUT, 1, RGB[1]) - set_pin_data(IC_OUTPUT, 2, RGB[2]) - set_pin_data(IC_OUTPUT, 3, RGB[3]) - push_data() - activate_pin(2) - - -// - rgb to hsv - // -/obj/item/integrated_circuit/converter/rgb2hsv - name = "rgb to hsv" - desc = "This circuit can convert a RGB (Red, Blue, and Green) color to a HSV (Hue, Saturation and Value) color." - extended_desc = "All values for the RGB colors are situated between 0 and 255." - icon_state = "hsv-hex" - inputs = list( - "red" = IC_PINTYPE_NUMBER, - "green" = IC_PINTYPE_NUMBER, - "blue" = IC_PINTYPE_NUMBER - ) - outputs = list( - "hue" = IC_PINTYPE_NUMBER, - "saturation" = IC_PINTYPE_NUMBER, - "value" = IC_PINTYPE_NUMBER - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/rgb2hsv/do_work() - var/red = get_pin_data(IC_INPUT, 1) - var/blue = get_pin_data(IC_INPUT, 2) - var/green = get_pin_data(IC_INPUT, 3) - if(!red || !blue || !green) - set_pin_data(IC_OUTPUT, 1, 0) - set_pin_data(IC_OUTPUT, 2, 0) - set_pin_data(IC_OUTPUT, 3, 0) - else - var/list/HSV = ReadHSV(RGBtoHSV(rgb(red,blue,green))) - - set_pin_data(IC_OUTPUT, 1, HSV[1]) - set_pin_data(IC_OUTPUT, 2, HSV[2]) - set_pin_data(IC_OUTPUT, 3, HSV[3]) - push_data() - activate_pin(2) - - -// - hexadecimal to hsv - // -/obj/item/integrated_circuit/converter/hex2hsv - name = "hexadecimal to hsv" - desc = "This circuit can convert a Hexadecimal RGB color into a HSV (Hue, Saturation and Value) color." - extended_desc = "Hexadecimal colors follow the format #RRBBGG, RR being the red value, BB the blue value and GG the green value. They are written in hexadecimal, giving each color a value from 0 (00) to 255 (FF)." - icon_state = "hsv-hex" - inputs = list("hexadecimal rgb" = IC_PINTYPE_COLOR) - outputs = list( - "hue" = IC_PINTYPE_NUMBER, - "saturation" = IC_PINTYPE_NUMBER, - "value" = IC_PINTYPE_NUMBER - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/hex2hsv/do_work() - pull_data() - var/rgb = get_pin_data(IC_INPUT, 1) - if(!rgb) - set_pin_data(IC_OUTPUT, 1, 0) - set_pin_data(IC_OUTPUT, 2, 0) - set_pin_data(IC_OUTPUT, 3, 0) - return - else - var/list/hsv = ReadHSV(RGBtoHSV(rgb)) - set_pin_data(IC_OUTPUT, 1, hsv[1]) - set_pin_data(IC_OUTPUT, 2, hsv[2]) - set_pin_data(IC_OUTPUT, 3, hsv[3]) - push_data() - activate_pin(2) - - -// - hex 2 rgb - // -/obj/item/integrated_circuit/converter/hex2rgb - name = "hexadecimal to rgb" - desc = "This circuit can convert a Hexadecimal RGB color into a RGB (Red, Blue and Green color." - extended_desc = "Hexadecimal colors follow the format #RRBBGG, RR being the red value, BB the blue value and GG the green value. They are written in hexadecimal, giving each color a value from 0 (00) to 255 (FF)." - icon_state = "hsv-hex" - inputs = list("hexadecimal rgb" = IC_PINTYPE_COLOR) - outputs = list( - "red" = IC_PINTYPE_NUMBER, - "green" = IC_PINTYPE_NUMBER, - "blue" = IC_PINTYPE_NUMBER - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/converter/hex2rgb/do_work() - var/rgb = get_pin_data(IC_INPUT, 1) - if(!rgb) - set_pin_data(IC_OUTPUT, 1, 0) - set_pin_data(IC_OUTPUT, 2, 0) - set_pin_data(IC_OUTPUT, 3, 0) - else - var/list/RGB = ReadRGB(rgb) - - set_pin_data(IC_OUTPUT, 1, RGB[1]) - set_pin_data(IC_OUTPUT, 2, RGB[2]) - set_pin_data(IC_OUTPUT, 3, RGB[3]) - - push_data() - activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/data_transfer.dm b/code/modules/integrated_electronics/subtypes/data_transfer.dm deleted file mode 100644 index f723c78b22298..0000000000000 --- a/code/modules/integrated_electronics/subtypes/data_transfer.dm +++ /dev/null @@ -1,208 +0,0 @@ -/obj/item/integrated_circuit/transfer - category_text = "Data Transfer" - power_draw_per_use = 2 - -/obj/item/integrated_circuit/transfer/multiplexer - name = "two multiplexer" - desc = "This is what those in the business tend to refer to as a 'mux', or data selector. It moves data from one of the selected inputs to the output." - extended_desc = "The first input pin is used to select which of the other input pins which has its data moved to the output. \ - If the input selection is outside the valid range then no output is given." - complexity = 2 - icon_state = "mux2" - inputs = list("input selection" = IC_PINTYPE_NUMBER) - outputs = list("output" = IC_PINTYPE_ANY) - activators = list("select" = IC_PINTYPE_PULSE_IN, "on select" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 4 - var/number_of_pins = 2 - -/obj/item/integrated_circuit/transfer/multiplexer/Initialize() - for(var/i = 1 to number_of_pins) - inputs["input [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called. - - complexity = number_of_pins - . = ..() - desc += " It has [number_of_pins] input pins." - extended_desc += " This multiplexer has a range from 1 to [inputs.len - 1]." - -/obj/item/integrated_circuit/transfer/multiplexer/do_work() - var/input_index = get_pin_data(IC_INPUT, 1) - - if(!isnull(input_index) && (input_index >= 1 && input_index < inputs.len)) - set_pin_data(IC_OUTPUT, 1,get_pin_data(IC_INPUT, input_index + 1)) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/transfer/multiplexer/medium - name = "four multiplexer" - icon_state = "mux4" - number_of_pins = 4 - -/obj/item/integrated_circuit/transfer/multiplexer/large - name = "eight multiplexer" - w_class = WEIGHT_CLASS_SMALL - icon_state = "mux8" - number_of_pins = 8 - -/obj/item/integrated_circuit/transfer/multiplexer/huge - name = "sixteen multiplexer" - icon_state = "mux16" - w_class = WEIGHT_CLASS_SMALL - number_of_pins = 16 - -/obj/item/integrated_circuit/transfer/demultiplexer - name = "two demultiplexer" - desc = "This is what those in the business tend to refer to as a 'demux'. It moves data from the input to one of the selected outputs." - extended_desc = "The first input pin is used to select which of the output pins is given the data from the second input pin. \ - If the output selection is outside the valid range then no output is given." - complexity = 2 - icon_state = "dmux2" - inputs = list("output selection" = IC_PINTYPE_NUMBER, "input" = IC_PINTYPE_ANY) - outputs = list() - activators = list("select" = IC_PINTYPE_PULSE_IN, "on select" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 4 - var/number_of_pins = 2 - -/obj/item/integrated_circuit/transfer/demultiplexer/Initialize() - for(var/i = 1 to number_of_pins) - outputs["output [i]"] = IC_PINTYPE_ANY - complexity = number_of_pins - - . = ..() - desc += " It has [number_of_pins] output pins." - extended_desc += " This demultiplexer has a range from 1 to [outputs.len]." - -/obj/item/integrated_circuit/transfer/demultiplexer/do_work() - var/output_index = get_pin_data(IC_INPUT, 1) - if(!isnull(output_index) && (output_index >= 1 && output_index <= outputs.len)) - var/datum/integrated_io/O = outputs[output_index] - O.data = get_pin_data(IC_INPUT, 2) - O.push_data() - - activate_pin(2) - -/obj/item/integrated_circuit/transfer/demultiplexer/medium - name = "four demultiplexer" - icon_state = "dmux4" - number_of_pins = 4 - -/obj/item/integrated_circuit/transfer/demultiplexer/large - name = "eight demultiplexer" - icon_state = "dmux8" - w_class = WEIGHT_CLASS_SMALL - number_of_pins = 8 - -/obj/item/integrated_circuit/transfer/demultiplexer/huge - name = "sixteen demultiplexer" - icon_state = "dmux16" - w_class = WEIGHT_CLASS_SMALL - number_of_pins = 16 - -/obj/item/integrated_circuit/transfer/pulsedemultiplexer - name = "two pulse demultiplexer" - desc = "Selector switch to choose the pin to be activated by number." - extended_desc = "The first input pin is used to select which of the pulse out pins will be activated after activation of the circuit. \ - If the output selection is outside the valid range then no output is given." - complexity = 2 - icon_state = "dmux2" - inputs = list("output selection" = IC_PINTYPE_NUMBER) - outputs = list() - activators = list("select" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 4 - var/number_of_pins = 2 - -/obj/item/integrated_circuit/transfer/pulsedemultiplexer/Initialize() - for(var/i = 1 to number_of_pins) - activators["output [i]"] = IC_PINTYPE_PULSE_OUT - complexity = number_of_pins - - . = ..() - desc += " It has [number_of_pins] output pins." - extended_desc += " This pulse demultiplexer has a range from 1 to [activators.len - 1]." - -/obj/item/integrated_circuit/transfer/pulsedemultiplexer/do_work() - var/output_index = get_pin_data(IC_INPUT, 1) - - if(output_index == CLAMP(output_index, 1, number_of_pins)) - activate_pin(round(output_index + 1 ,1)) - -/obj/item/integrated_circuit/transfer/pulsedemultiplexer/medium - name = "four pulse demultiplexer" - icon_state = "dmux4" - number_of_pins = 4 - -/obj/item/integrated_circuit/transfer/pulsedemultiplexer/large - name = "eight pulse demultiplexer" - icon_state = "dmux8" - w_class = WEIGHT_CLASS_SMALL - number_of_pins = 8 - -/obj/item/integrated_circuit/transfer/pulsedemultiplexer/huge - name = "sixteen pulse demultiplexer" - icon_state = "dmux16" - w_class = WEIGHT_CLASS_SMALL - number_of_pins = 16 - -/obj/item/integrated_circuit/transfer/pulsemultiplexer - name = "two pulse multiplexer" - desc = "Pulse in pins to choose the pin value to be sent." - extended_desc = "The input pulses are used to select which of the input pins has its data moved to the output." - complexity = 2 - icon_state = "dmux2" - inputs = list() - outputs = list("output" = IC_PINTYPE_ANY) - activators = list("on selected" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 4 - var/number_of_pins = 2 - -/obj/item/integrated_circuit/transfer/pulsemultiplexer/Initialize() - for(var/i = 1 to number_of_pins) - inputs["input [i]"] = IC_PINTYPE_ANY - for(var/i = 1 to number_of_pins) - activators["input [i]"] = IC_PINTYPE_PULSE_IN - complexity = number_of_pins - - . = ..() - desc += " It has [number_of_pins] pulse in pins and [number_of_pins] output pins." - extended_desc += " This pulse multiplexer has a range from 1 to [activators.len - 1]." - -/obj/item/integrated_circuit/transfer/pulsemultiplexer/do_work(ord) - var/input_index = ord - 2 - - if(!isnull(input_index) && (input_index >= 0 && input_index < inputs.len)) - set_pin_data(IC_OUTPUT, 1,get_pin_data(IC_INPUT, input_index + 1)) - push_data() - activate_pin(1) - -/obj/item/integrated_circuit/transfer/pulsemultiplexer/medium - name = "four pulse multiplexer" - icon_state = "dmux4" - number_of_pins = 4 - -/obj/item/integrated_circuit/transfer/pulsemultiplexer/large - name = "eight pulse multiplexer" - icon_state = "dmux8" - w_class = WEIGHT_CLASS_SMALL - number_of_pins = 8 - -/obj/item/integrated_circuit/transfer/pulsemultiplexer/huge - name = "sixteen pulse multiplexer" - icon_state = "dmux16" - w_class = WEIGHT_CLASS_SMALL - number_of_pins = 16 - -/obj/item/integrated_circuit/transfer/wire_node - name = "wire node" - desc = "Just a wire node to make wiring easier. Transfers the pulse from in to out." - icon_state = "wire_node" - activators = list("pulse in" = IC_PINTYPE_PULSE_IN, "pulse out" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 0 - complexity = 0 - size = 0.1 - -/obj/item/integrated_circuit/transfer/wire_node/do_work() - activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm deleted file mode 100644 index c3ba39b876a60..0000000000000 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ /dev/null @@ -1,1472 +0,0 @@ -/obj/item/integrated_circuit/input - var/can_be_asked_input = 0 - category_text = "Input" - power_draw_per_use = 5 - -/obj/item/integrated_circuit/input/proc/ask_for_input(mob/user) - return - -/obj/item/integrated_circuit/input/button - name = "button" - desc = "This tiny button must do something, right?" - icon_state = "button" - complexity = 1 - can_be_asked_input = 1 - inputs = list() - outputs = list() - activators = list("on pressed" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/input/button/ask_for_input(mob/user) //Bit misleading name for this specific use. - to_chat(user, "You press the button labeled '[displayed_name]'.") - activate_pin(1) - -/obj/item/integrated_circuit/input/toggle_button - name = "toggle button" - desc = "It toggles on, off, on, off..." - icon_state = "toggle_button" - complexity = 1 - can_be_asked_input = 1 - inputs = list() - outputs = list("on" = IC_PINTYPE_BOOLEAN) - activators = list("on toggle" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/input/toggle_button/ask_for_input(mob/user) // Ditto. - set_pin_data(IC_OUTPUT, 1, !get_pin_data(IC_OUTPUT, 1)) - push_data() - activate_pin(1) - to_chat(user, "You toggle the button labeled '[displayed_name]' [get_pin_data(IC_OUTPUT, 1) ? "on" : "off"].") - -/obj/item/integrated_circuit/input/numberpad - name = "number pad" - desc = "This small number pad allows someone to input a number into the system." - icon_state = "numberpad" - complexity = 2 - can_be_asked_input = 1 - inputs = list() - outputs = list("number entered" = IC_PINTYPE_NUMBER) - activators = list("on entered" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 4 - -/obj/item/integrated_circuit/input/numberpad/ask_for_input(mob/user) - var/new_input = input(user, "Enter a number, please.",displayed_name) as null|num - if(isnum_safe(new_input) && user.IsAdvancedToolUser()) - set_pin_data(IC_OUTPUT, 1, new_input) - push_data() - activate_pin(1) - -/obj/item/integrated_circuit/input/textpad - name = "text pad" - desc = "This small text pad allows someone to input a string into the system." - icon_state = "textpad" - complexity = 2 - can_be_asked_input = 1 - inputs = list() - outputs = list("string entered" = IC_PINTYPE_STRING) - activators = list("on entered" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 4 - -/obj/item/integrated_circuit/input/textpad/ask_for_input(mob/user) - var/new_input = stripped_multiline_input(user, "Enter some words, please.",displayed_name) - if(istext(new_input) && user.IsAdvancedToolUser()) - set_pin_data(IC_OUTPUT, 1, new_input) - push_data() - activate_pin(1) - -/obj/item/integrated_circuit/input/colorpad - name = "color pad" - desc = "This small color pad allows someone to input a hexadecimal color into the system." - icon_state = "colorpad" - complexity = 2 - can_be_asked_input = 1 - inputs = list() - outputs = list("color entered" = IC_PINTYPE_STRING) - activators = list("on entered" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 4 - -/obj/item/integrated_circuit/input/colorpad/ask_for_input(mob/user) - var/new_color = input(user, "Enter a color, please.", "Color", "#ffffff") as color|null - if(new_color && user.IsAdvancedToolUser()) - set_pin_data(IC_OUTPUT, 1, new_color) - push_data() - activate_pin(1) - -/obj/item/integrated_circuit/input/med_scanner - name = "integrated medical analyser" - desc = "A very small version of the common medical analyser. This allows the machine to know how healthy someone is." - icon_state = "medscan" - complexity = 4 - inputs = list("target" = IC_PINTYPE_REF) - outputs = list( - "total health %" = IC_PINTYPE_NUMBER, - "total missing health" = IC_PINTYPE_NUMBER - ) - activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 40 - -/obj/item/integrated_circuit/input/med_scanner/do_work() - var/mob/living/H = get_pin_data_as_type(IC_INPUT, 1, /mob/living) - if(!istype(H)) //Invalid input - return - if(H.Adjacent(get_turf(src))) // Like normal analysers, it can't be used at range. - var/total_health = round(H.health/H.getMaxHealth(), 0.01)*100 - var/missing_health = H.getMaxHealth() - H.health - - set_pin_data(IC_OUTPUT, 1, total_health) - set_pin_data(IC_OUTPUT, 2, missing_health) - - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/adv_med_scanner - name = "integrated adv. medical analyser" - desc = "A very small version of the medbot's medical analyser. This allows the machine to know how healthy someone is. \ - This type is much more precise, allowing the machine to know much more about the target than a normal analyzer." - icon_state = "medscan_adv" - complexity = 12 - inputs = list("target" = IC_PINTYPE_REF) - outputs = list( - "total health %" = IC_PINTYPE_NUMBER, - "total missing health" = IC_PINTYPE_NUMBER, - "brute damage" = IC_PINTYPE_NUMBER, - "burn damage" = IC_PINTYPE_NUMBER, - "tox damage" = IC_PINTYPE_NUMBER, - "oxy damage" = IC_PINTYPE_NUMBER, - "clone damage" = IC_PINTYPE_NUMBER - ) - activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 80 - -/obj/item/integrated_circuit/input/adv_med_scanner/do_work() - var/mob/living/H = get_pin_data_as_type(IC_INPUT, 1, /mob/living) - if(!istype(H)) //Invalid input - return - if(H in view(get_turf(src))) // Like medbot's analyzer it can be used in range.. - var/total_health = round(H.health/H.getMaxHealth(), 0.01)*100 - var/missing_health = H.getMaxHealth() - H.health - - set_pin_data(IC_OUTPUT, 1, total_health) - set_pin_data(IC_OUTPUT, 2, missing_health) - set_pin_data(IC_OUTPUT, 3, H.getBruteLoss()) - set_pin_data(IC_OUTPUT, 4, H.getFireLoss()) - set_pin_data(IC_OUTPUT, 5, H.getToxLoss()) - set_pin_data(IC_OUTPUT, 6, H.getOxyLoss()) - set_pin_data(IC_OUTPUT, 7, H.getCloneLoss()) - - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/slime_scanner - name = "slime scanner" - desc = "A very small version of the xenobio analyser. This allows the machine to know every needed properties of slime. Output mutation list is non-associative." - icon_state = "medscan_adv" - complexity = 12 - inputs = list("target" = IC_PINTYPE_REF) - outputs = list( - "colour" = IC_PINTYPE_STRING, - "adult" = IC_PINTYPE_BOOLEAN, - "nutrition" = IC_PINTYPE_NUMBER, - "charge" = IC_PINTYPE_NUMBER, - "health" = IC_PINTYPE_NUMBER, - "possible mutation" = IC_PINTYPE_LIST, - "genetic destability" = IC_PINTYPE_NUMBER, - "slime core amount" = IC_PINTYPE_NUMBER, - "Growth progress" = IC_PINTYPE_NUMBER, - ) - activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 80 - -/obj/item/integrated_circuit/input/slime_scanner/do_work() - var/mob/living/simple_animal/slime/T = get_pin_data_as_type(IC_INPUT, 1, /mob/living/simple_animal/slime) - if(!isslime(T)) //Invalid input - return - if(T in view(get_turf(src))) // Like medbot's analyzer it can be used in range.. - - set_pin_data(IC_OUTPUT, 1, T.colour) - set_pin_data(IC_OUTPUT, 2, T.is_adult) - set_pin_data(IC_OUTPUT, 3, T.nutrition/T.get_max_nutrition()) - set_pin_data(IC_OUTPUT, 4, T.powerlevel) - set_pin_data(IC_OUTPUT, 5, round(T.health/T.maxHealth,0.01)*100) - set_pin_data(IC_OUTPUT, 6, uniqueList(T.slime_mutation)) - set_pin_data(IC_OUTPUT, 7, T.mutation_chance) - set_pin_data(IC_OUTPUT, 8, T.cores) - set_pin_data(IC_OUTPUT, 9, T.amount_grown/SLIME_EVOLUTION_THRESHOLD) - - - push_data() - activate_pin(2) - - - -/obj/item/integrated_circuit/input/plant_scanner - name = "integrated plant analyzer" - desc = "A very small version of the plant analyser. This allows the machine to know all valuable parameters of plants in trays. \ - It can only scan plants, not seeds or fruits." - icon_state = "medscan_adv" - complexity = 12 - inputs = list("target" = IC_PINTYPE_REF) - outputs = list( - "plant type" = IC_PINTYPE_STRING, - "age" = IC_PINTYPE_NUMBER, - "potency" = IC_PINTYPE_NUMBER, - "yield" = IC_PINTYPE_NUMBER, - "Maturation speed" = IC_PINTYPE_NUMBER, - "Production speed" = IC_PINTYPE_NUMBER, - "Endurance" = IC_PINTYPE_NUMBER, - "Lifespan" = IC_PINTYPE_NUMBER, - "Weed Growth Rate" = IC_PINTYPE_NUMBER, - "Weed Vulnerability" = IC_PINTYPE_NUMBER, - "Weed level" = IC_PINTYPE_NUMBER, - "Pest level" = IC_PINTYPE_NUMBER, - "Toxicity level" = IC_PINTYPE_NUMBER, - "Water level" = IC_PINTYPE_NUMBER, - "Nutrition level" = IC_PINTYPE_NUMBER, - "harvest" = IC_PINTYPE_NUMBER, - "dead" = IC_PINTYPE_NUMBER, - "plant health" = IC_PINTYPE_NUMBER, - "self sustaining" = IC_PINTYPE_NUMBER, - "using irrigation" = IC_PINTYPE_NUMBER, - "connected trays" = IC_PINTYPE_LIST - ) - activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 10 - -/obj/item/integrated_circuit/input/plant_scanner/do_work() - var/obj/machinery/hydroponics/H = get_pin_data_as_type(IC_INPUT, 1, /obj/machinery/hydroponics) - if(!istype(H)) //Invalid input - return - for(var/i=1, i<=outputs.len, i++) - set_pin_data(IC_OUTPUT, i, null) - if(H in view(get_turf(src))) // Like medbot's analyzer it can be used in range.. - if(H.myseed) - set_pin_data(IC_OUTPUT, 1, H.myseed.plantname) - set_pin_data(IC_OUTPUT, 2, H.age) - set_pin_data(IC_OUTPUT, 3, H.myseed.potency) - set_pin_data(IC_OUTPUT, 4, H.myseed.yield) - set_pin_data(IC_OUTPUT, 5, H.myseed.maturation) - set_pin_data(IC_OUTPUT, 6, H.myseed.production) - set_pin_data(IC_OUTPUT, 7, H.myseed.endurance) - set_pin_data(IC_OUTPUT, 8, H.myseed.lifespan) - set_pin_data(IC_OUTPUT, 9, H.myseed.weed_rate) - set_pin_data(IC_OUTPUT, 10, H.myseed.weed_chance) - set_pin_data(IC_OUTPUT, 11, H.weedlevel) - set_pin_data(IC_OUTPUT, 12, H.pestlevel) - set_pin_data(IC_OUTPUT, 13, H.toxic) - set_pin_data(IC_OUTPUT, 14, H.waterlevel) - set_pin_data(IC_OUTPUT, 15, H.nutrilevel) - set_pin_data(IC_OUTPUT, 16, H.harvest) - set_pin_data(IC_OUTPUT, 17, H.dead) - set_pin_data(IC_OUTPUT, 18, H.plant_health) - set_pin_data(IC_OUTPUT, 19, H.self_sustaining) - set_pin_data(IC_OUTPUT, 20, H.using_irrigation) - set_pin_data(IC_OUTPUT, 21, H.FindConnected()) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/gene_scanner - name = "gene scanner" - desc = "This circuit will scan the target plant for traits and reagent genes. Output is non-associative." - extended_desc = "This allows the machine to scan plants in trays for reagent and trait genes. \ - It can only scan plants, not seeds or fruits." - inputs = list( - "target" = IC_PINTYPE_REF - ) - outputs = list( - "traits" = IC_PINTYPE_LIST, - "reagents" = IC_PINTYPE_LIST - ) - activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT) - icon_state = "medscan_adv" - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/input/gene_scanner/do_work() - var/list/gtraits = list() - var/list/greagents = list() - var/obj/machinery/hydroponics/H = get_pin_data_as_type(IC_INPUT, 1, /obj/machinery/hydroponics) - if(!istype(H)) //Invalid input - return - for(var/i=1, i<=outputs.len, i++) - set_pin_data(IC_OUTPUT, i, null) - if(H in view(get_turf(src))) // Like medbot's analyzer it can be used in range.. - if(H.myseed) - for(var/datum/plant_gene/reagent/G in H.myseed.genes) - greagents.Add(G.get_name()) - - for(var/datum/plant_gene/trait/G in H.myseed.genes) - gtraits.Add(G.get_name()) - - set_pin_data(IC_OUTPUT, 1, gtraits) - set_pin_data(IC_OUTPUT, 2, greagents) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/input/examiner - name = "examiner" - desc = "It's a little machine vision system. It can return the name, description, distance, \ - relative coordinates, total amount of reagents, maximum amount of reagents, density, and opacity of the referenced object." - icon_state = "video_camera" - complexity = 6 - inputs = list( - "target" = IC_PINTYPE_REF - ) - outputs = list( - "name" = IC_PINTYPE_STRING, - "description" = IC_PINTYPE_STRING, - "X" = IC_PINTYPE_NUMBER, - "Y" = IC_PINTYPE_NUMBER, - "distance" = IC_PINTYPE_NUMBER, - "max reagents" = IC_PINTYPE_NUMBER, - "amount of reagents" = IC_PINTYPE_NUMBER, - "density" = IC_PINTYPE_BOOLEAN, - "opacity" = IC_PINTYPE_BOOLEAN, - "occupied turf" = IC_PINTYPE_REF - ) - activators = list( - "scan" = IC_PINTYPE_PULSE_IN, - "on scanned" = IC_PINTYPE_PULSE_OUT, - "not scanned" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 80 - -/obj/item/integrated_circuit/input/examiner/do_work() - var/atom/H = get_pin_data_as_type(IC_INPUT, 1, /atom) - var/turf/T = get_turf(src) - - if(!istype(H) || !(H in view(T))) - activate_pin(3) - else - set_pin_data(IC_OUTPUT, 1, H.name) - set_pin_data(IC_OUTPUT, 2, H.desc) - - if(istype(H, /mob/living)) - var/mob/living/M = H - var/msg = M.examine() - if(msg) - set_pin_data(IC_OUTPUT, 2, msg) - - set_pin_data(IC_OUTPUT, 3, H.x-T.x) - set_pin_data(IC_OUTPUT, 4, H.y-T.y) - set_pin_data(IC_OUTPUT, 5, sqrt((H.x-T.x)*(H.x-T.x)+ (H.y-T.y)*(H.y-T.y))) - var/mr = 0 - var/tr = 0 - if(H.reagents) - mr = H.reagents.maximum_volume - tr = H.reagents.total_volume - set_pin_data(IC_OUTPUT, 6, mr) - set_pin_data(IC_OUTPUT, 7, tr) - set_pin_data(IC_OUTPUT, 8, H.CanPass(assembly ? assembly : src, get_turf(H))) - set_pin_data(IC_OUTPUT, 9, H.opacity) - set_pin_data(IC_OUTPUT, 10, get_turf(H)) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/turfpoint - name = "Tile pointer" - desc = "This circuit will get a tile ref with the provided absolute coordinates." - extended_desc = "If the machine cannot see the target, it will not be able to calculate the correct direction.\ - This circuit only works while inside an assembly." - icon_state = "numberpad" - complexity = 5 - inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER) - outputs = list("tile" = IC_PINTYPE_REF) - activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 40 - -/obj/item/integrated_circuit/input/turfpoint/do_work() - if(!assembly) - activate_pin(3) - return - var/turf/T = get_turf(assembly) - var/target_x = CLAMP(get_pin_data(IC_INPUT, 1), 0, world.maxx) - var/target_y = CLAMP(get_pin_data(IC_INPUT, 2), 0, world.maxy) - var/turf/A = locate(target_x, target_y, T.z) - set_pin_data(IC_OUTPUT, 1, null) - if(!A || !(A in view(T))) - activate_pin(3) - return - else - set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/turfscan - name = "tile analyzer" - desc = "This circuit can analyze the contents of the scanned turf, and can read letters on the turf." - icon_state = "video_camera" - complexity = 5 - inputs = list( - "target" = IC_PINTYPE_REF - ) - outputs = list( - "located ref" = IC_PINTYPE_LIST, - "Written letters" = IC_PINTYPE_STRING, - "area" = IC_PINTYPE_STRING - ) - activators = list( - "scan" = IC_PINTYPE_PULSE_IN, - "on scanned" = IC_PINTYPE_PULSE_OUT, - "not scanned" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 40 - cooldown_per_use = 10 - -/obj/item/integrated_circuit/input/turfscan/do_work() - var/turf/scanned_turf = get_pin_data_as_type(IC_INPUT, 1, /turf) - var/turf/circuit_turf = get_turf(src) - var/area_name = get_area_name(scanned_turf) - if(!istype(scanned_turf)) //Invalid input - activate_pin(3) - return - - if(scanned_turf in view(circuit_turf)) // This is a camera. It can't examine things that it can't see. - var/list/turf_contents = new() - for(var/obj/U in scanned_turf) - turf_contents += WEAKREF(U) - for(var/mob/U in scanned_turf) - turf_contents += WEAKREF(U) - set_pin_data(IC_OUTPUT, 1, turf_contents) - set_pin_data(IC_OUTPUT, 3, area_name) - var/list/St = new() - for(var/obj/effect/decal/cleanable/crayon/I in scanned_turf) - St.Add(I.icon_state) - if(St.len) - set_pin_data(IC_OUTPUT, 2, jointext(St, ",", 1, 0)) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/turfpoint - name = "tile pointer" - desc = "This circuit will get tile ref with given absolute coorinates." - extended_desc = "If the machine cannot see the target, it will not be able to scan it.\ - This circuit will only work in an assembly." - icon_state = "numberpad" - complexity = 5 - inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER) - outputs = list("tile" = IC_PINTYPE_REF) - activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT,"not scanned" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 40 - -/obj/item/integrated_circuit/input/turfpoint/do_work() - if(!assembly) - activate_pin(3) - return - var/turf/T = get_turf(assembly) - var/target_x = CLAMP(get_pin_data(IC_INPUT, 1), 0, world.maxx) - var/target_y = CLAMP(get_pin_data(IC_INPUT, 2), 0, world.maxy) - var/turf/A = locate(target_x, target_y, T.z) - set_pin_data(IC_OUTPUT, 1, null) - if(!A || !(A in view(T))) - activate_pin(3) - return - else - set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/turfscan - name = "tile analyzer" - desc = "This machine vision system can analyze contents of desired tile.And can read letters on floor." - icon_state = "video_camera" - complexity = 5 - inputs = list( - "target" = IC_PINTYPE_REF - ) - outputs = list( - "located ref" = IC_PINTYPE_LIST, - "Written letters" = IC_PINTYPE_STRING - ) - activators = list( - "scan" = IC_PINTYPE_PULSE_IN, - "on scanned" = IC_PINTYPE_PULSE_OUT, - "not scanned" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 40 - cooldown_per_use = 10 - -/obj/item/integrated_circuit/input/turfscan/do_work() - var/atom/movable/H = get_pin_data_as_type(IC_INPUT, 1, /atom) - var/turf/T = get_turf(src) - var/turf/E = get_turf(H) - if(!istype(H)) //Invalid input - return - - if(H in view(T)) // This is a camera. It can't examine thngs,that it can't see. - var/list/cont = new() - if(E.contents.len) - for(var/i = 1 to E.contents.len) - var/atom/U = E.contents[i] - cont += WEAKREF(U) - set_pin_data(IC_OUTPUT, 1, cont) - var/list/St = new() - for(var/obj/effect/decal/cleanable/crayon/I in E.contents) - St.Add(I.icon_state) - if(St.len) - set_pin_data(IC_OUTPUT, 2, jointext(St, ",", 1, 0)) - push_data() - activate_pin(2) - else - activate_pin(3) - -/obj/item/integrated_circuit/input/local_locator - name = "local locator" - desc = "This is needed for certain devices that demand a reference for a target to act upon. This type only locates something \ - that is holding the machine containing it." - inputs = list() - outputs = list("located ref" = IC_PINTYPE_REF, - "is ground" = IC_PINTYPE_BOOLEAN, - "is creature" = IC_PINTYPE_BOOLEAN) - activators = list("locate" = IC_PINTYPE_PULSE_IN, - "on scanned" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 20 - -/obj/item/integrated_circuit/input/local_locator/do_work() - var/datum/integrated_io/O = outputs[1] - O.data = null - if(assembly) - O.data = WEAKREF(assembly.loc) - set_pin_data(IC_OUTPUT, 2, isturf(assembly.loc)) - set_pin_data(IC_OUTPUT, 3, ismob(assembly.loc)) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/adjacent_locator - name = "adjacent locator" - desc = "This is needed for certain devices that demand a reference for a target to act upon. This type only locates something \ - that is standing up to a meter away from the machine." - extended_desc = "The first pin requires a ref to the kind of object that you want the locator to acquire. This means that it will \ - give refs to nearby objects that are similar. If more than one valid object is found nearby, it will choose one of them at \ - random." - inputs = list("desired type ref" = IC_PINTYPE_REF) - outputs = list("located ref" = IC_PINTYPE_REF) - activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT, - "not found" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 30 - -/obj/item/integrated_circuit/input/adjacent_locator/do_work() - var/datum/integrated_io/I = inputs[1] - var/datum/integrated_io/O = outputs[1] - O.data = null - - if(!isweakref(I.data)) - return - var/atom/A = I.data.resolve() - if(!A) - return - var/desired_type = A.type - - var/list/nearby_things = range(1, get_turf(src)) - var/list/valid_things = list() - for(var/atom/thing in nearby_things) - if(thing.type != desired_type) - continue - valid_things.Add(thing) - if(valid_things.len) - O.data = WEAKREF(pick(valid_things)) - activate_pin(2) - else - activate_pin(3) - O.push_data() - -/obj/item/integrated_circuit/input/advanced_locator_list - complexity = 6 - name = "list advanced locator" - desc = "This is needed for certain devices that demand list of names for a target to act upon. This type locates something \ - that is standing in given radius of up to 8 meters. Output is non-associative. Input will only consider keys if associative." - extended_desc = "The first pin requires a list of the kinds of objects that you want the locator to acquire. It will locate nearby objects by name and description, \ - and will then provide a list of all found objects which are similar. \ - The second pin is a radius." - inputs = list("desired type ref" = IC_PINTYPE_LIST, "radius" = IC_PINTYPE_NUMBER) - outputs = list("located ref" = IC_PINTYPE_LIST) - activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT,"not found" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 30 - var/radius = 1 - cooldown_per_use = 10 - -/obj/item/integrated_circuit/input/advanced_locator_list/on_data_written() - var/rad = get_pin_data(IC_INPUT, 2) - - if(isnum_safe(rad)) - rad = CLAMP(rad, 0, 8) - radius = rad - -/obj/item/integrated_circuit/input/advanced_locator_list/do_work() - var/datum/integrated_io/I = inputs[1] - var/datum/integrated_io/O = outputs[1] - O.data = null - var/list/input_list = list() - input_list = I.data - if(length(input_list)) //if there is no input don't do anything. - var/turf/T = get_turf(src) - var/list/nearby_things = view(radius,T) - var/list/valid_things = list() - for(var/item in input_list) - if(!isnull(item) && !isnum_safe(item)) - if(istext(item)) - for(var/i in nearby_things) - var/atom/thing = i - if(ismob(thing) && !isliving(thing)) - continue - if(findtext(addtext(thing.name," ",thing.desc), item, 1, 0) ) - valid_things.Add(WEAKREF(thing)) - else - var/atom/A = item - var/desired_type = A.type - for(var/i in nearby_things) - var/atom/thing = i - if(thing.type != desired_type) - continue - if(ismob(thing) && !isliving(thing)) - continue - valid_things.Add(WEAKREF(thing)) - if(valid_things.len) - O.data = valid_things - O.push_data() - activate_pin(2) - else - O.push_data() - activate_pin(3) - else - O.push_data() - activate_pin(3) - -/obj/item/integrated_circuit/input/advanced_locator - complexity = 6 - name = "advanced locator" - desc = "This is needed for certain devices that demand a reference for a target to act upon. This type locates something \ - that is standing in given radius of up to 8 meters" - extended_desc = "The first pin requires a ref to the kind of object that you want the locator to acquire. This means that it will \ - give refs to nearby objects which are similar. If this pin is a string, the locator will search for an \ - item matching the desired text in its name and description. If more than one valid object is found nearby, it will choose one of them at \ - random. The second pin is a radius." - inputs = list("desired type" = IC_PINTYPE_ANY, "radius" = IC_PINTYPE_NUMBER) - outputs = list("located ref" = IC_PINTYPE_REF) - activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT,"not found" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 30 - var/radius = 1 - -/obj/item/integrated_circuit/input/advanced_locator/on_data_written() - var/rad = get_pin_data(IC_INPUT, 2) - if(isnum_safe(rad)) - rad = CLAMP(rad, 0, 8) - radius = rad - -/obj/item/integrated_circuit/input/advanced_locator/do_work() - var/datum/integrated_io/I = inputs[1] - var/datum/integrated_io/O = outputs[1] - O.data = null - var/turf/T = get_turf(src) - var/list/nearby_things = view(radius,T) - var/list/valid_things = list() - if(isweakref(I.data)) - var/atom/A = I.data.resolve() - var/desired_type = A.type - if(desired_type) - for(var/i in nearby_things) - var/atom/thing = i - if(ismob(thing) && !isliving(thing)) - continue - if(thing.type == desired_type) - valid_things.Add(thing) - else if(istext(I.data)) - var/DT = I.data - for(var/i in nearby_things) - var/atom/thing = i - if(ismob(thing) && !isliving(thing)) - continue - if(findtext(addtext(thing.name," ",thing.desc), DT, 1, 0) ) - valid_things.Add(thing) - if(valid_things.len) - O.data = WEAKREF(pick(valid_things)) - O.push_data() - activate_pin(2) - else - O.push_data() - activate_pin(3) - -/obj/item/integrated_circuit/input/signaler - name = "integrated signaler" - desc = "Signals from a signaler can be received with this, allowing for remote control. It can also send signals." - extended_desc = "When a signal is received from another signaler, the 'on signal received' activator pin will be pulsed. \ - The two input pins are to configure the integrated signaler's settings. Note that the frequency should not have a decimal in it, \ - meaning the default frequency is expressed as 1457, not 145.7. To send a signal, pulse the 'send signal' activator pin." - icon_state = "signal" - complexity = 4 - inputs = list("frequency" = IC_PINTYPE_NUMBER,"code" = IC_PINTYPE_NUMBER) - outputs = list() - activators = list( - "send signal" = IC_PINTYPE_PULSE_IN, - "on signal sent" = IC_PINTYPE_PULSE_OUT, - "on signal received" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - action_flags = IC_ACTION_LONG_RANGE - power_draw_idle = 5 - power_draw_per_use = 40 - cooldown_per_use = 5 - var/frequency = FREQ_SIGNALER - var/code = DEFAULT_SIGNALER_CODE - var/datum/radio_frequency/radio_connection - var/hearing_range = 1 - -/obj/item/integrated_circuit/input/signaler/Initialize() - . = ..() - spawn(40) - set_frequency(frequency) - // Set the pins so when someone sees them, they won't show as null - set_pin_data(IC_INPUT, 1, frequency) - set_pin_data(IC_INPUT, 2, code) - -/obj/item/integrated_circuit/input/signaler/Destroy() - SSradio.remove_object(src,frequency) - - frequency = 0 - return ..() - -/obj/item/integrated_circuit/input/signaler/on_data_written() - var/new_freq = get_pin_data(IC_INPUT, 1) - var/new_code = get_pin_data(IC_INPUT, 2) - if(isnum_safe(new_freq) && new_freq > 0) - set_frequency(new_freq) - if(isnum_safe(new_code)) - code = new_code - - -/obj/item/integrated_circuit/input/signaler/do_work() // Sends a signal. - if(!radio_connection) - return - - var/datum/signal/signal = new(list("code" = code)) - radio_connection.post_signal(src, signal) - activate_pin(2) - -/obj/item/integrated_circuit/input/signaler/proc/set_frequency(new_frequency) - if(!frequency) - return - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_SIGNALER) - -/obj/item/integrated_circuit/input/signaler/receive_signal(datum/signal/signal) - var/new_code = get_pin_data(IC_INPUT, 2) - var/code = 0 - - if(isnum_safe(new_code)) - code = new_code - if(!signal) - return 0 - if(signal.data["code"] != code) - return 0 - if(signal.source == src) // Don't trigger ourselves. - return 0 - - activate_pin(3) - audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range) - for(var/CHM in get_hearers_in_view(hearing_range, src)) - if(ismob(CHM)) - var/mob/LM = CHM - LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) - -/obj/item/integrated_circuit/input/ntnet_packet - name = "NTNet networking circuit" - desc = "Enables the sending and receiving of messages over NTNet via packet data protocol." - extended_desc = "Data can be sent or received using the second pin on each side, \ - with additonal data reserved for the third pin. When a message is received, the second activation pin \ - will pulse whatever is connected to it. Pulsing the first activation pin will send a message. Messages \ - can be sent to multiple recepients. Addresses must be separated with a semicolon, like this: Address1;Address2;Etc." - icon_state = "signal" - complexity = 2 - cooldown_per_use = 1 - inputs = list( - "target NTNet addresses"= IC_PINTYPE_STRING, - "data to send" = IC_PINTYPE_STRING, - "secondary text" = IC_PINTYPE_STRING, - "passkey" = IC_PINTYPE_STRING - ) - outputs = list( - "address received" = IC_PINTYPE_STRING, - "data received" = IC_PINTYPE_STRING, - "secondary text received" = IC_PINTYPE_STRING, - "passkey" = IC_PINTYPE_STRING, - "is_broadcast" = IC_PINTYPE_BOOLEAN - ) - activators = list("send data" = IC_PINTYPE_PULSE_IN, "on data received" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - action_flags = IC_ACTION_LONG_RANGE - power_draw_per_use = 50 - var/address - -/obj/item/integrated_circuit/input/ntnet_packet/Initialize() - . = ..() - var/datum/component/ntnet_interface/net = LoadComponent(/datum/component/ntnet_interface) - address = net.hardware_id - net.differentiate_broadcast = FALSE - desc += " This circuit's NTNet hardware address is: [address]" - -/obj/item/integrated_circuit/input/ntnet_packet/do_work() - var/target_address = get_pin_data(IC_INPUT, 1) - var/message = get_pin_data(IC_INPUT, 2) - var/text = get_pin_data(IC_INPUT, 3) - - var/datum/netdata/data = new - data.recipient_ids = splittext(target_address, ";") - var/key = get_pin_data(IC_INPUT, 4) // hippie start -- adds passkey back in - data.standard_format_data(message, text, key) // hippie end - ntnet_send(data) - -/obj/item/integrated_circuit/input/ntnet_receive(datum/netdata/data) - set_pin_data(IC_OUTPUT, 1, data.sender_id) - set_pin_data(IC_OUTPUT, 2, data.data["data"]) - set_pin_data(IC_OUTPUT, 3, data.data["data_secondary"]) - set_pin_data(IC_OUTPUT, 4, data.data["encrypted_passkey"]) - set_pin_data(IC_OUTPUT, 5, data.broadcast) - - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/ntnet_advanced - name = "Low level NTNet transreceiver" - desc = "Enables the sending and receiving of messages over NTNet via packet data protocol. Allows advanced control of message contents and signalling. Must use associative lists. Outputs associative list. Has a slower transmission rate than normal NTNet circuits, due to increased data processing complexity." - extended_desc = "Data can be sent or received using the second pin on each side, \ - When a message is received, the second activation pin will pulse whatever is connected to it. \ - Pulsing the first activation pin will send a message. Messages can be sent to multiple recepients. \ - Addresses must be separated with a semicolon, like this: Address1;Address2;Etc." - icon_state = "signal" - complexity = 4 - cooldown_per_use = 10 - inputs = list( - "target NTNet addresses"= IC_PINTYPE_STRING, - "data" = IC_PINTYPE_LIST, - ) - outputs = list("received data" = IC_PINTYPE_LIST, "is_broadcast" = IC_PINTYPE_BOOLEAN) - activators = list("send data" = IC_PINTYPE_PULSE_IN, "on data received" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - action_flags = IC_ACTION_LONG_RANGE - power_draw_per_use = 50 - var/address - -/obj/item/integrated_circuit/input/ntnet_advanced/Initialize() - . = ..() - var/datum/component/ntnet_interface/net = LoadComponent(/datum/component/ntnet_interface) - address = net.hardware_id - net.differentiate_broadcast = FALSE - desc += " This circuit's NTNet hardware address is: [address]" - -/obj/item/integrated_circuit/input/ntnet_advanced/do_work() - var/target_address = get_pin_data(IC_INPUT, 1) - var/list/message = get_pin_data(IC_INPUT, 2) - if(!islist(message)) - message = list() - var/datum/netdata/data = new - data.recipient_ids = splittext(target_address, ";") - data.data = message - //data.passkey = assembly.access_card.access - ntnet_send(data) - -/obj/item/integrated_circuit/input/ntnet_advanced/ntnet_receive(datum/netdata/data) - set_pin_data(IC_OUTPUT, 1, data.data) - set_pin_data(IC_OUTPUT, 2, data.broadcast) - push_data() - activate_pin(2) - -//This circuit gives information on where the machine is. -/obj/item/integrated_circuit/input/gps - name = "global positioning system" - desc = "This allows you to easily know the position of a machine containing this device." - extended_desc = "The coordinates that the GPS outputs are absolute, not relative. The full coords output has the coords separated by commas and is in string format." - icon_state = "gps" - complexity = 4 - inputs = list() - outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER, "Z" = IC_PINTYPE_NUMBER, "full coords" = IC_PINTYPE_STRING) - activators = list("get coordinates" = IC_PINTYPE_PULSE_IN, "on get coordinates" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 30 - -/obj/item/integrated_circuit/input/gps/do_work() - var/turf/T = get_turf(src) - - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - set_pin_data(IC_OUTPUT, 3, null) - set_pin_data(IC_OUTPUT, 4, null) - if(!T) - return - - set_pin_data(IC_OUTPUT, 1, T.x) - set_pin_data(IC_OUTPUT, 2, T.y) - set_pin_data(IC_OUTPUT, 3, T.z) - set_pin_data(IC_OUTPUT, 4, "[T.x],[T.y],[T.z]") - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/microphone - name = "microphone" - desc = "Useful for spying on people, or for voice-activated machines." - extended_desc = "This will automatically translate most languages it hears to Galactic Common. \ - The first activation pin is always pulsed when the circuit hears someone talk, while the second one \ - is only triggered if it hears someone speaking a language other than Galactic Common." - icon_state = "recorder" - complexity = 8 - inputs = list() - flags_1 = CONDUCT_1 | HEAR_1 - outputs = list( - "speaker" = IC_PINTYPE_STRING, - "message" = IC_PINTYPE_STRING - ) - activators = list("on message received" = IC_PINTYPE_PULSE_OUT, "on translation" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 5 - -/obj/item/integrated_circuit/input/microphone/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode) - . = ..() - var/translated = FALSE - if(speaker && message) - if(raw_message) - if(message_langs != get_selected_language()) - translated = TRUE - set_pin_data(IC_OUTPUT, 1, speaker.GetVoice()) - set_pin_data(IC_OUTPUT, 2, raw_message) - - push_data() - activate_pin(1) - if(translated) - activate_pin(2) - -/obj/item/integrated_circuit/input/sensor - name = "sensor" - desc = "Scans and obtains a reference for any objects or persons near you. All you need to do is shove the machine in their face." - extended_desc = "If the 'ignore storage' pin is set to true, the sensor will disregard scanning various storage containers such as backpacks." - icon_state = "recorder" - complexity = 12 - inputs = list("ignore storage" = IC_PINTYPE_BOOLEAN) - outputs = list("scanned" = IC_PINTYPE_REF) - activators = list("on scanned" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 120 - -/obj/item/integrated_circuit/input/sensor/sense(atom/A, mob/user, prox) - if(!prox || !A || (ismob(A) && !isliving(A))) - return FALSE - if(!check_then_do_work()) - return FALSE - var/ignore_bags = get_pin_data(IC_INPUT, 1) - if(ignore_bags) - var/datum/component/storage/STR = A.GetComponent(/datum/component/storage) - if(STR) - return FALSE - set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) - push_data() - to_chat(user, "You scan [A] with [assembly].") - activate_pin(1) - return TRUE - -/obj/item/integrated_circuit/input/sensor/ranged - name = "ranged sensor" - desc = "Scans and obtains a reference for any objects or persons in range. All you need to do is point the machine towards the target." - extended_desc = "If the 'ignore storage' pin is set to true, the sensor will disregard scanning various storage containers such as backpacks." - icon_state = "recorder" - complexity = 36 - inputs = list("ignore storage" = IC_PINTYPE_BOOLEAN) - outputs = list("scanned" = IC_PINTYPE_REF) - activators = list("on scanned" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 120 - -/obj/item/integrated_circuit/input/sensor/ranged/sense(atom/A, mob/user) - if(!user || !A || (ismob(A) && !isliving(A))) - return FALSE - if(user.client) - if(!(A in view(user.client))) - return FALSE - else - if(!(A in view(user))) - return FALSE - if(!check_then_do_work()) - return FALSE - var/ignore_bags = get_pin_data(IC_INPUT, 1) - if(ignore_bags) - if(istype(A, /obj/item/storage)) - return FALSE - set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) - push_data() - to_chat(user, "You scan [A] with [assembly].") - activate_pin(1) - return TRUE - -/obj/item/integrated_circuit/input/obj_scanner - name = "scanner" - desc = "Scans and obtains a reference for any objects you use on the assembly." - extended_desc = "If the 'put down' pin is set to true, the assembly will take the scanned object from your hands to its location. \ - Useful for interaction with the grabber. The scanner only works using the help intent." - icon_state = "recorder" - complexity = 4 - inputs = list("put down" = IC_PINTYPE_BOOLEAN) - outputs = list("scanned" = IC_PINTYPE_REF) - activators = list("on scanned" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 20 - -/obj/item/integrated_circuit/input/obj_scanner/attackby_react(var/atom/A,var/mob/user,intent) - if(intent!=INTENT_HELP) - return FALSE - if(!check_then_do_work()) - return FALSE - var/pu = get_pin_data(IC_INPUT, 1) - if(pu) - user.transferItemToLoc(A,drop_location()) - set_pin_data(IC_OUTPUT, 1, WEAKREF(A)) - push_data() - to_chat(user, "You let [assembly] scan [A].") - activate_pin(1) - return TRUE - -/obj/item/integrated_circuit/input/internalbm - name = "internal battery monitor" - desc = "This monitors the charge level of an internal battery." - icon_state = "internalbm" - extended_desc = "This circuit will give you the values of charge, max charge, and the current percentage of the internal battery on demand." - w_class = WEIGHT_CLASS_TINY - complexity = 1 - inputs = list() - outputs = list( - "cell charge" = IC_PINTYPE_NUMBER, - "max charge" = IC_PINTYPE_NUMBER, - "percentage" = IC_PINTYPE_NUMBER, - "refference to assembly" = IC_PINTYPE_REF, - "refference to cell" = IC_PINTYPE_REF - ) - activators = list("read" = IC_PINTYPE_PULSE_IN, "on read" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 1 - -/obj/item/integrated_circuit/input/internalbm/do_work() - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - set_pin_data(IC_OUTPUT, 3, null) - set_pin_data(IC_OUTPUT, 4, null) - set_pin_data(IC_OUTPUT, 5, null) - if(assembly) - set_pin_data(IC_OUTPUT, 4, WEAKREF(assembly)) - if(assembly.battery) - set_pin_data(IC_OUTPUT, 1, assembly.battery.charge) - set_pin_data(IC_OUTPUT, 2, assembly.battery.maxcharge) - set_pin_data(IC_OUTPUT, 3, 100*assembly.battery.charge/assembly.battery.maxcharge) - set_pin_data(IC_OUTPUT, 5, WEAKREF(assembly.battery)) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/externalbm - name = "external battery monitor" - desc = "This can read the battery state of any device in view." - icon_state = "externalbm" - extended_desc = "This circuit will give you the charge, max charge, and the current percentage values of any device or battery in view." - w_class = WEIGHT_CLASS_TINY - complexity = 2 - inputs = list("target" = IC_PINTYPE_REF) - outputs = list( - "cell charge" = IC_PINTYPE_NUMBER, - "max charge" = IC_PINTYPE_NUMBER, - "percentage" = IC_PINTYPE_NUMBER - ) - activators = list("read" = IC_PINTYPE_PULSE_IN, "on read" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 1 - -/obj/item/integrated_circuit/input/externalbm/do_work() - - var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - set_pin_data(IC_OUTPUT, 3, null) - if(AM) - var/obj/item/stock_parts/cell/C = AM.get_cell() - if(C) - var/turf/A = get_turf(src) - if(get_turf(AM) in view(A)) - set_pin_data(IC_OUTPUT, 1, C.charge) - set_pin_data(IC_OUTPUT, 2, C.maxcharge) - set_pin_data(IC_OUTPUT, 3, C.percent()) - push_data() - activate_pin(2) - return - -/obj/item/integrated_circuit/input/ntnetsc - name = "NTNet scanner" - desc = "This can return the NTNet IDs of a component inside the given object, if there are any." - icon_state = "signalsc" - w_class = WEIGHT_CLASS_TINY - complexity = 2 - inputs = list("target" = IC_PINTYPE_REF) - outputs = list( - "id" = IC_PINTYPE_STRING - ) - activators = list("read" = IC_PINTYPE_PULSE_IN, "found" = IC_PINTYPE_PULSE_OUT,"not found" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 1 - -/obj/item/integrated_circuit/input/ntnetsc/do_work() - var/atom/AM = get_pin_data_as_type(IC_INPUT, 1, /atom) - var/datum/component/ntnet_interface/net - - if(AM) - var/list/processing_list = list(AM) - while(processing_list.len && !net) - var/atom/A = processing_list[1] - processing_list.Cut(1, 2) - //Byond does not allow things to be in multiple contents, or double parent-child hierarchies, so only += is needed - //This is also why we don't need to check against assembled as we go along - processing_list += A.contents - net = A.GetComponent(/datum/component/ntnet_interface) - - if(net) - set_pin_data(IC_OUTPUT, 1, net.hardware_id) - push_data() - activate_pin(2) - else - set_pin_data(IC_OUTPUT, 1, null) - push_data() - activate_pin(3) - -/obj/item/integrated_circuit/input/matscan - name = "material scanner" - desc = "This special module is designed to get information about material containers of different machinery, \ - like ORM, lathes, etc." - icon_state = "video_camera" - complexity = 6 - inputs = list( - "target" = IC_PINTYPE_REF - ) - outputs = list( - "Iron" = IC_PINTYPE_NUMBER, - "Glass" = IC_PINTYPE_NUMBER, - "Silver" = IC_PINTYPE_NUMBER, - "Gold" = IC_PINTYPE_NUMBER, - "Diamond" = IC_PINTYPE_NUMBER, - "Solid Plasma" = IC_PINTYPE_NUMBER, - "Uranium" = IC_PINTYPE_NUMBER, - "Bananium" = IC_PINTYPE_NUMBER, - "Titanium" = IC_PINTYPE_NUMBER, - "Bluespace Mesh" = IC_PINTYPE_NUMBER, - "Biomass" = IC_PINTYPE_NUMBER, - ) - activators = list( - "scan" = IC_PINTYPE_PULSE_IN, - "on scanned" = IC_PINTYPE_PULSE_OUT, - "not scanned" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 40 - var/list/mtypes = list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace, /datum/material/biomass) - - -/obj/item/integrated_circuit/input/matscan/do_work() - var/atom/movable/H = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) - var/turf/T = get_turf(src) - var/datum/component/material_container/mt = H.GetComponent(/datum/component/material_container) - if(!mt) //Invalid input - return - if(H in view(T)) // This is a camera. It can't examine thngs,that it can't see. - for(var/I in 1 to mtypes.len) - var/amount = mt.materials[mtypes[I]] - if(amount) - set_pin_data(IC_OUTPUT, I, amount) - else - set_pin_data(IC_OUTPUT, I, null) - push_data() - activate_pin(2) - else - activate_pin(3) - -/obj/item/integrated_circuit/input/atmospheric_analyzer - name = "atmospheric analyzer" - desc = "A miniaturized analyzer which can scan anything that contains gases. Leave target as NULL to scan the air around the assembly." - extended_desc = "The nth element of gas amounts is the number of moles of the \ - nth gas in gas list. \ - Pressure is in kPa, temperature is in Kelvin. \ - Due to programming limitations, scanning an object that does \ - not contain a gas will return the air around it instead." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "target" = IC_PINTYPE_REF - ) - outputs = list( - "gas list" = IC_PINTYPE_LIST, - "gas amounts" = IC_PINTYPE_LIST, - "total moles" = IC_PINTYPE_NUMBER, - "pressure" = IC_PINTYPE_NUMBER, - "temperature" = IC_PINTYPE_NUMBER, - "volume" = IC_PINTYPE_NUMBER - ) - activators = list( - "scan" = IC_PINTYPE_PULSE_IN, - "on success" = IC_PINTYPE_PULSE_OUT, - "on failure" = IC_PINTYPE_PULSE_OUT - ) - power_draw_per_use = 5 - -/obj/item/integrated_circuit/input/atmospheric_analyzer/do_work() - for(var/i=1 to 6) - set_pin_data(IC_OUTPUT, i, null) - var/atom/target = get_pin_data_as_type(IC_INPUT, 1, /atom) - if(!target) - target = get_turf(src) - if( get_dist(get_turf(target),get_turf(src)) > 1 ) - activate_pin(3) - return - - var/datum/gas_mixture/air_contents = target.return_air() - if(!air_contents) - activate_pin(3) - return - - var/list/gases = air_contents.get_gases() - var/list/gas_names = list() - var/list/gas_amounts = list() - for(var/id in gases) - var/name = GLOB.meta_gas_info[id][META_GAS_NAME] - var/amt = round(air_contents.get_moles(id), 0.001) - gas_names.Add(name) - gas_amounts.Add(amt) - - set_pin_data(IC_OUTPUT, 1, gas_names) - set_pin_data(IC_OUTPUT, 2, gas_amounts) - set_pin_data(IC_OUTPUT, 3, round(air_contents.total_moles(), 0.001)) - set_pin_data(IC_OUTPUT, 4, round(air_contents.return_pressure(), 0.001)) - set_pin_data(IC_OUTPUT, 5, round(air_contents.return_temperature(), 0.001)) - set_pin_data(IC_OUTPUT, 6, round(air_contents.return_volume(), 0.001)) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/input/data_card_reader - name = "data card reader" - desc = "A circuit that can read from and write to data cards." - extended_desc = "Setting the \"write mode\" boolean to true will cause any data cards that are used on the assembly to replace\ - their existing function and data strings with the given strings, if it is set to false then using a data card on the assembly will cause\ - the function and data strings stored on the card to be written to the output pins." - icon_state = "card_reader" - complexity = 4 - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "function" = IC_PINTYPE_STRING, - "data to store" = IC_PINTYPE_STRING, - "write mode" = IC_PINTYPE_BOOLEAN - ) - outputs = list( - "function" = IC_PINTYPE_STRING, - "stored data" = IC_PINTYPE_STRING - ) - activators = list( - "on write" = IC_PINTYPE_PULSE_OUT, - "on read" = IC_PINTYPE_PULSE_OUT - ) - -/obj/item/integrated_circuit/input/data_card_reader/attackby_react(obj/item/I, mob/living/user, intent) - var/obj/item/card/data/card = I.GetCard() - var/write_mode = get_pin_data(IC_INPUT, 3) - if(card) - if(write_mode == TRUE) - card.function = get_pin_data(IC_INPUT, 1) - card.data = get_pin_data(IC_INPUT, 2) - push_data() - activate_pin(1) - else - set_pin_data(IC_OUTPUT, 1, card.function) - set_pin_data(IC_OUTPUT, 2, card.data) - push_data() - activate_pin(2) - else - return FALSE - return TRUE - -//Adding some color to cards aswell, because why not -/obj/item/card/data/attackby(obj/item/I, mob/living/user) - if(istype(I, /obj/item/integrated_electronics/detailer)) - var/obj/item/integrated_electronics/detailer/D = I - detail_color = D.detail_color - update_icon() - return ..() - - -//Interceptor -//Intercepts a telecomms signal, aka a radio message (;halp getting griff) -//Inputs: -//On (Boolean): If on, the circuit intercepts radio signals. Otherwise it does not. This doesn't affect no pass! -//No pass (Boolean): Decides if the signal will be silently intercepted -// (false) or also blocked from being sent on the radio (true) -//Outputs: -//Source: name of the mob -//Job: job of the mob -//content: the actual message -//spans: a list of spans, there's not much info about this but stuff like robots will have "robot" span -/obj/item/integrated_circuit/input/tcomm_interceptor - name = "telecommunication interceptor" - desc = "This circuit allows for telecomms signals \ - to be fetched prior to being broadcasted." - extended_desc = "Similar \ - to the old NTSL system of realtime signal modification, \ - the circuit connects to telecomms and fetches data \ - for each signal, which can be sent normally or blocked, \ - for cases such as other circuits modifying certain data. \ - Beware, this cannot stop signals from unreachable areas, such \ - as space or zlevels other than station's one." - complexity = 30 - cooldown_per_use = 0.1 - w_class = WEIGHT_CLASS_SMALL - inputs = list( - "intercept" = IC_PINTYPE_BOOLEAN, - "no pass" = IC_PINTYPE_BOOLEAN - ) - outputs = list( - "source" = IC_PINTYPE_STRING, - "job" = IC_PINTYPE_STRING, - "content" = IC_PINTYPE_STRING, - "spans" = IC_PINTYPE_LIST, - "frequency" = IC_PINTYPE_NUMBER - ) - activators = list( - "on intercept" = IC_PINTYPE_PULSE_OUT - ) - power_draw_idle = 0 - spawn_flags = IC_SPAWN_RESEARCH - var/obj/machinery/telecomms/receiver/circuit/receiver - var/list/freq_blacklist = list(FREQ_CENTCOM,FREQ_SYNDICATE,FREQ_CTF_RED,FREQ_CTF_BLUE) - -/obj/item/integrated_circuit/input/tcomm_interceptor/Initialize() - . = ..() - receiver = new(src) - receiver.holder = src - -/obj/item/integrated_circuit/input/tcomm_interceptor/Destroy() - qdel(receiver) - GLOB.ic_jammers -= src - ..() - -/obj/item/integrated_circuit/input/tcomm_interceptor/receive_signal(datum/signal/signal) - if((signal.transmission_method == TRANSMISSION_SUBSPACE) && get_pin_data(IC_INPUT, 1)) - if(signal.frequency in freq_blacklist) - return - set_pin_data(IC_OUTPUT, 1, signal.data["name"]) - set_pin_data(IC_OUTPUT, 2, signal.data["job"]) - set_pin_data(IC_OUTPUT, 3, signal.data["message"]) - set_pin_data(IC_OUTPUT, 4, signal.data["spans"]) - set_pin_data(IC_OUTPUT, 5, signal.frequency) - push_data() - activate_pin(1) - -/obj/item/integrated_circuit/input/tcomm_interceptor/on_data_written() - if(get_pin_data(IC_INPUT, 2)) - GLOB.ic_jammers |= src - if(get_pin_data(IC_INPUT, 1)) - power_draw_idle = 200 - else - power_draw_idle = 100 - else - GLOB.ic_jammers -= src - if(get_pin_data(IC_INPUT, 1)) - power_draw_idle = 100 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/input/tcomm_interceptor/power_fail() - set_pin_data(IC_INPUT, 1, 0) - set_pin_data(IC_INPUT, 2, 0) - -/obj/item/integrated_circuit/input/tcomm_interceptor/disconnect_all() - set_pin_data(IC_INPUT, 1, 0) - set_pin_data(IC_INPUT, 2, 0) - ..() - - -// -Inputlist- // -/obj/item/integrated_circuit/input/selection - name = "selection circuit" - desc = "This circuit lets you choose between different strings from a selection." - extended_desc = "This circuit lets you choose between up to 4 different values from selection of up to 8 strings that you can set. Null values are ignored and the chosen value is put out in selected." - icon_state = "addition" - can_be_asked_input = 1 - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "A" = IC_PINTYPE_STRING, - "B" = IC_PINTYPE_STRING, - "C" = IC_PINTYPE_STRING, - "D" = IC_PINTYPE_STRING, - "E" = IC_PINTYPE_STRING, - "F" = IC_PINTYPE_STRING, - "G" = IC_PINTYPE_STRING, - "H" = IC_PINTYPE_STRING - ) - activators = list( - "on selected" = IC_PINTYPE_PULSE_OUT - ) - outputs = list( - "selected" = IC_PINTYPE_STRING - ) - -/obj/item/integrated_circuit/input/selection/ask_for_input(mob/user) - var/list/selection = list() - for(var/k in 1 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(istext(I)) - selection.Add(I) - var/selected = input(user,"Choose input.","Selection") in selection - if(!selected) - return - set_pin_data(IC_OUTPUT, 1, selected) - push_data() - activate_pin(1) - - -// -storage examiner- // **works** -/obj/item/integrated_circuit/input/storage_examiner - name = "storage examiner circuit" - desc = "This circuit lets you scan a storage's content. (backpacks, toolboxes etc.)" - extended_desc = "The items are put out as reference, which makes it possible to interact with them. Additionally also gives the amount of items." - icon_state = "grabber" - can_be_asked_input = 1 - complexity = 6 - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "storage" = IC_PINTYPE_REF - ) - activators = list( - "examine" = IC_PINTYPE_PULSE_IN, - "on examined" = IC_PINTYPE_PULSE_OUT - ) - outputs = list( - "item amount" = IC_PINTYPE_NUMBER, - "item list" = IC_PINTYPE_LIST - ) - power_draw_per_use = 85 - -/obj/item/integrated_circuit/input/storage_examiner/do_work() - var/obj/item/storage = get_pin_data_as_type(IC_INPUT, 1, /obj/item) - if(!istype(storage,/obj/item/storage)) - return - - set_pin_data(IC_OUTPUT, 1, storage.contents.len) - - var/list/regurgitated_contents = list() - for(var/obj/o in storage.contents) - regurgitated_contents.Add(WEAKREF(o)) - - - set_pin_data(IC_OUTPUT, 2, regurgitated_contents) - push_data() - activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/lists.dm b/code/modules/integrated_electronics/subtypes/lists.dm deleted file mode 100644 index adc79e748b4f2..0000000000000 --- a/code/modules/integrated_electronics/subtypes/lists.dm +++ /dev/null @@ -1,462 +0,0 @@ -//These circuits do things with lists, and use special list pins for stability. -/obj/item/integrated_circuit/lists - complexity = 1 - inputs = list( - "input" = IC_PINTYPE_LIST - ) - outputs = list( - "result" = IC_PINTYPE_STRING - ) - activators = list( - "compute" = IC_PINTYPE_PULSE_IN, - "on computed" = IC_PINTYPE_PULSE_OUT - ) - category_text = "Lists" - power_draw_per_use = 20 - cooldown_per_use = 10 - -/obj/item/integrated_circuit/lists/pick - name = "pick circuit" - desc = "This circuit will pick a random element from the input list, and output that element." - extended_desc = "The input list is not modified." - icon_state = "addition" - outputs = list( - "result" = IC_PINTYPE_ANY - ) - activators = list( - "compute" = IC_PINTYPE_PULSE_IN, - "on success" = IC_PINTYPE_PULSE_OUT, - "on failure" = IC_PINTYPE_PULSE_OUT, - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - cooldown_per_use = 1 - -/obj/item/integrated_circuit/lists/pick/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) // List pins guarantee that there is a list inside, even if just an empty one. - if(input_list.len) - set_pin_data(IC_OUTPUT, 1, pick(input_list)) - push_data() - activate_pin(2) - else - activate_pin(3) - - -/obj/item/integrated_circuit/lists/append - name = "append circuit" - desc = "This circuit will add an element to a list." - extended_desc = "The new element will always be at the bottom of the list." - inputs = list( - "list to append" = IC_PINTYPE_LIST, - "input" = IC_PINTYPE_ANY - ) - outputs = list( - "appended list" = IC_PINTYPE_LIST - ) - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/lists/append/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/list/output_list = list() - var/new_entry = get_pin_data(IC_INPUT, 2) - output_list = input_list.Copy() - output_list.Add(new_entry) - - set_pin_data(IC_OUTPUT, 1, output_list) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/lists/search - name = "search circuit" - desc = "This circuit will get the index location of the desired element in a list." - extended_desc = "Search will start at position 1 and will return the first matching position." - inputs = list( - "list" = IC_PINTYPE_LIST, - "item" = IC_PINTYPE_ANY - ) - outputs = list( - "index" = IC_PINTYPE_NUMBER - ) - activators = list( - "compute" = IC_PINTYPE_PULSE_IN, - "on success" = IC_PINTYPE_PULSE_OUT, - "on failure" = IC_PINTYPE_PULSE_OUT, - ) - icon_state = "addition" - complexity = 2 - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - cooldown_per_use = 1 - -/obj/item/integrated_circuit/lists/search/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/output = input_list.Find(get_pin_data(IC_INPUT, 2)) - - set_pin_data(IC_OUTPUT, 1, output) - push_data() - - if(output) - activate_pin(2) - else - activate_pin(3) - - -/obj/item/integrated_circuit/lists/filter - name = "filter circuit" - desc = "This circuit will search through a list for anything matching the desired element(s) and outputs two lists: \ - one containing only the matching elements, and one with the matching elements filtered out." - extended_desc = "Sample accepts lists. If no match is found, the original list is sent to output 1." - inputs = list( - "input list" = IC_PINTYPE_LIST, - "sample" = IC_PINTYPE_ANY - ) - outputs = list( - "list filtered" = IC_PINTYPE_LIST, - "list matched" = IC_PINTYPE_LIST - ) - activators = list( - "compute" = IC_PINTYPE_PULSE_IN, - "on match" = IC_PINTYPE_PULSE_OUT, - "on no match" = IC_PINTYPE_PULSE_OUT - ) - complexity = 6 - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/lists/filter/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/sample = get_pin_data(IC_INPUT, 2) - var/list/sample_list = islist(sample) ? uniqueList(sample) : null - var/list/output_list1 = input_list.Copy() - var/list/output_list2 = list() - var/list/output = list() - - for(var/input_item in input_list) - if(sample_list) - for(var/sample_item in sample_list) - if(!isnull(sample_item)) - if(istext(input_item) && istext(sample_item) && findtext(input_item, sample_item)) - output += input_item - if(istype(input_item, /atom) && istext(sample_item)) - var/atom/input_item_atom = input_item - if(istext(sample_item) && findtext(input_item_atom.name, sample_item)) - output += input_item - if(!istext(input_item)) - if(input_item == sample_item) - output += input_item - else - if(!isnull(sample)) - if(istext(input_item) && istext(sample) && findtext(input_item, sample)) - output += input_item - continue - if(istype(input_item, /atom) && istext(sample)) - var/atom/input_itema = input_item - if(findtext(input_itema.name, sample)) - output += input_item - if(!istext(input_item)) - if(input_item == sample) - output += input_item - - output_list1.Remove(output) - output_list2.Add(output) - set_pin_data(IC_OUTPUT, 1, output_list1) - set_pin_data(IC_OUTPUT, 2, output_list2) - push_data() - - output_list1 ~! input_list ? activate_pin(2) : activate_pin(3) - -/obj/item/integrated_circuit/lists/listset - name = "list set circuit" - desc = "This circuit will remove any duplicate entries from a list." - extended_desc = "If there are no duplicate entries, the output list will be unchanged." - inputs = list( - "list" = IC_PINTYPE_LIST - ) - outputs = list( - "list filtered" = IC_PINTYPE_LIST - ) - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/lists/listset/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - input_list = uniqueList(input_list) - - set_pin_data(IC_OUTPUT, 1, input_list) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/lists/at - name = "at circuit" - desc = "This circuit will pick an element from a list by the input index." - extended_desc = "If there is no element at the given index, the result will be null." - inputs = list( - "list" = IC_PINTYPE_LIST, - "index" = IC_PINTYPE_INDEX - ) - outputs = list( - "item" = IC_PINTYPE_ANY - ) - activators = list( - "compute" = IC_PINTYPE_PULSE_IN, - "on success" = IC_PINTYPE_PULSE_OUT, - "on failure" = IC_PINTYPE_PULSE_OUT - ) - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - cooldown_per_use = 1 - -/obj/item/integrated_circuit/lists/at/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/index = get_pin_data(IC_INPUT, 2) - - // Check if index is valid - if(index > input_list.len) - set_pin_data(IC_OUTPUT, 1, null) - push_data() - activate_pin(3) - return - - set_pin_data(IC_OUTPUT, 1, input_list[index]) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/lists/delete - name = "delete circuit" - desc = "This circuit will remove an element from a list by the index." - extended_desc = "If there is no element at the given index, the result list will be unchanged." - inputs = list( - "list" = IC_PINTYPE_LIST, - "index" = IC_PINTYPE_INDEX - ) - outputs = list( - "item" = IC_PINTYPE_LIST - ) - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/lists/delete/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/list/red_list = list() - var/index = get_pin_data(IC_INPUT, 2) - - if(length(input_list)) - for(var/j in 1 to input_list.len) - var/I = input_list[j] - if(j != index) - red_list.Add(I) - set_pin_data(IC_OUTPUT, 1, red_list) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/lists/write - name = "write circuit" - desc = "This circuit will write an element to a list at the given index location." - extended_desc = "If there is no element at the given index, it will output the same list as before." - inputs = list( - "list" = IC_PINTYPE_LIST, - "index" = IC_PINTYPE_INDEX, - "item" = IC_PINTYPE_ANY - ) - outputs = list( - "redacted list" = IC_PINTYPE_LIST - ) - activators = list( - "compute" = IC_PINTYPE_PULSE_IN, - "on success" = IC_PINTYPE_PULSE_OUT, - "on failure" = IC_PINTYPE_PULSE_OUT, - ) - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/lists/write/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/index = get_pin_data(IC_INPUT, 2) - var/item = get_pin_data(IC_INPUT, 3) - - // Check if index is valid - if(index > input_list.len) - set_pin_data(IC_OUTPUT, 1, input_list) - push_data() - activate_pin(3) - return - - if(!islist(item)) - var/list/red_list = input_list.Copy() //crash proof - red_list[index] = item - set_pin_data(IC_OUTPUT, 1, red_list) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/lists/len - name = "len circuit" - desc = "This circuit will return the length of the list." - inputs = list( - "list" = IC_PINTYPE_LIST, - ) - outputs = list( - "item" = IC_PINTYPE_NUMBER - ) - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/lists/len/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - set_pin_data(IC_OUTPUT, 1, input_list.len) - push_data() - activate_pin(2) - cooldown_per_use = 1 - - -/obj/item/integrated_circuit/lists/jointext - name = "join text circuit" - desc = "This circuit will combine two lists into one, and output it as a string." - extended_desc = "Default settings will encode the entire list into a string." - icon_state = "join" - inputs = list( - "list to join" = IC_PINTYPE_LIST,// - "delimiter" = IC_PINTYPE_STRING, - "start" = IC_PINTYPE_INDEX, - "end" = IC_PINTYPE_NUMBER - ) - inputs_default = list( - "2" = ", ", - "4" = 0 - ) - outputs = list( - "joined text" = IC_PINTYPE_STRING - ) - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - cooldown_per_use = 1 - -/obj/item/integrated_circuit/lists/jointext/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/delimiter = get_pin_data(IC_INPUT, 2) - var/start = get_pin_data(IC_INPUT, 3) - var/end = get_pin_data(IC_INPUT, 4) - - var/result = null - - if(input_list.len && delimiter && !isnull(start) && !isnull(end)) - result = jointext(input_list, delimiter, start, end) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/lists/constructor - name = "large list constructor" - desc = "This circuit will build a list out of up to sixteen input values." - icon_state = "constr8" - inputs = list() - outputs = list( - "result" = IC_PINTYPE_LIST - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/number_of_pins = 16 - -/obj/item/integrated_circuit/lists/constructor/Initialize() - for(var/i = 1 to number_of_pins) - inputs["input [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called. - complexity = number_of_pins / 2 - . = ..() - -/obj/item/integrated_circuit/lists/constructor/do_work() - var/list/output_list = list() - for(var/i = 1 to number_of_pins) - var/data = get_pin_data(IC_INPUT, i) - - // No nested lists - if(!islist(data)) - output_list += data - else - output_list += null - - set_pin_data(IC_OUTPUT, 1, output_list) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/lists/constructor/small - name = "list constructor" - desc = "This circuit will build a list out of up to four input values." - icon_state = "constr" - number_of_pins = 4 - -/obj/item/integrated_circuit/lists/constructor/medium - name = "medium list constructor" - desc = "This circuit will build a list out of up to eight input values." - icon_state = "constr8" - number_of_pins = 8 - - -/obj/item/integrated_circuit/lists/deconstructor - name = "large list deconstructor" - desc = "This circuit will write the first sixteen entries of its input list, starting with the index, into the output values." - icon_state = "deconstr8" - inputs = list( - "input" = IC_PINTYPE_LIST, - "index" = IC_PINTYPE_INDEX - ) - outputs = list() - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/number_of_pins = 16 - -/obj/item/integrated_circuit/lists/deconstructor/Initialize() - for(var/i = 1 to number_of_pins) - outputs["output [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called. - complexity = number_of_pins / 2 - . = ..() - -/obj/item/integrated_circuit/lists/deconstructor/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/start_index = get_pin_data(IC_INPUT, 2) - - for(var/i = 1 to number_of_pins) - var/list_index = i + start_index - 1 - if(list_index > input_list.len) - set_pin_data(IC_OUTPUT, i, null) - else - set_pin_data(IC_OUTPUT, i, input_list[list_index]) - - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/lists/deconstructor/small - name = "list deconstructor" - desc = "This circuit will write the first four entries of its input list, starting with the index, into the output values." - icon_state = "deconstr" - number_of_pins = 4 - -/obj/item/integrated_circuit/lists/deconstructor/medium - name = "medium list deconstructor" - desc = "This circuit will write the first eight entries of its input list, starting with the index, into the output values." - number_of_pins = 8 - - -// - Join circuit - // -/obj/item/integrated_circuit/lists/join - name = "join circuit" - desc = "This circuit is a huge fan of shipping. It joins 2 lists together." - extended_desc = "Elements found in both lists will not be removed and can be found twice in the list." - inputs = list( - "first list" = IC_PINTYPE_LIST, - "second list" = IC_PINTYPE_LIST - ) - outputs = list( - "joined list" = IC_PINTYPE_LIST - ) - icon_state = "addition" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/lists/join/do_work() - var/list/input_list = get_pin_data(IC_INPUT, 1) - var/list/input_list2 = get_pin_data(IC_INPUT, 2) - - set_pin_data(IC_OUTPUT, 1, input_list+input_list2) - push_data() - activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/logic.dm b/code/modules/integrated_electronics/subtypes/logic.dm deleted file mode 100644 index a6144eec4063b..0000000000000 --- a/code/modules/integrated_electronics/subtypes/logic.dm +++ /dev/null @@ -1,223 +0,0 @@ -/obj/item/integrated_circuit/logic - name = "logic gate" - desc = "This tiny chip will decide for you!" - extended_desc = "Logic circuits will treat a null, 0, and a \"\" string value as FALSE and anything else as TRUE." - complexity = 1 - outputs = list("result" = IC_PINTYPE_BOOLEAN) - activators = list("compare" = IC_PINTYPE_PULSE_IN) - category_text = "Logic" - power_draw_per_use = 1 - -/obj/item/integrated_circuit/logic/do_work() - push_data() - -/obj/item/integrated_circuit/logic/binary - inputs = list("A" = IC_PINTYPE_ANY,"B" = IC_PINTYPE_ANY) - activators = list("compare" = IC_PINTYPE_PULSE_IN, "on true result" = IC_PINTYPE_PULSE_OUT, "on false result" = IC_PINTYPE_PULSE_OUT) - -/obj/item/integrated_circuit/logic/binary/do_work() - var/datum/integrated_io/A = inputs[1] - var/datum/integrated_io/B = inputs[2] - var/datum/integrated_io/O = outputs[1] - O.data = do_compare(A, B) ? TRUE : FALSE - - if(get_pin_data(IC_OUTPUT, 1)) - activate_pin(2) - else - activate_pin(3) - ..() - -/obj/item/integrated_circuit/logic/binary/proc/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - return FALSE - -/obj/item/integrated_circuit/logic/binary/proc/comparable(var/datum/integrated_io/A, var/datum/integrated_io/B) - return (isnum_safe(A.data) && isnum_safe(B.data)) || (istext(A.data) && istext(B.data)) - -/obj/item/integrated_circuit/logic/unary - inputs = list("A" = IC_PINTYPE_ANY) - activators = list("compare" = IC_PINTYPE_PULSE_IN, "on compare" = IC_PINTYPE_PULSE_OUT) - -/obj/item/integrated_circuit/logic/unary/do_work() - var/datum/integrated_io/A = inputs[1] - var/datum/integrated_io/O = outputs[1] - O.data = do_check(A) ? TRUE : FALSE - ..() - activate_pin(2) - -/obj/item/integrated_circuit/logic/unary/proc/do_check(var/datum/integrated_io/A) - return FALSE - -/obj/item/integrated_circuit/logic/binary/equals - name = "equal gate" - desc = "This gate compares two values, and outputs TRUE if both are the same." - icon_state = "equal" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/logic/binary/equals/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - return A.data == B.data - -/obj/item/integrated_circuit/logic/binary/jklatch - name = "JK latch" - desc = "This gate is a synchronized JK latch." - icon_state = "jklatch" - inputs = list("J" = IC_PINTYPE_ANY,"K" = IC_PINTYPE_ANY) - outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN) - activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/lstate=FALSE - -/obj/item/integrated_circuit/logic/binary/jklatch/do_work() - var/datum/integrated_io/A = inputs[1] - var/datum/integrated_io/B = inputs[2] - var/datum/integrated_io/O = outputs[1] - var/datum/integrated_io/Q = outputs[2] - if(A.data) - if(B.data) - lstate=!lstate - else - lstate = TRUE - else - if(B.data) - lstate=FALSE - O.data = lstate ? TRUE : FALSE - Q.data = !lstate ? TRUE : FALSE - if(get_pin_data(IC_OUTPUT, 1)) - activate_pin(2) - else - activate_pin(3) - push_data() - -/obj/item/integrated_circuit/logic/binary/rslatch - name = "RS latch" - desc = "This gate is a synchronized RS latch. If both R and S are true, its state will not change." - icon_state = "sr_nor" - inputs = list("S" = IC_PINTYPE_ANY,"R" = IC_PINTYPE_ANY) - outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN) - activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/lstate=FALSE - -/obj/item/integrated_circuit/logic/binary/rslatch/do_work() - var/datum/integrated_io/A = inputs[1] - var/datum/integrated_io/B = inputs[2] - var/datum/integrated_io/O = outputs[1] - var/datum/integrated_io/Q = outputs[2] - if(A.data) - if(!B.data) - lstate=TRUE - else - if(B.data) - lstate=FALSE - O.data = lstate ? TRUE : FALSE - Q.data = !lstate ? TRUE : FALSE - if(get_pin_data(IC_OUTPUT, 1)) - activate_pin(2) - else - activate_pin(3) - push_data() - -/obj/item/integrated_circuit/logic/binary/gdlatch - name = "gated D latch" - desc = "This gate is a synchronized gated D latch." - icon_state = "gated_d" - inputs = list("D" = IC_PINTYPE_ANY,"E" = IC_PINTYPE_ANY) - outputs = list("Q" = IC_PINTYPE_BOOLEAN,"!Q" = IC_PINTYPE_BOOLEAN) - activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/lstate=FALSE - -/obj/item/integrated_circuit/logic/binary/gdlatch/do_work() - var/datum/integrated_io/A = inputs[1] - var/datum/integrated_io/B = inputs[2] - var/datum/integrated_io/O = outputs[1] - var/datum/integrated_io/Q = outputs[2] - if(B.data) - if(A.data) - lstate=TRUE - else - lstate=FALSE - - O.data = lstate ? TRUE : FALSE - Q.data = !lstate ? TRUE : FALSE - if(get_pin_data(IC_OUTPUT, 1)) - activate_pin(2) - else - activate_pin(3) - push_data() - -/obj/item/integrated_circuit/logic/binary/not_equals - name = "not equal gate" - desc = "This gate compares two values, and outputs TRUE if both are different." - icon_state = "not_equal" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/logic/binary/not_equals/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - return A.data != B.data - -/obj/item/integrated_circuit/logic/binary/and - name = "and gate" - desc = "This gate will output TRUE if both inputs evaluate to true." - icon_state = "and" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/logic/binary/and/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - return A.data && B.data - -/obj/item/integrated_circuit/logic/binary/or - name = "or gate" - desc = "This gate will output TRUE if one of the inputs evaluate to true." - icon_state = "or" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/logic/binary/or/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - return A.data || B.data - -/obj/item/integrated_circuit/logic/binary/less_than - name = "less than gate" - desc = "This will output TRUE if the first input is less than the second input." - icon_state = "less_than" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/logic/binary/less_than/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - if(comparable(A, B)) - return A.data < B.data - -/obj/item/integrated_circuit/logic/binary/less_than_or_equal - name = "less than or equal gate" - desc = "This will output TRUE if the first input is less than, or equal to the second input." - icon_state = "less_than_or_equal" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/logic/binary/less_than_or_equal/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - if(comparable(A, B)) - return A.data <= B.data - -/obj/item/integrated_circuit/logic/binary/greater_than - name = "greater than gate" - desc = "This will output TRUE if the first input is greater than the second input." - icon_state = "greater_than" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/logic/binary/greater_than/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - if(comparable(A, B)) - return A.data > B.data - -/obj/item/integrated_circuit/logic/binary/greater_than_or_equal - name = "greater than or equal gate" - desc = "This will output TRUE if the first input is greater than, or equal to the second input." - icon_state = "greater_than_or_equal" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/logic/binary/greater_than_or_equal/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B) - if(comparable(A, B)) - return A.data >= B.data - -/obj/item/integrated_circuit/logic/unary/not - name = "not gate" - desc = "This gate inverts what's fed into it." - icon_state = "not" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - activators = list("invert" = IC_PINTYPE_PULSE_IN, "on inverted" = IC_PINTYPE_PULSE_OUT) - -/obj/item/integrated_circuit/logic/unary/not/do_check(var/datum/integrated_io/A) - return !A.data diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm deleted file mode 100644 index 13f49e9e68144..0000000000000 --- a/code/modules/integrated_electronics/subtypes/manipulation.dm +++ /dev/null @@ -1,747 +0,0 @@ -/obj/item/integrated_circuit/manipulation - category_text = "Manipulation" - -/obj/item/integrated_circuit/manipulation/weapon_firing - name = "weapon firing mechanism" - desc = "This somewhat complicated system allows one to slot in a gun, direct it towards a position, and remotely fire it." - extended_desc = "The firing mechanism can slot in any energy weapon. \ - The first and second inputs need to be numbers which correspond to coordinates for the gun to fire at relative to the machine itself. \ - The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Mode will switch between \ - lethal (TRUE) or stun (FALSE) modes. It uses the internal battery of the weapon itself, not the assembly. If you wish to fire the gun while the circuit is in \ - hand, you will need to use an assembly that is a gun." - complexity = 20 - max_allowed = 1 - w_class = WEIGHT_CLASS_SMALL - size = 3 - inputs = list( - "target X rel" = IC_PINTYPE_NUMBER, - "target Y rel" = IC_PINTYPE_NUMBER, - "mode" = IC_PINTYPE_BOOLEAN - ) - outputs = list("reference to gun" = IC_PINTYPE_REF) - activators = list( - "fire" = IC_PINTYPE_PULSE_IN - - ) - var/obj/item/gun/energy/installed_gun = null - spawn_flags = IC_SPAWN_RESEARCH - action_flags = IC_ACTION_COMBAT - power_draw_per_use = 0 - ext_cooldown = 1 - var/mode = FALSE - - var/stun_projectile = null //stun mode projectile type - var/stun_projectile_sound - var/lethal_projectile = null //lethal mode projectile type - var/lethal_projectile_sound - - demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby() - - - -/obj/item/integrated_circuit/manipulation/weapon_firing/Destroy() - qdel(installed_gun) - return ..() - -/obj/item/integrated_circuit/manipulation/weapon_firing/attackby(var/obj/O, var/mob/user) - if(istype(O, /obj/item/gun/energy)) - var/obj/item/gun/gun = O - if(installed_gun) - to_chat(user, "There's already a weapon installed.") - return - user.transferItemToLoc(gun,src) - installed_gun = gun - var/list/gun_properties = gun.get_turret_properties() - to_chat(user, "You slide \the [gun] into the firing mechanism.") - playsound(src, 'sound/items/Crowbar.ogg', 50, 1) - stun_projectile = gun_properties["stun_projectile"] - stun_projectile_sound = gun_properties["stun_projectile_sound"] - lethal_projectile = gun_properties["lethal_projectile"] - lethal_projectile_sound = gun_properties["lethal_projectile_sound"] - if(gun_properties["shot_delay"]) - cooldown_per_use = gun_properties["shot_delay"]*10 - if(cooldown_per_use<30) - cooldown_per_use = 30 - if(gun_properties["reqpower"]) - power_draw_per_use = gun_properties["reqpower"] - set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun)) - push_data() - else - ..() - -/obj/item/integrated_circuit/manipulation/weapon_firing/attack_self(var/mob/user) - if(installed_gun) - installed_gun.forceMove(drop_location()) - to_chat(user, "You slide \the [installed_gun] out of the firing mechanism.") - size = initial(size) - playsound(src, 'sound/items/Crowbar.ogg', 50, 1) - installed_gun = null - set_pin_data(IC_OUTPUT, 1, WEAKREF(null)) - push_data() - else - to_chat(user, "There's no weapon to remove from the mechanism.") - -/obj/item/integrated_circuit/manipulation/weapon_firing/do_work() - if(!installed_gun || !installed_gun.handle_pins()) - return - if(!isturf(assembly.loc) && !(assembly.can_fire_equipped && ishuman(assembly.loc))) - return - set_pin_data(IC_OUTPUT, 1, WEAKREF(installed_gun)) - push_data() - var/datum/integrated_io/xo = inputs[1] - var/datum/integrated_io/yo = inputs[2] - var/datum/integrated_io/mode1 = inputs[3] - - mode = mode1.data - if(assembly) - if(isnum_safe(xo.data)) - xo.data = round(xo.data, 1) - if(isnum_safe(yo.data)) - yo.data = round(yo.data, 1) - - var/turf/T = get_turf(assembly) - var/target_x = CLAMP(T.x + xo.data, 0, world.maxx) - var/target_y = CLAMP(T.y + yo.data, 0, world.maxy) - - assembly.visible_message("[assembly] fires [installed_gun]!") - shootAt(locate(target_x, target_y, T.z)) - -/obj/item/integrated_circuit/manipulation/weapon_firing/proc/shootAt(turf/target) - var/turf/T = get_turf(src) - var/turf/U = target - if(!istype(T) || !istype(U)) - return - if(!installed_gun.cell) - return - if(!installed_gun.cell.charge) - return - var/obj/item/ammo_casing/energy/shot = installed_gun.ammo_type[mode?2:1] - if(installed_gun.cell.charge < shot.e_cost) - return - if(!shot) - return - update_icon() - var/obj/item/projectile/A - if(!mode) - A = new stun_projectile(T) - playsound(loc, stun_projectile_sound, 75, 1) - else - A = new lethal_projectile(T) - playsound(loc, lethal_projectile_sound, 75, 1) - installed_gun.cell.use(shot.e_cost) - //Shooting Code: - A.preparePixelProjectile(target, src) - A.fire() - log_attack("[assembly] [REF(assembly)] has fired [installed_gun].") - return A - -/obj/item/integrated_circuit/manipulation/locomotion - name = "locomotion circuit" - desc = "This allows a machine to move in a given direction." - icon_state = "locomotion" - extended_desc = "The circuit accepts a 'dir' number as a direction to move towards. \ - Pulsing the 'step towards dir' activator pin will cause the machine to move one step in that direction, assuming it is not \ - being held, or anchored in some way. It should be noted that the ability to move is dependant on the type of assembly that this circuit inhabits; only drone assemblies can move." - w_class = WEIGHT_CLASS_SMALL - complexity = 10 - max_allowed = 4 - cooldown_per_use = 1 SECONDS - ext_cooldown = 1 SECONDS - inputs = list("direction" = IC_PINTYPE_DIR) - outputs = list("obstacle" = IC_PINTYPE_REF) - activators = list("step towards dir" = IC_PINTYPE_PULSE_IN,"on step"=IC_PINTYPE_PULSE_OUT,"blocked"=IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - action_flags = IC_ACTION_MOVEMENT - power_draw_per_use = 100 - -/obj/item/integrated_circuit/manipulation/locomotion/do_work() - ..() - var/turf/T = get_turf(src) - if(T && assembly) - if(assembly.anchored || !assembly.can_move()) - return - if(assembly.loc == T) // Check if we're held by someone. If the loc is the floor, we're not held. - var/datum/integrated_io/wanted_dir = inputs[1] - if(isnum_safe(wanted_dir.data)) - if(step(assembly, wanted_dir.data)) - activate_pin(2) - return - else - set_pin_data(IC_OUTPUT, 1, WEAKREF(assembly.collw)) - push_data() - activate_pin(3) - return FALSE - return FALSE - -/obj/item/integrated_circuit/manipulation/grenade - name = "grenade primer" - desc = "This circuit comes with the ability to attach most types of grenades and prime them at will." - extended_desc = "The time between priming and detonation is limited to between 1 to 12 seconds, but is optional. \ - If the input is not set, not a number, or a number less than 1, the grenade's built-in timing will be used. \ - Beware: Once primed, there is no aborting the process!" - icon_state = "grenade" - complexity = 30 - max_allowed = 1 - cooldown_per_use = 10 - inputs = list("detonation time" = IC_PINTYPE_NUMBER) - outputs = list("reference to grenade" = IC_PINTYPE_REF) - activators = list("prime grenade" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_RESEARCH - action_flags = IC_ACTION_COMBAT - var/obj/item/grenade/attached_grenade - var/pre_attached_grenade_type - demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby() - -/obj/item/integrated_circuit/manipulation/grenade/Initialize() - . = ..() - if(pre_attached_grenade_type) - var/grenade = new pre_attached_grenade_type(src) - attach_grenade(grenade) - -/obj/item/integrated_circuit/manipulation/grenade/Destroy() - if(attached_grenade && !attached_grenade.active) - attached_grenade.forceMove(loc) - detach_grenade() - return ..() - -/obj/item/integrated_circuit/manipulation/grenade/attackby(var/obj/item/grenade/G, var/mob/user) - if(istype(G)) - if(attached_grenade) - to_chat(user, "There is already a grenade attached!") - else if(user.transferItemToLoc(G,src)) - user.visible_message("\The [user] attaches \a [G] to \the [src]!", "You attach \the [G] to \the [src].") - attach_grenade(G) - G.forceMove(src) - else - return ..() - -/obj/item/integrated_circuit/manipulation/grenade/attack_self(var/mob/user) - if(attached_grenade) - user.visible_message("\The [user] removes \an [attached_grenade] from \the [src]!", "You remove \the [attached_grenade] from \the [src].") - user.put_in_hands(attached_grenade) - detach_grenade() - else - return ..() - -/obj/item/integrated_circuit/manipulation/grenade/do_work() - if(attached_grenade && !attached_grenade.active) - var/datum/integrated_io/detonation_time = inputs[1] - var/dt - if(isnum_safe(detonation_time.data) && detonation_time.data > 0) - dt = CLAMP(detonation_time.data, 1, 12)*10 - else - dt = 15 - addtimer(CALLBACK(attached_grenade, /obj/item/grenade.proc/prime), dt) - var/atom/holder = loc - message_admins("activated a grenade assembly. Last touches: Assembly: [holder.fingerprintslast] Circuit: [fingerprintslast] Grenade: [attached_grenade.fingerprintslast]") - -// These procs do not relocate the grenade, that's the callers responsibility -/obj/item/integrated_circuit/manipulation/grenade/proc/attach_grenade(var/obj/item/grenade/G) - attached_grenade = G - G.forceMove(src) - desc += " \An [attached_grenade] is attached to it!" - set_pin_data(IC_OUTPUT, 1, WEAKREF(G)) - -/obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade() - if(!attached_grenade) - return - attached_grenade.forceMove(drop_location()) - set_pin_data(IC_OUTPUT, 1, WEAKREF(null)) - attached_grenade = null - desc = initial(desc) - -/obj/item/integrated_circuit/manipulation/plant_module - name = "plant manipulation module" - desc = "Used to uproot weeds and harvest/plant trays." - icon_state = "plant_m" - extended_desc = "The circuit accepts a reference to a hydroponic tray or an item on an adjacent tile. \ - Mode input (0-harvest, 1-uproot weeds, 2-uproot plant, 3-plant seed) determines action. \ - Harvesting outputs a list of the harvested plants." - w_class = WEIGHT_CLASS_TINY - complexity = 10 - inputs = list("tray" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER,"item" = IC_PINTYPE_REF) - outputs = list("result" = IC_PINTYPE_LIST) - activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 50 - -/obj/item/integrated_circuit/manipulation/plant_module/do_work() - ..() - var/obj/acting_object = get_object() - var/obj/OM = get_pin_data_as_type(IC_INPUT, 1, /obj) - var/obj/O = get_pin_data_as_type(IC_INPUT, 3, /obj/item) - - if(!check_target(OM)) - push_data() - activate_pin(2) - return - - if(istype(OM,/obj/structure/spacevine) && check_target(OM) && get_pin_data(IC_INPUT, 2) == 2) - qdel(OM) - push_data() - activate_pin(2) - return - - var/obj/machinery/hydroponics/TR = OM - if(istype(TR)) - switch(get_pin_data(IC_INPUT, 2)) - if(0) - var/list/harvest_output = TR.attack_hand() - for(var/i in 1 to length(harvest_output)) - harvest_output[i] = WEAKREF(harvest_output[i]) - - if(length(harvest_output)) - set_pin_data(IC_OUTPUT, 1, harvest_output) - push_data() - if(1) - TR.weedlevel = 0 - TR.update_icon() - if(2) - if(TR.myseed) //Could be that they're just using it as a de-weeder - TR.age = 0 - TR.plant_health = 0 - if(TR.harvest) - TR.harvest = FALSE //To make sure they can't just put in another seed and insta-harvest it - qdel(TR.myseed) - TR.myseed = null - TR.weedlevel = 0 //Has a side effect of cleaning up those nasty weeds - TR.dead = 0 - TR.update_icon() - if(3) - if(!check_target(O)) - activate_pin(2) - return FALSE - - else if(istype(O, /obj/item/seeds) && !istype(O, /obj/item/seeds/sample)) - if(!TR.myseed) - if(istype(O, /obj/item/seeds/kudzu)) - investigate_log("had Kudzu planted in it by [acting_object] at [AREACOORD(src)]","kudzu") - acting_object.visible_message("[acting_object] plants [O].") - TR.dead = 0 - TR.myseed = O - TR.age = 1 - TR.plant_health = TR.myseed.endurance - TR.lastcycle = world.time - O.forceMove(TR) - TR.update_icon() - activate_pin(2) - -/obj/item/integrated_circuit/manipulation/seed_extractor - name = "seed extractor module" - desc = "Used to extract seeds from grown produce." - icon_state = "plant_m" - extended_desc = "The circuit accepts a reference to a plant item and extracts seeds from it, outputting the results to a list." - complexity = 8 - inputs = list("target" = IC_PINTYPE_REF) - outputs = list("result" = IC_PINTYPE_LIST) - activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 50 - -/obj/item/integrated_circuit/manipulation/seed_extractor/do_work() - ..() - var/obj/O = get_pin_data_as_type(IC_INPUT, 1, /obj/item) - if(!check_target(O)) - push_data() - activate_pin(2) - return - - var/list/seed_output = seedify(O, -1) - for(var/i in 1 to length(seed_output)) - seed_output[i] = WEAKREF(seed_output[i]) - - if(seed_output.len) - set_pin_data(IC_OUTPUT, 1, seed_output) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/manipulation/grabber - name = "grabber" - desc = "A circuit with its own inventory for items. Used to grab and store things." - icon_state = "grabber" - extended_desc = "This circuit accepts a reference to an object to be grabbed, and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, -1 to eject all objects, and -2 to eject the target. If you throw something from a grabber's inventory with a thrower, the grabber will update its outputs accordingly." - w_class = WEIGHT_CLASS_SMALL - size = 3 - cooldown_per_use = 5 - complexity = 10 - max_allowed = 1 - inputs = list("target" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER) - outputs = list("first" = IC_PINTYPE_REF, "last" = IC_PINTYPE_REF, "amount" = IC_PINTYPE_NUMBER,"contents" = IC_PINTYPE_LIST) - activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - action_flags = IC_ACTION_COMBAT - power_draw_per_use = 50 - var/max_items = 10 - -/obj/item/integrated_circuit/manipulation/grabber/do_work() - //There shouldn't be any target required to eject all contents - var/mode = get_pin_data(IC_INPUT, 2) - switch(mode) - if(-1) - drop_all() - if(0) - if(contents.len) - drop(contents[1]) - - var/obj/item/AM = get_pin_data_as_type(IC_INPUT, 1, /obj/item) - if(!QDELETED(AM) && !istype(AM, /obj/item/electronic_assembly) && !istype(AM, /obj/item/transfer_valve) && !istype(AM, /obj/item/twohanded) && !istype(assembly.loc, /obj/item/implant/storage)) - switch(mode) - if(1) - grab(AM) - if(-2) - drop(AM) - update_outputs() - activate_pin(2) - -/obj/item/integrated_circuit/manipulation/grabber/proc/grab(obj/item/AM) - var/max_w_class = assembly.w_class - if(check_target(AM)) - if(contents.len < max_items && AM.w_class <= max_w_class) - var/atom/A = get_object() - A.investigate_log("picked up ([AM]) with [src].", INVESTIGATE_CIRCUIT) - AM.forceMove(src) - -/obj/item/integrated_circuit/manipulation/grabber/proc/drop(obj/item/AM, turf/T = drop_location()) - if(!(AM in contents)) - return - var/atom/A = get_object() - A.investigate_log("dropped ([AM]) from [src].", INVESTIGATE_CIRCUIT) - AM.forceMove(T) - -/obj/item/integrated_circuit/manipulation/grabber/proc/drop_all() - if(contents.len) - var/turf/T = drop_location() - var/obj/item/U - for(U in src) - drop(U, T) - -/obj/item/integrated_circuit/manipulation/grabber/proc/update_outputs() - if(contents.len) - set_pin_data(IC_OUTPUT, 1, WEAKREF(contents[1])) - set_pin_data(IC_OUTPUT, 2, WEAKREF(contents[contents.len])) - else - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - set_pin_data(IC_OUTPUT, 3, contents.len) - set_pin_data(IC_OUTPUT, 4, contents) - push_data() - -/obj/item/integrated_circuit/manipulation/grabber/attack_self(var/mob/user) - drop_all() - update_outputs() - push_data() - -/obj/item/integrated_circuit/manipulation/claw - name = "pulling claw" - desc = "Circuit which can pull things.." - icon_state = "pull_claw" - extended_desc = "This circuit accepts a reference to a thing to be pulled. Modes: 0 for release. 1 for pull." - w_class = WEIGHT_CLASS_SMALL - size = 3 - cooldown_per_use = 5 - max_allowed = 1 - complexity = 10 - inputs = list("target" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_INDEX,"dir" = IC_PINTYPE_DIR) - outputs = list("is pulling" = IC_PINTYPE_BOOLEAN) - activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT,"released" = IC_PINTYPE_PULSE_OUT,"pull to dir" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 50 - ext_cooldown = 1 - var/max_grab = GRAB_PASSIVE - -/obj/item/integrated_circuit/manipulation/claw/do_work(ord) - var/obj/acting_object = get_object() - var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) - var/mode = get_pin_data(IC_INPUT, 2) - switch(ord) - if(1) - mode = CLAMP(mode, GRAB_PASSIVE, max_grab) - if(AM) - if(check_target(AM, exclude_contents = TRUE)) - acting_object.investigate_log("grabbed ([AM]) using [src].", INVESTIGATE_CIRCUIT) - acting_object.start_pulling(AM,mode) - if(acting_object.pulling) - set_pin_data(IC_OUTPUT, 1, TRUE) - else - set_pin_data(IC_OUTPUT, 1, FALSE) - push_data() - - if(4) - if(acting_object.pulling) - var/dir = get_pin_data(IC_INPUT, 3) - var/turf/G =get_step(get_turf(acting_object),dir) - var/atom/movable/pullee = acting_object.pulling - var/turf/Pl = get_turf(pullee) - var/turf/F = get_step_towards(Pl,G) - if(acting_object.Adjacent(F)) - if(!step_towards(pullee, F)) - F = get_step_towards2(Pl,G) - if(acting_object.Adjacent(F)) - step_towards(pullee, F) - activate_pin(2) - -/obj/item/integrated_circuit/manipulation/claw/stop_pulling() - set_pin_data(IC_OUTPUT, 1, FALSE) - activate_pin(3) - push_data() - ..() - - - - -/obj/item/integrated_circuit/manipulation/matman - name = "material manager" - desc = "This circuit is designed for automatic storage and distribution of materials." - extended_desc = "The first input takes a ref of a machine with a material container. \ - Second input is used for inserting material stacks into the internal material storage. \ - Inputs 3-13 are used to transfer materials between target machine and circuit storage. \ - Positive values will take that number of materials from another machine. \ - Negative values will fill another machine from internal storage. Outputs show current stored amounts of mats." - icon_state = "grabber" - complexity = 16 - inputs = list( - "target" = IC_PINTYPE_REF, - "sheets to insert" = IC_PINTYPE_NUMBER, - "Iron" = IC_PINTYPE_NUMBER, - "Glass" = IC_PINTYPE_NUMBER, - "Silver" = IC_PINTYPE_NUMBER, - "Gold" = IC_PINTYPE_NUMBER, - "Diamond" = IC_PINTYPE_NUMBER, - "Uranium" = IC_PINTYPE_NUMBER, - "Solid Plasma" = IC_PINTYPE_NUMBER, - "Bluespace Mesh" = IC_PINTYPE_NUMBER, - "Bananium" = IC_PINTYPE_NUMBER, - "Titanium" = IC_PINTYPE_NUMBER, - ) - outputs = list( - "self ref" = IC_PINTYPE_REF, - "Total amount" = IC_PINTYPE_NUMBER, - "Iron" = IC_PINTYPE_NUMBER, - "Glass" = IC_PINTYPE_NUMBER, - "Silver" = IC_PINTYPE_NUMBER, - "Gold" = IC_PINTYPE_NUMBER, - "Diamond" = IC_PINTYPE_NUMBER, - "Uranium" = IC_PINTYPE_NUMBER, - "Solid Plasma" = IC_PINTYPE_NUMBER, - "Bluespace Mesh" = IC_PINTYPE_NUMBER, - "Bananium" = IC_PINTYPE_NUMBER, - "Titanium" = IC_PINTYPE_NUMBER - ) - activators = list( - "insert sheet" = IC_PINTYPE_PULSE_IN, - "transfer mats" = IC_PINTYPE_PULSE_IN, - "on success" = IC_PINTYPE_PULSE_OUT, - "on failure" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_IN, - "on push ref" = IC_PINTYPE_PULSE_IN - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 40 - ext_cooldown = 1 - cooldown_per_use = 10 - var/list/mtypes = list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace) - -/obj/item/integrated_circuit/manipulation/matman/Initialize() - var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, - list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace), 0, - FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) - materials.max_amount =100000 - materials.precise_insertion = TRUE - .=..() - -/obj/item/integrated_circuit/manipulation/matman/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - set_pin_data(IC_OUTPUT, 2, materials.total_amount) - for(var/I in 1 to mtypes.len) - var/amount = materials.materials[mtypes[I]] - if(amount) - set_pin_data(IC_OUTPUT, I+2, amount) - push_data() - -/obj/item/integrated_circuit/manipulation/matman/proc/is_insertion_ready(mob/user) - return TRUE - -/obj/item/integrated_circuit/manipulation/matman/do_work(ord) - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/atom/movable/H = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) - if(!check_target(H)) - activate_pin(4) - return - var/turf/T = get_turf(H) - switch(ord) - if(1) - var/obj/item/stack/sheet/S = H - if(!S) - activate_pin(4) - return - if(materials.insert_stack(S, CLAMP(get_pin_data(IC_INPUT, 2),0,100), multiplier = 1) ) - AfterMaterialInsert() - activate_pin(3) - else - activate_pin(4) - if(2) - var/datum/component/material_container/mt = H.GetComponent(/datum/component/material_container) - var/suc - for(var/I in 1 to mtypes.len) - var/datum/material/M = materials.materials[mtypes[I]] - if(M) - var/U = CLAMP(get_pin_data(IC_INPUT, I+2),-100000,100000) - if(!U) - continue - if(!mt) //Invalid input - if(U>0) - if(materials.retrieve_sheets(materials.amount2sheet(U), mtypes[I], T)) - suc = TRUE - else - if(mt.transer_amt_to(materials, U, mtypes[I])) - suc = TRUE - if(suc) - AfterMaterialInsert() - activate_pin(3) - else - activate_pin(4) - if(5) - set_pin_data(IC_OUTPUT, 1, WEAKREF(src)) - AfterMaterialInsert() - activate_pin(6) - -/obj/item/integrated_circuit/manipulation/matman/Destroy() - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.retrieve_all() - .=..() - - - - -// - inserter circuit - // -/obj/item/integrated_circuit/manipulation/inserter - name = "inserter" - desc = "A nimble circuit that puts stuff inside a storage like a backpack and can take it out aswell." - icon_state = "grabber" - extended_desc = "This circuit accepts a reference to an object to be inserted or extracted depending on mode. If a storage is given for extraction, the extracted item will be put in the new storage. Modes: 1 insert, 0 to extract." - w_class = WEIGHT_CLASS_SMALL - size = 3 - cooldown_per_use = 5 - complexity = 10 - inputs = list("target object" = IC_PINTYPE_REF, "target container" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER) - activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - action_flags = IC_ACTION_COMBAT - power_draw_per_use = 20 - var/max_items = 10 - -/obj/item/integrated_circuit/manipulation/inserter/do_work() - //There shouldn't be any target required to eject all contents - var/obj/item/target_obj = get_pin_data_as_type(IC_INPUT, 1, /obj/item) - if(!target_obj) - return - - var/distance = get_dist(get_turf(src),get_turf(target_obj)) - if(distance > 1 || distance < 0) - return - - var/obj/item/storage/container = get_pin_data_as_type(IC_INPUT, 2, /obj/item) - var/mode = get_pin_data(IC_INPUT, 3) - switch(mode) - if(1) //Not working - if(!container || !istype(container,/obj/item/storage) || !Adjacent(container)) - return - - var/datum/component/storage/STR = container.GetComponent(/datum/component/storage) - if(!STR) - return - - STR.attackby(src, target_obj) - - else - var/datum/component/storage/STR = target_obj.loc.GetComponent(/datum/component/storage) - if(!STR) - return - - if(!container || !istype(container,/obj/item/storage) || !Adjacent(container)) - STR.remove_from_storage(target_obj,drop_location()) - else - STR.remove_from_storage(target_obj,container) - -// Renamer circuit. Renames the assembly it is in. Useful in cooperation with telecomms-based circuits. -/obj/item/integrated_circuit/manipulation/renamer - name = "renamer" - desc = "A small circuit that renames the assembly it is in. Useful paired with speech-based circuits." - icon_state = "internalbm" - extended_desc = "This circuit accepts a string as input, and can be pulsed to rewrite the current assembly's name with said string. On success, it pulses the default pulse-out wire." - inputs = list("name" = IC_PINTYPE_STRING) - outputs = list("current name" = IC_PINTYPE_STRING) - activators = list("rename" = IC_PINTYPE_PULSE_IN,"get name" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) - power_draw_per_use = 1 - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/manipulation/renamer/do_work(var/n) - if(!assembly) - return - switch(n) - if(1) - var/new_name = get_pin_data(IC_INPUT, 1) - if(new_name) - assembly.name = new_name - - else - set_pin_data(IC_OUTPUT, 1, assembly.name) - push_data() - - activate_pin(3) - - - -// - redescribing circuit - // -/obj/item/integrated_circuit/manipulation/redescribe - name = "redescriber" - desc = "Takes any string as an input and will set it as the assembly's description." - extended_desc = "Strings should can be of any length." - icon_state = "speaker" - cooldown_per_use = 10 - complexity = 3 - inputs = list("text" = IC_PINTYPE_STRING) - outputs = list("description" = IC_PINTYPE_STRING) - activators = list("redescribe" = IC_PINTYPE_PULSE_IN,"get description" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/manipulation/redescribe/do_work(var/n) - if(!assembly) - return - - switch(n) - if(1) - assembly.desc = get_pin_data(IC_INPUT, 1) - - else - set_pin_data(IC_OUTPUT, 1, assembly.desc) - push_data() - - activate_pin(3) - -// - repainting circuit - // -/obj/item/integrated_circuit/manipulation/repaint - name = "auto-repainter" - desc = "There's an oddly high amount of spraying cans fitted right inside this circuit." - extended_desc = "Takes a value in hexadecimal and uses it to repaint the assembly it is in." - cooldown_per_use = 10 - complexity = 3 - inputs = list("color" = IC_PINTYPE_COLOR) - outputs = list("current color" = IC_PINTYPE_COLOR) - activators = list("repaint" = IC_PINTYPE_PULSE_IN,"get color" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/manipulation/repaint/do_work(var/n) - if(!assembly) - return - - switch(n) - if(1) - assembly.detail_color = get_pin_data(IC_INPUT, 1) - assembly.update_icon() - - else - set_pin_data(IC_OUTPUT, 1, assembly.detail_color) - push_data() - - activate_pin(3) diff --git a/code/modules/integrated_electronics/subtypes/memory.dm b/code/modules/integrated_electronics/subtypes/memory.dm deleted file mode 100644 index 0392f92e5ac3b..0000000000000 --- a/code/modules/integrated_electronics/subtypes/memory.dm +++ /dev/null @@ -1,140 +0,0 @@ -/obj/item/integrated_circuit/memory - name = "memory chip" - desc = "This tiny chip can store one piece of data." - icon_state = "memory" - complexity = 1 - inputs = list() - outputs = list() - activators = list("set" = IC_PINTYPE_PULSE_IN, "on set" = IC_PINTYPE_PULSE_OUT) - category_text = "Memory" - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 1 - var/number_of_pins = 1 - -/obj/item/integrated_circuit/memory/Initialize() - for(var/i = 1 to number_of_pins) - inputs["input [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called. - outputs["output [i]"] = IC_PINTYPE_ANY - complexity = number_of_pins - . = ..() - -/obj/item/integrated_circuit/memory/examine(mob/user) - ..() - var/i - for(i = 1, i <= outputs.len, i++) - var/datum/integrated_io/O = outputs[i] - var/data = "nothing" - if(isweakref(O.data)) - var/datum/d = O.data_as_type(/datum) - if(d) - data = "[d]" - else if(!isnull(O.data)) - data = O.data - to_chat(user, "\The [src] has [data] saved to address [i].") - -/obj/item/integrated_circuit/memory/do_work() - for(var/i = 1 to inputs.len) - var/datum/integrated_io/I = inputs[i] - var/datum/integrated_io/O = outputs[i] - O.data = I.data - O.push_data() - activate_pin(2) - -/obj/item/integrated_circuit/memory/tiny - name = "small memory circuit" - desc = "This circuit can store two pieces of data." - icon_state = "memory4" - power_draw_per_use = 2 - number_of_pins = 2 - -/obj/item/integrated_circuit/memory/medium - name = "medium memory circuit" - desc = "This circuit can store four pieces of data." - icon_state = "memory4" - power_draw_per_use = 2 - number_of_pins = 4 - -/obj/item/integrated_circuit/memory/large - name = "large memory circuit" - desc = "This big circuit can store eight pieces of data." - icon_state = "memory8" - power_draw_per_use = 4 - number_of_pins = 8 - -/obj/item/integrated_circuit/memory/huge - name = "large memory stick" - desc = "This stick of memory can store up up to sixteen pieces of data." - icon_state = "memory16" - w_class = WEIGHT_CLASS_SMALL - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 8 - number_of_pins = 16 - -/obj/item/integrated_circuit/memory/constant - name = "constant chip" - desc = "This tiny chip can store one piece of data, which cannot be overwritten without disassembly." - icon_state = "memory" - inputs = list() - outputs = list("output pin" = IC_PINTYPE_ANY) - activators = list("push data" = IC_PINTYPE_PULSE_IN) - var/accepting_refs = FALSE - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - number_of_pins = 0 - -/obj/item/integrated_circuit/memory/constant/do_work() - var/datum/integrated_io/O = outputs[1] - O.push_data() - -/obj/item/integrated_circuit/memory/constant/emp_act() - for(var/i in 1 to activators.len) - var/datum/integrated_io/activate/A = activators[i] - A.scramble() - -/obj/item/integrated_circuit/memory/constant/save_special() - var/datum/integrated_io/O = outputs[1] - if(istext(O.data) || isnum_safe(O.data)) - return O.data - -/obj/item/integrated_circuit/memory/constant/load_special(special_data) - var/datum/integrated_io/O = outputs[1] - if(istext(special_data) || isnum_safe(special_data)) - O.data = special_data - -/obj/item/integrated_circuit/memory/constant/attack_self(mob/user) - var/datum/integrated_io/O = outputs[1] - if(!user.IsAdvancedToolUser()) - return - var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null") - - var/new_data = null - switch(type_to_use) - if("string") - accepting_refs = FALSE - new_data = input("Now type in a string.","[src] string writing") as null|text - if(istext(new_data) && user.IsAdvancedToolUser()) - O.data = new_data - to_chat(user, "You set \the [src]'s memory to [O.display_data(O.data)].") - if("number") - accepting_refs = FALSE - new_data = input("Now type in a number.","[src] number writing") as null|num - if(isnum_safe(new_data) && user.IsAdvancedToolUser()) - O.data = new_data - to_chat(user, "You set \the [src]'s memory to [O.display_data(O.data)].") - if("ref") - accepting_refs = TRUE - to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \ - an object for a ref of that object to save it in memory.") - if("null") - O.data = null - to_chat(user, "You set \the [src]'s memory to absolutely nothing.") - -/obj/item/integrated_circuit/memory/constant/afterattack(atom/target, mob/living/user, proximity) - . = ..() - if(accepting_refs && proximity) - var/datum/integrated_io/O = outputs[1] - O.data = WEAKREF(target) - visible_message("[user] slides \a [src]'s over \the [target].") - to_chat(user, "You set \the [src]'s memory to a reference to [O.display_data(O.data)]. The ref scanner is \ - now off.") - accepting_refs = FALSE - diff --git a/code/modules/integrated_electronics/subtypes/mining.dm b/code/modules/integrated_electronics/subtypes/mining.dm deleted file mode 100644 index 3dc9a45118c55..0000000000000 --- a/code/modules/integrated_electronics/subtypes/mining.dm +++ /dev/null @@ -1,105 +0,0 @@ -/obj/item/integrated_circuit/mining/ore_analyzer - name = "ore analyzer" - desc = "Analyzes a rock for its ore type." - extended_desc = "Takes a reference for an object and checks if it is a rock first. If that is the case, it outputs the mineral \ - inside the rock." - category_text = "Mining" - ext_cooldown = 1 - complexity = 20 - cooldown_per_use = 1 SECONDS - inputs = list( - "rock" = IC_PINTYPE_REF - ) - outputs = list( - "ore type" = IC_PINTYPE_STRING, - "amount" = IC_PINTYPE_NUMBER - ) - activators = list( - "analyze" = IC_PINTYPE_PULSE_IN, - "on analyzed" = IC_PINTYPE_PULSE_OUT, - "on found" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 20 - var/turf/closed/mineral/rock - var/mineral - - -/obj/item/integrated_circuit/mining/ore_analyzer/do_work() - rock = get_pin_data(IC_INPUT, 1) - if(!istype(rock,/turf/closed/mineral)) - mineral = null - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - - push_data() - activate_pin(2) - return - - if(rock.mineralType) - mineral = "[rock.mineralType]" - mineral = copytext(mineral, findlasttextEx(mineral, "/")+1) - else - mineral = "rock" - - set_pin_data(IC_OUTPUT, 1, mineral) - set_pin_data(IC_OUTPUT, 2, rock.mineralAmt) - push_data() - - activate_pin(2) - activate_pin(3) - - -/obj/item/integrated_circuit/mining/mining_drill - name = "mining drill" - desc = "A mining drill that can drill through rocks." - extended_desc = "A mining drill that activates on sensing a mineable rock. It takes some time to get the job done and \ - has to not be moved during that time." - category_text = "Mining" - ext_cooldown = 1 - complexity = 20 - cooldown_per_use = 3 SECONDS - inputs = list( - "rock" = IC_PINTYPE_REF - ) - outputs = list() - activators = list( - "mine" = IC_PINTYPE_PULSE_IN, - "on success" = IC_PINTYPE_PULSE_OUT, - "on failure" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 100 - - var/busy = FALSE - var/usedx - var/usedy - var/turf/closed/mineral/rock - -/obj/item/integrated_circuit/mining/mining_drill/do_work() - rock = get_pin_data(IC_INPUT, 1) - - if(!rock) - activate_pin(3) - return - if(!(ismineralturf(rock) && rock.Adjacent(assembly)) || busy) - activate_pin(3) - return - - busy = TRUE - usedx = assembly.loc.x - usedy = assembly.loc.y - playsound(src, 'sound/items/drill_use.ogg',50,1) - addtimer(CALLBACK(src, .proc/drill), 50) - - -/obj/item/integrated_circuit/mining/mining_drill/proc/drill() - busy = FALSE - // The assembly was moved, hence stopping the mining OR the rock was mined before - if(usedx != assembly.loc.x || usedy != assembly.loc.y || !rock) - activate_pin(3) - return FALSE - - activate_pin(2) - rock.gets_drilled() - return(TRUE) diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm deleted file mode 100644 index 04d5f6e67a3f4..0000000000000 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ /dev/null @@ -1,496 +0,0 @@ -/obj/item/integrated_circuit/output - category_text = "Output" - speech_span = SPAN_ROBOT - -/obj/item/integrated_circuit/output/screen - name = "small screen" - extended_desc = " use <br> to start a new line" - desc = "Takes any data type as an input, and displays it to the user upon examining." - icon_state = "screen" - inputs = list("displayed data" = IC_PINTYPE_ANY) - outputs = list() - activators = list("load data" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 10 - var/eol = "<br>" - var/stuff_to_display = null - -/obj/item/integrated_circuit/output/screen/disconnect_all() - ..() - stuff_to_display = null - -/obj/item/integrated_circuit/output/screen/any_examine(mob/user) - var/shown_label = "" - if(displayed_name && displayed_name != name) - shown_label = " labeled '[displayed_name]'" - - to_chat(user, "There is \a [src][shown_label], which displays [!isnull(stuff_to_display) ? "'[stuff_to_display]'" : "nothing"].") - -/obj/item/integrated_circuit/output/screen/do_work() - var/datum/integrated_io/I = inputs[1] - if(isweakref(I.data)) - var/datum/d = I.data_as_type(/datum) - if(d) - stuff_to_display = "[d]" - else - stuff_to_display = replacetext("[I.data]", eol , " ") - -/obj/item/integrated_circuit/output/screen/large - name = "large screen" - desc = "Takes any data type as an input and displays it to anybody near the device when pulsed. \ - It can also be examined to see the last thing it displayed." - icon_state = "screen_medium" - power_draw_per_use = 20 - -/obj/item/integrated_circuit/output/screen/large/do_work() - ..() - - if(isliving(assembly.loc))//this whole block just returns if the assembly is neither in a mobs hands or on the ground - var/mob/living/H = assembly.loc - if(H.get_active_held_item() != assembly && H.get_inactive_held_item() != assembly) - return - else - if(!isturf(assembly.loc)) - return - - 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, "[icon2html(O.icon, world, O.icon_state)] [stuff_to_display]") - if(assembly) - assembly.investigate_log("displayed \"[html_encode(stuff_to_display)]\" with [type].", INVESTIGATE_CIRCUIT) - else - investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT) - -/obj/item/integrated_circuit/output/light - name = "light" - desc = "A basic light which can be toggled on/off when pulsed." - icon_state = "light" - complexity = 4 - max_allowed = 4 - inputs = list() - outputs = list() - activators = list("toggle light" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/light_toggled = 0 - var/light_brightness = 3 - var/light_rgb = "#FFFFFF" - power_draw_idle = 0 // Adjusted based on brightness. - -/obj/item/integrated_circuit/output/light/do_work() - light_toggled = !light_toggled - update_lighting() - -/obj/item/integrated_circuit/output/light/proc/update_lighting() - if(light_toggled) - if(assembly) - assembly.set_light(l_range = light_brightness, l_power = 1, l_color = light_rgb) - else - if(assembly) - assembly.set_light(0) - power_draw_idle = light_toggled ? light_brightness * 2 : 0 - -/obj/item/integrated_circuit/output/light/power_fail() // Turns off the flashlight if there's no power left. - light_toggled = FALSE - update_lighting() - -/obj/item/integrated_circuit/output/light/advanced - name = "advanced light" - desc = "A light that takes a hexadecimal color value and a brightness value, and can be toggled on/off by pulsing it." - icon_state = "light_adv" - complexity = 8 - inputs = list( - "color" = IC_PINTYPE_COLOR, - "brightness" = IC_PINTYPE_NUMBER - ) - outputs = list() - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/output/light/advanced/on_data_written() - update_lighting() - -/obj/item/integrated_circuit/output/light/advanced/update_lighting() - var/new_color = get_pin_data(IC_INPUT, 1) - var/brightness = get_pin_data(IC_INPUT, 2) - - if(new_color && isnum_safe(brightness)) - brightness = CLAMP(brightness, 0, 4) - light_rgb = new_color - light_brightness = brightness - - ..() - -/obj/item/integrated_circuit/output/sound - name = "speaker circuit" - desc = "A miniature speaker is attached to this component." - icon_state = "speaker" - complexity = 8 - cooldown_per_use = 4 SECONDS - inputs = list( - "sound ID" = IC_PINTYPE_STRING, - "volume" = IC_PINTYPE_NUMBER, - "frequency" = IC_PINTYPE_BOOLEAN - ) - max_allowed = 5 - outputs = list() - activators = list("play sound" = IC_PINTYPE_PULSE_IN) - power_draw_per_use = 10 - var/list/sounds = list() - -/obj/item/integrated_circuit/output/sound/Initialize() - .= ..() - extended_desc = list() - extended_desc += "The first input pin determines which sound is used. The choices are; " - extended_desc += jointext(sounds, ", ") - extended_desc += ". The second pin determines the volume of sound that is played" - extended_desc += ", and the third determines if the frequency of the sound will vary with each activation." - extended_desc = jointext(extended_desc, null) - -/obj/item/integrated_circuit/output/sound/do_work() - var/ID = get_pin_data(IC_INPUT, 1) - var/vol = get_pin_data(IC_INPUT, 2) - var/freq = get_pin_data(IC_INPUT, 3) - if(!isnull(ID) && !isnull(vol)) - var/selected_sound = sounds[ID] - if(!selected_sound) - return - vol = CLAMP(vol ,0 , 100) - playsound(get_turf(src), selected_sound, vol, freq, -1) - var/atom/A = get_object() - A.investigate_log("played a sound ([selected_sound]) as [type].", INVESTIGATE_CIRCUIT) - -/obj/item/integrated_circuit/output/sound/on_data_written() - power_draw_per_use = get_pin_data(IC_INPUT, 2) * 15 - -/obj/item/integrated_circuit/output/sound/beeper - name = "beeper circuit" - desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit has a variety of beeps, boops, and buzzes to choose from." - sounds = list( - "beep" = 'sound/machines/twobeep.ogg', - "chime" = 'sound/machines/chime.ogg', - "buzz sigh" = 'sound/machines/buzz-sigh.ogg', - "buzz twice" = 'sound/machines/buzz-two.ogg', - "ping" = 'sound/machines/ping.ogg', - "synth yes" = 'sound/machines/synth_yes.ogg', - "synth no" = 'sound/machines/synth_no.ogg', - "warning buzz" = 'sound/machines/warning-buzzer.ogg' - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/output/sound/beepsky - name = "securitron sound circuit" - desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is similar to those used in Securitrons." - sounds = list( - "creep" = 'sound/voice/beepsky/creep.ogg', - "criminal" = 'sound/voice/beepsky/criminal.ogg', - "freeze" = 'sound/voice/beepsky/freeze.ogg', - "god" = 'sound/voice/beepsky/god.ogg', - "i am the law" = 'sound/voice/beepsky/iamthelaw.ogg', - "insult" = 'sound/voice/beepsky/insult.ogg', - "radio" = 'sound/voice/beepsky/radio.ogg', - "secure day" = 'sound/voice/beepsky/secureday.ogg', - ) - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/output/sound/medbot - name = "medbot sound circuit" - desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is often found in medical robots." - sounds = list( - "surgeon" = 'sound/voice/medbot/surgeon.ogg', - "radar" = 'sound/voice/medbot/radar.ogg', - "feel better" = 'sound/voice/medbot/feelbetter.ogg', - "patched up" = 'sound/voice/medbot/patchedup.ogg', - "injured" = 'sound/voice/medbot/injured.ogg', - "insult" = 'sound/voice/medbot/insult.ogg', - "coming" = 'sound/voice/medbot/coming.ogg', - "help" = 'sound/voice/medbot/help.ogg', - "live" = 'sound/voice/medbot/live.ogg', - "lost" = 'sound/voice/medbot/lost.ogg', - "flies" = 'sound/voice/medbot/flies.ogg', - "catch" = 'sound/voice/medbot/catch.ogg', - "delicious" = 'sound/voice/medbot/delicious.ogg', - "apple" = 'sound/voice/medbot/apple.ogg', - "no" = 'sound/voice/medbot/no.ogg', - ) - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/output/sound/vox - name = "ai vox sound circuit" - desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is often found in AI announcement systems." - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/output/sound/vox/Initialize() - .= ..() - sounds = GLOB.vox_sounds - extended_desc = "The first input pin determines which sound is used. It uses the AI Vox Broadcast word list. So either experiment to find words that work, or ask the AI to help in figuring them out. The second pin determines the volume of sound that is played, and the third determines if the frequency of the sound will vary with each activation." - -/obj/item/integrated_circuit/output/text_to_speech - name = "text-to-speech circuit" - desc = "Takes any string as an input and will make the device say the string when pulsed." - extended_desc = "This unit is more advanced than the plain speaker circuit, able to transpose any valid text to speech." - icon_state = "speaker" - cooldown_per_use = 10 - complexity = 12 - inputs = list("text" = IC_PINTYPE_STRING) - outputs = list() - activators = list("to speech" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 60 - -/obj/item/integrated_circuit/output/text_to_speech/do_work() - text = get_pin_data(IC_INPUT, 1) - if(!isnull(text)) - var/atom/movable/A = get_object() - var/sanitized_text = sanitize(text) - A.say(sanitized_text) - if (assembly) - log_say("[assembly] [REF(assembly)] : [sanitized_text]") - else - log_say("[name] ([type]) : [sanitized_text]") - -/obj/item/integrated_circuit/output/video_camera - name = "video camera circuit" - desc = "Takes a string as a name and a boolean to determine whether it is on, and uses this to be a camera linked to a list of networks you choose." - extended_desc = "The camera is linked to a list of camera networks of your choosing. Common choices are 'rd' for the research network, 'ss13' for the main station network (visible to AI), 'mine' for the mining network, and 'thunder' for the thunderdome network (viewable from bar)." - icon_state = "video_camera" - w_class = WEIGHT_CLASS_TINY - complexity = 10 - inputs = list( - "camera name" = IC_PINTYPE_STRING, - "camera active" = IC_PINTYPE_BOOLEAN, - "camera network" = IC_PINTYPE_LIST - ) - inputs_default = list("1" = "video camera circuit", "3" = list("rd")) - outputs = list() - activators = list() - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - action_flags = IC_ACTION_LONG_RANGE - power_draw_idle = 0 // Raises to 20 when on. - var/obj/machinery/camera/camera - var/updating = FALSE - -/obj/item/integrated_circuit/output/video_camera/New() - ..() - camera = new(src) - camera.network = list("rd") - on_data_written() - -/obj/item/integrated_circuit/output/video_camera/Destroy() - QDEL_NULL(camera) - return ..() - -/obj/item/integrated_circuit/output/video_camera/proc/set_camera_status(var/status) - if(camera) - camera.status = status - GLOB.cameranet.updatePortableCamera(camera) - power_draw_idle = camera.status ? 20 : 0 - if(camera.status) // Ensure that there's actually power. - if(!draw_idle_power()) - power_fail() - -/obj/item/integrated_circuit/output/video_camera/on_data_written() - if(camera) - var/cam_name = get_pin_data(IC_INPUT, 1) - var/cam_active = get_pin_data(IC_INPUT, 2) - var/list/new_network = get_pin_data(IC_INPUT, 3) - if(!isnull(cam_name)) - camera.c_tag = cam_name - if(!isnull(new_network)) - camera.network = new_network - set_camera_status(cam_active) - -/obj/item/integrated_circuit/output/video_camera/power_fail() - if(camera) - set_camera_status(0) - set_pin_data(IC_INPUT, 2, FALSE) - -/obj/item/integrated_circuit/output/video_camera/ext_moved(oldLoc, dir) - . = ..() - update_camera_location(oldLoc) - -#define VIDEO_CAMERA_BUFFER 10 -/obj/item/integrated_circuit/output/video_camera/proc/update_camera_location(oldLoc) - oldLoc = get_turf(oldLoc) - if(!QDELETED(camera) && !updating && oldLoc != get_turf(src)) - updating = TRUE - addtimer(CALLBACK(src, .proc/do_camera_update, oldLoc), VIDEO_CAMERA_BUFFER) -#undef VIDEO_CAMERA_BUFFER - -/obj/item/integrated_circuit/output/video_camera/proc/do_camera_update(oldLoc) - if(!QDELETED(camera) && oldLoc != get_turf(src)) - GLOB.cameranet.updatePortableCamera(camera) - updating = FALSE - -/obj/item/integrated_circuit/output/led - name = "light-emitting diode" - desc = "RGB LED. Takes a boolean value in, and if the boolean value is 'true-equivalent', the LED will be marked as lit on examine." - extended_desc = "TRUE-equivalent values are: Non-empty strings, non-zero numbers, and valid refs." - complexity = 0.1 - max_allowed = 4 - icon_state = "led" - inputs = list( - "lit" = IC_PINTYPE_BOOLEAN, - "color" = IC_PINTYPE_COLOR - ) - outputs = list() - activators = list() - inputs_default = list( - "2" = "#FF0000" - ) - power_draw_idle = 0 // Raises to 1 when lit. - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/led_color = "#FF0000" - -/obj/item/integrated_circuit/output/led/on_data_written() - power_draw_idle = get_pin_data(IC_INPUT, 1) ? 1 : 0 - led_color = get_pin_data(IC_INPUT, 2) - -/obj/item/integrated_circuit/output/led/power_fail() - set_pin_data(IC_INPUT, 1, FALSE) - -/obj/item/integrated_circuit/output/led/external_examine(mob/user) - var/text_output = "There is " - - if(name == displayed_name) - text_output += "\an [name]" - else - text_output += "\an ["\improper[name]"] labeled '[displayed_name]'" - text_output += " which is currently [get_pin_data(IC_INPUT, 1) ? "lit *" : "unlit"]." - to_chat(user, text_output) - -/obj/item/integrated_circuit/output/diagnostic_hud - name = "AR interface" - desc = "Takes an icon name as an input, and will update the status hud when data is written to it." - extended_desc = "Takes an icon name as an input, and will update the status hud when data is written to it, this means it can change the icon and have the icon stay that way even if the circuit is removed. The acceptable inputs are 'alert', 'move', 'working', 'patrol', 'called', and 'heart'. Any input other than that will return the icon to its default state." - var/list/icons = list( - "alert" = "hudalert", - "move" = "hudmove", - "working" = "hudworkingleft", - "patrol" = "hudpatrolleft", - "called" = "hudcalledleft", - "heart" = "hudsentientleft" - ) - complexity = 1 - icon_state = "led" - inputs = list( - "icon" = IC_PINTYPE_STRING - ) - outputs = list() - activators = list() - power_draw_idle = 0 - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/output/diagnostic_hud/on_data_written() - var/ID = get_pin_data(IC_INPUT, 1) - var/selected_icon = icons[ID] - if(assembly) - if(selected_icon) - assembly.prefered_hud_icon = selected_icon - else - assembly.prefered_hud_icon = "hudstat" - //update the diagnostic hud - assembly.diag_hud_set_circuitstat() - - -//Text to radio -//Outputs a simple string into radio (good to couple with the interceptor) -//Input: -//Text: the actual string to output -//Frequency: what channel to output in. This is a STRING, not a number, due to how comms work. It has to be the frequency without the dot, aka for common you need to put "1459" -/obj/item/integrated_circuit/output/text_to_radio - name = "text-to-radio circuit" - desc = "Takes any string as an input and will make the device output it in the radio with the frequency chosen as input." - extended_desc = "Similar to the text-to-speech circuit, except the fact that the text is converted into a subspace signal and broadcasted to the desired frequency, or 1459 as default.\ - The frequency is a number, and doesn't need the dot. Example: Common frequency is 145.9, so the result is 1459 as a number." - icon_state = "speaker" - complexity = 15 - inputs = list("text" = IC_PINTYPE_STRING, "frequency" = IC_PINTYPE_NUMBER) - outputs = list("encryption keys" = IC_PINTYPE_LIST) - activators = list("broadcast" = IC_PINTYPE_PULSE_IN) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 100 - cooldown_per_use = 0.1 - var/list/whitelisted_freqs = list() // special freqs can be used by inserting encryption keys - var/list/encryption_keys = list() - var/obj/item/radio/headset/integrated/radio - -/obj/item/integrated_circuit/output/text_to_radio/Initialize() - . = ..() - radio = new(src) - radio.frequency = FREQ_COMMON - GLOB.ic_speakers += src - -/obj/item/integrated_circuit/output/text_to_radio/Destroy() - qdel(radio) - GLOB.ic_speakers -= src - ..() - -/obj/item/integrated_circuit/output/text_to_radio/on_data_written() - var/freq = get_pin_data(IC_INPUT, 2) - if(!(freq in whitelisted_freqs)) - freq = sanitize_frequency(get_pin_data(IC_INPUT, 2), radio.freerange) - radio.set_frequency(freq) - -/obj/item/integrated_circuit/output/text_to_radio/do_work() - text = get_pin_data(IC_INPUT, 1) - if(!isnull(text)) - var/atom/movable/A = get_object() - var/sanitized_text = sanitize(text) - radio.talk_into(A, sanitized_text, ) - if (assembly) - log_say("[assembly] [REF(assembly)] : [sanitized_text]") - -/obj/item/integrated_circuit/output/text_to_radio/attackby(obj/O, mob/user) - if(istype(O, /obj/item/encryptionkey)) - user.transferItemToLoc(O,src) - encryption_keys += O - recalculate_channels() - to_chat(user, "You slide \the [O] inside the circuit.") - else - ..() - -/obj/item/integrated_circuit/output/text_to_radio/proc/recalculate_channels() - whitelisted_freqs.Cut() - set_pin_data(IC_INPUT, 2, 1459) - radio.set_frequency(FREQ_COMMON) //reset it - var/list/weakreffd_ekeys = list() - for(var/o in encryption_keys) - var/obj/item/encryptionkey/K = o - weakreffd_ekeys += WEAKREF(K) - for(var/i in K.channels) - whitelisted_freqs |= GLOB.radiochannels[i] - set_pin_data(IC_OUTPUT, 1, weakreffd_ekeys) - - -/obj/item/integrated_circuit/output/text_to_radio/attack_self(mob/user) - if(encryption_keys.len) - for(var/i in encryption_keys) - var/obj/O = i - O.forceMove(drop_location()) - encryption_keys.Cut() - set_pin_data(IC_OUTPUT, 1, WEAKREF(null)) - to_chat(user, "You slide the encryption keys out of the circuit.") - recalculate_channels() - else - to_chat(user, "There are no encryption keys to remove from the mechanism.") - -/obj/item/radio/headset/integrated - -/obj/item/integrated_circuit/output/screen/large - name = "medium screen" - -/obj/item/integrated_circuit/output/screen/extralarge // the subtype is called "extralarge" because tg brought back medium screens and they named the subtype /screen/large - name = "large screen" - desc = "Takes any data type as an input and displays it to the user upon examining, and to all nearby beings when pulsed." - icon_state = "screen_large" - power_draw_per_use = 40 - cooldown_per_use = 10 - -/obj/item/integrated_circuit/output/screen/extralarge/do_work() - ..() - var/obj/O = assembly ? get_turf(assembly) : loc - O.visible_message("[icon2html(O.icon, world, O.icon_state)] [stuff_to_display]") - if(assembly) - assembly.investigate_log("displayed \"[html_encode(stuff_to_display)]\" with [type].", INVESTIGATE_CIRCUIT) - else - investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT) diff --git a/code/modules/integrated_electronics/subtypes/power.dm b/code/modules/integrated_electronics/subtypes/power.dm deleted file mode 100644 index df6ab06eb0106..0000000000000 --- a/code/modules/integrated_electronics/subtypes/power.dm +++ /dev/null @@ -1,208 +0,0 @@ -/obj/item/integrated_circuit/power/ - category_text = "Power - Active" - -/obj/item/integrated_circuit/power/transmitter - name = "power transmission circuit" - desc = "This can wirelessly transmit electricity from an assembly's battery towards a nearby machine." - icon_state = "power_transmitter" - extended_desc = "This circuit transmits 5 kJ of electricity every time the activator pin is pulsed. The input pin must be \ - a reference to a machine to send electricity to. This can be a battery, or anything containing a battery. The machine can exist \ - inside the assembly, or adjacent to it. The power is sourced from the assembly's power cell. If the target is outside of the assembly, \ - some power is lost due to ineffiency." - w_class = WEIGHT_CLASS_SMALL - complexity = 16 - inputs = list("target" = IC_PINTYPE_REF) - outputs = list( - "target cell charge" = IC_PINTYPE_NUMBER, - "target cell max charge" = IC_PINTYPE_NUMBER, - "target cell percentage" = IC_PINTYPE_NUMBER - ) - activators = list("transmit" = IC_PINTYPE_PULSE_IN, "on transmit" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 500 // Inefficency has to come from somewhere. - var/amount_to_move = 5000 - -/obj/item/integrated_circuit/power/transmitter/large - name = "large power transmission circuit" - desc = "This can wirelessly transmit a lot of electricity from an assembly's battery towards a nearby machine. Warning: Do not operate in flammable environments." - extended_desc = "This circuit transmits 20 kJ of electricity every time the activator pin is pulsed. The input pin must be \ - a reference to a machine to send electricity to. This can be a battery, or anything containing a battery. The machine can exist \ - inside the assembly, or adjacent to it. The power is sourced from the assembly's power cell. If the target is outside of the assembly, \ - some power is lost due to ineffiency. Warning! Don't stack more than 1 power transmitter, as it becomes less efficient for every other \ - transmission circuit in its own assembly and other nearby ones." - w_class = WEIGHT_CLASS_BULKY - complexity = 32 - power_draw_per_use = 2000 - amount_to_move = 20000 - -/obj/item/integrated_circuit/power/transmitter/do_work() - - var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) - if(!AM) - return FALSE - if(istype(AM, /obj/item/gun/energy)) - return FALSE - if(!assembly) - return FALSE // Pointless to do everything else if there's no battery to draw from. - var/obj/item/stock_parts/cell/cell = AM.get_cell() - if(cell) - var/transfer_amount = amount_to_move - var/turf/A = get_turf(src) - var/turf/B = get_turf(AM) - if(A.Adjacent(B)) - if(AM.loc != assembly) - transfer_amount *= 0.8 // Losses due to distance. - var/list/U=A.GetAllContents(/obj/item/integrated_circuit/power/transmitter) - transfer_amount *= 1 / U.len - set_pin_data(IC_OUTPUT, 1, cell.charge) - set_pin_data(IC_OUTPUT, 2, cell.maxcharge) - set_pin_data(IC_OUTPUT, 3, cell.percent()) - activate_pin(2) - push_data() - if(cell.charge == cell.maxcharge) - return FALSE - if(transfer_amount && assembly.draw_power(amount_to_move)) // CELLRATE is already handled in draw_power() - cell.give(transfer_amount * GLOB.CELLRATE) - if(istype(AM, /obj/item)) - var/obj/item/I = AM - I.update_icon() - return TRUE - else - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - set_pin_data(IC_OUTPUT, 3, null) - activate_pin(2) - push_data() - return FALSE - -/obj/item/integrated_circuit/power/transmitter/large/do_work() - if(..()) // If the above code succeeds, do this below. - var/atom/movable/acting_object = get_object() - if(prob(20)) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(12, 1, src) - s.start() - acting_object.visible_message("\The [acting_object] makes some sparks!") - return TRUE - - -// - wire connector - // -/obj/item/integrated_circuit/power/transmitter/wire_connector - name = "wire connector" - desc = "Connects to a wire and allows to read the power, charge it or charge itself from the wire's power." - extended_desc = "This circuit will automatically attempt to locate and connect to wires on the floor beneath it when pulsed. \ - You must set a target before connecting. It can also transfer energy up to 2kJ from the assembly \ - to a wire and backwards if negative values are set for energy transfer." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "charge" = IC_PINTYPE_NUMBER - ) - activators = list( - "toggle connection" = IC_PINTYPE_PULSE_IN, - "transfer power" = IC_PINTYPE_PULSE_IN, - "on connected" = IC_PINTYPE_PULSE_OUT, - "on connection failed" = IC_PINTYPE_PULSE_OUT, - "on disconnected" = IC_PINTYPE_PULSE_OUT - ) - outputs = list( - "connected cable" = IC_PINTYPE_REF, - "powernet power" = IC_PINTYPE_NUMBER, - "powernet load" = IC_PINTYPE_NUMBER - ) - complexity = 35 - power_draw_per_use = 100 - amount_to_move = 0 - - var/obj/structure/cable/connected_cable - -/obj/item/integrated_circuit/power/transmitter/wire_connector/Destroy() - connected_cable = null - return ..() - -/obj/item/integrated_circuit/power/transmitter/wire_connector/Initialize() - START_PROCESSING(SSobj, src) - . = ..() - -//Does wire things -/obj/item/integrated_circuit/power/transmitter/wire_connector/process() - ..() - update_cable() - push_data() - -//If the assembly containing this is moved from the tile the wire is in, the connection breaks -/obj/item/integrated_circuit/power/transmitter/wire_connector/ext_moved() - if(connected_cable) - if(get_dist(get_object(), connected_cable) > 0) - // The connected cable is removed - connected_cable = null - set_pin_data(IC_OUTPUT, 1, null) - push_data() - activate_pin(5) - - -/obj/item/integrated_circuit/power/transmitter/wire_connector/on_data_written() - var/charge_num = get_pin_data(IC_INPUT, 1) - //In case someone sets that pin to null - if(!charge_num) - amount_to_move = 0 - return - - amount_to_move = CLAMP(charge_num,-2000, 2000) - -/obj/item/integrated_circuit/power/transmitter/wire_connector/do_work(var/n) - if(n == 1) - // If there is a connection, disconnect - if(connected_cable) - connected_cable = null - set_pin_data(IC_OUTPUT, 1, null) - push_data() - activate_pin(5) - return - - var/obj/structure/cable/foundcable = locate() in get_turf(src) - // If no connector can't connect - if(!foundcable || foundcable.invisibility != 0) - set_pin_data(IC_OUTPUT, 1, null) - push_data() - activate_pin(4) - return - connected_cable = foundcable - update_cable() - push_data() - activate_pin(3) - return - - - if(!connected_cable || !assembly) - return - - if(!assembly.battery) - return - - //No charge transfer, no need to syphon tickrates with scripts - if(!amount_to_move || amount_to_move == 0) - return - - //Second clamp: set the number between what the battery and powernet allows - var/obj/item/stock_parts/cell/battery = assembly.battery - amount_to_move = CLAMP(amount_to_move, -connected_cable.powernet.avail, battery.charge) - - if(amount_to_move > 0) - connected_cable.powernet.newavail += battery.use(amount_to_move) - return - connected_cable.powernet.avail -= battery.give(-amount_to_move) - -/obj/item/integrated_circuit/power/transmitter/wire_connector/proc/update_cable() - if(get_dist(get_object(), connected_cable) > 0) - connected_cable = null - - if(!connected_cable || connected_cable.invisibility != 0) - set_pin_data(IC_OUTPUT, 1, null) - set_pin_data(IC_OUTPUT, 2, null) - set_pin_data(IC_OUTPUT, 3, null) - return - - var/datum/powernet/analyzed_net = connected_cable.powernet - set_pin_data(IC_OUTPUT, 1, WEAKREF(connected_cable)) - set_pin_data(IC_OUTPUT, 2, analyzed_net.viewavail) - set_pin_data(IC_OUTPUT, 3, analyzed_net.viewload) diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm deleted file mode 100644 index c706cf5373f42..0000000000000 --- a/code/modules/integrated_electronics/subtypes/reagents.dm +++ /dev/null @@ -1,858 +0,0 @@ -#define IC_SMOKE_REAGENTS_MINIMUM_UNITS 10 - -/obj/item/integrated_circuit/reagent - category_text = "Reagent" - resistance_flags = UNACIDABLE | FIRE_PROOF - cooldown_per_use = 10 - var/volume = 0 - -/obj/item/integrated_circuit/reagent/Initialize() - . = ..() - if(volume) - create_reagents(volume, OPENCONTAINER) - push_vol() - -/obj/item/integrated_circuit/reagent/proc/push_vol() - set_pin_data(IC_OUTPUT, 1, reagents.total_volume) - push_data() - -// Hydroponics trays have no reagents holder and handle reagents in their own snowflakey way. -// This is a dirty hack to make injecting reagents into them work. -// TODO: refactor that. -/obj/item/integrated_circuit/reagent/proc/inject_tray(obj/machinery/hydroponics/tray, atom/movable/source, amount) - var/atom/movable/acting_object = get_object() - var/list/trays = list(tray) - var/visi_msg = "[acting_object] transfers fluid into [tray]" - - if(amount > 30 && source.reagents.total_volume >= 30 && tray.using_irrigation) - trays = tray.FindConnected() - if (trays.len > 1) - visi_msg += ", setting off the irrigation system" - - acting_object.visible_message("[visi_msg].") - playsound(loc, 'sound/effects/slosh.ogg', 25, 1) - - var/split = round(amount/trays.len) - - for(var/obj/machinery/hydroponics/H in trays) - var/datum/reagents/temp_reagents = new /datum/reagents() - temp_reagents.my_atom = H - - source.reagents.trans_to(temp_reagents, split) - H.applyChemicals(temp_reagents) - - temp_reagents.clear_reagents() - qdel(temp_reagents) - -/obj/item/integrated_circuit/reagent/injector - name = "integrated hypo-injector" - desc = "This scary looking thing is able to pump liquids into, or suck liquids out of, whatever it's pointed at." - icon_state = "injector" - extended_desc = "This autoinjector can push up to 30 units of reagents into another container or someone else outside of the machine. The target \ - must be adjacent to the machine, and if it is a person, they cannot be wearing thick clothing. Negative given amounts makes the injector suck out reagents instead." - - volume = 30 - - complexity = 20 - max_allowed = 1 - cooldown_per_use = 6 SECONDS - inputs = list( - "target" = IC_PINTYPE_REF, - "injection amount" = IC_PINTYPE_NUMBER - ) - inputs_default = list( - "2" = 5 - ) - outputs = list( - "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_SELFREF - ) - activators = list( - "inject" = IC_PINTYPE_PULSE_IN, - "on injected" = IC_PINTYPE_PULSE_OUT, - "on fail" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_IN - - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 15 - var/direction_mode = SYRINGE_INJECT - var/transfer_amount = 10 - var/busy = FALSE - -/obj/item/integrated_circuit/reagent/injector/on_reagent_change(changetype) - push_vol() - -/obj/item/integrated_circuit/reagent/injector/on_data_written() - var/new_amount = get_pin_data(IC_INPUT, 2) - if(new_amount < 0) - new_amount = -new_amount - direction_mode = SYRINGE_DRAW - else - direction_mode = SYRINGE_INJECT - if(isnum_safe(new_amount)) - new_amount = CLAMP(new_amount, 0, volume) - transfer_amount = new_amount - - -/obj/item/integrated_circuit/reagent/injector/do_work(ord) - switch(ord) - if(1) - inject() - if(4) - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - -/obj/item/integrated_circuit/reagent/injector/proc/inject() - set waitfor = FALSE // Don't sleep in a proc that is called by a processor without this set, otherwise it'll delay the entire thing - var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) - var/atom/movable/acting_object = get_object() - - if(busy || !check_target(AM)) - activate_pin(3) - return - - if(!AM.reagents) - if(istype(AM, /obj/machinery/hydroponics) && direction_mode == SYRINGE_INJECT && reagents.total_volume && transfer_amount)//injection into tray. - inject_tray(AM, src, transfer_amount) - activate_pin(2) - return - activate_pin(3) - return - - if(direction_mode == SYRINGE_INJECT) - if(!reagents.total_volume || !AM.is_injectable() || AM.reagents.holder_full()) - activate_pin(3) - return - - if(isliving(AM)) - var/mob/living/L = AM - if(!L.can_inject(null, 0)) - activate_pin(3) - return - - //Always log attemped injections for admins - var/contained = reagents.log_list() - log_combat(src, L, "attempted to inject", addition="which had [contained]") - L.visible_message("[acting_object] is trying to inject [L]!", \ - "[acting_object] is trying to inject you!") - busy = TRUE - if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0))) - var/fraction = min(transfer_amount/reagents.total_volume, 1) - reagents.reaction(L, INJECT, fraction) - reagents.trans_to(L, transfer_amount) - log_combat(src, L, "injected", addition="which had [contained]") - L.visible_message("[acting_object] injects [L] with its needle!", \ - "[acting_object] injects you with its needle!") - else - busy = FALSE - activate_pin(3) - return - busy = FALSE - - else - reagents.trans_to(AM, transfer_amount) - - if(direction_mode == SYRINGE_DRAW) - if(reagents.total_volume >= reagents.maximum_volume) - acting_object.visible_message("[acting_object] tries to draw from [AM], but the injector is full.") - activate_pin(3) - return - - var/tramount = abs(transfer_amount) - - if(isliving(AM)) - var/mob/living/L = AM - L.visible_message("[acting_object] is trying to take a blood sample from [L]!", \ - "[acting_object] is trying to take a blood sample from you!") - busy = TRUE - if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0))) - if(L.transfer_blood_to(src, tramount)) - L.visible_message("[acting_object] takes a blood sample from [L]!", \ - "[acting_object] takes a blood sample from you!") - else - L.visible_message("[acting_object] fails to take a blood sample from [L].", \ - "[acting_object] fails to take a blood sample from you!") - busy = FALSE - activate_pin(3) - return - busy = FALSE - - else - if(!AM.reagents.total_volume) - acting_object.visible_message("[acting_object] tries to draw from [AM], but it is empty!") - activate_pin(3) - return - - if(!AM.is_drawable()) - activate_pin(3) - return - tramount = min(tramount, AM.reagents.total_volume) - AM.reagents.trans_to(src, tramount) - activate_pin(2) - - - -/obj/item/integrated_circuit/reagent/pump - name = "reagent pump" - desc = "Moves liquids safely inside a machine, or even nearby it." - icon_state = "reagent_pump" - extended_desc = "This is a pump which will move liquids from the source ref to the target ref. The third pin determines \ - how much liquid is moved per pulse, between 0 and 50. The pump can move reagents to any open container inside the machine, or \ - outside the machine if it is adjacent to the machine." - - complexity = 8 - inputs = list("source" = IC_PINTYPE_REF, "target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER) - inputs_default = list("3" = 5) - outputs = list() - activators = list("transfer reagents" = IC_PINTYPE_PULSE_IN, "on transfer" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/transfer_amount = 10 - var/direction_mode = SYRINGE_INJECT - power_draw_per_use = 10 - -/obj/item/integrated_circuit/reagent/pump/on_data_written() - var/new_amount = get_pin_data(IC_INPUT, 3) - if(new_amount < 0) - new_amount = -new_amount - direction_mode = SYRINGE_DRAW - else - direction_mode = SYRINGE_INJECT - if(isnum_safe(new_amount)) - new_amount = CLAMP(new_amount, 0, 50) - transfer_amount = new_amount - -/obj/item/integrated_circuit/reagent/pump/do_work() - var/atom/movable/source = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) - var/atom/movable/target = get_pin_data_as_type(IC_INPUT, 2, /atom/movable) - - // Check for invalid input. - if(!check_target(source) || !check_target(target)) - return - - // If the pump is pumping backwards, swap target and source. - if(!direction_mode) - var/temp_source = source - source = target - target = temp_source - - if(!source.reagents) - return - - if(!target.reagents) - // Hydroponics trays have no reagents holder and handle reagents in their own snowflakey way. - // This is a dirty hack to make injecting reagents into them work. - if(istype(target, /obj/machinery/hydroponics) && source.reagents.total_volume) - inject_tray(target, source, transfer_amount) - activate_pin(2) - return - - if(!source.is_drainable() || !target.is_refillable()) - return - - source.reagents.trans_to(target, transfer_amount) - activate_pin(2) - -/obj/item/integrated_circuit/reagent/storage - cooldown_per_use = 1 - name = "reagent storage" - desc = "Stores liquid inside the device away from electrical components. It can store up to 60u." - icon_state = "reagent_storage" - extended_desc = "This is effectively an internal beaker." - - volume = 60 - - complexity = 4 - inputs = list() - outputs = list( - "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_SELFREF - ) - activators = list("push ref" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - - - -/obj/item/integrated_circuit/reagent/storage/do_work() - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - -/obj/item/integrated_circuit/reagent/storage/on_reagent_change(changetype) - push_vol() - -/obj/item/integrated_circuit/reagent/storage/big - name = "big reagent storage" - icon_state = "reagent_storage_big" - desc = "Stores liquid inside the device away from electrical components. Can store up to 180u." - - volume = 180 - - complexity = 16 - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/reagent/storage/cryo - name = "cryo reagent storage" - desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. This will also prevent reactions." - icon_state = "reagent_storage_cryo" - extended_desc = "This is effectively an internal cryo beaker." - - complexity = 8 - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/reagent/storage/cryo/Initialize() - . = ..() - ENABLE_BITFIELD(reagents.flags, NO_REACT) - -/obj/item/integrated_circuit/reagent/storage/grinder - name = "reagent grinder" - desc = "This is a reagent grinder. It accepts a ref to something, and refines it into reagents. It can store up to 100u." - icon_state = "blender" - extended_desc = "" - inputs = list( - "target" = IC_PINTYPE_REF, - ) - outputs = list( - "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_SELFREF - ) - activators = list( - "grind" = IC_PINTYPE_PULSE_IN, - "on grind" = IC_PINTYPE_PULSE_OUT, - "on fail" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_IN - ) - volume = 100 - power_draw_per_use = 150 - complexity = 16 - spawn_flags = IC_SPAWN_RESEARCH - - -/obj/item/integrated_circuit/reagent/storage/grinder/do_work(ord) - switch(ord) - if(1) - grind() - if(4) - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - -/obj/item/integrated_circuit/reagent/storage/grinder/proc/grind() - if(reagents.total_volume >= reagents.maximum_volume) - activate_pin(3) - return FALSE - var/obj/item/I = get_pin_data_as_type(IC_INPUT, 1, /obj/item) - if(istype(I)&&(I.grind_results)&&check_target(I)&&(I.on_grind(src) != -1)) - if(istype(I, /obj/item/reagent_containers)) - var/obj/item/reagent_containers/p = I - if(p.prevent_grinding) - return FALSE - reagents.add_reagent_list(I.grind_results) - if(I.reagents) - I.reagents.trans_to(src, I.reagents.total_volume) - qdel(I) - activate_pin(2) - return TRUE - activate_pin(3) - return FALSE - -/obj/item/integrated_circuit/reagent/storage/juicer - name = "reagent juicer" - desc = "This is a reagent juicer. It accepts a ref to something and refines it into reagents. It can store up to 100u." - icon_state = "blender" - extended_desc = "" - inputs = list( - "target" = IC_PINTYPE_REF, - ) - outputs = list( - "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_SELFREF - ) - activators = list( - "juice" = IC_PINTYPE_PULSE_IN, - "on juice" = IC_PINTYPE_PULSE_OUT, - "on fail" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_IN - ) - volume = 100 - power_draw_per_use = 150 - complexity = 16 - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/reagent/storage/juicer/do_work(ord) - switch(ord) - if(1) - juice() - if(4) - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - -/obj/item/integrated_circuit/reagent/storage/juicer/proc/juice() - if(reagents.total_volume >= reagents.maximum_volume) - activate_pin(3) - return FALSE - var/obj/item/I = get_pin_data_as_type(IC_INPUT, 1, /obj/item) - if(istype(I)&&check_target(I)&&(I.juice_results)&&(I.on_juice() != -1)) - reagents.add_reagent_list(I.juice_results) - qdel(I) - activate_pin(2) - return TRUE - activate_pin(3) - return FALSE - - - -/obj/item/integrated_circuit/reagent/storage/scan - name = "reagent scanner" - desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. On pulse this beaker will send list of contained reagents." - icon_state = "reagent_scan" - extended_desc = "Mostly useful for filtering reagents." - - complexity = 8 - outputs = list( - "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_SELFREF, - "list of reagents" = IC_PINTYPE_LIST - ) - activators = list( - "scan" = IC_PINTYPE_PULSE_IN, - "push ref" = IC_PINTYPE_PULSE_IN - ) - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/reagent/storage/scan/do_work(ord) - switch(ord) - if(1) - var/cont[0] - for(var/datum/reagent/RE in reagents.reagent_list) - cont += RE - set_pin_data(IC_OUTPUT, 3, cont) - push_data() - if(2) - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - -/obj/item/integrated_circuit/reagent/filter - name = "reagent filter" - desc = "Filters liquids by list of desired or unwanted reagents." - icon_state = "reagent_filter" - extended_desc = "This is a filter which will move liquids from the source to its target. \ - If the amount in the fourth pin is positive, it will move all reagents except those in the unwanted list. \ - If the amount in the fourth pin is negative, it will only move the reagents in the wanted list. \ - The third pin determines how many reagents are moved per pulse, between 0 and 50. Amount is given for each separate reagent." - - complexity = 8 - inputs = list( - "source" = IC_PINTYPE_REF, - "target" = IC_PINTYPE_REF, - "injection amount" = IC_PINTYPE_NUMBER, - "list of reagents" = IC_PINTYPE_LIST - ) - inputs_default = list( - "3" = 5 - ) - outputs = list() - activators = list( - "transfer reagents" = IC_PINTYPE_PULSE_IN, - "on transfer" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/transfer_amount = 10 - var/direction_mode = SYRINGE_INJECT - power_draw_per_use = 10 - -/obj/item/integrated_circuit/reagent/filter/on_data_written() - var/new_amount = get_pin_data(IC_INPUT, 3) - if(new_amount < 0) - new_amount = -new_amount - direction_mode = SYRINGE_DRAW - else - direction_mode = SYRINGE_INJECT - if(isnum_safe(new_amount)) - new_amount = CLAMP(new_amount, 0, 50) - transfer_amount = new_amount - -/obj/item/integrated_circuit/reagent/filter/do_work() - var/atom/movable/source = get_pin_data_as_type(IC_INPUT, 1, /atom/movable) - var/atom/movable/target = get_pin_data_as_type(IC_INPUT, 2, /atom/movable) - var/list/demand = get_pin_data(IC_INPUT, 4) - - // Check for invalid input. - if(!check_target(source) || !check_target(target)) - return - - if(!source.reagents || !target.reagents) - return - - // FALSE in those procs makes mobs invalid targets. - if(!source.is_drawable(FALSE) || !target.is_injectable(FALSE)) - return - - if(target.reagents.maximum_volume - target.reagents.total_volume <= 0) - return - - for(var/datum/reagent/G in source.reagents.reagent_list) - if(!direction_mode) - if(G in demand) - source.reagents.trans_id_to(target, G.type, transfer_amount) - else - if(!(G in demand)) - source.reagents.trans_id_to(target, G.type, transfer_amount) - activate_pin(2) - push_data() - -/obj/item/integrated_circuit/reagent/storage/heater - name = "chemical heater" - desc = "Stores liquid inside the device away from electrical components. It can store up to 60u. It will heat or cool the reagents \ - to the target temperature when turned on." - icon_state = "heater" - complexity = 8 - inputs = list( - "target temperature" = IC_PINTYPE_NUMBER, - "on" = IC_PINTYPE_BOOLEAN - ) - inputs_default = list("1" = 300) - outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_SELFREF,"temperature" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_RESEARCH - var/heater_coefficient = 0.1 - -/obj/item/integrated_circuit/reagent/storage/heater/on_data_written() - if(get_pin_data(IC_INPUT, 2)) - power_draw_idle = 30 - else - power_draw_idle = 0 - -/obj/item/integrated_circuit/reagent/storage/heater/Initialize() - .=..() - START_PROCESSING(SScircuit, src) - -/obj/item/integrated_circuit/reagent/storage/heater/Destroy() - STOP_PROCESSING(SScircuit, src) - return ..() - -/obj/item/integrated_circuit/reagent/storage/heater/process() - if(!power_draw_idle) - return - var/target_temperature = CLAMP(get_pin_data(IC_INPUT, 1), 0, 3000) - if(reagents.chem_temp > target_temperature) - reagents.chem_temp += min(-1, (target_temperature - reagents.chem_temp) * heater_coefficient) - if(reagents.chem_temp < target_temperature) - reagents.chem_temp += max(1, (target_temperature - reagents.chem_temp) * heater_coefficient) - - reagents.chem_temp = round(reagents.chem_temp) - reagents.handle_reactions() - set_pin_data(IC_OUTPUT, 3, reagents.chem_temp) - push_data() - - -/obj/item/integrated_circuit/reagent/smoke - name = "smoke generator" - desc = "Unlike most electronics, creating smoke is completely intentional." - icon_state = "smoke" - extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \ - into the smoke clouds when activated. The reagents are consumed when the smoke is made." - ext_cooldown = 1 - volume = 100 - complexity = 20 - cooldown_per_use = 1 SECONDS - inputs = list() - outputs = list( - "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_SELFREF - ) - activators = list( - "create smoke" = IC_PINTYPE_PULSE_IN, - "on smoked" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_IN - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 20 - var/smoke_radius = 5 - var/notified = FALSE - -/obj/item/integrated_circuit/reagent/smoke/on_reagent_change(changetype) - //reset warning only if we have reagents now - if(changetype == ADD_REAGENT) - notified = FALSE - push_vol() -/obj/item/integrated_circuit/reagent/smoke/do_work(ord) - switch(ord) - if(1) - if(!reagents || (reagents.total_volume < IC_SMOKE_REAGENTS_MINIMUM_UNITS)) - return - var/location = get_turf(src) - var/datum/effect_system/smoke_spread/chem/S = new - S.attach(location) - playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3) - if(S) - S.set_up(reagents, smoke_radius, location, notified) - if(!notified) - notified = TRUE - S.start() - reagents.clear_reagents() - activate_pin(2) - if(3) - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - -// - Integrated extinguisher - // -/obj/item/integrated_circuit/reagent/extinguisher - name = "integrated extinguisher" - desc = "This circuit sprays any of its contents out like an extinguisher." - icon_state = "injector" - extended_desc = "This circuit can hold up to 30 units of any given chemicals. On each use, it sprays these reagents like a fire extinguisher." - - volume = 30 - - complexity = 20 - cooldown_per_use = 6 SECONDS - inputs = list( - "target X rel" = IC_PINTYPE_NUMBER, - "target Y rel" = IC_PINTYPE_NUMBER - ) - outputs = list( - "volume" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_SELFREF - ) - activators = list( - "spray" = IC_PINTYPE_PULSE_IN, - "on sprayed" = IC_PINTYPE_PULSE_OUT, - "on fail" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_IN - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 15 - max_allowed = 2 - var/busy = FALSE - -/obj/item/integrated_circuit/reagent/extinguisher/Initialize() - .=..() - set_pin_data(IC_OUTPUT,2, src) - -/obj/item/integrated_circuit/reagent/extinguisher/on_reagent_change(changetype) - push_vol() - -/obj/item/integrated_circuit/reagent/extinguisher/do_work() - //Check if enough volume - set_pin_data(IC_OUTPUT, 1, reagents.total_volume) - if(!reagents || reagents.total_volume < 5 || busy) - push_data() - activate_pin(3) - return - - playsound(loc, 'sound/effects/extinguish.ogg', 75, 1, -3) - //Get the tile on which the water particle spawns - var/turf/Spawnpoint = get_turf(src) - if(!Spawnpoint) - push_data() - activate_pin(3) - return - - //Get direction and target turf for each water particle - var/turf/T = locate(Spawnpoint.x + get_pin_data(IC_INPUT, 1),Spawnpoint.y + get_pin_data(IC_INPUT, 2),Spawnpoint.z) - if(!T) - push_data() - activate_pin(3) - return - var/direction = get_dir(Spawnpoint, T) - var/turf/T1 = get_step(T,turn(direction, 90)) - var/turf/T2 = get_step(T,turn(direction, -90)) - var/list/the_targets = list(T,T1,T2) - busy = TRUE - - // Create list with particles and their targets - var/list/water_particles=list() - for(var/a=0, a<5, a++) - var/obj/effect/particle_effect/water/W = new /obj/effect/particle_effect/water(get_turf(src)) - water_particles[W] = pick(the_targets) - var/datum/reagents/R = new/datum/reagents(5) - W.reagents = R - R.my_atom = W - reagents.trans_to(W,1) - - //Make em move dat ass, hun - addtimer(CALLBACK(src, /obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles, water_particles), 2) - -//This whole proc is a loop -/obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles(var/list/particles, var/repetitions=0) - //Check if there's anything in here first - if(!particles || particles.len == 0) - return - // Second loop: Get all the water particles and make them move to their target - for(var/obj/effect/particle_effect/water/W in particles) - var/turf/my_target = particles[W] - if(!W) - continue - step_towards(W,my_target) - if(!W.reagents) - continue - W.reagents.reaction(get_turf(W)) - for(var/A in get_turf(W)) - W.reagents.reaction(A) - if(W.loc == my_target) - break - if(repetitions < 4) - repetitions++ //Can't have math operations in addtimer(CALLBACK()) - addtimer(CALLBACK(src, /obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles, particles, repetitions), 2) - else - push_data() - activate_pin(2) - busy = FALSE - - -// - Drain circuit - // -/obj/item/integrated_circuit/reagent/drain - name = "chemical drain circuit" - desc = "This circuit either eliminates reagents by creating a puddle or can suck up chemicals on tiles." - icon_state = "injector" - extended_desc = "Set mode to FALSE to eliminate reagents and TRUE to drain." - - volume = 20 - - complexity = 10 - inputs = list( - "mode" = IC_PINTYPE_BOOLEAN - ) - outputs = list( - "volume" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_SELFREF - ) - activators = list( - "drain" = IC_PINTYPE_PULSE_IN, - "on drained" = IC_PINTYPE_PULSE_OUT, - "on fail" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_IN - ) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 15 - - -/obj/item/integrated_circuit/reagent/drain/Initialize() - .=..() - set_pin_data(IC_OUTPUT,2, src) - - -/obj/item/integrated_circuit/reagent/drain/do_work() - if(get_pin_data(IC_OUTPUT, 1, reagents.total_volume)) - if(!reagents || !reagents.total_volume) - push_data() - activate_pin(3) - return - // Put the reagents on the floortile the assembly is on - reagents.reaction(get_turf(src)) - reagents.clear_reagents() - push_data() - activate_pin(2) - return - - else - if(reagents) - if(reagents.total_volume >= volume) - push_data() - activate_pin(3) - return - // Favorably, drain it from a chemicals pile, else, try something different - var/obj/effect/decal/cleanable/drainedchems = locate(/obj/effect/decal/cleanable) in get_turf(src) - if(!drainedchems || !drainedchems.reagents || drainedchems.reagents.total_volume == 0) - push_data() - activate_pin(3) - return - drainedchems.reagents.trans_to(src, 30, 0.5) - if(drainedchems.reagents.total_volume == 0) - qdel(drainedchems) - push_data() - activate_pin(2) - - -/obj/item/integrated_circuit/reagent/drain/on_reagent_change(changetype) - push_vol() - - -// - Beaker Connector - // -/obj/item/integrated_circuit/input/beaker_connector - category_text = "Reagent" - cooldown_per_use = 1 - name = "beaker slot" - desc = "Lets you add a beaker to your assembly and remove it even when the assembly is closed." - icon_state = "reagent_storage" - extended_desc = "It can help you extract reagents easier." - complexity = 4 - - inputs = list() - outputs = list( - "volume used" = IC_PINTYPE_NUMBER, - "current beaker" = IC_PINTYPE_REF - ) - activators = list( - "on insert" = IC_PINTYPE_PULSE_OUT, - "on remove" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_OUT - ) - - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - can_be_asked_input = TRUE - demands_object_input = TRUE - can_input_object_when_closed = TRUE - - var/obj/item/reagent_containers/glass/beaker/current_beaker - - -/obj/item/integrated_circuit/input/beaker_connector/attackby(var/obj/item/reagent_containers/I, var/mob/living/user) - //Check if it truly is a reagent container - if(!istype(I,/obj/item/reagent_containers/glass/beaker)) - to_chat(user,"The [I.name] doesn't seem to fit in here.") - return - - //Check if there is no other beaker already inside - if(current_beaker) - to_chat(user,"There is already a reagent container inside.") - return - - //The current beaker is the one we just attached, its location is inside the circuit - current_beaker = I - user.transferItemToLoc(I,src) - - to_chat(user,"You put the [I.name] inside the beaker connector.") - - //Set the pin to a weak reference of the current beaker - push_vol() - set_pin_data(IC_OUTPUT, 2, WEAKREF(current_beaker)) - push_data() - activate_pin(1) - activate_pin(3) - - -/obj/item/integrated_circuit/input/beaker_connector/ask_for_input(mob/user) - attack_self(user) - - -/obj/item/integrated_circuit/input/beaker_connector/attack_self(mob/user) - //Check if no beaker attached - if(!current_beaker) - to_chat(user, "There is currently no beaker attached.") - return - - //Remove beaker and put in user's hands/location - to_chat(user, "You take [current_beaker] out of the beaker connector.") - user.put_in_hands(current_beaker) - current_beaker = null - //Remove beaker reference - push_vol() - set_pin_data(IC_OUTPUT, 2, null) - push_data() - activate_pin(2) - activate_pin(3) - - -/obj/item/integrated_circuit/input/beaker_connector/proc/push_vol() - var/beakerVolume = 0 - if(current_beaker) - beakerVolume = current_beaker.reagents.total_volume - - set_pin_data(IC_OUTPUT, 1, beakerVolume) - push_data() - - -/obj/item/reagent_containers/glass/beaker/on_reagent_change() - ..() - if(istype(loc,/obj/item/integrated_circuit/input/beaker_connector)) - var/obj/item/integrated_circuit/input/beaker_connector/current_circuit = loc - current_circuit.push_vol() diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm deleted file mode 100644 index 36b0e2ffa3fe9..0000000000000 --- a/code/modules/integrated_electronics/subtypes/smart.dm +++ /dev/null @@ -1,370 +0,0 @@ -/obj/item/integrated_circuit/smart - category_text = "Smart" - -/obj/item/integrated_circuit/smart/basic_pathfinder - name = "basic pathfinder" - desc = "This complex circuit is able to determine what direction a given target is." - extended_desc = "This circuit uses a miniturized integrated camera to determine where the target is. If the machine \ - cannot see the target, it will not be able to calculate the correct direction." - icon_state = "numberpad" - complexity = 5 - inputs = list("target" = IC_PINTYPE_REF,"ignore obstacles" = IC_PINTYPE_BOOLEAN) - outputs = list("dir" = IC_PINTYPE_DIR) - activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 40 - -/obj/item/integrated_circuit/smart/basic_pathfinder/do_work() - var/datum/integrated_io/I = inputs[1] - set_pin_data(IC_OUTPUT, 1, null) - if(!isweakref(I.data)) - activate_pin(3) - return - var/atom/A = I.data.resolve() - if(!A) - activate_pin(3) - return - if(!(A in view(get_turf(src)))) - push_data() - activate_pin(3) - return // Can't see the target. - - if(get_pin_data(IC_INPUT, 2)) - set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_turf(A))) - else - set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_step_towards2(get_turf(src),A))) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/smart/coord_basic_pathfinder - name = "coordinate pathfinder" - desc = "This complex circuit is able to determine what direction a given target is." - extended_desc = "This circuit uses absolute coordinates to determine where the target is. If the machine \ - cannot see the target, it will not be able to calculate the correct direction. \ - This circuit will only work while inside an assembly." - icon_state = "numberpad" - complexity = 5 - inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER,"ignore obstacles" = IC_PINTYPE_BOOLEAN) - outputs = list( "dir" = IC_PINTYPE_DIR, - "distance" = IC_PINTYPE_NUMBER - ) - activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 40 - -/obj/item/integrated_circuit/smart/coord_basic_pathfinder/do_work() - if(!assembly) - activate_pin(3) - return - var/turf/T = get_turf(assembly) - var/target_x = CLAMP(get_pin_data(IC_INPUT, 1), 0, world.maxx) - var/target_y = CLAMP(get_pin_data(IC_INPUT, 2), 0, world.maxy) - var/turf/A = locate(target_x, target_y, T.z) - set_pin_data(IC_OUTPUT, 1, null) - if(!A||A==T) - activate_pin(3) - return - if(get_pin_data(IC_INPUT, 2)) - set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_turf(A))) - else - set_pin_data(IC_OUTPUT, 1, get_dir(get_turf(src), get_step_towards2(get_turf(src),A))) - set_pin_data(IC_OUTPUT, 2, sqrt((A.x-T.x)*(A.x-T.x)+ (A.y-T.y)*(A.y-T.y))) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/smart/advanced_pathfinder - name = "advanced pathfinder" - desc = "This circuit uses a complex processor for long-range pathfinding." - extended_desc = "This circuit uses absolute coordinates to find its target. A path will be generated to the target, taking obstacles into account, \ - and pathing around any instances of said input. The passkey provided from a card reader is used to calculate a valid path through airlocks." - icon_state = "numberpad" - complexity = 40 - cooldown_per_use = 50 - inputs = list("X target" = IC_PINTYPE_NUMBER,"Y target" = IC_PINTYPE_NUMBER,"obstacle" = IC_PINTYPE_REF,"access" = IC_PINTYPE_STRING) - outputs = list("X" = IC_PINTYPE_LIST,"Y" = IC_PINTYPE_LIST) - activators = list("calculate path" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 80 - var/obj/item/card/id/idc - -/obj/item/integrated_circuit/smart/advanced_pathfinder/Initialize() - .=..() - idc = new(src) - -/obj/item/integrated_circuit/smart/advanced_pathfinder/do_work() - if(!assembly) - activate_pin(3) - return - //idc.access = assembly.access_card.access // hippie start -- readded xor decryption - hippie_xor_decrypt() // hippie end - var/turf/a_loc = get_turf(assembly) - var/list/P = cir_get_path_to(assembly, locate(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2),a_loc.z), /turf/proc/Distance_cardinal, 0, 200, id=idc, exclude=get_turf(get_pin_data_as_type(IC_INPUT,3, /atom)), simulated_only = 0) - - if(!P) - activate_pin(3) - return - - var/list/Xn = new/list(P.len) - var/list/Yn = new/list(P.len) - var/turf/T - for(var/i =1 to P.len) - T=P[i] - Xn[i] = T.x - Yn[i] = T.y - set_pin_data(IC_OUTPUT, 1, Xn) - set_pin_data(IC_OUTPUT, 2, Yn) - push_data() - activate_pin(2) - - -// - MMI Tank - // -/obj/item/integrated_circuit/input/mmi_tank - name = "man-machine interface tank" - desc = "This circuit is just a jar filled with an artificial liquid mimicking the cerebrospinal fluid." - extended_desc = "This jar can hold 1 man-machine interface and let it take control of some basic functions of the assembly." - complexity = 29 - inputs = list("laws" = IC_PINTYPE_LIST) - outputs = list( - "man-machine interface" = IC_PINTYPE_REF, - "direction" = IC_PINTYPE_DIR, - "click target" = IC_PINTYPE_REF - ) - activators = list( - "move" = IC_PINTYPE_PULSE_OUT, - "left" = IC_PINTYPE_PULSE_OUT, - "right" = IC_PINTYPE_PULSE_OUT, - "up" = IC_PINTYPE_PULSE_OUT, - "down" = IC_PINTYPE_PULSE_OUT, - "leftclick" = IC_PINTYPE_PULSE_OUT, - "shiftclick" = IC_PINTYPE_PULSE_OUT, - "altclick" = IC_PINTYPE_PULSE_OUT, - "ctrlclick" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 150 - can_be_asked_input = TRUE - demands_object_input = TRUE - - var/obj/item/mmi/installed_brain - -/obj/item/integrated_circuit/input/mmi_tank/attackby(var/obj/item/mmi/O, var/mob/user) - if(!istype(O,/obj/item/mmi)) - to_chat(user,"You can't put that inside.") - return - if(installed_brain) - to_chat(user,"There's already a brain inside.") - return - user.transferItemToLoc(O,src) - installed_brain = O - can_be_asked_input = FALSE - to_chat(user, "You gently place \the man-machine interface inside the tank.") - to_chat(O, "You are slowly being placed inside the man-machine-interface tank.") - O.brainmob.remote_control=src - set_pin_data(IC_OUTPUT, 1, O) - -/obj/item/integrated_circuit/input/mmi_tank/attack_self(var/mob/user) - if(installed_brain) - RemoveBrain() - to_chat(user, "You slowly lift [installed_brain] out of the MMI tank.") - playsound(src, 'sound/items/Crowbar.ogg', 50, 1) - installed_brain = null - push_data() - else - to_chat(user, "You don't see any brain swimming in the tank.") - -/obj/item/integrated_circuit/input/mmi_tank/Destroy() - RemoveBrain() - ..() - -/obj/item/integrated_circuit/input/mmi_tank/relaymove(var/n,var/dir) - set_pin_data(IC_OUTPUT, 2, dir) - do_work(1) - switch(dir) - if(8) activate_pin(2) - if(4) activate_pin(3) - if(1) activate_pin(4) - if(2) activate_pin(5) - -/obj/item/integrated_circuit/input/mmi_tank/do_work(var/n) - push_data() - activate_pin(n) - -/obj/item/integrated_circuit/input/mmi_tank/proc/RemoveBrain() - if(installed_brain) - can_be_asked_input = TRUE - installed_brain.forceMove(drop_location()) - set_pin_data(IC_OUTPUT, 1, WEAKREF(null)) - if(installed_brain.brainmob) - installed_brain.brainmob.remote_control = null - - -//Brain changes -/mob/living/brain/var/check_bot_self = FALSE - -/mob/living/brain/ClickOn(atom/A, params) - ..() - if(!istype(remote_control,/obj/item/integrated_circuit/input/mmi_tank)) - return - var/obj/item/integrated_circuit/input/mmi_tank/brainholder=remote_control - brainholder.set_pin_data(IC_OUTPUT, 3, A) - var/list/modifiers = params2list(params) - - if(modifiers["shift"]) - brainholder.do_work(7) - return - if(modifiers["alt"]) - brainholder.do_work(8) - return - if(modifiers["ctrl"]) - brainholder.do_work(9) - return - - if(istype(A,/obj/item/electronic_assembly)) - var/obj/item/electronic_assembly/CheckedAssembly = A - - if(brainholder in CheckedAssembly.assembly_components) - var/obj/item/electronic_assembly/holdingassembly=A - check_bot_self=TRUE - - if(holdingassembly.opened) - holdingassembly.ui_interact(src) - holdingassembly.attack_self(src) - check_bot_self=FALSE - return - - brainholder.do_work(6) - -/mob/living/brain/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) - return check_bot_self - -/obj/item/integrated_circuit/smart/advanced_pathfinder/proc/hippie_xor_decrypt() - var/Ps = get_pin_data(IC_INPUT, 4) - if(!Ps) - return - var/list/Pl = json_decode(XorEncrypt(hextostr(Ps, TRUE), SScircuit.cipherkey)) - if(Pl&&islist(Pl)) - idc.access = Pl - -// - pAI connector circuit - // -/obj/item/integrated_circuit/input/pAI_connector - name = "pAI connector circuit" - desc = "This circuit lets you fit in a personal artificial intelligence to give it some form of control over the bot." - extended_desc = "You can wire various functions to it." - complexity = 29 - inputs = list("laws" = IC_PINTYPE_LIST) - outputs = list( - "personal artificial intelligence" = IC_PINTYPE_REF, - "direction" = IC_PINTYPE_DIR, - "click target" = IC_PINTYPE_REF - ) - activators = list( - "move" = IC_PINTYPE_PULSE_OUT, - "left" = IC_PINTYPE_PULSE_OUT, - "right" = IC_PINTYPE_PULSE_OUT, - "up" = IC_PINTYPE_PULSE_OUT, - "down" = IC_PINTYPE_PULSE_OUT, - "leftclick" = IC_PINTYPE_PULSE_OUT, - "shiftclick" = IC_PINTYPE_PULSE_OUT, - "altclick" = IC_PINTYPE_PULSE_OUT, - "ctrlclick" = IC_PINTYPE_PULSE_OUT, - "shiftctrlclick" = IC_PINTYPE_PULSE_OUT - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 150 - can_be_asked_input = TRUE - demands_object_input = TRUE - - var/obj/item/paicard/installed_pai - -/obj/item/integrated_circuit/input/pAI_connector/attackby(var/obj/item/paicard/O, var/mob/user) - if(!istype(O,/obj/item/paicard)) - to_chat(user,"You can't put that inside.") - return - if(installed_pai) - to_chat(user,"There's already a pAI connected to this.") - return - user.transferItemToLoc(O,src) - installed_pai = O - can_be_asked_input = FALSE - to_chat(user, "You slowly connect the circuit's pins to the [installed_pai].") - to_chat(O, "You are slowly being connected to the pAI connector.") - O.pai.remote_control=src - set_pin_data(IC_OUTPUT, 1, O) - -/obj/item/integrated_circuit/input/pAI_connector/attack_self(var/mob/user) - if(installed_pai) - RemovepAI() - to_chat(user, "You slowly disconnect the circuit's pins from the [installed_pai].") - playsound(src, 'sound/items/Crowbar.ogg', 50, 1) - installed_pai = null - push_data() - else - to_chat(user, "The connection port is empty.") - -/obj/item/integrated_circuit/input/pAI_connector/relaymove(var/n,var/dir) - set_pin_data(IC_OUTPUT, 2, dir) - do_work(1) - switch(dir) - if(8) activate_pin(2) - if(4) activate_pin(3) - if(1) activate_pin(4) - if(2) activate_pin(5) - -/obj/item/integrated_circuit/input/pAI_connector/do_work(var/n) - push_data() - activate_pin(n) - - -/obj/item/integrated_circuit/input/pAI_connector/Destroy() - RemovepAI() - ..() - -/obj/item/integrated_circuit/input/pAI_connector/proc/RemovepAI() - if(installed_pai) - can_be_asked_input = TRUE - installed_pai.forceMove(drop_location()) - set_pin_data(IC_OUTPUT, 1, WEAKREF(null)) - installed_pai.pai.remote_control = null - - -//pAI changes -/mob/living/silicon/pai/var/check_bot_self = FALSE - -/mob/living/silicon/pai/ClickOn(atom/A, params) - ..() - if(!istype(remote_control,/obj/item/integrated_circuit/input/pAI_connector)) - return - var/obj/item/integrated_circuit/input/pAI_connector/paiholder=remote_control - paiholder.set_pin_data(IC_OUTPUT, 3, A) - var/list/modifiers = params2list(params) - - if(modifiers["shift"] && modifiers["ctrl"]) - paiholder.do_work(10) - return - if(modifiers["shift"]) - paiholder.do_work(7) - return - if(modifiers["alt"]) - paiholder.do_work(8) - return - if(modifiers["ctrl"]) - paiholder.do_work(9) - return - - if(istype(A,/obj/item/electronic_assembly)) - var/obj/item/electronic_assembly/CheckedAssembly = A - - if(paiholder in CheckedAssembly.assembly_components) - var/obj/item/electronic_assembly/holdingassembly=A - check_bot_self=TRUE - - if(holdingassembly.opened) - holdingassembly.ui_interact(src) - holdingassembly.attack_self(src) - check_bot_self=FALSE - return - - paiholder.do_work(6) - -/mob/living/silicon/pai/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) - return check_bot_self diff --git a/code/modules/integrated_electronics/subtypes/text.dm b/code/modules/integrated_electronics/subtypes/text.dm deleted file mode 100644 index 6b3041e2c5b77..0000000000000 --- a/code/modules/integrated_electronics/subtypes/text.dm +++ /dev/null @@ -1,271 +0,0 @@ -/obj/item/integrated_circuit/text - name = "text thingy" - desc = "Does text-processing related things." - category_text = "Text" - complexity = 1 - - -/obj/item/integrated_circuit/text/lowercase - name = "lowercase string converter" - desc = "this circuit will cause a string to come out in all lowercase." - icon_state = "lowercase" - inputs = list("input" = IC_PINTYPE_STRING) - outputs = list("output" = IC_PINTYPE_STRING) - activators = list("to lowercase" = IC_PINTYPE_PULSE_IN, "on converted" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/text/lowercase/do_work() - var/result = null - pull_data() - var/incoming = get_pin_data(IC_INPUT, 1) - if(!isnull(incoming)) - result = lowertext(incoming) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/text/uppercase - name = "uppercase string converter" - desc = "THIS WILL CAUSE A STRING TO COME OUT IN ALL UPPERCASE." - icon_state = "uppercase" - inputs = list("input" = IC_PINTYPE_STRING) - outputs = list("output" = IC_PINTYPE_STRING) - activators = list("to uppercase" = IC_PINTYPE_PULSE_IN, "on converted" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/text/uppercase/do_work() - var/result = null - pull_data() - var/incoming = get_pin_data(IC_INPUT, 1) - if(!isnull(incoming)) - result = uppertext(incoming) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/text/concatenator - name = "concatenator" - desc = "This can join up to 8 strings together to get a string with a maximum of 512 characters." - complexity = 4 - inputs = list() - outputs = list("result" = IC_PINTYPE_STRING) - activators = list("concatenate" = IC_PINTYPE_PULSE_IN, "on concatenated" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - var/number_of_pins = 8 - var/max_string_length = 512 - -/obj/item/integrated_circuit/text/concatenator/Initialize() - for(var/i = 1 to number_of_pins) - inputs["input [i]"] = IC_PINTYPE_STRING - . = ..() - -/obj/item/integrated_circuit/text/concatenator/do_work() - var/result = null - var/spamprotection - for(var/k in 1 to inputs.len) - var/I = get_pin_data(IC_INPUT, k) - if(!isnull(I)) - if((result ? length(result) : 0) + length(I) > max_string_length) - spamprotection = (result ? length(result) : 0) + length(I) - break - result = result + I - - if(spamprotection >= max_string_length*1.75 && assembly) - if(assembly.fingerprintslast) - var/mob/M = get_mob_by_key(assembly.fingerprintslast) - var/more = "" - if(M) - more = "[ADMIN_LOOKUPFLW(M)] " - message_admins("A concatenator circuit has greatly exceeded its [max_string_length] character limit with a total of [spamprotection] characters, and has been deleted. Assembly last touched by [more ? more : assembly.fingerprintslast].") - investigate_log("A concatenator circuit has greatly exceeded its [max_string_length] character limit with a total of [spamprotection] characters, and has been deleted. Assembly last touched by [assembly.fingerprintslast].", INVESTIGATE_CIRCUIT) - else - message_admins("A concatenator circuit has greatly exceeded its [max_string_length] character limit with a total of [spamprotection] characters, and has been deleted. No associated key.") - investigate_log("A concatenator circuit has greatly exceeded its [max_string_length] character limit with a total of [spamprotection] characters, and has been deleted. No associated key.", INVESTIGATE_CIRCUIT) - qdel(assembly) - return - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/text/concatenator/small - name = "small concatenator" - desc = "This can join up to 4 strings together to get a string with a maximum of 256 characters." - complexity = 2 - number_of_pins = 4 - max_string_length = 256 - -/obj/item/integrated_circuit/text/concatenator/large - name = "large concatenator" - desc = "This can join up to 16 strings together to get a string with a maximum of 1024 characters." - complexity = 6 - number_of_pins = 16 - max_string_length = 1024 - -/obj/item/integrated_circuit/text/separator - name = "separator" - desc = "This splits a single string into two at the relative split point." - extended_desc = "This circuit splits a given string into two, based on the string and the index value. \ - The index splits the string after the given index, including spaces. So 'a person' with an index of '3' \ - will split into 'a p' and 'erson'." - icon_state = "split" - complexity = 4 - inputs = list( - "string to split" = IC_PINTYPE_STRING, - "index" = IC_PINTYPE_NUMBER, - ) - outputs = list( - "before split" = IC_PINTYPE_STRING, - "after split" = IC_PINTYPE_STRING - ) - activators = list("separate" = IC_PINTYPE_PULSE_IN, "on separated" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/text/separator/do_work() - var/text = get_pin_data(IC_INPUT, 1) - var/index = get_pin_data(IC_INPUT, 2) - - var/split = min(index+1, length(text)) - - var/before_text = copytext(text, 1, split) - var/after_text = copytext(text, split, 0) - - set_pin_data(IC_OUTPUT, 1, before_text) - set_pin_data(IC_OUTPUT, 2, after_text) - push_data() - - activate_pin(2) - -/obj/item/integrated_circuit/text/indexer - name = "indexer" - desc = "This circuit takes a string and an index value, then returns the character found at in the string at the given index." - extended_desc = "Make sure the index is not longer or shorter than the string length. If you don't, the circuit will return empty." - icon_state = "split" - complexity = 4 - inputs = list( - "string to index" = IC_PINTYPE_STRING, - "index" = IC_PINTYPE_NUMBER, - ) - outputs = list( - "found character" = IC_PINTYPE_STRING - ) - activators = list("index" = IC_PINTYPE_PULSE_IN, "on indexed" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/text/indexer/do_work() - var/strin = get_pin_data(IC_INPUT, 1) - var/ind = get_pin_data(IC_INPUT, 2) - if(ind > 0 && ind <= length(strin)) - set_pin_data(IC_OUTPUT, 1, strin[ind]) - else - set_pin_data(IC_OUTPUT, 1, "") - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/text/findstring - name = "find text" - desc = "This outputs the position of the sample in the string, or returns 0." - extended_desc = "The first pin is the string to be examined. The second pin is the sample to be found. \ - For example, inputting 'my wife has caught on fire' with 'has' as the sample will give you position 9. \ - This circuit isn't case sensitive, and it does not ignore spaces." - complexity = 4 - inputs = list( - "string" = IC_PINTYPE_STRING, - "sample" = IC_PINTYPE_STRING, - ) - outputs = list( - "position" = IC_PINTYPE_NUMBER - ) - activators = list("search" = IC_PINTYPE_PULSE_IN, "after search" = IC_PINTYPE_PULSE_OUT, "found" = IC_PINTYPE_PULSE_OUT, "not found" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - - -/obj/item/integrated_circuit/text/findstring/do_work() - var/position = findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2)) - - set_pin_data(IC_OUTPUT, 1, position) - push_data() - - activate_pin(2) - if(position) - activate_pin(3) - else - activate_pin(4) - -/obj/item/integrated_circuit/text/stringlength - name = "get length" - desc = "This circuit will return the number of characters in a string." - complexity = 1 - inputs = list( - "string" = IC_PINTYPE_STRING - ) - outputs = list( - "length" = IC_PINTYPE_NUMBER - ) - activators = list("get length" = IC_PINTYPE_PULSE_IN, "on acquisition" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/text/stringlength/do_work() - set_pin_data(IC_OUTPUT, 1, length(get_pin_data(IC_INPUT, 1))) - push_data() - - activate_pin(2) - -/obj/item/integrated_circuit/text/exploders - name = "string exploder" - desc = "This splits a single string into a list of strings." - extended_desc = "This circuit splits a given string into a list of strings based on the string and given delimiter. \ - For example, 'eat this burger' will be converted to list('eat','this','burger'). Leave the delimiter null to get a list \ - of every individual character." - icon_state = "split" - complexity = 4 - inputs = list( - "string to split" = IC_PINTYPE_STRING, - "delimiter" = IC_PINTYPE_STRING, - ) - outputs = list( - "list" = IC_PINTYPE_LIST - ) - activators = list("separate" = IC_PINTYPE_PULSE_IN, "on separated" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/text/exploders/do_work() - var/strin = get_pin_data(IC_INPUT, 1) - var/delimiter = get_pin_data(IC_INPUT, 2) - if(delimiter == null) - set_pin_data(IC_OUTPUT, 1, string2charlist(strin)) - else - set_pin_data(IC_OUTPUT, 1, splittext(strin, delimiter)) - push_data() - - activate_pin(2) - - -// - Text Replacer - // -/obj/item/integrated_circuit/text/text_replacer - name = "replace circuit" - desc = "Replaces all of one bit of text with another" - extended_desc = "Takes a string(haystack) and puts out the string while having a certain word(needle) replaced with another. Maximum 512 characters." - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - inputs = list( - "haystack" = IC_PINTYPE_STRING, - "needle" = IC_PINTYPE_STRING, - "replacement" = IC_PINTYPE_STRING - ) - activators = list( - "replace" = IC_PINTYPE_PULSE_IN, - "on replaced" = IC_PINTYPE_PULSE_OUT - ) - outputs = list( - "replaced string" = IC_PINTYPE_STRING - ) - cooldown_per_use = (10 SECONDS) - complexity = 6 - var/max_len = MAX_MESSAGE_LEN / 2 - -/obj/item/integrated_circuit/text/text_replacer/do_work() - set_pin_data(IC_OUTPUT, 1, replacetext(copytext(get_pin_data(IC_INPUT, 1), 1, max_len), copytext(get_pin_data(IC_INPUT, 2), 1, max_len), copytext(get_pin_data(IC_INPUT, 3), 1, max_len))) - push_data() - activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm deleted file mode 100644 index 5a98650676ac6..0000000000000 --- a/code/modules/integrated_electronics/subtypes/time.dm +++ /dev/null @@ -1,187 +0,0 @@ -/obj/item/integrated_circuit/time - name = "time circuit" - desc = "Now you can build your own clock!" - complexity = 1 - inputs = list() - outputs = list() - category_text = "Time" - -/obj/item/integrated_circuit/time/delay - name = "two-sec delay circuit" - desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ - This circuit is set to send a pulse after a delay of two seconds." - icon_state = "delay-20" - var/delay = 2 SECONDS - activators = list("incoming"= IC_PINTYPE_PULSE_IN,"outgoing" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 2 - -/obj/item/integrated_circuit/time/delay/do_work() - addtimer(CALLBACK(src, .proc/activate_pin, 2), delay) - -/obj/item/integrated_circuit/time/delay/five_sec - name = "five-sec delay circuit" - desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ - This circuit is set to send a pulse after a delay of five seconds." - icon_state = "delay-50" - delay = 5 SECONDS - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/time/delay/one_sec - name = "one-sec delay circuit" - desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ - This circuit is set to send a pulse after a delay of one second." - icon_state = "delay-10" - delay = 1 SECONDS - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/time/delay/half_sec - name = "half-sec delay circuit" - desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ - This circuit is set to send a pulse after a delay of half a second." - icon_state = "delay-5" - delay = 0.5 SECONDS - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/time/delay/tenth_sec - name = "tenth-sec delay circuit" - desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \ - This circuit is set to send a pulse after a delay of 1/10th of a second." - icon_state = "delay-1" - delay = 0.1 SECONDS - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/time/delay/custom - name = "custom delay circuit" - desc = "This sends a pulse signal out after a delay defined in tenths of a second, critical for ensuring proper control \ - flow in a complex machine. This circuit's delay can be customized, between 1/10th of a second to one hour. \ - The delay is updated upon receiving a pulse." - extended_desc = "The delay is defined in tenths of a second. For instance, 4 will be a delay of 0.4 seconds, or 15 for 1.5 seconds." - icon_state = "delay" - inputs = list("delay time" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/time/delay/custom/on_data_written() - ..() - var/delay_input = get_pin_data(IC_INPUT, 1) - if(!isnum_safe(delay_input)) - return - if(delay_input < 1 || delay_input > 1 HOURS) // Value had to be clamped, update the pin. Check's here to avoid infinitely setting the pin. - set_pin_data(IC_INPUT, 1, CLAMP(delay_input ,1 ,1 HOURS)) - return - delay = delay_input - -/obj/item/integrated_circuit/time/ticker - name = "ticker circuit" - desc = "This circuit sends an automatic pulse every four seconds." - icon_state = "tick-m" - complexity = 4 - var/delay = 4 SECONDS - var/next_fire = 0 - var/is_running = FALSE - inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN) - activators = list("outgoing pulse" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 4 - -/obj/item/integrated_circuit/time/ticker/Destroy() - STOP_PROCESSING(SSfastprocess, src) - return ..() - -/obj/item/integrated_circuit/time/ticker/on_data_written() - var/do_tick = get_pin_data(IC_INPUT, 1) - if(do_tick && !is_running) - is_running = TRUE - START_PROCESSING(SSfastprocess, src) - else if(!do_tick && is_running) - is_running = FALSE - STOP_PROCESSING(SSfastprocess, src) - -/obj/item/integrated_circuit/time/ticker/process() - if(world.time > next_fire) - next_fire = world.time + delay - activate_pin(1) - - -/obj/item/integrated_circuit/time/ticker/custom - name = "custom ticker" - desc = "This advanced circuit sends an automatic pulse every given interval, defined in tenths of a second." - extended_desc ="This advanced circuit sends an automatic pulse every given interval, defined in tenths of a second. \ - For example, setting the time pin to 4 will send a pulse every 0.4 seconds, or 15 for every 1.5 seconds." - icon_state = "tick-f" - complexity = 8 - delay = 2 SECONDS - inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN,"delay time" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 8 - -/obj/item/integrated_circuit/time/ticker/custom/on_data_written() - ..() - var/delay_input = get_pin_data(IC_INPUT, 2) - if(!isnum_safe(delay_input)) - return - if(delay_input < 1 || delay_input > 1 HOURS) - set_pin_data(IC_INPUT, 2, CLAMP(delay_input ,1 ,1 HOURS)) - return - delay = delay_input - -/obj/item/integrated_circuit/time/ticker/fast - name = "fast ticker" - desc = "This advanced circuit sends an automatic pulse every two seconds." - icon_state = "tick-f" - complexity = 6 - delay = 2 SECONDS - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 8 - -/obj/item/integrated_circuit/time/ticker/slow - name = "slow ticker" - desc = "This simple circuit sends an automatic pulse every six seconds." - icon_state = "tick-s" - complexity = 2 - delay = 6 SECONDS - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 2 - -/obj/item/integrated_circuit/time/clock - name = "integrated clock (NT Common Time)" - desc = "Tells you what the time is, in Nanotrasen Common Time." //round time - icon_state = "clock" - inputs = list() - outputs = list( - "time" = IC_PINTYPE_STRING, - "hours" = IC_PINTYPE_NUMBER, - "minutes" = IC_PINTYPE_NUMBER, - "seconds" = IC_PINTYPE_NUMBER, - "absolute decisecond elapsed time" = IC_PINTYPE_NUMBER - ) - activators = list("get time" = IC_PINTYPE_PULSE_IN, "on time got" = IC_PINTYPE_PULSE_OUT) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 2 - -/obj/item/integrated_circuit/time/clock/proc/get_time() - return world.time - -/obj/item/integrated_circuit/time/clock/do_work() - var/current_time = get_time() - set_pin_data(IC_OUTPUT, 1, time2text(current_time, "hh:mm:ss") ) - set_pin_data(IC_OUTPUT, 2, text2num(time2text(current_time, "hh") ) ) - set_pin_data(IC_OUTPUT, 3, text2num(time2text(current_time, "mm") ) ) - set_pin_data(IC_OUTPUT, 4, text2num(time2text(current_time, "ss") ) ) - set_pin_data(IC_OUTPUT, 5, current_time) - push_data() - activate_pin(2) - -/obj/item/integrated_circuit/time/clock/station - name = "integrated clock (Station Time)" - desc = "Tells you what the time is, in terms and adjusted for your local station or planet" - -/obj/item/integrated_circuit/time/clock/station/get_time() - return station_time() - -/obj/item/integrated_circuit/time/clock/bluespace - name = "integrated clock (Bluespace Absolute Time)" - desc = "Tells you what the time is, in Bluespace Absolute Time, unaffected by local time dilation or other phenomenon." - -/obj/item/integrated_circuit/time/clock/bluespace/get_time() - return REALTIMEOFDAY diff --git a/code/modules/integrated_electronics/subtypes/trig.dm b/code/modules/integrated_electronics/subtypes/trig.dm deleted file mode 100644 index cefa25e945193..0000000000000 --- a/code/modules/integrated_electronics/subtypes/trig.dm +++ /dev/null @@ -1,138 +0,0 @@ -//These circuits do not-so-simple math. -/obj/item/integrated_circuit/trig - complexity = 1 - inputs = list( - "A" = IC_PINTYPE_NUMBER, - "B" = IC_PINTYPE_NUMBER, - "C" = IC_PINTYPE_NUMBER, - "D" = IC_PINTYPE_NUMBER, - "E" = IC_PINTYPE_NUMBER, - "F" = IC_PINTYPE_NUMBER, - "G" = IC_PINTYPE_NUMBER, - "H" = IC_PINTYPE_NUMBER - ) - outputs = list("result" = IC_PINTYPE_NUMBER) - activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT) - category_text = "Trig" - extended_desc = "Input and output are in degrees." - power_draw_per_use = 1 // Still cheap math. - -// Sine // - -/obj/item/integrated_circuit/trig/sine - name = "sin circuit" - desc = "Only evil if you're allergic to math. Takes a degree and outputs the sine of said degree." - icon_state = "sine" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/trig/sine/do_work() - pull_data() - var/result = null - var/A = get_pin_data(IC_INPUT, 1) - if(!isnull(A)) - result = sin(A) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Cosine // - -/obj/item/integrated_circuit/trig/cosine - name = "cos circuit" - desc = "Takes a degree and outputs the cosine of said degree." - icon_state = "cosine" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/trig/cosine/do_work() - pull_data() - var/result = null - var/A = get_pin_data(IC_INPUT, 1) - if(!isnull(A)) - result = cos(A) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Tangent // - -/obj/item/integrated_circuit/trig/tangent - name = "tan circuit" - desc = "Takes a degree and outputs the tangent of said degree." - icon_state = "tangent" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/trig/tangent/do_work() - pull_data() - var/result = null - var/A = get_pin_data(IC_INPUT, 1) - if(!isnull(A)) - result = TAN(A) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Cosecant // - -/obj/item/integrated_circuit/trig/cosecant - name = "cosecant (CSC) circuit" - desc = "Takes a degree and outputs the cosecant of said degree." - icon_state = "cosecant" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/trig/cosecant/do_work() - pull_data() - var/result = null - var/A = get_pin_data(IC_INPUT, 1) - if(!isnull(A)) - result = CSC(A) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Secant // - -/obj/item/integrated_circuit/trig/secant - name = "secant (SEC) circuit" - desc = "Takes a degree and outputs the secant of said degree." - icon_state = "secant" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/trig/secant/do_work() - pull_data() - var/result = null - var/A = get_pin_data(IC_INPUT, 1) - if(!isnull(A)) - result = SEC(A) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) - -// Cotangent // - -/obj/item/integrated_circuit/trig/cotangent - name = "cotangent (COT) circuit" - desc = "Takes a degree and outputs the cotangent of said degree." - icon_state = "cotangent" - inputs = list("A" = IC_PINTYPE_NUMBER) - spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - -/obj/item/integrated_circuit/trig/cotangent/do_work() - pull_data() - var/result = null - var/A = get_pin_data(IC_INPUT, 1) - if(!isnull(A)) - result = COT(A) - - set_pin_data(IC_OUTPUT, 1, result) - push_data() - activate_pin(2) diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm index 6e9e9a70b20d1..8f7a7f6da009c 100644 --- a/code/modules/jobs/access.dm +++ b/code/modules/jobs/access.dm @@ -129,21 +129,21 @@ return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE) /proc/get_all_accesses() - return list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, + return list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_SEC_RECORDS, ACCESS_BRIG, ACCESS_BRIGPHYS, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_MEDICAL, ACCESS_GENETICS, ACCESS_MORGUE, ACCESS_RD, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_CHEMISTRY, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_HEADS, ACCESS_CAPTAIN, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_TECH_STORAGE, ACCESS_CHAPEL_OFFICE, ACCESS_ATMOSPHERICS, ACCESS_KITCHEN, - ACCESS_BAR, ACCESS_JANITOR, ACCESS_CREMATORIUM, ACCESS_ROBOTICS, ACCESS_CARGO, ACCESS_CONSTRUCTION, - ACCESS_HYDROPONICS, ACCESS_LIBRARY, ACCESS_LAWYER, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_QM, ACCESS_SURGERY, + ACCESS_BAR, ACCESS_JANITOR, ACCESS_CREMATORIUM, ACCESS_ROBOTICS, ACCESS_CARGO, ACCESS_CONSTRUCTION, ACCESS_AUX_BASE, + ACCESS_HYDROPONICS, ACCESS_LIBRARY, ACCESS_LAWYER, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_QM, ACCESS_EXPLORATION, ACCESS_SURGERY, ACCESS_THEATRE, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_MAILSORTING, ACCESS_WEAPONS, ACCESS_MECH_MINING, ACCESS_MECH_ENGINE, ACCESS_MECH_SCIENCE, ACCESS_MECH_SECURITY, ACCESS_MECH_MEDICAL, ACCESS_VAULT, ACCESS_MINING_STATION, ACCESS_XENOBIOLOGY, ACCESS_CE, ACCESS_HOP, ACCESS_HOS, ACCESS_APOTHECARY, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_MINERAL_STOREROOM, ACCESS_MINISAT, ACCESS_NETWORK, ACCESS_CLONING) /proc/get_all_centcom_access() - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE, ACCESS_CENT_TELEPORTER, ACCESS_CENT_CAPTAIN) + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_LIVING, ACCESS_CENT_STORAGE, ACCESS_CENT_TELEPORTER, ACCESS_CENT_CAPTAIN, ACCESS_CENT_BAR) /proc/get_ert_access(class) switch(class) @@ -159,6 +159,12 @@ /proc/get_all_syndicate_access() return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) +/proc/get_all_away_access() + return list(ACCESS_AWAY_GENERAL, ACCESS_AWAY_MAINT, ACCESS_AWAY_MED, ACCESS_AWAY_SEC, ACCESS_AWAY_ENGINE, ACCESS_AWAY_GENERIC1, ACCESS_AWAY_GENERIC2, ACCESS_AWAY_GENERIC3, ACCESS_AWAY_GENERIC4) + +/proc/get_every_access() + return get_all_accesses() + get_all_centcom_access() + get_all_syndicate_access() + get_all_away_access() + ACCESS_BLOODCULT + ACCESS_CLOCKCULT + /proc/get_region_accesses(code) switch(code) if(0) @@ -166,15 +172,15 @@ if(1) //station general return list(ACCESS_KITCHEN,ACCESS_BAR, ACCESS_HYDROPONICS, ACCESS_JANITOR, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_LIBRARY, ACCESS_THEATRE, ACCESS_LAWYER) if(2) //security - return list(ACCESS_SEC_DOORS, ACCESS_WEAPONS, ACCESS_SECURITY, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_MECH_SECURITY, ACCESS_HOS) + return list(ACCESS_SEC_DOORS, ACCESS_SEC_RECORDS, ACCESS_WEAPONS, ACCESS_SECURITY, ACCESS_BRIG, ACCESS_BRIGPHYS, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_MECH_SECURITY, ACCESS_HOS) if(3) //medbay return list(ACCESS_MEDICAL, ACCESS_GENETICS, ACCESS_CLONING, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_SURGERY, ACCESS_MECH_MEDICAL, ACCESS_CMO, ACCESS_APOTHECARY) if(4) //research return list(ACCESS_RESEARCH, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_GENETICS, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_MECH_SCIENCE, ACCESS_MINISAT, ACCESS_RD, ACCESS_NETWORK) if(5) //engineering and maintenance - return list(ACCESS_CONSTRUCTION, ACCESS_MAINT_TUNNELS, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_TECH_STORAGE, ACCESS_ATMOSPHERICS, ACCESS_MECH_ENGINE, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_CE) + return list(ACCESS_CONSTRUCTION, ACCESS_AUX_BASE, ACCESS_MAINT_TUNNELS, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_TECH_STORAGE, ACCESS_ATMOSPHERICS, ACCESS_MECH_ENGINE, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_CE) if(6) //supply - return list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MECH_MINING, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO, ACCESS_QM, ACCESS_VAULT) + return list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MECH_MINING, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO, ACCESS_EXPLORATION, ACCESS_QM, ACCESS_VAULT) if(7) //command return list(ACCESS_HEADS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_GATEWAY, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_HOP, ACCESS_CAPTAIN, ACCESS_VAULT) @@ -221,6 +227,8 @@ return "Toxins Lab" if(ACCESS_CHEMISTRY) return "Chemistry Lab" + if(ACCESS_BRIGPHYS) + return "Brig Physician" if(ACCESS_RD) return "RD Office" if(ACCESS_BAR) @@ -277,6 +285,8 @@ return "CMO Office" if(ACCESS_QM) return "Quartermaster" + if(ACCESS_EXPLORATION) + return "Exploration Dock" if(ACCESS_SURGERY) return "Surgery" if(ACCESS_THEATRE) @@ -311,6 +321,8 @@ return "Gateway" if(ACCESS_SEC_DOORS) return "Brig" + if(ACCESS_SEC_RECORDS) + return "Security Records" if(ACCESS_MINERAL_STOREROOM) return "Mineral Storage" if(ACCESS_MINISAT) @@ -331,6 +343,8 @@ return "Science Mech Access" if(ACCESS_MECH_ENGINE) return "Engineering Mech Access" + if(ACCESS_AUX_BASE) + return "Auxiliary Base" /proc/get_centcom_access_desc(A) switch(A) @@ -354,14 +368,24 @@ return "Code Scotch" /proc/get_all_jobs() - return list("Assistant", "Captain", "Head of Personnel", "Bartender", "Cook", "Botanist", "Quartermaster", "Cargo Technician", - "Shaft Miner", "Clown", "Mime", "Janitor", "Curator", "Lawyer", "Chaplain", "Chief Engineer", "Station Engineer", - "Atmospheric Technician", "Chief Medical Officer", "Medical Doctor", "Chemist", "Geneticist", "Virologist", "Paramedic", - "Research Director", "Scientist", "Roboticist", "Head of Security", "Warden", "Detective", "Security Officer", "Brig Physician", - "Deputy", "Psychologist", "Barber") + return list("Captain", + // Service + "Assistant", "Head of Personnel", "Bartender", "Cook", "Botanist", "Janitor", "Curator", + "Chaplain", "Lawyer", "Clown", "Mime", "Barber", "Stage Magician", + // Cargo + "Quartermaster", "Cargo Technician","Shaft Miner", + // Engineering + "Chief Engineer", "Station Engineer", "Atmospheric Technician", + // R&D + "Research Director", "Scientist", "Roboticist", "Exploration Crew", + // Medical + "Chief Medical Officer", "Medical Doctor", "Chemist", "Geneticist", "Virologist", "Paramedic", "Psychiatrist", + // Security + "Head of Security", "Warden", "Detective", "Security Officer", "Brig Physician", "Deputy") + // Each job is supposed to be in their department due to the HoP console. -/proc/get_all_job_icons() //For all existing HUD icons - return get_all_jobs() + list("Prisoner", "King") +/proc/get_all_job_icons() //We need their HUD icons, but we don't want to give these jobs to people from the job list of HoP console. + return get_all_jobs() + list("Prisoner", "King", "VIP", "Debtor", "Acting Captain") /proc/get_all_centcom_jobs() return list("VIP Guest","Custodian","Thunderdome Overseer","CentCom Official","Medical Officer","Death Commando","Research Officer","Special Ops Officer","Admiral","CentCom Commander","Emergency Response Team Commander","Security Response Officer","Engineer Response Officer", "Medical Response Officer","CentCom Bartender","Comedy Response Officer", "HONK Squad Trooper") diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm index e021f47bfa693..508422246a702 100644 --- a/code/modules/jobs/job_exp.dm +++ b/code/modules/jobs/job_exp.dm @@ -1,7 +1,6 @@ GLOBAL_LIST_EMPTY(exp_to_update) GLOBAL_PROTECT(exp_to_update) - // Procs /datum/job/proc/required_playtime_remaining(client/C) if(!C) @@ -59,79 +58,11 @@ GLOBAL_PROTECT(exp_to_update) amount += explist[job] return amount -/client/proc/get_exp_report() - if(!CONFIG_GET(flag/use_exp_tracking)) - return "Tracking is disabled in the server configuration file." - var/list/play_records = prefs.exp - if(!play_records.len) - set_exp_from_db() - play_records = prefs.exp - if(!play_records.len) - return "[key] has no records." - var/return_text = list() - return_text += "
Jobs Unlocked:
Jobs Not Unlocked:
" + "[dat]", "window=book[window_size != null ? ";size=[window_size]" : ""]") - user.visible_message("[user] opens a book titled \"[title]\" and begins reading intently.") - SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "book_nerd", /datum/mood_event/book_nerd) onclose(user, "book") else to_chat(user, "This book is completely blank!") @@ -239,7 +242,7 @@ var/newtitle = reject_bad_text(stripped_input(user, "Write a new title:")) if(!user.canUseTopic(src, BE_CLOSE, literate)) return - if (length(newtitle) > 20) + if (length(newtitle) > 50) to_chat(user, "That title won't fit on the cover!") return if(!newtitle) @@ -301,7 +304,7 @@ scanner.computer.inventory.Add(src) to_chat(user, "[I]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'") - else if(istype(I, /obj/item/kitchen/knife) || I.tool_behaviour == TOOL_WIRECUTTER) + else if((istype(I, /obj/item/kitchen/knife) || I.tool_behaviour == TOOL_WIRECUTTER) && !(flags_1 & HOLOGRAM_1)) to_chat(user, "You begin to carve out [title]...") if(do_after(user, 30, target = src)) to_chat(user, "You carve out the pages from [title]! You didn't want to read it anyway.") diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 2d52a48bb328b..606f4835a1f45 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -27,6 +27,7 @@ var/author var/SQLquery clockwork = TRUE //it'd look weird + broken_overlay_emissive = TRUE /obj/machinery/computer/libraryconsole/ui_interact(mob/user) . = ..() @@ -316,15 +317,19 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums popup.open() /obj/machinery/computer/libraryconsole/bookmanagement/proc/findscanner(viewrange) - for(var/obj/machinery/libraryscanner/S in range(viewrange, get_turf(src))) - return S - return null + return locate(/obj/machinery/libraryscanner) in range(viewrange, get_turf(src)) /obj/machinery/computer/libraryconsole/bookmanagement/proc/print_forbidden_lore(mob/user) - if (prob(50)) - new /obj/item/melee/cultblade/dagger(get_turf(src)) - 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 sinister dagger sitting on the desk. You don't even remember where it came from...") - + switch(rand(1,3)) + if(1) + new /obj/item/melee/cultblade/dagger(get_turf(src)) + 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 sinister dagger sitting on the desk. You don't even remember where it came from...") + if(2) + new /obj/item/clockwork/clockwork_slab(get_turf(src)) + 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 strange metal tablet sitting on the desk. You don't even remember where it came from...") + if(3) + new /obj/item/forbidden_book(get_turf(src)) + 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 an ominous book, bound by a chain, sitting on the desk. You don't even remember where it came from...") user.visible_message("[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.", 2) /obj/machinery/computer/libraryconsole/bookmanagement/attackby(obj/item/W, mob/user, params) diff --git a/code/modules/library/soapstone.dm b/code/modules/library/soapstone.dm index 78212e4f92aa2..e25c4ed5a24e9 100644 --- a/code/modules/library/soapstone.dm +++ b/code/modules/library/soapstone.dm @@ -155,8 +155,6 @@ var/hash = rustg_hash_string(RUSTG_HASH_MD5, hidden_message) var/newcolor = copytext_char(hash, 1, 7) add_atom_colour("#[newcolor]", FIXED_COLOUR_PRIORITY) - light_color = "#[newcolor]" - set_light(1) /obj/structure/chisel_message/proc/pack() var/list/data = list() diff --git a/code/modules/lighting/emissive_blocker.dm b/code/modules/lighting/emissive_blocker.dm new file mode 100644 index 0000000000000..b69a474009ee8 --- /dev/null +++ b/code/modules/lighting/emissive_blocker.dm @@ -0,0 +1,44 @@ +/** + * Internal atom that copies an appearance on to the blocker plane + * + * Copies an appearance vis render_target and render_source on to the emissive blocking plane. + * This means that the atom in question will block any emissive sprites. + * This should only be used internally. If you are directly creating more of these, you're + * almost guaranteed to be doing something wrong. + */ +/atom/movable/emissive_blocker + name = "" + plane = EMISSIVE_BLOCKER_PLANE + layer = EMISSIVE_BLOCKER_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + //Why? + //render_targets copy the transform of the target as well, but vis_contents also applies the transform + //to what's in it. Applying RESET_TRANSFORM here makes vis_contents not apply the transform. + //Since only render_target handles transform we don't get any applied transform "stacking" + appearance_flags = RESET_TRANSFORM + +/atom/movable/emissive_blocker/Initialize(mapload, source) + . = ..() + verbs.Cut() //Cargo culting from lighting object, this maybe affects memory usage? + + render_source = source + +/atom/movable/emissive_blocker/ex_act(severity) + return FALSE + +/atom/movable/emissive_blocker/singularity_act() + return + +/atom/movable/emissive_blocker/singularity_pull() + return + +/atom/movable/emissive_blocker/blob_act() + return + +/atom/movable/emissive_blocker/onTransitZ() + return + +//Prevents people from moving these after creation, because they shouldn't be. +/atom/movable/emissive_blocker/forceMove(atom/destination, no_tp=FALSE, harderforce = FALSE) + if(harderforce) + return ..() diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm index 13676a5cb4f49..878d41aeee829 100644 --- a/code/modules/lighting/lighting_area.dm +++ b/code/modules/lighting/lighting_area.dm @@ -10,11 +10,20 @@ if (IS_DYNAMIC_LIGHTING(src)) cut_overlay(/obj/effect/fullbright) + if(lighting_overlay) + cut_overlay(lighting_overlay) + if(lighting_overlay_opacity && lighting_overlay_colour) + lighting_overlay = new /obj/effect/fullbright + lighting_overlay.color = lighting_overlay_colour + lighting_overlay.alpha = lighting_overlay_opacity + add_overlay(lighting_overlay) for (var/turf/T in src) if (IS_DYNAMIC_LIGHTING(T)) T.lighting_build_overlay() else + if(lighting_overlay) + cut_overlay(lighting_overlay) add_overlay(/obj/effect/fullbright) for (var/turf/T in src) if (T.lighting_object) @@ -27,4 +36,18 @@ if("dynamic_lighting") set_dynamic_lighting(var_value) return TRUE + if("lighting_overlay_colour") + ..() + if(lighting_overlay) + cut_overlay(lighting_overlay) + lighting_overlay.color = var_value + add_overlay(lighting_overlay) + return TRUE + if("lighting_overlay_opacity") + ..() + if(lighting_overlay) + cut_overlay(lighting_overlay) + lighting_overlay.alpha = var_value + add_overlay(lighting_overlay) + return TRUE return ..() diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index c18c7e831a476..be73c636f8a94 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -136,6 +136,6 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, if (!force) return QDEL_HINT_LETMELIVE - stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.") + stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% intellectually disabled and needs his head checked. Thanks. Send them my regards by the way.") return ..() diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index 05e0cabe35bc9..d2fae0c1ca55f 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -16,7 +16,7 @@ /atom/movable/lighting_object/Initialize(mapload) . = ..() - verbs.Cut() + remove_verb(verbs) atom_colours.Cut() myturf = loc @@ -101,7 +101,7 @@ #if LIGHTING_SOFT_THRESHOLD != 0 var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD #else - // Because of floating points?, it won't even be a flat 0. + // Because of floating points�?, it won't even be a flat 0. // This number is mostly arbitrary. var/set_luminosity = max > 1e-6 #endif diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm index 5086b0c9d29c5..6cd25efe2b231 100644 --- a/code/modules/lighting/lighting_setup.dm +++ b/code/modules/lighting/lighting_setup.dm @@ -1,5 +1,5 @@ /proc/create_all_lighting_objects() - for(var/area/A in world) + for(var/area/A in GLOB.sortedAreas) if(!IS_DYNAMIC_LIGHTING(A)) continue diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index dc965bbe3e392..d12a129e89f24 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -63,7 +63,12 @@ if (needs_update) GLOB.lighting_update_lights -= src - . = ..() + top_atom = null + source_atom = null + source_turf = null + pixel_turf = null + + return ..() // Yes this doesn't align correctly on anything other than 4 width tabs. // If you want it to go switch everybody to elastic tab stops. @@ -234,13 +239,14 @@ var/oldlum = source_turf.luminosity source_turf.luminosity = CEILING(light_range, 1) for(T in view(CEILING(light_range, 1), source_turf)) - if((!IS_DYNAMIC_LIGHTING(T) && !T.light_sources) || T.has_opaque_atom) + if((!IS_DYNAMIC_LIGHTING(T) && !T.light_sources)) continue - if (!T.lighting_corners_initialised) - T.generate_missing_corners() - for (thing in T.corners) - C = thing - corners[C] = 0 + if(!T.has_opaque_atom) + if (!T.lighting_corners_initialised) + T.generate_missing_corners() + for (thing in T.corners) + C = thing + corners[C] = 0 turfs += T source_turf.luminosity = oldlum diff --git a/code/modules/mapexporting/mapexporter.dm b/code/modules/mapexporting/mapexporter.dm index a886db0636ec3..d71b94f89c72a 100644 --- a/code/modules/mapexporting/mapexporter.dm +++ b/code/modules/mapexporting/mapexporter.dm @@ -62,18 +62,19 @@ GLOBAL_LIST_INIT(save_file_chars, list( var/turf/place = sortedmap[x][y] var/area/location var/list/objects + var/area/AR = get_area(place) //If there is nothing there, save as a noop (For odd shapes) if(!place) place = /turf/template_noop location = /area/template_noop objects = list() //Ignore things in space, must be a space turf and the area has to be empty space - else if(istype(place, /turf/open/space) && get_area(place).type == /area/space && !(save_flag & SAVE_SPACE)) + else if(istype(place, /turf/open/space) && istype(AR, /area/space) && !(save_flag & SAVE_SPACE)) place = /turf/template_noop location = /area/template_noop //Stuff to add else - location = get_area(place).type + location = AR.type objects = place place = place.type //====Saving shuttles only / non shuttles only==== diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm index 4294c4464b127..92ba6b5814054 100644 --- a/code/modules/mapping/map_template.dm +++ b/code/modules/mapping/map_template.dm @@ -7,7 +7,17 @@ var/datum/parsed_map/cached_map var/keep_cached_map = FALSE -/datum/map_template/New(path = null, rename = null, cache = FALSE) + ///if true, turfs loaded from this template are placed on top of the turfs already there, defaults to TRUE + var/should_place_on_top = TRUE + + ///if true, creates a list of all atoms created by this template loading, defaults to FALSE + var/returns_created_atoms = FALSE + + ///the list of atoms created by this template being loaded, only populated if returns_created_atoms is TRUE + var/list/created_atoms = list() + //make sure this list is accounted for/cleared if you request it from ssatoms! + +/datum/map_template/New(path = null, rename = null, cache = FALSE, admin_load = FALSE) if(path) mappath = path if(mappath) @@ -25,42 +35,80 @@ cached_map = parsed return bounds -/datum/parsed_map/proc/initTemplateBounds() +/datum/map_template/proc/initTemplateBounds(list/bounds, init_atmos = TRUE) + if (!bounds) //something went wrong + stack_trace("[name] template failed to initialize correctly!") + return + var/list/obj/machinery/atmospherics/atmos_machines = list() var/list/obj/structure/cable/cables = list() - var/list/atom/atoms = list() + var/list/atom/movable/movables = list() var/list/area/areas = list() - var/list/turfs = block( locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), - locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])) - var/list/border = block(locate(max(bounds[MAP_MINX]-1, 1), max(bounds[MAP_MINY]-1, 1), bounds[MAP_MINZ]), - locate(min(bounds[MAP_MAXX]+1, world.maxx), min(bounds[MAP_MAXY]+1, world.maxy), bounds[MAP_MAXZ])) - turfs - for(var/L in turfs) - var/turf/B = L - atoms += B - areas |= B.loc - for(var/A in B) - atoms += A - if(istype(A, /obj/structure/cable)) - cables += A + var/list/turfs = block( + locate( + bounds[MAP_MINX], + bounds[MAP_MINY], + bounds[MAP_MINZ] + ), + locate( + bounds[MAP_MAXX], + bounds[MAP_MAXY], + bounds[MAP_MAXZ] + ) + ) + for(var/turf/current_turf as anything in turfs) + var/area/current_turfs_area = current_turf.loc + areas |= current_turfs_area + if(!SSatoms.initialized) + continue + + for(var/movable_in_turf in current_turf) + movables += movable_in_turf + if(istype(movable_in_turf, /obj/structure/cable)) + cables += movable_in_turf continue - if(istype(A, /obj/machinery/atmospherics)) - atmos_machines += A - for(var/L in border) - var/turf/T = L - T.air_update_turf(TRUE) //calculate adjacent turfs along the border to prevent runtimes + if(istype(movable_in_turf, /obj/machinery/atmospherics)) + atmos_machines += movable_in_turf + // Not sure if there is some importance here to make sure the area is in z + // first or not. Its defined In Initialize yet its run first in templates + // BEFORE so... hummm SSmapping.reg_in_areas_in_z(areas) - SSatoms.InitializeAtoms(atoms) + if(!SSatoms.initialized) + return + + SSatoms.InitializeAtoms(areas + turfs + movables, returns_created_atoms ? created_atoms : null) + + // NOTE, now that Initialize and LateInitialize run correctly, do we really + // need these two below? SSmachines.setup_template_powernets(cables) SSair.setup_template_machinery(atmos_machines) -/datum/map_template/proc/load_new_z() + if(init_atmos) + //calculate all turfs inside the border + var/list/template_and_bordering_turfs = block( + locate( + max(bounds[MAP_MINX]-1, 1), + max(bounds[MAP_MINY]-1, 1), + bounds[MAP_MINZ] + ), + locate( + min(bounds[MAP_MAXX]+1, world.maxx), + min(bounds[MAP_MAXY]+1, world.maxy), + bounds[MAP_MAXZ] + ) + ) + for(var/turf/affected_turf as anything in template_and_bordering_turfs) + affected_turf.air_update_turf(TRUE) + affected_turf.levelupdate() + +/datum/map_template/proc/load_new_z(orbital_body_type, list/level_traits = list(ZTRAIT_AWAY = TRUE)) var/x = round((world.maxx - width)/2) var/y = round((world.maxy - height)/2) - var/datum/space_level/level = SSmapping.add_new_zlevel(name, list(ZTRAIT_AWAY = TRUE)) - var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE) + var/datum/space_level/level = SSmapping.add_new_zlevel(name, level_traits, orbital_body_type = orbital_body_type) + var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=should_place_on_top) var/list/bounds = parsed.bounds if(!bounds) return FALSE @@ -68,13 +116,13 @@ repopulate_sorted_areas() //initialize things that are normally initialized after map load - parsed.initTemplateBounds() + initTemplateBounds(bounds) smooth_zlevel(world.maxz) log_game("Z-level [name] loaded at [x],[y],[world.maxz]") return level -/datum/map_template/proc/load(turf/T, centered = FALSE) +/datum/map_template/proc/load(turf/T, centered = FALSE, init_atmos = TRUE) if(centered) T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z) if(!T) @@ -84,11 +132,22 @@ if(T.y+height > world.maxy) return + var/list/border = block(locate(max(T.x, 1), max(T.y, 1), T.z), + locate(min(T.x+width, world.maxx), min(T.y+height, world.maxy), T.z)) + for(var/L in border) + var/turf/turf_to_disable = L + turf_to_disable.ImmediateDisableAdjacency() + // Accept cached maps, but don't save them automatically - we don't want // ruins clogging up memory for the whole round. var/datum/parsed_map/parsed = cached_map || new(file(mappath)) cached_map = keep_cached_map ? parsed : null - if(!parsed.load(T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)) + + var/list/turf_blacklist = list() + update_blacklist(T, turf_blacklist) + + parsed.turf_blacklist = turf_blacklist + if(!parsed.load(T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=should_place_on_top)) return var/list/bounds = parsed.bounds if(!bounds) @@ -98,11 +157,14 @@ repopulate_sorted_areas() //initialize things that are normally initialized after map load - parsed.initTemplateBounds() + initTemplateBounds(bounds, init_atmos) log_game("[name] loaded at [T.x],[T.y],[T.z]") return bounds +/datum/map_template/proc/update_blacklist(turf/T, list/input_blacklist) + return + /datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE) var/turf/placement = T if(centered) @@ -114,6 +176,6 @@ //for your ever biggening badminnery kevinz000 //⤠- Cyberboss -/proc/load_new_z_level(var/file, var/name) +/proc/load_new_z_level(var/file, var/name, orbital_body_type) var/datum/map_template/template = new(file, name) - template.load_new_z() + template.load_new_z(orbital_body_type = orbital_body_type) diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 98c0ed209f7d5..9af886776a731 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -213,7 +213,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava) if(target_type && !istype(A,target_type)) continue var/cargs = build_args() - A.AddComponent(arglist(cargs)) + A._AddComponent(cargs) qdel(src) return diff --git a/code/modules/mapping/preloader.dm b/code/modules/mapping/preloader.dm index 79afa8c032506..d966d8c0410bc 100644 --- a/code/modules/mapping/preloader.dm +++ b/code/modules/mapping/preloader.dm @@ -37,3 +37,12 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) name = "Turf Passthrough" icon_state = "noop" bullet_bounce_sound = null + +//The following two turfs can be used to hint if any genturfs below should be generated as closed or open +/turf/template_noop/closed + name = "Area Passthrough (prefer closed)" + icon_state = "noop_closed" + +/turf/template_noop/open + name = "Area Passthrough (prefer open)" + icon_state = "noop_open" diff --git a/code/modules/mapping/random_rooms.dm b/code/modules/mapping/random_rooms.dm index dd37032bc16b0..d87be0728c9a0 100644 --- a/code/modules/mapping/random_rooms.dm +++ b/code/modules/mapping/random_rooms.dm @@ -689,7 +689,7 @@ template_height = 3 template_width = 3 weight = 3 - + /datum/map_template/random_room/sk_rdm082 name = "Maint Chemistry" room_id = "sk_rdm082_maintmedical" @@ -1161,25 +1161,6 @@ template_width = 10 weight = 3 - -/datum/map_template/random_room/sk_rdm135 //this room is fun. - name = "Cluwne Altar" - room_id = "sk_rdm135_cluwnealtar" - mappath = "_maps/RandomRooms/10x10/sk_rdm135_cluwnealtar.dmm" - centerspawner = FALSE - template_height = 10 - template_width = 10 - weight = 1 //rare - -/datum/map_template/random_room/sk_rdm136 //this room is fun as well - name = "Tiny Cluwne Altar" - room_id = "sk_rdm136_tinycluwnealtar" - mappath = "_maps/RandomRooms/5x4/sk_rdm136_tinycluwnealtar.dmm" - centerspawner = FALSE - template_height = 4 - template_width = 5 - weight = 1 - /datum/map_template/random_room/sk_rdm137 name = "Tiny psych ward" room_id = "sk_rdm137_tinyshrink" @@ -1198,7 +1179,7 @@ template_width = 5 weight = 4 -/datum/map_template/random_room/sk_rdm139 +/datum/map_template/random_room/sk_rdm139 name = "containment cell" room_id = "sk_rdm139_containmentcell" mappath = "_maps/RandomRooms/3x3/containmentcell.dmm" @@ -1214,4 +1195,157 @@ centerspawner = FALSE template_height = 5 template_width = 3 - weight = 2 \ No newline at end of file + weight = 2 + +/datum/map_template/random_room/sk_rdm141 + name = "the place 6 sectors down" + room_id = "sk_rdm141_6sectorsdown" + mappath = "_maps/RandomRooms/10x10/sk_rdm141_6sectorsdown.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 + weight = 2 + +/datum/map_template/random_room/sk_rdm142 + name = "old diner" + room_id = "sk_rdm142_olddiner" + mappath = "_maps/RandomRooms/10x10/sk_rdm142_olddiner.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 + weight = 4 + +/datum/map_template/random_room/sk_rdm143 + name = "gamer cave" + room_id = "sk_rdm143_gamercave" + mappath = "_maps/RandomRooms/10x10/sk_rdm143_gamercave.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 + weight = 3 + +/datum/map_template/random_room/sk_rdm144 //has Stage Magician Spawner + name = "small stage and bar" + room_id = "sk_rdm144_smallmagician" + mappath = "_maps/RandomRooms/10x10/sk_rdm144_smallmagician.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 + weight = 3 + +/datum/map_template/random_room/sk_rdm145 //has tela anchor + name = "lady tesla altar" + room_id = "sk_rdm145_ladytesla_altar" + mappath = "_maps/RandomRooms/10x10/sk_rdm145_ladytesla_altar.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 + weight = 1 //rare + +/datum/map_template/random_room/sk_rdm146 + name = "blastdoor interchange" + room_id = "sk_rdm146_blastdoor_interchange" + mappath = "_maps/RandomRooms/10x10/sk_rdm146_blastdoor_interchange.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 + weight = 4 //common + +/datum/map_template/random_room/sk_rdm147 + name = "advanced micro botany" + room_id = "sk_rdm147_advbotany" + mappath = "_maps/RandomRooms/10x10/sk_rdm147_advbotany.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 + weight = 2 + +/datum/map_template/random_room/sk_rdm148 + name = "maintenance apiary" + room_id = "sk_rdm148_botany_apiary" + mappath = "_maps/RandomRooms/10x10/sk_rdm148_botany_apiary.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 + weight = 2 + +/datum/map_template/random_room/sk_rdm149 + name = "space window with crates" + room_id = "sk_rdm149_cratewindow" + mappath = "_maps/RandomRooms/10x5/sk_rdm149_cratewindow.dmm" + centerspawner = FALSE + template_height = 5 + template_width = 10 + weight = 3 + +/datum/map_template/random_room/sk_rdm150 + name = "small medical lobby" + room_id = "sk_rdm150_smallmedlobby" + mappath = "_maps/RandomRooms/10x5/sk_rdm150_smallmedlobby.dmm" + centerspawner = FALSE + template_height = 5 + template_width = 10 + weight = 3 //common + +/datum/map_template/random_room/sk_rdm151 //delicious + name = "small medical lobby" + room_id = "sk_rdm151_ratburger" + mappath = "_maps/RandomRooms/10x5/sk_rdm151_ratburger.dmm" + centerspawner = FALSE + template_height = 5 + template_width = 10 + weight = 1 //rare + +/datum/map_template/random_room/sk_rdm152 + name = "old genetics office" + room_id = "sk_rdm152_geneticsoffice" + mappath = "_maps/RandomRooms/10x5/sk_rdm152_geneticsoffice.dmm" + centerspawner = FALSE + template_height = 5 + template_width = 10 + weight = 2 + +/datum/map_template/random_room/sk_rdm153 //its a hobo den featuring Peter the pet frog. Includes a debtor spawn + name = "peters room" + room_id = "sk_rdm153_hobowithpeter" + mappath = "_maps/RandomRooms/10x5/sk_rdm153_hobowithpeter.dmm" + centerspawner = FALSE + template_height = 5 + template_width = 10 + weight = 2 + +/datum/map_template/random_room/sk_rdm154 //rare, has a cleaver. + name = "butchers den" + room_id = "sk_rdm154_butchersden" + mappath = "_maps/RandomRooms/10x5/sk_rdm154_butchersden.dmm" + centerspawner = FALSE + template_height = 5 + template_width = 10 + weight = 1 + +/datum/map_template/random_room/sk_rdm155 + name = "punji stick conveyor trap" + room_id = "sk_rdm155_punjiconveyor" + mappath = "_maps/RandomRooms/10x5/sk_rdm155_punjiconveyor.dmm" + centerspawner = FALSE + template_height = 5 + template_width = 10 + weight = 1 + +/datum/map_template/random_room/sk_rdm156 + name = "ancient interchange" + room_id = "sk_rdm156_oldairlock_interchange" + mappath = "_maps/RandomRooms/10x5/sk_rdm156_oldairlock_interchange.dmm" + centerspawner = FALSE + template_height = 5 + template_width = 10 + weight = 4 + stock = 2 + +/datum/map_template/random_room/sk_rdm157 + name = "Space Chess" + room_id = "sk_rdm157_chess" + mappath = "_maps/RandomRooms/10x10/sk_rdm157_chess.dmm" + centerspawner = FALSE + template_height = 10 + template_width = 10 diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm index 1beddbfdbbb21..9f73367f7aee2 100644 --- a/code/modules/mapping/reader.dm +++ b/code/modules/mapping/reader.dm @@ -23,6 +23,9 @@ var/list/bounds var/did_expand = FALSE + ///any turf in this list is skipped inside of build_coordinate + var/list/turf_blacklist = list() + // raw strings used to represent regexes more accurately // '' used to avoid confusing syntax highlighting var/static/regex/dmmRegex = new(@'"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}', "g") @@ -52,7 +55,11 @@ /datum/parsed_map/New(tfile, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper=INFINITY, measureOnly=FALSE) if(isfile(tfile)) original_path = "[tfile]" + var/temp_hack_garbage = tfile tfile = rustg_file_read(tfile) + //This is hacky garbage + if(tfile == "") //Might be an upload, try raw loading it. + tfile = file2text(temp_hack_garbage) else if(isnull(tfile)) // create a new datum without loading a map return @@ -310,6 +317,13 @@ //Instanciation //////////////// + for (var/turf_in_blacklist in turf_blacklist) + if (crds == turf_in_blacklist) //if the given turf is blacklisted, dont do anything with it + return + + //Keep a reference to the original area in case we need to tell it to generate this turf later + var/area/orig_area = crds.loc + //The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile //first instance the /area and remove it from the members list index = members.len @@ -327,6 +341,8 @@ if(GLOB.use_preloader && instance) world.preloader_load(instance) + else + orig_area = null // We won't be messing with the old area, null it //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect @@ -338,8 +354,23 @@ SSatoms.map_loader_begin() //instanciate the first /turf var/turf/T - if(members[first_turf_index] != /turf/template_noop) - T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],crds,no_changeturf,placeOnTop) + if(ispath(members[first_turf_index], /turf/template_noop)) + if(istype(crds, /turf/open/genturf)) + var/turf/open/genturf/genturf = crds + //If the new area is different from the original area, ensure the new turfs are generated as part of the original area + if(orig_area && orig_area.type != members[index]) + LAZYADD(orig_area.additional_genturfs, crds) + //Cave generation checks current area flags for generation; ignore them + genturf.force_generation = TRUE + //Pass on any hints for whether the turf should be open or closed + if(ispath(members[first_turf_index], /turf/template_noop/closed)) + genturf.genturf_hint = GENTURF_HINT_CLOSED + else if(ispath(members[first_turf_index], /turf/template_noop/open)) + genturf.genturf_hint = GENTURF_HINT_OPEN + else + ///Disable placeOnTop for genturfs, instead making sure to replace them + var/shouldPlaceOnTop = placeOnTop && !istype(crds, /turf/open/genturf) + T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],crds,no_changeturf,shouldPlaceOnTop) if(T) //if others /turf are presents, simulates the underlays piling effect @@ -480,4 +511,9 @@ /datum/parsed_map/Destroy() ..() + turf_blacklist.Cut() + parsed_bounds.Cut() + bounds.Cut() + grid_models.Cut() + gridSets.Cut() return QDEL_HINT_HARDDEL_NOW diff --git a/code/modules/mapping/ruins.dm b/code/modules/mapping/ruins.dm index fe2f0c65b7b73..eee1f635aa2f1 100644 --- a/code/modules/mapping/ruins.dm +++ b/code/modules/mapping/ruins.dm @@ -20,6 +20,8 @@ for(var/i in get_affected_turfs(central_turf, 1)) var/turf/T = i + for(var/obj/structure/spawner/nest in T) + qdel(nest) for(var/mob/living/simple_animal/monster in T) qdel(monster) for(var/obj/structure/flora/ash/plant in T) @@ -121,8 +123,6 @@ forced_ruins[linked] = forced_z ? forced_z : z_placed //I guess you might want a chain somehow if(PLACE_LAVA_RUIN) forced_ruins[linked] = pick(SSmapping.levels_by_trait(ZTRAIT_LAVA_RUINS)) - if(PLACE_SPACE_RUIN) - forced_ruins[linked] = pick(SSmapping.levels_by_trait(ZTRAIT_SPACE_RUINS)) if(PLACE_DEFAULT) forced_ruins[linked] = -1 forced_z = 0 diff --git a/code/modules/mapping/space_management/multiz_helpers.dm b/code/modules/mapping/space_management/multiz_helpers.dm index 74bd7c8782eff..fe5aa0b039151 100644 --- a/code/modules/mapping/space_management/multiz_helpers.dm +++ b/code/modules/mapping/space_management/multiz_helpers.dm @@ -45,4 +45,38 @@ else holder = UP dir |= holder - return dir \ No newline at end of file + return dir + +/proc/get_zs_in_range(z_level, max_z_range) + . = list(z_level) + if(max_z_range <= 0) + return + var/turf/center_turf = locate(world.maxx / 2, world.maxy / 2, z_level) + var/turf/temp = center_turf.above() + //Iterate upwards. + var/i = 0 + while(isturf(temp)) + . += temp + i ++ + if(i >= max_z_range) + break + temp = temp.above() + //Iterate downwards. + temp = center_turf.below() + i = 0 + while(isturf(temp)) + . += temp + i ++ + if(i >= max_z_range) + break + temp = temp.below() + +/proc/multi_z_dist(turf/T0, turf/T1) + if(T0.get_virtual_z_level() == T1.get_virtual_z_level()) + return get_dist(T0, T1) + if(is_station_level(T0.z) && is_station_level(T1.z)) + var/raw_dist = get_dist(T0, T1) + var/z_dist = abs(T0.z - T1.z) * MULTI_Z_DISTANCE + var/total_dist = raw_dist + z_dist + return total_dist + return INFINITY diff --git a/code/modules/mapping/space_management/space_level.dm b/code/modules/mapping/space_management/space_level.dm index cc9c6e11f173b..d696dfb47130f 100644 --- a/code/modules/mapping/space_management/space_level.dm +++ b/code/modules/mapping/space_management/space_level.dm @@ -6,9 +6,16 @@ var/linkage = SELFLOOPING var/xi var/yi //imaginary placements on the grid + //Z-levels orbital body + var/datum/orbital_object/z_linked/orbital_body + //Is something generating on this level? + var/generating = FALSE -/datum/space_level/New(new_z, new_name, list/new_traits = list()) +/datum/space_level/New(new_z, new_name, list/new_traits = list(), orbital_body_type) z_value = new_z name = new_name traits = new_traits set_linkage(new_traits[ZTRAIT_LINKAGE]) + if(orbital_body_type) + orbital_body = new orbital_body_type() + orbital_body.link_to_z(src) diff --git a/code/modules/mapping/space_management/zlevel_manager.dm b/code/modules/mapping/space_management/zlevel_manager.dm index 6129c5fd2b6a2..c50a2e60e3e7d 100644 --- a/code/modules/mapping/space_management/zlevel_manager.dm +++ b/code/modules/mapping/space_management/zlevel_manager.dm @@ -13,17 +13,18 @@ for (var/I in 1 to default_map_traits.len) var/list/features = default_map_traits[I] - var/datum/space_level/S = new(I, features[DL_NAME], features[DL_TRAITS]) + //All default levels are assumed to be phobos at this stage, since there is only 1. + var/datum/space_level/S = new(I, features[DL_NAME], features[DL_TRAITS], orbital_body_type = /datum/orbital_object/z_linked/phobos) z_list += S -/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, traits = list(), z_type = /datum/space_level) +/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, traits = list(), z_type = /datum/space_level, orbital_body_type) SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, args) var/new_z = z_list.len + 1 if (world.maxz < new_z) world.incrementMaxZ() CHECK_TICK // TODO: sleep here if the Z level needs to be cleared - var/datum/space_level/S = new z_type(new_z, name, traits) + var/datum/space_level/S = new z_type(new_z, name, traits, orbital_body_type) z_list += S return S diff --git a/code/modules/mentor/follow.dm b/code/modules/mentor/follow.dm index 7c53c5c0fb571..e5ec5adc90166 100644 --- a/code/modules/mentor/follow.dm +++ b/code/modules/mentor/follow.dm @@ -7,7 +7,7 @@ return mentor_datum.following = M usr.reset_perspective(M) - verbs += /client/proc/mentor_unfollow + add_verb(/client/proc/mentor_unfollow) to_chat(GLOB.admins, "MENTOR: [key_name(usr)] is now following [key_name(M)]") to_chat(usr, "Click the \"Stop Following\" button in the Mentor tab to stop following [key_name(M)].") log_mentor("[key_name(usr)] began following [key_name(M)]") @@ -20,7 +20,7 @@ if(!is_mentor()) return usr.reset_perspective() - verbs -= /client/proc/mentor_unfollow + remove_verb(/client/proc/mentor_unfollow) to_chat(GLOB.admins, "MENTOR: [key_name(usr)] is no longer following [key_name(mentor_datum.following)]") log_mentor("[key_name(usr)] stopped following [key_name(mentor_datum.following)]") mentor_datum.following = null diff --git a/code/modules/mentor/mentor_verbs.dm b/code/modules/mentor/mentor_verbs.dm index f7faa873eee54..7b3e5dd1d1b3a 100644 --- a/code/modules/mentor/mentor_verbs.dm +++ b/code/modules/mentor/mentor_verbs.dm @@ -6,7 +6,9 @@ GLOBAL_PROTECT(mentor_verbs) /client/proc/add_mentor_verbs() if(mentor_datum) - verbs += GLOB.mentor_verbs + add_verb(GLOB.mentor_verbs) + reset_badges() /client/proc/remove_mentor_verbs() - verbs -= GLOB.mentor_verbs + remove_verb(GLOB.mentor_verbs) + reset_badges() diff --git a/code/modules/mentor/mentorhelp.dm b/code/modules/mentor/mentorhelp.dm index f6be4410795b7..b90c95adc0394 100644 --- a/code/modules/mentor/mentorhelp.dm +++ b/code/modules/mentor/mentorhelp.dm @@ -10,9 +10,9 @@ if(!msg) return //remove out mentorhelp verb temporarily to prevent spamming of mentors. - verbs -= /client/verb/mentorhelp + remove_verb(/client/verb/mentorhelp) spawn(300) - verbs += /client/verb/mentorhelp // 30 second cool-down for mentorhelp + add_verb(/client/verb/mentorhelp) // 30 second cool-down for mentorhelp msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN)) if(!msg) return @@ -39,7 +39,7 @@ else .["present"]++ -/proc/key_name_mentor(var/whom, var/include_link = null, var/include_name = 0, var/include_follow = 0, var/char_name_only = 0) +/proc/key_name_mentor(var/whom, var/include_link = null, var/include_name = 0, var/char_name_only = 0) var/mob/M var/client/C var/key @@ -96,7 +96,4 @@ else . += "*no key*" - if(include_follow) - . += " (F)" - return . diff --git a/code/modules/mentor/mentorpm.dm b/code/modules/mentor/mentorpm.dm index 891daa9fb68fb..b2aa9ce4c0602 100644 --- a/code/modules/mentor/mentorpm.dm +++ b/code/modules/mentor/mentorpm.dm @@ -1,3 +1,5 @@ +//This file was ported from hippie, used to be indented with spaces, and is the single worst corner of this codebase next to voice radio. For the love of god please rewrite this. + //shows a list of clients we could send PMs to, then forwards our choice to cmd_Mentor_pm /client/proc/cmd_mentor_pm_panel() set category = "Mentor" @@ -24,11 +26,13 @@ C = M.client else if(istext(whom)) C = GLOB.directory[whom] - else if(istype(whom,/client)) + else if(istype(whom, /client)) C = whom if(!C) - if(is_mentor()) to_chat(src, "Error: Mentor-PM: Client not found.") - else mentorhelp(msg) //Mentor we are replying to left. Mentorhelp instead(check below) + if(is_mentor()) + to_chat(src, "Error: Mentor-PM: Client not found.") + else + mentorhelp(msg) //Mentor we are replying to left. Mentorhelp instead(check below) return //get message text, limit it's length.and clean/escape html @@ -43,7 +47,7 @@ to_chat(src, "Error: Mentor-PM: Client not found.") else mentorhelp(msg) //Mentor we are replying to has vanished, Mentorhelp instead (how the fuck does this work?let's hope it works,shrug) - return + return // Neither party is a mentor, they shouldn't be PMing! if (!C.is_mentor() && !is_mentor()) @@ -59,21 +63,21 @@ var/show_char = CONFIG_GET(flag/mentors_mobname_only) if(C.is_mentor()) if(is_mentor())//both are mentors - to_chat(C, "Mentor PM from-[key_name_mentor(src, C, 1, 0, 0)]: [msg]") - to_chat(src, "Mentor PM to-[key_name_mentor(C, C, 1, 0, 0)]: [msg]") + to_chat(C, "Mentor PM from-[key_name_mentor(src, C, 1, 0)]: [msg]") + to_chat(src, "Mentor PM to-[key_name_mentor(C, C, 1, 0)]: [msg]") else //recipient is an mentor but sender is not - to_chat(C, "Reply PM from-[key_name_mentor(src, C, 1, 0, show_char)]: [msg]") - to_chat(src, "Mentor PM to-[key_name_mentor(C, C, 1, 0, 0)]: [msg]") + to_chat(C, "Reply PM from-[key_name_mentor(src, C, 1, show_char)]: [msg]") + to_chat(src, "Mentor PM to-[key_name_mentor(C, C, 1, 0)]: [msg]") else if(is_mentor()) //sender is an mentor but recipient is not. - to_chat(C, "Mentor PM from-[key_name_mentor(src, C, 1, 0, 0)]: [msg]") - to_chat(src, "Mentor PM to-[key_name_mentor(C, C, 1, 0, show_char)]: [msg]") + to_chat(C, "Mentor PM from-[key_name_mentor(src, C, 1, 0)]: [msg]") + to_chat(src, "Mentor PM to-[key_name_mentor(C, C, 1, show_char)]: [msg]") //we don't use message_Mentors here because the sender/receiver might get it too var/show_char_sender = !is_mentor() && CONFIG_GET(flag/mentors_mobname_only) var/show_char_recip = !C.is_mentor() && CONFIG_GET(flag/mentors_mobname_only) for(var/client/X in GLOB.mentors | GLOB.admins) if(X.key!=key && X.key!=C.key) //check client/X is an Mentor and isn't the sender or recipient - to_chat(X, "Mentor PM: [key_name_mentor(src, X, 0, 0, show_char_sender)]->[key_name_mentor(C, X, 0, 0, show_char_recip)]: [msg]") //inform X + to_chat(X, "Mentor PM: [key_name_mentor(src, X, 0, show_char_sender)]->[key_name_mentor(C, X, 0, 0, show_char_recip)]: [msg]") //inform X diff --git a/code/modules/mentor/mentorsay.dm b/code/modules/mentor/mentorsay.dm index 17e24b4f5a01a..1143f95994200 100644 --- a/code/modules/mentor/mentorsay.dm +++ b/code/modules/mentor/mentorsay.dm @@ -1,6 +1,6 @@ /client/proc/cmd_mentor_say(msg as text) set category = "Mentor" - set name = "Msay" //Gave this shit a shorter name so you only have to time out "msay" rather than "mentor say" to use it --NeoFite + set name = "Msay" //Gave this shit a shorter name so you only have to type out "msay" rather than "mentor say" to use it --NeoFite set hidden = 1 if(!is_mentor()) return diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm index e083e838507e0..843cc4e76c76c 100644 --- a/code/modules/mining/abandoned_crates.dm +++ b/code/modules/mining/abandoned_crates.dm @@ -3,7 +3,6 @@ /obj/structure/closet/crate/secure/loot name = "abandoned crate" desc = "What could be inside?" - icon_state = "securecrate" integrity_failure = 0 //no breaking open the crate var/code = null var/lastattempt = null @@ -172,6 +171,7 @@ locked = FALSE cut_overlays() add_overlay("securecrateg") + add_overlay("[icon_door || icon_state]_door") //needs to put the door overlayer back cause of this snowflake code tamperproof = 0 // set explosion chance to zero, so we dont accidently hit it with a multitool and instantly die else if(!input || !sanitycheck || length(sanitised) != codelen) to_chat(user, "You leave the crate alone.") @@ -242,3 +242,9 @@ /obj/structure/closet/crate/secure/loot/deconstruct(disassembled = TRUE) boom() + +/obj/structure/closet/crate/secure/loot/emp_act(severity) + if(locked) + boom() + else + ..() \ No newline at end of file diff --git a/code/modules/mining/aux_base.dm b/code/modules/mining/aux_base.dm index 9ad890dbe2049..c311a5c4747ac 100644 --- a/code/modules/mining/aux_base.dm +++ b/code/modules/mining/aux_base.dm @@ -21,7 +21,7 @@ interface with the mining shuttle at the landing site if a mobile beacon is also var/launch_warning = TRUE var/list/turrets = list() //List of connected turrets - req_one_access = list(ACCESS_CARGO, ACCESS_CONSTRUCTION, ACCESS_HEADS, ACCESS_RESEARCH) + req_one_access = list(ACCESS_AUX_BASE, ACCESS_HEADS) var/possible_destinations clockwork = TRUE circuit = /obj/item/circuitboard/computer/auxillary_base diff --git a/code/modules/mining/aux_base_camera.dm b/code/modules/mining/aux_base_camera.dm index 02c54ffbd36e1..fb710abf7e388 100644 --- a/code/modules/mining/aux_base_camera.dm +++ b/code/modules/mining/aux_base_camera.dm @@ -1,22 +1,22 @@ //Aux base construction console -/mob/camera/aiEye/remote/base_construction +/mob/camera/ai_eye/remote/base_construction name = "construction holo-drone" move_on_shuttle = 1 //Allows any curious crew to watch the base after it leaves. (This is safe as the base cannot be modified once it leaves) icon = 'icons/obj/mining.dmi' icon_state = "construction_drone" var/area/starting_area -/mob/camera/aiEye/remote/base_construction/Initialize() +/mob/camera/ai_eye/remote/base_construction/Initialize() . = ..() starting_area = get_area(loc) -/mob/camera/aiEye/remote/base_construction/setLoc(var/t) +/mob/camera/ai_eye/remote/base_construction/setLoc(var/t) var/area/curr_area = get_area(t) if(curr_area == starting_area || istype(curr_area, /area/shuttle/auxillary_base)) return ..() //While players are only allowed to build in the base area, but consoles starting outside the base can move into the base area to begin work. -/mob/camera/aiEye/remote/base_construction/relaymove(mob/user, direct) +/mob/camera/ai_eye/remote/base_construction/relaymove(mob/user, direct) dir = direct //This camera eye is visible as a drone, and needs to keep the dir updated ..() @@ -74,7 +74,7 @@ spawn_spot = src - eyeobj = new /mob/camera/aiEye/remote/base_construction(get_turf(spawn_spot)) + eyeobj = new /mob/camera/ai_eye/remote/base_construction(get_turf(spawn_spot)) eyeobj.origin = src @@ -130,7 +130,7 @@ /datum/action/innate/aux_base //Parent aux base action icon_icon = 'icons/mob/actions/actions_construction.dmi' var/mob/living/C //Mob using the action - var/mob/camera/aiEye/remote/base_construction/remote_eye //Console's eye mob + var/mob/camera/ai_eye/remote/base_construction/remote_eye //Console's eye mob var/obj/machinery/computer/camera_advanced/base_construction/B //Console itself /datum/action/innate/aux_base/Activate() @@ -202,27 +202,27 @@ name = "Select Airlock Type" button_icon_state = "airlock_select" -datum/action/innate/aux_base/airlock_type/Activate() +/datum/action/innate/aux_base/airlock_type/Activate() if(..()) return B.RCD.change_airlock_setting() -datum/action/innate/aux_base/window_type +/datum/action/innate/aux_base/window_type name = "Select Window Type" button_icon_state = "window_select" -datum/action/innate/aux_base/window_type/Activate() +/datum/action/innate/aux_base/window_type/Activate() if(..()) return B.RCD.toggle_window_type() -datum/action/innate/aux_base/place_fan +/datum/action/innate/aux_base/place_fan name = "Place Tiny Fan" button_icon_state = "build_fan" -datum/action/innate/aux_base/place_fan/Activate() +/datum/action/innate/aux_base/place_fan/Activate() if(..()) return @@ -244,11 +244,11 @@ datum/action/innate/aux_base/place_fan/Activate() to_chat(owner, "Tiny fan placed. [B.fans_remaining] remaining.") playsound(fan_turf, 'sound/machines/click.ogg', 50, 1) -datum/action/innate/aux_base/install_turret +/datum/action/innate/aux_base/install_turret name = "Install Plasma Anti-Wildlife Turret" button_icon_state = "build_turret" -datum/action/innate/aux_base/install_turret/Activate() +/datum/action/innate/aux_base/install_turret/Activate() if(..()) return diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index bcd5f599848fe..21a6dd55abe50 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -10,9 +10,11 @@ max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT heat_protection = CHEST|GROIN|LEGS|ARMS hoodtype = /obj/item/clothing/head/hooded/explorer - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50) + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50, "stamina" = 20) allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe) resistance_flags = FIRE_PROOF + high_pressure_multiplier = 0.4 + flags_inv = HIDEJUMPSUIT /obj/item/clothing/head/hooded/explorer name = "explorer hood" @@ -22,8 +24,9 @@ flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50) + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50, "stamina" = 20) resistance_flags = FIRE_PROOF + high_pressure_multiplier = 0.4 /obj/item/clothing/suit/hooded/explorer/Initialize() . = ..() @@ -37,11 +40,12 @@ name = "explorer gas mask" desc = "A military-grade gas mask that can be connected to an air supply." icon_state = "gas_mining" + flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH visor_flags = BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS visor_flags_inv = HIDEFACIALHAIR - visor_flags_cover = MASKCOVERSMOUTH + visor_flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH actions_types = list(/datum/action/item_action/adjust) - armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 20, "acid" = 40) + armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 20, "acid" = 40, "stamina" = 10) resistance_flags = FIRE_PROOF /obj/item/clothing/mask/gas/explorer/attack_self(mob/user) @@ -49,7 +53,7 @@ /obj/item/clothing/mask/gas/explorer/adjustmask(user) ..() - w_class = mask_adjusted ? WEIGHT_CLASS_NORMAL : WEIGHT_CLASS_SMALL + w_class = mask_adjusted ? WEIGHT_CLASS_SMALL : WEIGHT_CLASS_NORMAL /obj/item/clothing/mask/gas/explorer/folded/Initialize() . = ..() @@ -64,8 +68,9 @@ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | LAVA_PROOF slowdown = 0 - armor = list("melee" = 70, "bullet" = 40, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + armor = list("melee" = 70, "bullet" = 40, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100, "stamina" = 40) allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe) + high_pressure_multiplier = 0.6 /obj/item/clothing/suit/space/hostile_environment/Initialize() . = ..() @@ -76,10 +81,10 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/item/clothing/suit/space/hostile_environment/process() +/obj/item/clothing/suit/space/hostile_environment/process(delta_time) var/mob/living/carbon/C = loc - if(istype(C) && prob(2)) //cursed by bubblegum - if(prob(15)) + if(istype(C) && DT_PROB(1, delta_time)) //cursed by bubblegum + if(DT_PROB(7.5, delta_time)) new /datum/hallucination/oh_yeah(C) to_chat(C, "[pick("I AM IMMORTAL.","I SHALL TAKE BACK WHAT'S MINE.","I SEE YOU.","YOU CANNOT ESCAPE ME FOREVER.","DEATH CANNOT HOLD ME.")]") else @@ -93,8 +98,9 @@ w_class = WEIGHT_CLASS_NORMAL max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT clothing_flags = THICKMATERIAL // no space protection - armor = list("melee" = 70, "bullet" = 40, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + armor = list("melee" = 70, "bullet" = 40, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100, "stamina" = 40) resistance_flags = FIRE_PROOF | LAVA_PROOF + high_pressure_multiplier = 0.6 /obj/item/clothing/head/helmet/space/hostile_environment/Initialize() . = ..() diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index a8b870d6aa255..a87023de5e524 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -1,5 +1,5 @@ /*********************Mining Hammer****************/ -/obj/item/twohanded/kinetic_crusher +/obj/item/kinetic_crusher icon = 'icons/obj/mining.dmi' icon_state = "crusher" item_state = "crusher0" @@ -11,8 +11,6 @@ force = 0 //You can't hit stuff unless wielded w_class = WEIGHT_CLASS_BULKY slot_flags = ITEM_SLOT_BACK - force_unwielded = 20 //It's never not wielded so these are the same - force_wielded = 0 throwforce = 5 block_upgrade_walk = 1 throw_speed = 4 @@ -30,15 +28,16 @@ var/light_on = FALSE var/brightness_on = 5 -/obj/item/twohanded/kinetic_crusher/Initialize() +/obj/item/kinetic_crusher/ComponentInitialize() . = ..() AddComponent(/datum/component/butchering, 60, 110) //technically it's huge and bulky, but this provides an incentive to use it + AddComponent(/datum/component/two_handed, force_unwielded=0, force_wielded=20) -/obj/item/twohanded/kinetic_crusher/Destroy() +/obj/item/kinetic_crusher/Destroy() QDEL_LIST(trophies) return ..() -/obj/item/twohanded/kinetic_crusher/examine(mob/living/user) +/obj/item/kinetic_crusher/examine(mob/living/user) . = ..() to_chat(user, "Mark a large creature with the destabilizing force, then hit them in melee to do [force + detonation_damage] damage.") to_chat(user, "Does [force + detonation_damage + backstab_bonus] damage if the target is backstabbed, instead of [force + detonation_damage].") @@ -46,7 +45,7 @@ var/obj/item/crusher_trophy/T = t to_chat(user, "It has \a [T] attached, which causes [T.effect_desc()].") -/obj/item/twohanded/kinetic_crusher/attackby(obj/item/I, mob/living/user) +/obj/item/kinetic_crusher/attackby(obj/item/I, mob/living/user) if(I.tool_behaviour == TOOL_CROWBAR) if(LAZYLEN(trophies)) to_chat(user, "You remove [src]'s trophies.") @@ -62,8 +61,8 @@ else return ..() -/obj/item/twohanded/kinetic_crusher/attack(mob/living/target, mob/living/carbon/user) - if(!wielded) +/obj/item/kinetic_crusher/attack(mob/living/target, mob/living/carbon/user) + if(!ISWIELDED(src)) to_chat(user, "[src] is too heavy to use with one hand. You fumble and drop everything.") user.drop_all_held_items() return @@ -79,9 +78,9 @@ if(!QDELETED(C) && !QDELETED(target)) C.total_damage += target_health - target.health //we did some damage, but let's not assume how much we did -/obj/item/twohanded/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag, clickparams) +/obj/item/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag, clickparams) . = ..() - if(!wielded) + if(!ISWIELDED(src)) return if(!proximity_flag && charged)//Mark a target, or mine a tile. var/turf/proj_turf = user.loc @@ -128,26 +127,27 @@ C.total_damage += detonation_damage L.apply_damage(detonation_damage, BRUTE, blocked = def_check) -/obj/item/twohanded/kinetic_crusher/proc/Recharge() +/obj/item/kinetic_crusher/proc/Recharge() if(!charged) charged = TRUE update_icon() playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) -/obj/item/twohanded/kinetic_crusher/ui_action_click(mob/user, actiontype) +/obj/item/kinetic_crusher/ui_action_click(mob/user, actiontype) light_on = !light_on playsound(user, 'sound/weapons/empty.ogg', 100, TRUE) update_brightness(user) update_icon() -/obj/item/twohanded/kinetic_crusher/proc/update_brightness(mob/user = null) +/obj/item/kinetic_crusher/proc/update_brightness(mob/user = null) if(light_on) set_light(brightness_on) else set_light(0) -/obj/item/twohanded/kinetic_crusher/update_icon() +/obj/item/kinetic_crusher/update_icon() ..() + var/wielded = ISWIELDED(src) cut_overlays() if(!charged) add_overlay("[icon_state]_uncharged") @@ -169,7 +169,7 @@ flag = "bomb" range = 6 log_override = TRUE - var/obj/item/twohanded/kinetic_crusher/hammer_synced + var/obj/item/kinetic_crusher/hammer_synced /obj/item/projectile/destabilizer/Destroy() hammer_synced = null @@ -201,19 +201,19 @@ var/denied_type = /obj/item/crusher_trophy /obj/item/crusher_trophy/examine(mob/living/user) - ..() - to_chat(user, "Causes [effect_desc()] when attached to a kinetic crusher.") + . = ..() + . += "Causes [effect_desc()] when attached to a kinetic crusher." /obj/item/crusher_trophy/proc/effect_desc() return "errors" /obj/item/crusher_trophy/attackby(obj/item/A, mob/living/user) - if(istype(A, /obj/item/twohanded/kinetic_crusher)) + if(istype(A, /obj/item/kinetic_crusher)) add_to(A, user) else ..() -/obj/item/crusher_trophy/proc/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user) +/obj/item/crusher_trophy/proc/add_to(obj/item/kinetic_crusher/H, mob/living/user) for(var/t in H.trophies) var/obj/item/crusher_trophy/T = t if(istype(T, denied_type) || istype(src, T.denied_type)) @@ -225,7 +225,7 @@ to_chat(user, "You attach [src] to [H].") return TRUE -/obj/item/crusher_trophy/proc/remove_from(obj/item/twohanded/kinetic_crusher/H, mob/living/user) +/obj/item/crusher_trophy/proc/remove_from(obj/item/kinetic_crusher/H, mob/living/user) forceMove(get_turf(H)) H.trophies -= src return TRUE @@ -312,12 +312,12 @@ /obj/item/crusher_trophy/legion_skull/effect_desc() return "a kinetic crusher to recharge [bonus_value*0.1] second\s faster" -/obj/item/crusher_trophy/legion_skull/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user) +/obj/item/crusher_trophy/legion_skull/add_to(obj/item/kinetic_crusher/H, mob/living/user) . = ..() if(.) H.charge_time -= bonus_value -/obj/item/crusher_trophy/legion_skull/remove_from(obj/item/twohanded/kinetic_crusher/H, mob/living/user) +/obj/item/crusher_trophy/legion_skull/remove_from(obj/item/kinetic_crusher/H, mob/living/user) . = ..() if(.) H.charge_time += bonus_value @@ -370,21 +370,19 @@ /obj/item/crusher_trophy/demon_claws/effect_desc() return "melee hits to do [bonus_value * 0.2] more damage and heal you for [bonus_value * 0.1], with 5X effect on mark detonation" -/obj/item/crusher_trophy/demon_claws/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user) +/obj/item/crusher_trophy/demon_claws/add_to(obj/item/kinetic_crusher/H, mob/living/user) . = ..() if(.) H.force += bonus_value * 0.2 - H.force_unwielded += bonus_value * 0.2 - H.force_wielded += bonus_value * 0.2 H.detonation_damage += bonus_value * 0.8 + AddComponent(/datum/component/two_handed, force_wielded=(20 + bonus_value * 0.2)) -/obj/item/crusher_trophy/demon_claws/remove_from(obj/item/twohanded/kinetic_crusher/H, mob/living/user) +/obj/item/crusher_trophy/demon_claws/remove_from(obj/item/kinetic_crusher/H, mob/living/user) . = ..() if(.) H.force -= bonus_value * 0.2 - H.force_unwielded -= bonus_value * 0.2 - H.force_wielded -= bonus_value * 0.2 H.detonation_damage -= bonus_value * 0.8 + AddComponent(/datum/component/two_handed, force_wielded=20) /obj/item/crusher_trophy/demon_claws/on_melee_hit(mob/living/target, mob/living/user) user.heal_ordered_damage(bonus_value * 0.1, damage_heal_order) diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm index f4d1afd8b7306..a687b1b46e4f9 100644 --- a/code/modules/mining/equipment/marker_beacons.dm +++ b/code/modules/mining/equipment/marker_beacons.dm @@ -37,8 +37,8 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sortList(list( /obj/item/stack/marker_beacon/examine(mob/user) . = ..() - . += {"Use in-hand to place a [singular_name].\n - Alt-click to select a color. Current color is [picked_color]."} + . += "Use in-hand to place a [singular_name].\n"+\ + "Alt-click to select a color. Current color is [picked_color]." /obj/item/stack/marker_beacon/update_icon() icon_state = "marker[lowertext(picked_color)]" @@ -72,7 +72,7 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sortList(list( icon = 'icons/obj/lighting.dmi' icon_state = "markerrandom" layer = BELOW_OPEN_DOOR_LAYER - armor = list("melee" = 50, "bullet" = 75, "laser" = 75, "energy" = 75, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 25, "acid" = 0) + armor = list("melee" = 50, "bullet" = 75, "laser" = 75, "energy" = 75, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 25, "acid" = 0, "stamina" = 0) max_integrity = 50 anchored = TRUE light_range = 2 @@ -135,7 +135,6 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sortList(list( return ..() /obj/structure/marker_beacon/AltClick(mob/living/user) - ..() if(!istype(user) || !user.canUseTopic(src, BE_CLOSE)) return var/input_color = input(user, "Choose a color.", "Beacon Color") as null|anything in GLOB.marker_beacon_colors diff --git a/code/modules/mining/equipment/mineral_scanner.dm b/code/modules/mining/equipment/mineral_scanner.dm index dadbdf2af3f69..46cf736273934 100644 --- a/code/modules/mining/equipment/mineral_scanner.dm +++ b/code/modules/mining/equipment/mineral_scanner.dm @@ -24,10 +24,12 @@ /obj/item/mining_scanner/admin /obj/item/mining_scanner/admin/attack_self(mob/user) - for(var/turf/closed/mineral/M in world) - if(M.scan_state) - M.icon_state = M.scan_state - qdel(src) + for(var/area/A as() in get_areas(/area, user.z)) + for(var/turf/closed/mineral/M in A) + if(M.scan_state) + var/obj/effect/temp_visual/mining_overlay/C = new /obj/effect/temp_visual/mining_overlay(M) + C.icon_state = M.scan_state + //qdel(src) /obj/item/t_scanner/adv_mining_scanner desc = "A scanner that automatically checks surrounding rock for useful minerals; it can also be used to stop gibtonite detonations. This one has an extended range." @@ -61,17 +63,54 @@ mineral_scan_pulse(t, range) /proc/mineral_scan_pulse(turf/T, range = world.view) - var/list/minerals = list() - for(var/turf/closed/mineral/M in range(range, T)) - if(M.scan_state) - minerals += M - if(LAZYLEN(minerals)) - for(var/turf/closed/mineral/M in minerals) - var/obj/effect/temp_visual/mining_overlay/oldC = locate(/obj/effect/temp_visual/mining_overlay) in M - if(oldC) - qdel(oldC) - var/obj/effect/temp_visual/mining_overlay/C = new /obj/effect/temp_visual/mining_overlay(M) - C.icon_state = M.scan_state + var/list/parsedrange = getviewsize(range) + var/xrange = (parsedrange[1] - 1) / 2 + var/yrange = (parsedrange[2] - 1) / 2 + var/cx = T.x + var/cy = T.y + for(var/r in 1 to max(xrange, yrange)) + var/xr = min(xrange, r) + var/yr = min(yrange, r) + var/turf/TL = locate(cx - xr, cy + yr, T.z) + var/turf/BL = locate(cx - xr, cy - yr, T.z) + var/turf/TR = locate(cx + xr, cy + yr, T.z) + var/turf/BR = locate(cx + xr, cy - yr, T.z) + var/list/turfs = list() + turfs += block(TL, TR) + turfs += block(TL, BL) + turfs |= block(BL, BR) + turfs |= block(BR, TR) + for(var/turf/closed/mineral/M in turfs) + new /obj/effect/temp_visual/mining_scanner(M) + if(M.scan_state) + var/obj/effect/temp_visual/mining_overlay/oldC = locate(/obj/effect/temp_visual/mining_overlay) in M + if(oldC) + qdel(oldC) + var/obj/effect/temp_visual/mining_overlay/C = new /obj/effect/temp_visual/mining_overlay(M) + C.icon_state = M.scan_state + sleep(1) + +/proc/pulse_effect(turf/T, range = world.view) + var/list/parsedrange = getviewsize(range) + var/xrange = (parsedrange[1] - 1) / 2 + var/yrange = (parsedrange[2] - 1) / 2 + var/cx = T.x + var/cy = T.y + for(var/r in 1 to max(xrange, yrange)) + var/xr = min(xrange, r) + var/yr = min(yrange, r) + var/turf/TL = locate(cx - xr, cy + yr, T.z) + var/turf/BL = locate(cx - xr, cy - yr, T.z) + var/turf/TR = locate(cx + xr, cy + yr, T.z) + var/turf/BR = locate(cx + xr, cy - yr, T.z) + var/list/turfs = list() + turfs += block(TL, TR) + turfs += block(TL, BL) + turfs |= block(BL, BR) + turfs |= block(BR, TR) + for(var/turf/M in turfs) + new /obj/effect/temp_visual/mining_scanner(M) + sleep(1) /obj/effect/temp_visual/mining_overlay plane = FULLSCREEN_PLANE @@ -85,3 +124,18 @@ /obj/effect/temp_visual/mining_overlay/Initialize() . = ..() animate(src, alpha = 0, time = duration, easing = EASE_IN) + +/obj/effect/temp_visual/mining_scanner + plane = FULLSCREEN_PLANE + layer = FLASH_LAYER + icon = 'icons/effects/mining_scanner.dmi' + appearance_flags = 0 + pixel_x = -224 + pixel_y = -224 + duration = 3 + alpha = 100 + icon_state = "mining_scan" + +/obj/effect/temp_visual/mining_scanner/Initialize() + . = ..() + animate(src, alpha = 0, time = duration, easing = EASE_IN) diff --git a/code/modules/mining/equipment/regenerative_core.dm b/code/modules/mining/equipment/regenerative_core.dm index a14b17c0ba57b..1f6cd4d11221e 100644 --- a/code/modules/mining/equipment/regenerative_core.dm +++ b/code/modules/mining/equipment/regenerative_core.dm @@ -12,9 +12,12 @@ if(!istype(C, /obj/item/organ/regenerative_core)) to_chat(user, "The stabilizer only works on certain types of monster organs, generally regenerative in nature.") return ..() + if(C.preserved) + to_chat(user, "[M] is already stabilised.") + return C.preserved() - to_chat(user, "You inject the [M] with the stabilizer. It will no longer go inert.") + to_chat(user, "You inject [M] with the stabilizer. It will no longer go inert.") qdel(src) /************************Hivelord core*******************/ diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index 5c68cdbcfb7a4..df2d72d1dc51b 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -5,7 +5,7 @@ dynamic_lighting = DYNAMIC_LIGHTING_FORCED requires_power = FALSE has_gravity = STANDARD_GRAVITY - valid_territory = FALSE + area_flags = BLOBS_ALLOWED | UNIQUE_AREA //Survival Capsule /obj/item/survivalcapsule @@ -33,8 +33,9 @@ /obj/item/survivalcapsule/examine(mob/user) . = ..() get_template() - . += "This capsule has the [template.name] stored." - . += template.description + if(template) + . += "This capsule has the [template.name] stored." + . += template.description /obj/item/survivalcapsule/attack_self() //Can't grab when capsule is New() because templates aren't loaded then @@ -91,14 +92,14 @@ icon_state = "capsulemed" icon = 'icons/obj/mining.dmi' template_id = "shelter_echo" - + /obj/item/survivalcapsule/space name = "space shelter capsule" desc = "A spaceworthy shelter designed for emergencies/construction in a bluespace capsule." icon_state = "capsuleeng" icon = 'icons/obj/mining.dmi' template_id = "shelter_eta" - + /obj/item/survivalcapsule/barricade name = "barricade capsule" desc = "A 3x3 glass barricade designed for security use with energy weapons." @@ -143,6 +144,7 @@ icon = 'icons/obj/doors/airlocks/survival/survival.dmi' overlays_file = 'icons/obj/doors/airlocks/survival/survival_overlays.dmi' assemblytype = /obj/structure/door_assembly/door_assembly_pod + anim_parts = "topbolts=0,6,0,3;bottombolts=0,-6,3,-6;top=0,4,0,2;bottom=0,-4,0,2;rightbolts=14,0,1.5,5;left=-15,0,1.5,5;right=14,0,1.5,5" /obj/machinery/door/airlock/survival_pod/glass opacity = FALSE @@ -349,7 +351,7 @@ /obj/item/energy_katana, /obj/item/hierophant_club, /obj/item/his_grace, - /obj/item/gun/ballistic/minigun, + /obj/item/gun/energy/minigun, /obj/item/gun/ballistic/automatic/l6_saw, /obj/item/gun/magic/staff/chaos, /obj/item/gun/magic/staff/spellblade, diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm index 1d769fa83cf34..07bd5b7d09443 100644 --- a/code/modules/mining/equipment/wormhole_jaunter.dm +++ b/code/modules/mining/equipment/wormhole_jaunter.dm @@ -58,7 +58,7 @@ var/mob/M = loc if(istype(M)) var/triggered = FALSE - if(M.get_item_by_slot(SLOT_BELT) == src) + if(M.get_item_by_slot(ITEM_SLOT_BELT) == src) if(power == 1) triggered = TRUE else if(power == 2 && prob(50)) @@ -70,7 +70,7 @@ activate(M) /obj/item/wormhole_jaunter/proc/chasm_react(mob/user) - if(user.get_item_by_slot(SLOT_BELT) == src) + if(user.get_item_by_slot(ITEM_SLOT_BELT) == src) to_chat(user, "Your [name] activates, saving you from the chasm!") SSblackbox.record_feedback("tally", "jaunter", 1, "Chasm") // chasm automatic activation activate(user, FALSE) diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm index bbba80d85a2b2..bf33eecde43ef 100644 --- a/code/modules/mining/fulton.dm +++ b/code/modules/mining/fulton.dm @@ -75,7 +75,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons) if(isliving(A)) var/mob/living/M = A M.Paralyze(320) // Keep them from moving during the duration of the extraction - M.buckled = 0 // Unbuckle them to prevent anchoring problems + M.buckled?.unbuckle_mob(M, TRUE) // Unbuckle them to prevent anchoring problems else A.anchored = TRUE A.density = FALSE @@ -112,9 +112,9 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons) L.SetSleeping(0) sleep(30) var/list/flooring_near_beacon = list() - for(var/turf/open/floor in orange(1, beacon)) + for(var/turf/open/floor in (RANGE_TURFS(1, beacon)-get_turf(beacon))) flooring_near_beacon += floor - holder_obj.forceMove(pick(flooring_near_beacon)) + do_teleport(holder_obj, pick(flooring_near_beacon), no_effects = TRUE, channel = TELEPORT_CHANNEL_FREE) animate(holder_obj, pixel_z = 10, time = 50) sleep(50) animate(holder_obj, pixel_z = 15, time = 10) @@ -169,7 +169,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons) /obj/effect/extraction_holder name = "extraction holder" - desc = "you shouldnt see this" + desc = "you shouldn't see this" var/atom/movable/stored_obj /obj/item/extraction_pack/proc/check_for_living_mobs(atom/A) diff --git a/code/modules/mining/laborcamp/laborshuttle.dm b/code/modules/mining/laborcamp/laborshuttle.dm index dbe607f42fa35..c4ceb4d51a795 100644 --- a/code/modules/mining/laborcamp/laborshuttle.dm +++ b/code/modules/mining/laborcamp/laborshuttle.dm @@ -1,4 +1,4 @@ -/obj/machinery/computer/shuttle/labor +/obj/machinery/computer/shuttle_flight/labor name = "labor shuttle console" desc = "Used to call and send the labor camp shuttle." circuit = /obj/item/circuitboard/computer/labor_shuttle @@ -7,21 +7,9 @@ req_access = list(ACCESS_BRIG) -/obj/machinery/computer/shuttle/labor/one_way +/obj/machinery/computer/shuttle_flight/labor/one_way name = "prisoner shuttle console" desc = "A one-way shuttle console, used to summon the shuttle to the labor camp." - possible_destinations = "laborcamp_away" + recall_docking_port_id = "laborcamp_away" circuit = /obj/item/circuitboard/computer/labor_shuttle/one_way req_access = list( ) - -/obj/machinery/computer/shuttle/labor/one_way/Topic(href, href_list) - if(href_list["move"]) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle("laborcamp") - if(!M) - to_chat(usr, "Cannot locate shuttle!") - return 0 - var/obj/docking_port/stationary/S = M.get_docked() - if(S && S.name == "laborcamp_away") - to_chat(usr, "Shuttle is already at the outpost!") - return 0 - ..() \ No newline at end of file diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index d179ab9a94373..250f35d82e5ce 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -18,9 +18,12 @@ GLOBAL_LIST(labor_sheet_values) /obj/machinery/mineral/labor_claim_console/Initialize() . = ..() - Radio = new/obj/item/radio(src) + Radio = new /obj/item/radio(src) Radio.listening = FALSE locate_stacking_machine() + //If we can't find a stacking machine end it all ok? + if(!stacking_machine) + return INITIALIZE_HINT_QDEL if(!GLOB.labor_sheet_values) var/sheet_list = list() @@ -31,6 +34,13 @@ GLOBAL_LIST(labor_sheet_values) sheet_list += list(list("ore" = initial(sheet.name), "value" = initial(sheet.point_value))) GLOB.labor_sheet_values = sortList(sheet_list, /proc/cmp_sheet_list) +/obj/machinery/mineral/labor_claim_console/Destroy() + QDEL_NULL(Radio) + if(stacking_machine) + stacking_machine.console = null + stacking_machine = null + return ..() + /proc/cmp_sheet_list(list/a, list/b) return a["value"] - b["value"] @@ -43,6 +53,7 @@ GLOBAL_LIST(labor_sheet_values) if(!ui) ui = new(user, src, "LaborClaimConsole") ui.open() + ui.set_autoupdate(TRUE) /obj/machinery/mineral/labor_claim_console/ui_static_data(mob/user) var/list/data = list() @@ -114,9 +125,7 @@ GLOBAL_LIST(labor_sheet_values) /obj/machinery/mineral/labor_claim_console/proc/locate_stacking_machine() stacking_machine = locate(/obj/machinery/mineral/stacking_machine, get_step(src, machinedir)) if(stacking_machine) - stacking_machine.CONSOLE = src - else - qdel(src) + stacking_machine.console = src /obj/machinery/mineral/labor_claim_console/emag_act(mob/user) if(!(obj_flags & EMAGGED)) diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm index 8be7ce7ee3bed..85bb74c97f57c 100644 --- a/code/modules/mining/lavaland/ash_flora.dm +++ b/code/modules/mining/lavaland/ash_flora.dm @@ -18,6 +18,7 @@ var/harvest_message_low = "You pick a mushroom, but fail to collect many shavings from its cap." var/harvest_message_med = "You pick a mushroom, carefully collecting the shavings from its cap." var/harvest_message_high = "You harvest and collect shavings from several mushroom caps." + var/destroy_on_harvest = FALSE var/harvested = FALSE var/base_icon var/regrowth_time_low = 8 MINUTES @@ -44,6 +45,8 @@ for(var/i in 1 to rand_harvested) new harvest(get_turf(src)) + if(destroy_on_harvest) + Destroy() icon_state = "[base_icon]p" name = harvested_name desc = harvested_desc @@ -58,7 +61,7 @@ harvested = FALSE /obj/structure/flora/ash/attackby(obj/item/W, mob/user, params) - if(!harvested && needs_sharp_harvest && W.sharpness) + if(!harvested && needs_sharp_harvest && W.is_sharp()) user.visible_message("[user] starts to harvest from [src] with [W].","You begin to harvest from [src] with [W].") if(do_after(user, harvest_time, target = src)) harvest(user) @@ -141,6 +144,21 @@ regrowth_time_low = 4800 regrowth_time_high = 7200 + +/obj/structure/flora/ash/strange + icon_state = "xpod" + name = "strange plant" + desc = "An alient plant born under warming glow of space radiation. What mysteries does it hold? Botanist should know." + harvest = /obj/item/reagent_containers/food/snacks/grown/random + needs_sharp_harvest = FALSE + harvest_amount_high = 2 + harvest_time = 10 + harvest_message_low = "You bravely pick a strange plant." + harvest_message_high = "You bravely pick a pair of strange plant." + light_range = 1.5 + light_power = 2.1 + destroy_on_harvest = TRUE + /obj/structure/flora/ash/cacti/Initialize(mapload) . = ..() // min dmg 3, max dmg 6, prob(70) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index b55eb29de51ad..7066e45779616 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -1,60 +1,59 @@ //The chests dropped by mob spawner tendrils. Also contains associated loot. -#define HIEROPHANT_CLUB_CARDINAL_DAMAGE 30 +#define HIEROPHANT_CLUB_CARDINAL_DAMAGE 15 /obj/structure/closet/crate/necropolis name = "necropolis chest" desc = "It's watching you closely." - icon_state = "necrocrate" + icon_state = "necro_crate" resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + door_anim_time = 0 /obj/structure/closet/crate/necropolis/tendril desc = "It's watching you suspiciously." + ///prevents bust_open to fire + integrity_failure = 0 + /// var to check if it got opened by a key + var/spawned_loot = FALSE -/obj/structure/closet/crate/necropolis/tendril/PopulateContents() - var/loot = rand(1,30) +/obj/structure/closet/crate/necropolis/tendril/Initialize() + . = ..() + RegisterSignal(src, COMSIG_PARENT_ATTACKBY, .proc/try_spawn_loot) + +/obj/structure/closet/crate/necropolis/tendril/proc/try_spawn_loot(datum/source, obj/item/item, mob/user, params) ///proc that handles key checking and generating loot + SIGNAL_HANDLER + + if(!istype(item, /obj/item/skeleton_key) || spawned_loot) + return FALSE + var/loot = rand(1,25) switch(loot) - if(1) - new /obj/item/shared_storage/red(src) - if(2) - new /obj/item/clothing/suit/space/hardsuit/cult(src) - if(3) - new /obj/item/soulstone/anybody(src) - if(4) - new /obj/item/katana/cursed(src) - if(5) - new /obj/item/clothing/glasses/godeye(src) - if(6) - new /obj/item/reagent_containers/glass/bottle/potion/flight(src) - if(7) - new /obj/item/pickaxe/diamond(src) - if(8) - if(prob(50)) - new /obj/item/disk/design_disk/modkit_disc/resonator_blast(src) - else - new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src) + if(1 to 2) + new /obj/item/disk/design_disk/modkit_disc/resonator_blast(src) //Doubled chance to receive upgrade disk that is directly relevant to mining + if(3 to 4) + new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src) + if(5 to 6) + new /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe(src) + if(7 to 8) + new /obj/item/disk/design_disk/modkit_disc/bounty(src) if(9) - new /obj/item/rod_of_asclepius(src) + new /obj/item/borg/upgrade/modkit/lifesteal(src) if(10) - new /obj/item/organ/heart/cursed/wizard(src) + new /obj/item/shared_storage/red(src) if(11) - new /obj/item/ship_in_a_bottle(src) + new /obj/item/clothing/glasses/godeye(src) if(12) - new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/beserker(src) + new /obj/item/reagent_containers/glass/bottle/potion/flight(src) if(13) - new /obj/item/jacobs_ladder(src) + new /obj/item/pickaxe/diamond(src) //Ashwalkers exist. This is actually a great drop for them if(14) - new /obj/item/nullrod/scythe/talking(src) + new /obj/item/rod_of_asclepius(src) if(15) - new /obj/item/nullrod/armblade(src) + new /obj/item/organ/heart/cursed/wizard(src) if(16) - new /obj/item/guardiancreator/hive(src) + new /obj/item/ship_in_a_bottle(src) if(17) - if(prob(50)) - new /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe(src) - else - new /obj/item/disk/design_disk/modkit_disc/bounty(src) + new /obj/item/jacobs_ladder(src) if(18) new /obj/item/warp_cube/red(src) if(19) @@ -64,25 +63,28 @@ if(21) new /obj/item/gun/magic/hook(src) if(22) - new /obj/item/voodoo(src) - if(23) - new /obj/item/grenade/clusterbuster/inferno(src) - if(24) - new /obj/item/reagent_containers/food/drinks/bottle/holywater/hell(src) - new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor(src) - if(25) - new /obj/item/book/granter/spell/summonitem(src) - if(26) new /obj/item/book_of_babel(src) - if(27) - new /obj/item/borg/upgrade/modkit/lifesteal(src) - new /obj/item/bedsheet/cult(src) - if(28) + if(23) new /obj/item/clothing/neck/necklace/memento_mori(src) - if(29) + if(24) new /obj/item/reagent_containers/glass/waterbottle/relic(src) - if(30) + if(25) new /obj/item/reagent_containers/glass/bottle/necropolis_seed(src) + spawned_loot = TRUE + qdel(item) + to_chat(user, "You disable the magic lock, revealing the loot.") + return TRUE + +/obj/structure/closet/crate/necropolis/tendril/can_open(mob/living/user, force = FALSE) + if(!spawned_loot) + return FALSE + return ..() + +/obj/structure/closet/crate/necropolis/tendril/examine(mob/user) + . = ..() + if(!spawned_loot) + . += "You need a skeleton key to open it." + //KA modkit design discs /obj/item/disk/design_disk/modkit_disc @@ -216,7 +218,7 @@ var/mob/living/carbon/human/active_owner /obj/item/clothing/neck/necklace/memento_mori/item_action_slot_check(slot) - return slot == SLOT_NECK + return slot == ITEM_SLOT_NECK /obj/item/clothing/neck/necklace/memento_mori/dropped(mob/user) ..() @@ -329,6 +331,8 @@ to_chat(orbits.parent, "Your vision returns to normal.") /obj/effect/wisp/proc/update_user_sight(mob/user) + SIGNAL_HANDLER + user.sight |= sight_flags if(!isnull(lighting_alpha)) user.lighting_alpha = min(user.lighting_alpha, lighting_alpha) @@ -357,15 +361,26 @@ var/obj/item/warp_cube/linked var/teleporting = FALSE +/obj/item/warp_cube/Destroy() + if(!QDELETED(linked)) + qdel(linked) + linked = null + return ..() + /obj/item/warp_cube/attack_self(mob/user) if(!linked) to_chat(user, "[src] fizzles uselessly.") return if(teleporting) return + var/turf/T = get_turf(src) + var/area/A1 = get_area(T) + var/area/A2 = get_area(linked) + if(A1.teleport_restriction || A2.teleport_restriction) + to_chat(user, "[src] fizzles gently as it fails to breach the bluespace veil.") + return teleporting = TRUE linked.teleporting = TRUE - var/turf/T = get_turf(src) new /obj/effect/temp_visual/warp_cube(T, user, teleport_color, TRUE) SSblackbox.record_feedback("tally", "warp_cube", 1, type) new /obj/effect/temp_visual/warp_cube(get_turf(linked), user, linked.teleport_color, FALSE) @@ -379,7 +394,7 @@ user.forceMove(get_turf(link_holder)) qdel(link_holder) return - link_holder.forceMove(get_turf(linked)) + do_teleport(link_holder, get_turf(linked), no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC) sleep(2.5) if(QDELETED(user)) qdel(link_holder) @@ -421,8 +436,8 @@ righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi' fire_sound = 'sound/weapons/batonextend.ogg' max_charges = 1 - item_flags = NEEDS_PERMIT | NOBLUDGEON - force = 18 + item_flags = NEEDS_PERMIT + force = 15 attack_weight = 2 /obj/item/ammo_casing/magic/hook @@ -437,11 +452,11 @@ icon_state = "hook" icon = 'icons/obj/lavaland/artefacts.dmi' pass_flags = PASSTABLE - damage = 25 + damage = 10 armour_penetration = 100 damage_type = BRUTE hitsound = 'sound/effects/splat.ogg' - paralyze = 30 + knockdown = 30 var/chain /obj/item/projectile/hook/fire(setAngle) @@ -692,13 +707,31 @@ ///Bosses +//Legion + +/obj/structure/closet/crate/necropolis/legion + name = "legion chest" + +/obj/structure/closet/crate/necropolis/legion/PopulateContents() + var/list/choices = subtypesof(/obj/machinery/anomalous_crystal) + var/random_crystal = pick(choices) + new random_crystal(src) + new /obj/effect/spawner/lootdrop/megafaunaore(src) + //Miniboss Miner +/obj/structure/closet/crate/necropolis/bdm + name = "blood-drunk miner chest" + +/obj/structure/closet/crate/necropolis/bdm/PopulateContents() + new /obj/item/melee/transforming/cleaving_saw(src) + new /obj/effect/spawner/lootdrop/megafaunaore(src) + /obj/item/melee/transforming/cleaving_saw name = "cleaving saw" desc = "This saw, effective at drawing the blood of beasts, transforms into a long cleaver that makes use of centrifugal force." - force = 12 - force_on = 20 //force when active + force = 8 + force_on = 15 //force when active throwforce = 20 throwforce_on = 20 icon = 'icons/obj/lavaland/artefacts.dmi' @@ -716,16 +749,16 @@ block_upgrade_walk = 1 w_class = WEIGHT_CLASS_BULKY sharpness = IS_SHARP - faction_bonus_force = 30 + faction_bonus_force = 45 nemesis_factions = list("mining", "boss") var/transform_cooldown var/swiping = FALSE /obj/item/melee/transforming/cleaving_saw/examine(mob/user) . = ..() - . += {"It is [active ? "open, will cleave enemies in a wide arc and deal additional damage to fauna":"closed, and can be used for rapid consecutive attacks that cause fauna to bleed"].\n - Both modes will build up existing bleed effects, doing a burst of high damage if the bleed is built up high enough.\n - Transforming it immediately after an attack causes the next attack to come out faster."} + . += "It is [active ? "open, will cleave enemies in a wide arc and deal additional damage to fauna":"closed, and can be used for rapid consecutive attacks that cause fauna to bleed"].\n"+\ + "Both modes will build up existing bleed effects, doing a burst of high damage if the bleed is built up high enough.\n"+\ + "Transforming it immediately after an attack causes the next attack to come out faster." /obj/item/melee/transforming/cleaving_saw/suicide_act(mob/user) user.visible_message("[user] is [active ? "closing [src] on [user.p_their()] neck" : "opening [src] into [user.p_their()] chest"]! It looks like [user.p_theyre()] trying to commit suicide!") @@ -792,24 +825,10 @@ name = "dragon chest" /obj/structure/closet/crate/necropolis/dragon/PopulateContents() - var/loot = rand(1,4) - switch(loot) - if(1) - new /obj/item/melee/ghost_sword(src) - if(2) - new /obj/item/lava_staff(src) - if(3) - new /obj/item/book/granter/spell/sacredflame(src) - new /obj/item/gun/magic/wand/fireball(src) - if(4) - new /obj/item/dragons_blood(src) + new /obj/effect/spawner/lootdrop/megafaunaore(src) + new /obj/item/dragons_blood(src) -/obj/structure/closet/crate/necropolis/dragon/crusher - name = "firey dragon chest" - -/obj/structure/closet/crate/necropolis/dragon/crusher/PopulateContents() - ..() - new /obj/item/crusher_trophy/tail_spike(src) +// Ghost Sword - left in for other references and admin shenanigans /obj/item/melee/ghost_sword name = "\improper spectral blade" @@ -919,7 +938,7 @@ return var/mob/living/carbon/human/H = user - var/random = rand(1,4) + var/random = rand(1,3) switch(random) if(1) @@ -931,11 +950,6 @@ to_chat(user, "Your flesh begins to melt! Miraculously, you seem fine otherwise.") H.set_species(/datum/species/skeleton) if(3) - to_chat(user, "Power courses through you! You can now shift your form at will.") - if(user.mind) - var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/D = new - user.mind.AddSpell(D) - if(4) to_chat(user, "You feel like you could walk straight through lava now.") H.weather_immunities |= "lava" @@ -949,7 +963,7 @@ agent = "dragon's blood" desc = "What do dragons have to do with Space Station 13?" stage_prob = 20 - severity = DISEASE_SEVERITY_BIOHAZARD + danger = DISEASE_BIOHAZARD visibility_flags = 0 stage1 = list("Your bones ache.") stage2 = list("Your skin feels scaly.") @@ -993,7 +1007,7 @@ if(is_type_in_typecache(target, banned_turfs)) return - if(target in view(user.client.view, get_turf(user))) + if(user in viewers(user.client.view, get_turf(target))) var/turf/open/T = get_turf(target) if(!istype(T)) @@ -1033,21 +1047,7 @@ /obj/structure/closet/crate/necropolis/bubblegum/PopulateContents() new /obj/item/clothing/suit/space/hostile_environment(src) new /obj/item/clothing/head/helmet/space/hostile_environment(src) - var/loot = rand(1,3) - switch(loot) - if(1) - new /obj/item/mayhem(src) - if(2) - new /obj/item/blood_contract(src) - if(3) - new /obj/item/gun/magic/staff/spellblade(src) - -/obj/structure/closet/crate/necropolis/bubblegum/crusher - name = "bloody bubblegum chest" - -/obj/structure/closet/crate/necropolis/bubblegum/crusher/PopulateContents() - ..() - new /obj/item/crusher_trophy/demon_claws(src) + new /obj/effect/spawner/lootdrop/megafaunaore(src) /obj/item/mayhem name = "mayhem in a bottle" @@ -1119,19 +1119,19 @@ return ..() /obj/structure/closet/crate/necropolis/colossus/PopulateContents() - var/list/choices = subtypesof(/obj/machinery/anomalous_crystal) - var/random_crystal = pick(choices) - new random_crystal(src) new /obj/item/organ/vocal_cords/colossus(src) + new /obj/effect/spawner/lootdrop/megafaunaore(src) -/obj/structure/closet/crate/necropolis/colossus/crusher - name = "angelic colossus chest" - -/obj/structure/closet/crate/necropolis/colossus/crusher/PopulateContents() - ..() - new /obj/item/crusher_trophy/blaster_tubes(src) //Hierophant + +/obj/structure/closet/crate/necropolis/hierophant + name = "hierophant chest" + +/obj/structure/closet/crate/necropolis/hierophant/PopulateContents() + new /obj/item/hierophant_club(src) + new /obj/effect/spawner/lootdrop/megafaunaore(src) + /obj/item/hierophant_club name = "hierophant club" desc = "The strange technology of this large club allows various nigh-magical feats. It used to beat you, but now you can set the beat." @@ -1144,7 +1144,7 @@ inhand_y_dimension = 64 slot_flags = ITEM_SLOT_BACK w_class = WEIGHT_CLASS_BULKY - force = 15 + force = 5 //Melee attacks also invoke a 15 burn damage AoE, for a total of 20 damage attack_verb = list("clubbed", "beat", "pummeled") hitsound = 'sound/weapons/sonic_jackhammer.ogg' actions_types = list(/datum/action/item_action/vortex_recall, /datum/action/item_action/toggle_unfriendly_fire) @@ -1171,7 +1171,7 @@ for(var/obj/item/I in user) if(I != src) user.dropItemToGround(I) - for(var/turf/T in RANGE_TURFS(1, user)) + for(var/turf/T as() in RANGE_TURFS(1, user)) var/obj/effect/temp_visual/hierophant/blast/B = new(T, user, TRUE) B.damage = 0 user.dropItemToGround(src) //Drop us last, so it goes on top of their stuff @@ -1191,12 +1191,12 @@ if(ismineralturf(target) && get_dist(user, target) < 6) //target is minerals, we can hit it(even if we can't see it) INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user) timer = world.time + cooldown_time - else if(target in view(5, get_turf(user))) //if the target is in view, hit it + else if(user in viewers(5, get_turf(target))) //if the target is in view, hit it timer = world.time + cooldown_time if(isliving(target) && chaser_timer <= world.time) //living and chasers off cooldown? fire one! chaser_timer = world.time + chaser_cooldown var/obj/effect/temp_visual/hierophant/chaser/C = new(get_turf(user), user, target, chaser_speed, friendly_fire_check) - C.damage = 30 + C.damage = 15 C.monster_damage_boost = FALSE log_combat(user, target, "fired a chaser at", src) else @@ -1316,13 +1316,13 @@ user.log_message("teleported self from [AREACOORD(source)] to [beacon]", LOG_GAME) new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user) new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, user) - for(var/t in RANGE_TURFS(1, T)) + for(var/turf/t as() in RANGE_TURFS(1, T)) var/obj/effect/temp_visual/hierophant/blast/B = new /obj/effect/temp_visual/hierophant/blast(t, user, TRUE) //blasts produced will not hurt allies B.damage = 30 - for(var/t in RANGE_TURFS(1, source)) + for(var/turf/t as() in RANGE_TURFS(1, source)) var/obj/effect/temp_visual/hierophant/blast/B = new /obj/effect/temp_visual/hierophant/blast(t, user, TRUE) //but absolutely will hurt enemies B.damage = 30 - for(var/mob/living/L in range(1, source)) + for(var/mob/living/L in hearers(1, source)) INVOKE_ASYNC(src, .proc/teleport_mob, source, L, T, user) //regardless, take all mobs near us along sleep(6) //at this point the blasts detonate if(beacon) @@ -1350,7 +1350,7 @@ sleep(2) if(!M) return - M.forceMove(turf_to_teleport_to) + do_teleport(M, turf_to_teleport_to, no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC) sleep(1) if(!M) return @@ -1399,17 +1399,27 @@ var/obj/effect/temp_visual/hierophant/blast/B = new(t, user, friendly_fire_check) B.damage = 15 //keeps monster damage boost due to lower damage - //Just some minor stuff /obj/structure/closet/crate/necropolis/puzzle name = "puzzling chest" /obj/structure/closet/crate/necropolis/puzzle/PopulateContents() - var/loot = rand(1,3) + var/loot = rand(1,5) switch(loot) if(1) - new /obj/item/soulstone/anybody(src) + new /obj/item/disk/design_disk/modkit_disc/resonator_blast(src) if(2) - new /obj/item/wisp_lantern(src) + new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src) if(3) - new /obj/item/prisoncube(src) + new /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe(src) + if(4) + new /obj/item/disk/design_disk/modkit_disc/bounty(src) + if(5) + new /obj/item/borg/upgrade/modkit/lifesteal(src) + +/obj/item/skeleton_key + name = "skeleton key" + desc = "An artifact usually found in the hands of the natives of lavaland, which NT now holds a monopoly on." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "skeleton_key" + w_class = WEIGHT_CLASS_SMALL diff --git a/code/modules/mining/lavaland/ruins/gym.dm b/code/modules/mining/lavaland/ruins/gym.dm index d9cdf70ced234..d3a09764b8235 100644 --- a/code/modules/mining/lavaland/ruins/gym.dm +++ b/code/modules/mining/lavaland/ruins/gym.dm @@ -33,6 +33,9 @@ . = ..() if(.) return + if(!Adjacent(user)) + to_chat(user, "You're too far away to get swole!") + return if(obj_flags & IN_USE) to_chat(user, "It's already in use - wait a bit.") return @@ -51,10 +54,10 @@ user.pixel_y = 0 var/finishmessage = pick("You feel stronger!","You feel like you can take on the world!","You feel robust!","You feel indestructible!") - + if (user.client) SSmedals.UnlockMedal(MEDAL_USE_WEIGHT_MACHINE,user.client) - + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "exercise", /datum/mood_event/exercise) icon_state = initial(icon_state) diff --git a/code/modules/mining/machine_bluespaceminer.dm b/code/modules/mining/machine_bluespaceminer.dm deleted file mode 100644 index 226e66bdf9907..0000000000000 --- a/code/modules/mining/machine_bluespaceminer.dm +++ /dev/null @@ -1,40 +0,0 @@ -/obj/machinery/mineral/bluespace_miner - name = "bluespace mining machine" - desc = "A machine that uses the magic of Bluespace to slowly generate materials and add them to a linked ore silo." - icon = 'icons/obj/machines/mining_machines.dmi' - icon_state = "bs_miner" - density = TRUE - circuit = /obj/item/circuitboard/machine/bluespace_miner - layer = BELOW_OBJ_LAYER - var/list/ore_rates = list(/datum/material/iron = 0.6, /datum/material/glass = 0.6, /datum/material/copper = 0.4, /datum/material/plasma = 0.2, /datum/material/silver = 0.2, /datum/material/gold = 0.1, /datum/material/titanium = 0.1, /datum/material/uranium = 0.1, /datum/material/diamond = 0.1) - var/datum/component/remote_materials/materials - -/obj/machinery/mineral/bluespace_miner/Initialize(mapload) - . = ..() - materials = AddComponent(/datum/component/remote_materials, "bsm", mapload) - -/obj/machinery/mineral/bluespace_miner/Destroy() - materials = null - return ..() - -/obj/machinery/mineral/bluespace_miner/multitool_act(mob/living/user, obj/item/multitool/M) - if(istype(M)) - if(!M.buffer || !istype(M.buffer, /obj/machinery/ore_silo)) - to_chat(user, "You need to multitool the ore silo first.") - return FALSE - -/obj/machinery/mineral/bluespace_miner/examine(mob/user) - . = ..() - if(!materials?.silo) - . += "No ore silo connected. Use a multi-tool to link an ore silo to this machine." - else if(materials?.on_hold()) - . += "Ore silo access is on hold, please contact the quartermaster." - -/obj/machinery/mineral/bluespace_miner/process() - if(!materials?.silo || materials?.on_hold()) - return - var/datum/component/material_container/mat_container = materials.mat_container - if(!mat_container || panel_open || !powered()) - return - var/datum/material/ore = pick(ore_rates) - mat_container.insert_amount_mat((ore_rates[ore] * 1000), ore) diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index 47096a9a601c2..e40a8ad72e5c0 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -1,11 +1,50 @@ -#define SMELT_AMOUNT 10 +/// Smelt amount per second +#define SMELT_AMOUNT 5 /**********************Mineral processing unit console**************************/ /obj/machinery/mineral + processing_flags = START_PROCESSING_MANUALLY + subsystem_type = /datum/controller/subsystem/processing/fastprocess + /// The current direction of `input_turf`, in relation to the machine. var/input_dir = NORTH + /// The current direction, in relation to the machine, that items will be output to. var/output_dir = SOUTH + /// The turf the machines listens to for items to pick up. Calls the `pickup_item()` proc. + var/turf/input_turf = null + /// Determines if this machine needs to pick up items. Used to avoid registering signals to `/mineral` machines that don't pickup items. + var/needs_item_input = FALSE +/obj/machinery/mineral/Initialize(mapload) + . = ..() + if(needs_item_input) + register_input_turf() + +/// Gets the turf in the `input_dir` direction adjacent to the machine, and registers signals for ATOM_ENTERED and ATOM_CREATED. Calls the `pickup_item()` proc when it recieves these signals. +/obj/machinery/mineral/proc/register_input_turf() + input_turf = get_step(src, input_dir) + if(input_turf) // make sure there is actually a turf + RegisterSignal(input_turf, list(COMSIG_ATOM_CREATED, COMSIG_ATOM_ENTERED), .proc/pickup_item) + +/// Unregisters signals that are registered the machine's input turf, if it has one. +/obj/machinery/mineral/proc/unregister_input_turf() + if(input_turf) + UnregisterSignal(input_turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_CREATED)) + +/** + Base proc for all `/mineral` subtype machines to use. Place your item pickup behavior in this proc when you override it for your specific machine. + Called when the COMSIG_ATOM_ENTERED and COMSIG_ATOM_CREATED signals are sent. + Arguments: + * source - the turf that is listening for the signals. + * target - the atom that just moved onto the `source` turf. + * oldLoc - the old location that `target` was at before moving onto `source`. +*/ +/obj/machinery/mineral/proc/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + SIGNAL_HANDLER + + return + +/// Generic unloading proc. Takes an atom as an argument and forceMove's it to the turf adjacent to this machine in the `output_dir` direction. /obj/machinery/mineral/proc/unload_mineral(atom/movable/S) S.forceMove(drop_location()) var/turf/T = get_step(src,output_dir) @@ -19,7 +58,6 @@ density = TRUE var/obj/machinery/mineral/processing_unit/machine = null var/machinedir = EAST - speed_process = TRUE /obj/machinery/mineral/processing_unit_console/Initialize() . = ..() @@ -58,6 +96,7 @@ if(href_list["set_on"]) machine.on = (href_list["set_on"] == "on") + machine.begin_processing() updateUsrDialog() return @@ -75,6 +114,7 @@ icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "furnace" density = TRUE + needs_item_input = TRUE var/obj/machinery/mineral/CONSOLE = null var/on = FALSE var/datum/material/selected_material = null @@ -93,11 +133,9 @@ QDEL_NULL(stored_research) return ..() -/obj/machinery/mineral/processing_unit/HasProximity(atom/movable/AM) - if(istype(AM, /obj/item/stack/ore) && AM.loc == get_step(src, input_dir)) - process_ore(AM) - /obj/machinery/mineral/processing_unit/proc/process_ore(obj/item/stack/ore/O) + if(QDELETED(O)) + return var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/material_amount = materials.get_item_material_amount(O) if(!materials.has_space(material_amount)) @@ -142,23 +180,31 @@ return dat -/obj/machinery/mineral/processing_unit/process() - if (on) +/obj/machinery/mineral/processing_unit/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + if(QDELETED(target)) + return + if(istype(target, /obj/item/stack/ore)) + process_ore(target) + +/obj/machinery/mineral/processing_unit/process(delta_time) + if(on) if(selected_material) - smelt_ore() + smelt_ore(delta_time) else if(selected_alloy) - smelt_alloy() + smelt_alloy(delta_time) if(CONSOLE) CONSOLE.updateUsrDialog() + else + end_processing() -/obj/machinery/mineral/processing_unit/proc/smelt_ore() +/obj/machinery/mineral/processing_unit/proc/smelt_ore(delta_time = 2) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/datum/material/mat = selected_material if(mat) - var/sheets_to_remove = (materials.materials[mat] >= (MINERAL_MATERIAL_AMOUNT * SMELT_AMOUNT) ) ? SMELT_AMOUNT : round(materials.materials[mat] / MINERAL_MATERIAL_AMOUNT) + var/sheets_to_remove = (materials.materials[mat] >= (MINERAL_MATERIAL_AMOUNT * SMELT_AMOUNT * delta_time) ) ? SMELT_AMOUNT * delta_time : round(materials.materials[mat] / MINERAL_MATERIAL_AMOUNT) if(!sheets_to_remove) on = FALSE else @@ -166,13 +212,13 @@ materials.retrieve_sheets(sheets_to_remove, mat, out) -/obj/machinery/mineral/processing_unit/proc/smelt_alloy() +/obj/machinery/mineral/processing_unit/proc/smelt_alloy(delta_time = 2) var/datum/design/alloy = stored_research.isDesignResearchedID(selected_alloy) //check if it's a valid design if(!alloy) on = FALSE return - var/amount = can_smelt(alloy) + var/amount = can_smelt(alloy, delta_time) if(!amount) on = FALSE @@ -183,11 +229,11 @@ generate_mineral(alloy.build_path) -/obj/machinery/mineral/processing_unit/proc/can_smelt(datum/design/D) +/obj/machinery/mineral/processing_unit/proc/can_smelt(datum/design/D, delta_time = 2) if(D.make_reagents.len) return FALSE - var/build_amount = SMELT_AMOUNT + var/build_amount = SMELT_AMOUNT * delta_time var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 47e537f7d08ec..42d6e8649c722 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -10,20 +10,19 @@ input_dir = NORTH output_dir = SOUTH req_access = list(ACCESS_MINERAL_STOREROOM) - speed_process = TRUE circuit = /obj/item/circuitboard/machine/ore_redemption - + needs_item_input = TRUE + processing_flags = START_PROCESSING_MANUALLY layer = BELOW_OBJ_LAYER var/obj/item/card/id/inserted_id var/points = 0 - var/ore_pickup_rate = 15 var/sheet_per_ore = 1 var/point_upgrade = 1 var/list/ore_values = list(/datum/material/iron = 1, /datum/material/glass = 1, /datum/material/copper = 5, /datum/material/plasma = 15, /datum/material/silver = 16, /datum/material/gold = 18, /datum/material/titanium = 30, /datum/material/uranium = 30, /datum/material/diamond = 50, /datum/material/bluespace = 50, /datum/material/bananium = 60) - var/message_sent = FALSE - var/list/ore_buffer = list() + /// Variable that holds a timer which is used for callbacks to `send_console_message()`. Used for preventing multiple calls to this proc while the ORM is eating a stack of ores. + var/console_notify_timer var/datum/techweb/stored_research var/obj/item/disk/design_disk/inserted_disk var/datum/component/remote_materials/materials @@ -39,39 +38,34 @@ return ..() /obj/machinery/mineral/ore_redemption/RefreshParts() - var/ore_pickup_rate_temp = 15 var/point_upgrade_temp = 1 var/sheet_per_ore_temp = 1 for(var/obj/item/stock_parts/matter_bin/B in component_parts) sheet_per_ore_temp = 0.65 + (0.35 * B.rating) - for(var/obj/item/stock_parts/manipulator/M in component_parts) - ore_pickup_rate_temp = 15 * M.rating for(var/obj/item/stock_parts/micro_laser/L in component_parts) point_upgrade_temp = 0.65 + (0.35 * L.rating) - ore_pickup_rate = ore_pickup_rate_temp point_upgrade = point_upgrade_temp sheet_per_ore = round(sheet_per_ore_temp, 0.01) /obj/machinery/mineral/ore_redemption/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Smelting [sheet_per_ore] sheet(s) per piece of ore. Reward point generation at [point_upgrade*100]%. Ore pickup speed at [ore_pickup_rate]." + . += "The status display reads: Smelting [sheet_per_ore] sheet(s) per piece of ore. Reward point generation at [point_upgrade*100]%." if(panel_open) . += "Alt-click to rotate the input and output direction." /obj/machinery/mineral/ore_redemption/proc/smelt_ore(obj/item/stack/ore/O) + if(QDELETED(O)) + return var/datum/component/material_container/mat_container = materials.mat_container if (!mat_container) return + console_notify_timer = null + if(O.refined_type == null) return - ore_buffer -= O - - if(O?.refined_type) - points += O.points * point_upgrade * O.amount - var/material_amount = mat_container.get_item_material_amount(O) if(!material_amount) @@ -81,6 +75,8 @@ unload_mineral(O) else + if(O?.refined_type) + points += O.points * point_upgrade * O.amount var/mats = O.materials & mat_container.materials var/amount = O.amount mat_container.insert_item(O, sheet_per_ore) //insert it @@ -114,17 +110,15 @@ return build_amount /obj/machinery/mineral/ore_redemption/proc/process_ores(list/ores_to_process) - var/current_amount = 0 for(var/ore in ores_to_process) - if(current_amount >= ore_pickup_rate) - break smelt_ore(ore) /obj/machinery/mineral/ore_redemption/proc/send_console_message() var/datum/component/material_container/mat_container = materials.mat_container if(!mat_container || !is_station_level(z)) return - message_sent = TRUE + + console_notify_timer = null var/area/A = get_area(src) var/msg = "Now available in [A]: " @@ -150,26 +144,34 @@ )) signal.send_to_receivers() -/obj/machinery/mineral/ore_redemption/process() +/obj/machinery/mineral/ore_redemption/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + if(QDELETED(target)) + return if(!materials.mat_container || panel_open || !powered()) return - var/atom/input = get_step(src, input_dir) - var/obj/structure/ore_box/OB = locate() in input - if(OB) - input = OB - - for(var/obj/item/stack/ore/O in input) - if(QDELETED(O)) - continue - ore_buffer |= O - O.forceMove(src) - CHECK_TICK - - if(LAZYLEN(ore_buffer)) - message_sent = FALSE - process_ores(ore_buffer) - else if(!message_sent) - send_console_message() + + if(istype(target, /obj/structure/ore_box)) + var/obj/structure/ore_box/box = target + process_ores(box.contents) + box.ui_update() + else if(istype(target, /obj/item/stack/ore)) + var/obj/item/stack/ore/O = target + smelt_ore(O) + else + return + + if(!console_notify_timer) + // gives 5 seconds for a load of ores to be sucked up by the ORM before it sends out request console notifications. This should be enough time for most deposits that people make + console_notify_timer = addtimer(CALLBACK(src, .proc/send_console_message), 5 SECONDS) + +/obj/machinery/mineral/ore_redemption/default_unfasten_wrench(mob/user, obj/item/I) + . = ..() + if(!.) + return + if(anchored) + register_input_turf() // someone just wrenched us down, re-register the turf + else + unregister_input_turf() // someone just un-wrenched us, unregister the turf /obj/machinery/mineral/ore_redemption/attackby(obj/item/W, mob/user, params) if(default_unfasten_wrench(user, W)) @@ -197,13 +199,14 @@ return ..() /obj/machinery/mineral/ore_redemption/AltClick(mob/living/user) - ..() if(!user.canUseTopic(src, BE_CLOSE)) return - if (panel_open) + if(panel_open) input_dir = turn(input_dir, -90) output_dir = turn(output_dir, -90) to_chat(user, "You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)].") + unregister_input_turf() // someone just rotated the input and output directions, unregister the old turf + register_input_turf() // register the new one return TRUE @@ -215,6 +218,7 @@ if(!ui) ui = new(user, src, "OreRedemptionMachine") ui.open() + ui.set_autoupdate(TRUE) // Material amounts /obj/machinery/mineral/ore_redemption/ui_data(mob/user) var/list/data = list() @@ -234,6 +238,8 @@ for(var/v in stored_research.researched_designs) var/datum/design/D = SSresearch.techweb_design_by_id(v) data["alloys"] += list(list("name" = D.name, "id" = D.id, "amount" = can_smelt_alloy(D))) + else + data["alloys"] = null // In case we lose mat_container while UI is open somehow if (!mat_container) data["disconnected"] = "local mineral storage is unavailable" @@ -241,6 +247,8 @@ data["disconnected"] = "no ore silo connection is available; storing locally" else if (materials.on_hold()) data["disconnected"] = "mineral withdrawal is on hold" + else + data["disconnected"] = null data["diskDesigns"] = list() data["hasDisk"] = FALSE @@ -265,11 +273,11 @@ if(points) if(I?.mining_points += points) points = 0 + . = TRUE else to_chat(usr, "No ID detected.") else to_chat(usr, "No points to claim.") - return TRUE if("Release") if(!mat_container) return @@ -303,26 +311,26 @@ mats[mat] = MINERAL_MATERIAL_AMOUNT materials.silo_log(src, "released", -count, "sheets", mats) //Logging deleted for quick coding - return TRUE + . = TRUE if("diskInsert") var/obj/item/disk/design_disk/disk = usr.get_active_held_item() if(istype(disk)) if(!usr.transferItemToLoc(disk,src)) return inserted_disk = disk + . = TRUE else to_chat(usr, "Not a valid Design Disk!") - return TRUE if("diskEject") if(inserted_disk) usr.put_in_hands(inserted_disk) inserted_disk = null - return TRUE + . = TRUE if("diskUpload") var/n = text2num(params["design"]) if(inserted_disk && inserted_disk.blueprints && inserted_disk.blueprints[n]) stored_research.add_design(inserted_disk.blueprints[n]) - return TRUE + . = TRUE if("Smelt") if(!mat_container) return @@ -347,9 +355,9 @@ else output = new alloy.build_path(src) unload_mineral(output) + . = TRUE else to_chat(usr, "Required access not found.") - return TRUE /obj/machinery/mineral/ore_redemption/ex_act(severity, target) do_sparks(5, TRUE, src) diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm index 61cb675cf057f..45cb07e25dad9 100644 --- a/code/modules/mining/machine_silo.dm +++ b/code/modules/mining/machine_silo.dm @@ -15,14 +15,22 @@ GLOBAL_LIST_EMPTY(silo_access_logs) /obj/machinery/ore_silo/Initialize(mapload) . = ..() - AddComponent(/datum/component/material_container, - list(/datum/material/iron, /datum/material/glass, /datum/material/copper, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace, /datum/material/plastic), - INFINITY, - FALSE, - /obj/item/stack, - null, - null, - TRUE) + var/static/list/materials_list = list( + /datum/material/iron, + /datum/material/glass, + /datum/material/copper, + /datum/material/silver, + /datum/material/gold, + /datum/material/diamond, + /datum/material/plasma, + /datum/material/uranium, + /datum/material/bananium, + /datum/material/titanium, + /datum/material/bluespace, + /datum/material/plastic, + ) + AddComponent(/datum/component/material_container, materials_list, INFINITY, allowed_types=/obj/item/stack, _disable_attackby=TRUE) + if (!GLOB.ore_silo_default && mapload && is_station_level(z)) GLOB.ore_silo_default = src @@ -43,6 +51,7 @@ GLOBAL_LIST_EMPTY(silo_access_logs) /obj/machinery/ore_silo/proc/remote_attackby(obj/machinery/M, mob/user, obj/item/stack/I) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + // stolen from /datum/component/material_container/proc/OnAttackBy if(user.a_intent != INTENT_HELP) return @@ -62,8 +71,19 @@ GLOBAL_LIST_EMPTY(silo_access_logs) return TRUE /obj/machinery/ore_silo/attackby(obj/item/W, mob/user, params) - if (istype(W, /obj/item/stack)) + if(default_deconstruction_screwdriver(user, icon_state, icon_state, W)) + updateUsrDialog() + return + + if(default_deconstruction_crowbar(W)) + return + + if(!powered()) + return ..() + + if(istype(W, /obj/item/stack)) return remote_attackby(src, user, W) + return ..() /obj/machinery/ore_silo/ui_interact(mob/user) diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 35ca8b8e2a8ee..b6575073bfa5b 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -14,7 +14,13 @@ . = ..() machine = locate(/obj/machinery/mineral/stacking_machine, get_step(src, machinedir)) if (machine) - machine.CONSOLE = src + machine.console = src + +/obj/machinery/mineral/stacking_unit_console/Destroy() + if(machine) + machine.console = null + machine = null + return ..() /obj/machinery/mineral/stacking_unit_console/ui_interact(mob/user) . = ..() @@ -74,7 +80,7 @@ circuit = /obj/item/circuitboard/machine/stacking_machine input_dir = EAST output_dir = WEST - var/obj/machinery/mineral/stacking_unit_console/CONSOLE + var/obj/machinery/mineral/stacking_unit_console/console var/stk_types = list() var/stk_amt = list() var/stack_list[0] //Key: Type. Value: Instance of type. @@ -88,23 +94,38 @@ materials = AddComponent(/datum/component/remote_materials, "stacking", mapload, FALSE, mapload && force_connect) /obj/machinery/mineral/stacking_machine/Destroy() - CONSOLE = null + if(console) + console.machine = null + console = null materials = null return ..() /obj/machinery/mineral/stacking_machine/HasProximity(atom/movable/AM) + if(QDELETED(AM)) + return if(istype(AM, /obj/item/stack/sheet) && AM.loc == get_step(src, input_dir)) - process_sheet(AM) + var/obj/effect/portal/P = locate() in AM.loc + if(P) + visible_message("[src] attempts to stack the portal!") + message_admins("Stacking machine exploded via [P.creator ? key_name(P.creator) : "UNKNOWN"]'s portal at [AREACOORD(src)]") + log_game("Stacking machine exploded via [P.creator ? key_name(P.creator) : "UNKNOWN"]'s portal at [AREACOORD(src)]") + explosion(src.loc, 0, 1, 2, 3) + if(!QDELETED(src)) + qdel(src) + else + process_sheet(AM) /obj/machinery/mineral/stacking_machine/multitool_act(mob/living/user, obj/item/multitool/M) if(istype(M)) if(istype(M.buffer, /obj/machinery/mineral/stacking_unit_console)) - CONSOLE = M.buffer - CONSOLE.machine = src + console = M.buffer + console.machine = src to_chat(user, "You link [src] to the console in [M]'s buffer.") return TRUE /obj/machinery/mineral/stacking_machine/proc/process_sheet(obj/item/stack/sheet/inp) + if(QDELETED(inp)) + return var/key = inp.merge_type var/obj/item/stack/sheet/storage = stack_list[key] if(!storage) //It's the first of this sheet added diff --git a/code/modules/mining/machine_unloading.dm b/code/modules/mining/machine_unloading.dm index cea90bfce029c..b8f3ed70fa647 100644 --- a/code/modules/mining/machine_unloading.dm +++ b/code/modules/mining/machine_unloading.dm @@ -8,24 +8,17 @@ density = TRUE input_dir = WEST output_dir = EAST - speed_process = TRUE + needs_item_input = TRUE + processing_flags = START_PROCESSING_MANUALLY -/obj/machinery/mineral/unloading_machine/process() - var/turf/T = get_step(src,input_dir) - if(T) - var/limit - for(var/obj/structure/ore_box/B in T) - for (var/obj/item/stack/ore/O in B) - B.contents -= O - unload_mineral(O) - limit++ - if (limit>=10) - return - CHECK_TICK - CHECK_TICK - for(var/obj/item/I in T) - unload_mineral(I) - limit++ - if (limit>=10) - return - CHECK_TICK \ No newline at end of file +/obj/machinery/mineral/unloading_machine/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + if(QDELETED(target)) + return + if(istype(target, /obj/structure/ore_box)) + var/obj/structure/ore_box/box = target + for(var/obj/item/stack/ore/O in box) + unload_mineral(O) + box.ui_update() + else if(istype(target, /obj/item/stack/ore)) + var/obj/item/stack/ore/O = target + unload_mineral(O) diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index 070cfdf14124c..4d9fe0863b8f7 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -1,117 +1,53 @@ /**********************Mining Equipment Vendor**************************/ -/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" +/obj/machinery/vendor + name = "equipment vendor" + processing_flags = START_PROCESSING_MANUALLY + subsystem_type = /datum/controller/subsystem/processing/fastprocess density = TRUE - circuit = /obj/item/circuitboard/machine/mining_equipment_vendor - - var/icon_deny = "mining-deny" + var/icon_deny var/obj/item/card/id/inserted_id - var/list/prize_list = list( //if you add something to this, please, for the love of god, sort it by price/type. use tabs and not spaces. - new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 10), - new /datum/data/mining_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100), - new /datum/data/mining_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 300), - new /datum/data/mining_equipment("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100), - new /datum/data/mining_equipment("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100), - new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150), - new /datum/data/mining_equipment("Soap", /obj/item/soap/nanotrasen, 200), - new /datum/data/mining_equipment("Laser Pointer", /obj/item/laser_pointer, 300), - new /datum/data/mining_equipment("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300), - new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 400), - new /datum/data/mining_equipment("Fulton Beacon", /obj/item/fulton_core, 400), - new /datum/data/mining_equipment("Shelter Capsule", /obj/item/survivalcapsule, 400), - new /datum/data/mining_equipment("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500), - new /datum/data/mining_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 500), - new /datum/data/mining_equipment("Point Transfer Card", /obj/item/card/mining_point_card, 500), - new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/medipen/survival, 500), - new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600), - new /datum/data/mining_equipment("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600), - new /datum/data/mining_equipment("Jaunter", /obj/item/wormhole_jaunter, 750), - new /datum/data/mining_equipment("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750), - new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750), - new /datum/data/mining_equipment("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800), - new /datum/data/mining_equipment("Resonator", /obj/item/resonator, 800), - new /datum/data/mining_equipment("Fulton Pack", /obj/item/extraction_pack, 1000), - new /datum/data/mining_equipment("Lazarus Injector", /obj/item/lazarus_injector, 1000), - new /datum/data/mining_equipment("Silver Pickaxe", /obj/item/pickaxe/silver, 1000), - new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffelbag/mining_conscript, 1000), - new /datum/data/mining_equipment("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000), - new /datum/data/mining_equipment("Space Cash", /obj/item/stack/spacecash/c1000, 2000), - new /datum/data/mining_equipment("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000), - new /datum/data/mining_equipment("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000), - new /datum/data/mining_equipment("Super Resonator", /obj/item/resonator/upgraded, 2500), - new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500), - new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000), - new /datum/data/mining_equipment("Luxury Bar Capsule", /obj/item/survivalcapsule/luxuryelite, 10000), - new /datum/data/mining_equipment("Mining Encampment Capsule", /obj/item/survivalcapsule/encampment, 5000), - new /datum/data/mining_equipment("Nanotrasen Minebot", /mob/living/simple_animal/hostile/mining_drone, 800), - new /datum/data/mining_equipment("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400), - new /datum/data/mining_equipment("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400), - new /datum/data/mining_equipment("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600), - new /datum/data/mining_equipment("Minebot AI Upgrade", /obj/item/slimepotion/slime/sentience/mining, 1000), - new /datum/data/mining_equipment("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100), - new /datum/data/mining_equipment("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100), - new /datum/data/mining_equipment("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150), - new /datum/data/mining_equipment("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250), - new /datum/data/mining_equipment("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300), - new /datum/data/mining_equipment("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000), - new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000), - new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000), - new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000) - ) - -/datum/data/mining_equipment - var/equipment_name = "generic" - var/equipment_path = null - var/cost = 0 - -/datum/data/mining_equipment/New(name, path, cost) - src.equipment_name = name - src.equipment_path = path - src.cost = cost + var/list/prize_list = list() -/obj/machinery/mineral/equipment_vendor/Initialize() +/obj/machinery/vendor/Initialize() . = ..() build_inventory() -/obj/machinery/mineral/equipment_vendor/proc/build_inventory() +/obj/machinery/vendor/proc/build_inventory() for(var/p in prize_list) - var/datum/data/mining_equipment/M = p + var/datum/data/vendor_equipment/M = p GLOB.vending_products[M.equipment_path] = 1 -/obj/machinery/mineral/equipment_vendor/power_change() +/obj/machinery/vendor/power_change() ..() update_icon() -/obj/machinery/mineral/equipment_vendor/update_icon() +/obj/machinery/vendor/update_icon() if(powered()) icon_state = initial(icon_state) else icon_state = "[initial(icon_state)]-off" -/obj/machinery/mineral/equipment_vendor/ui_assets(mob/user) +/obj/machinery/vendor/ui_assets(mob/user) return list( get_asset_datum(/datum/asset/spritesheet/vending), ) -/obj/machinery/mineral/equipment_vendor/ui_state(mob/user) +/obj/machinery/vendor/ui_state(mob/user) return GLOB.default_state -/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user, datum/tgui/ui) +/obj/machinery/vendor/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "MiningVendor") ui.open() -/obj/machinery/mineral/equipment_vendor/ui_static_data(mob/user) +/obj/machinery/vendor/ui_static_data(mob/user) . = list() .["product_records"] = list() - for(var/datum/data/mining_equipment/prize in prize_list) + for(var/datum/data/vendor_equipment/prize in prize_list) var/list/product_data = list( path = replacetext(replacetext("[prize.equipment_path]", "/obj/item/", ""), "/", "-"), name = prize.equipment_name, @@ -120,16 +56,17 @@ ) .["product_records"] += list(product_data) -/obj/machinery/mineral/equipment_vendor/ui_data(mob/user) +/obj/machinery/vendor/ui_data(mob/user) . = list() var/mob/living/carbon/human/H var/obj/item/card/id/C + .["user"] = null if(ishuman(user)) H = user C = H.get_idcard(TRUE) if(C) .["user"] = list() - .["user"]["points"] = C.mining_points + .["user"]["points"] = get_points(C) if(C.registered_account) .["user"]["name"] = C.registered_account.account_holder if(C.registered_account.account_job) @@ -137,7 +74,7 @@ else .["user"]["job"] = "No Job" -/obj/machinery/mineral/equipment_vendor/ui_act(action, params) +/obj/machinery/vendor/ui_act(action, params) if(..()) return @@ -149,32 +86,123 @@ to_chat(usr, "Error: An ID is required!") flick(icon_deny, src) return - var/datum/data/mining_equipment/prize = locate(params["ref"]) in prize_list + var/datum/data/vendor_equipment/prize = locate(params["ref"]) in prize_list if(!prize || !(prize in prize_list)) to_chat(usr, "Error: Invalid choice!") flick(icon_deny, src) return - if(prize.cost > I.mining_points) + if(prize.cost > get_points(I)) to_chat(usr, "Error: Insufficient points for [prize.equipment_name] on [I]!") flick(icon_deny, src) return - I.mining_points -= prize.cost + subtract_points(I, prize.cost) to_chat(usr, "[src] clanks to life briefly before vending [prize.equipment_name]!") new prize.equipment_path(loc) SSblackbox.record_feedback("nested tally", "mining_equipment_bought", 1, list("[type]", "[prize.equipment_path]")) . = TRUE -/obj/machinery/mineral/equipment_vendor/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/mining_voucher)) - RedeemVoucher(I, user) - return +/obj/machinery/vendor/attackby(obj/item/I, mob/user, params) if(default_deconstruction_screwdriver(user, "mining-open", "mining", I)) return if(default_deconstruction_crowbar(I)) return return ..() -/obj/machinery/mineral/equipment_vendor/proc/RedeemVoucher(obj/item/mining_voucher/voucher, mob/redeemer) +/obj/machinery/vendor/ex_act(severity, target) + do_sparks(5, TRUE, src) + if(prob(50 / severity) && severity < 3) + qdel(src) + +/obj/machinery/vendor/proc/subtract_points(obj/item/card/id/I, amount) + I.mining_points -= amount + +/obj/machinery/vendor/proc/get_points(obj/item/card/id/I) + return I.mining_points + +/obj/machinery/vendor/mining + 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" + density = TRUE + circuit = /obj/item/circuitboard/machine/mining_equipment_vendor + + icon_deny = "mining-deny" + prize_list = list( //if you add something to this, please, for the love of god, sort it by price/type. use tabs and not spaces. + new /datum/data/vendor_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 5), + new /datum/data/vendor_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 75), + new /datum/data/vendor_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 150), + new /datum/data/vendor_equipment("Shelter Capsule", /obj/item/survivalcapsule, 400), + new /datum/data/vendor_equipment("Regen. Core Stabilizer", /obj/item/hivelordstabilizer, 400), + new /datum/data/vendor_equipment("Skeleton Key", /obj/item/skeleton_key, 750), + new /datum/data/vendor_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/medipen/survival, 500), + new /datum/data/vendor_equipment("Brute Healing Kit", /obj/item/storage/firstaid/brute, 600), + new /datum/data/vendor_equipment("Burn Healing Kit", /obj/item/storage/firstaid/fire, 600), + new /datum/data/vendor_equipment("Advanced Healing Kit", /obj/item/storage/firstaid/advanced, 1200), + new /datum/data/vendor_equipment("Fulton Beacon", /obj/item/fulton_core, 500), + new /datum/data/vendor_equipment("Fulton Extraction Pack", /obj/item/extraction_pack, 1000), + new /datum/data/vendor_equipment("Jaunter", /obj/item/wormhole_jaunter, 750), + new /datum/data/vendor_equipment("Advanced Ore Scanner", /obj/item/t_scanner/adv_mining_scanner, 800), + new /datum/data/vendor_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 500), + new /datum/data/vendor_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2000), + new /datum/data/vendor_equipment("Proto-Kinetic Crusher", /obj/item/kinetic_crusher, 800), + new /datum/data/vendor_equipment("Proto-Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 500), + new /datum/data/vendor_equipment("Resonator", /obj/item/resonator, 750), + new /datum/data/vendor_equipment("Upgraded Resonator", /obj/item/resonator/upgraded, 1500), + new /datum/data/vendor_equipment("Silver Pickaxe", /obj/item/pickaxe/silver, 500), + new /datum/data/vendor_equipment("Diamond Pickaxe", /obj/item/pickaxe/diamond, 1000), + new /datum/data/vendor_equipment("Mining Bot Companion", /mob/living/simple_animal/hostile/mining_drone, 800), + new /datum/data/vendor_equipment("Minebot Upgrade: Melee", /obj/item/mine_bot_upgrade, 400), + new /datum/data/vendor_equipment("Minebot Upgrade: Armor", /obj/item/mine_bot_upgrade/health, 400), + new /datum/data/vendor_equipment("Minebot Upgrade: Cooldown", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600), + new /datum/data/vendor_equipment("Minebot Upgrade: A.I.", /obj/item/slimepotion/slime/sentience/mining, 1000), + new /datum/data/vendor_equipment("P-KA Upgrade: Bot-Friendly", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100), + new /datum/data/vendor_equipment("P-KA Upgrade: Tracer Shots", /obj/item/borg/upgrade/modkit/tracer, 200), + new /datum/data/vendor_equipment("P-KA Upgrade: Adj. T. Shots", /obj/item/borg/upgrade/modkit/tracer/adjustable, 300), + new /datum/data/vendor_equipment("P-KA Upgrade: Range", /obj/item/borg/upgrade/modkit/range, 1000), + new /datum/data/vendor_equipment("P-KA Upgrade: Damage", /obj/item/borg/upgrade/modkit/damage, 1000), + new /datum/data/vendor_equipment("P-KA Upgrade: Cooldown", /obj/item/borg/upgrade/modkit/cooldown, 1000), + new /datum/data/vendor_equipment("P-KA Upgrade: Damage Radius", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000), + new /datum/data/vendor_equipment("P-KA Cosmetic Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 200), + new /datum/data/vendor_equipment("P-KA Cosmetic Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 200), + new /datum/data/vendor_equipment("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000), + new /datum/data/vendor_equipment("Expanded E. Oxygen Tank", /obj/item/tank/internals/emergency_oxygen/engi, 1000), + new /datum/data/vendor_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffelbag/mining_conscript, 1000), + new /datum/data/vendor_equipment("Survival Knife", /obj/item/kitchen/knife/combat/survival, 1000), + new /datum/data/vendor_equipment("Tracking Implant Kit", /obj/item/storage/box/minertracker, 1000), + new /datum/data/vendor_equipment("Point Transfer Card", /obj/item/card/mining_point_card, 500), + new /datum/data/vendor_equipment("GAR Mesons", /obj/item/clothing/glasses/meson/gar, 500), + new /datum/data/vendor_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000), + new /datum/data/vendor_equipment("Mining Outpost Capsule", /obj/item/survivalcapsule/encampment, 5000), + new /datum/data/vendor_equipment("Luxury Bar Capsule", /obj/item/survivalcapsule/luxuryelite, 10000), + new /datum/data/vendor_equipment("Lazarus Injector", /obj/item/lazarus_injector, 1000), + new /datum/data/vendor_equipment("1000 Space Cash", /obj/item/stack/spacecash/c1000, 2000), + new /datum/data/vendor_equipment("Pizza", /obj/item/pizzabox/margherita, 200), + new /datum/data/vendor_equipment("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100), + new /datum/data/vendor_equipment("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100), + new /datum/data/vendor_equipment("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150), + new /datum/data/vendor_equipment("Soap", /obj/item/soap/nanotrasen, 200), + new /datum/data/vendor_equipment("Laser Pointer", /obj/item/laser_pointer, 300), + new /datum/data/vendor_equipment("Toy Alien", /obj/item/clothing/mask/facehugger/toy, 300), + ) + +/datum/data/vendor_equipment + var/equipment_name = "generic" + var/equipment_path = null + var/cost = 0 + +/datum/data/vendor_equipment/New(name, path, cost) + src.equipment_name = name + src.equipment_path = path + src.cost = cost + +/obj/machinery/vendor/mining/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/mining_voucher)) + RedeemVoucher(I, user) + return + return ..() + +/obj/machinery/vendor/mining/proc/RedeemVoucher(obj/item/mining_voucher/voucher, mob/redeemer) var/items = list("Survival Capsule and Explorer's Webbing", "Resonator Kit", "Minebot Kit", "Extraction and Rescue Kit", "Crusher Kit", "Mining Conscription Kit") var/selection = input(redeemer, "Pick your equipment", "Mining Voucher Redemption") as null|anything in sortList(items) @@ -198,36 +226,31 @@ new /obj/item/stack/marker_beacon/thirty(drop_location) if("Crusher Kit") new /obj/item/extinguisher/mini(drop_location) - new /obj/item/twohanded/kinetic_crusher(drop_location) + new /obj/item/kinetic_crusher(drop_location) if("Mining Conscription Kit") new /obj/item/storage/backpack/duffelbag/mining_conscript(drop_location) SSblackbox.record_feedback("tally", "mining_voucher_redeemed", 1, selection) qdel(voucher) -/obj/machinery/mineral/equipment_vendor/ex_act(severity, target) - do_sparks(5, TRUE, src) - if(prob(50 / severity) && severity < 3) - qdel(src) - /****************Golem Point Vendor**************************/ -/obj/machinery/mineral/equipment_vendor/golem +/obj/machinery/vendor/mining/golem name = "golem ship equipment vendor" circuit = /obj/item/circuitboard/machine/mining_equipment_vendor/golem -/obj/machinery/mineral/equipment_vendor/golem/Initialize() +/obj/machinery/vendor/mining/golem/Initialize() . = ..() desc += "\nIt seems a few selections have been added." prize_list += list( - new /datum/data/mining_equipment("Extra Id", /obj/item/card/id/mining, 250), - new /datum/data/mining_equipment("Science Goggles", /obj/item/clothing/glasses/science, 250), - new /datum/data/mining_equipment("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 300), - new /datum/data/mining_equipment("Toolbelt", /obj/item/storage/belt/utility, 350), - new /datum/data/mining_equipment("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500), - new /datum/data/mining_equipment("Grey Slime Extract", /obj/item/slime_extract/grey, 1000), - new /datum/data/mining_equipment("Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1700), - new /datum/data/mining_equipment("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000) + new /datum/data/vendor_equipment("Extra Id", /obj/item/card/id/mining, 250), + new /datum/data/vendor_equipment("Science Goggles", /obj/item/clothing/glasses/science, 250), + new /datum/data/vendor_equipment("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 300), + new /datum/data/vendor_equipment("Toolbelt", /obj/item/storage/belt/utility, 350), + new /datum/data/vendor_equipment("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500), + new /datum/data/vendor_equipment("Grey Slime Extract", /obj/item/slime_extract/grey, 1000), + new /datum/data/vendor_equipment("P-KA Upgrade: Trigger Mod", /obj/item/borg/upgrade/modkit/trigger_guard, 1000), + new /datum/data/vendor_equipment("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000) ) /**********************Mining Equipment Vendor Items**************************/ @@ -261,27 +284,14 @@ ..() /obj/item/card/mining_point_card/examine(mob/user) - ..() - to_chat(user, "There's [points] point\s on the card.") + . = ..() + . += "There's [points] point\s on the card." ///Conscript kit -/obj/item/card/mining_access_card +/obj/item/card/id/pass/mining_access_card name = "mining access card" desc = "A small card, that when used on any ID, will add mining access." - icon_state = "data_1" - -/obj/item/card/mining_access_card/afterattack(atom/movable/AM, mob/user, proximity) - . = ..() - if(istype(AM, /obj/item/card/id) && proximity) - var/obj/item/card/id/I = AM - I.access |= ACCESS_MINING - I.access |= ACCESS_MINING_STATION - I.access |= ACCESS_MECH_MINING - I.access |= ACCESS_MINERAL_STOREROOM - I.access |= ACCESS_CARGO - to_chat(user, "You upgrade [I] with mining access.") - log_id("[key_name(user)] added mining access to '[I]' using [src] at [AREACOORD(user)].") - qdel(src) + access = list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MECH_MINING, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO) /obj/item/storage/backpack/duffelbag/mining_conscript name = "mining conscription kit" @@ -295,4 +305,4 @@ new /obj/item/clothing/suit/hooded/explorer(src) new /obj/item/encryptionkey/headset_cargo(src) new /obj/item/clothing/mask/gas/explorer(src) - new /obj/item/card/mining_access_card(src) + new /obj/item/card/id/pass/mining_access_card(src) diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index fa1e641a8190b..3dc17909227d4 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -71,17 +71,17 @@ /**********************Shuttle Computer**************************/ -/obj/machinery/computer/shuttle/mining +/obj/machinery/computer/shuttle_flight/mining name = "mining shuttle console" desc = "Used to call and send the mining shuttle." circuit = /obj/item/circuitboard/computer/mining_shuttle shuttleId = "mining" possible_destinations = "mining_home;mining_away;landing_zone_dock;mining_public" - no_destination_swap = 1 + req_access = list(ACCESS_MINING) var/static/list/dumb_rev_heads = list() //ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/machinery/computer/shuttle/mining/attack_hand(mob/user) +/obj/machinery/computer/shuttle_flight/mining/attack_hand(mob/user) if(is_station_level(user.z) && user.mind && is_head_revolutionary(user) && !(user.mind in dumb_rev_heads)) to_chat(user, "You get a feeling that leaving the station might be a REALLY dumb idea...") dumb_rev_heads += user.mind @@ -89,13 +89,12 @@ . = ..() //It is on lavaland, soooo.... -/obj/machinery/computer/shuttle/science +/obj/machinery/computer/shuttle_flight/science name = "science outpost shuttle console" desc = "Used to call and send the science shuttle." circuit = /obj/item/circuitboard/computer/science_shuttle shuttleId = "science" possible_destinations = "science_station;science_outpost" - no_destination_swap = 1 /**********************Mining car (Crate like thing, not the rail car)**************************/ @@ -103,3 +102,4 @@ desc = "A mining car. This one doesn't work on rails, but has to be dragged." name = "Mining car (not for rails)" icon_state = "miningcar" + door_anim_time = 0 diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm index d752467f2aa72..e70887698008f 100644 --- a/code/modules/mining/minebot.dm +++ b/code/modules/mining/minebot.dm @@ -3,7 +3,7 @@ #define MINEDRONE_ATTACK 2 /mob/living/simple_animal/hostile/mining_drone - name = "nanotrasen minebot" + name = "\improper Nanotrasen minebot" desc = "The instructions printed on the side read: This is a small robot used to support miners, can be set to search and collect loose ore, or to help fend off wildlife." gender = NEUTER icon = 'icons/mob/aibots.dmi' diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index 4d44e66c503dd..5dc2bbd05a450 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -7,6 +7,7 @@ icon_state = "coinpress0" density = TRUE input_dir = EAST + needs_item_input = TRUE var/obj/item/storage/bag/money/bag_to_use @@ -31,16 +32,21 @@ chosen = getmaterialref(chosen) -/obj/machinery/mineral/mint/process() - var/turf/T = get_step(src, input_dir) +/obj/machinery/mineral/mint/pickup_item(datum/source, atom/movable/target, atom/oldLoc) + if(QDELETED(target)) + return + if(!istype(target, /obj/item/stack)) + return + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + var/obj/item/stack/S = target - for(var/obj/item/stack/O in T) - var/inserted = materials.insert_item(O) - if(inserted) - qdel(O) + if(materials.insert_item(S)) + qdel(S) +/obj/machinery/mineral/mint/process() if(processing) + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/datum/material/M = chosen if(!M) @@ -68,6 +74,7 @@ if(!found_new) processing = FALSE else + end_processing() icon_state = "coinpress0" @@ -79,6 +86,7 @@ if(!ui) ui = new(user, src, "Mint") ui.open() + ui.set_autoupdate(TRUE) // Coins pressed (could be refactored to ui_update), material amounts /obj/machinery/mineral/mint/ui_data() var/list/data = list() @@ -114,13 +122,18 @@ if (!processing) produced_coins = 0 processing = TRUE + begin_processing() + . = TRUE if ("stoppress") processing = FALSE + end_processing() + . = TRUE if ("changematerial") var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) for(var/datum/material/mat in materials.materials) if (params["material_name"] == mat.name) chosen = mat + . = TRUE /obj/machinery/mineral/mint/proc/create_coins() var/turf/T = get_step(src,output_dir) diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 7c07bc065d942..f5e800dc07f7d 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -18,6 +18,8 @@ var/refined_type = null //What this ore defaults to being refined into novariants = TRUE // Ore stacks handle their icon updates themselves to keep the illusion that there's more going var/list/stack_overlays + var/scan_state = "" //Used by mineral turfs for their scan overlay. + var/spreadChance = 0 //Also used by mineral turfs for spreading veins /obj/item/stack/ore/update_icon() var/difference = min(ORESTACK_OVERLAYS_MAX, amount) - (LAZYLEN(stack_overlays)+1) @@ -71,6 +73,8 @@ points = 30 materials = list(/datum/material/uranium=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/mineral/uranium + scan_state = "rock_Uranium" + spreadChance = 5 /obj/item/stack/ore/iron name = "iron ore" @@ -80,6 +84,8 @@ points = 1 materials = list(/datum/material/iron=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/iron + scan_state = "rock_Iron" + spreadChance = 20 /obj/item/stack/ore/glass name = "sand pile" @@ -132,6 +138,8 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ points = 15 materials = list(/datum/material/plasma=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/mineral/plasma + scan_state = "rock_Plasma" + spreadChance = 8 /obj/item/stack/ore/plasma/welder_act(mob/living/user, obj/item/I) to_chat(user, "You can't hit a high enough temperature to smelt [src] properly!") @@ -145,6 +153,8 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ points = 5 materials = list(/datum/material/copper=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/mineral/copper + scan_state = "rock_Copper" + spreadChance = 5 /obj/item/stack/ore/silver name = "silver ore" @@ -154,6 +164,8 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ points = 16 materials = list(/datum/material/silver=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/mineral/silver + scan_state = "rock_Silver" + spreadChance = 5 /obj/item/stack/ore/gold name = "gold ore" @@ -163,6 +175,8 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ points = 18 materials = list(/datum/material/gold=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/mineral/gold + scan_state = "rock_Gold" + spreadChance = 5 /obj/item/stack/ore/diamond name = "diamond ore" @@ -172,6 +186,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ points = 50 materials = list(/datum/material/diamond=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/mineral/diamond + scan_state = "rock_Diamond" /obj/item/stack/ore/bananium name = "bananium ore" @@ -181,6 +196,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ points = 60 materials = list(/datum/material/bananium=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/mineral/bananium + scan_state = "rock_Bananium" /obj/item/stack/ore/titanium name = "titanium ore" @@ -190,6 +206,8 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ points = 50 materials = list(/datum/material/titanium=MINERAL_MATERIAL_AMOUNT) refined_type = /obj/item/stack/sheet/mineral/titanium + scan_state = "rock_Titanium" + spreadChance = 5 /obj/item/stack/ore/slag name = "slag" @@ -198,7 +216,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ item_state = "slag" singular_name = "slag chunk" -/obj/item/twohanded/required/gibtonite +/obj/item/gibtonite name = "gibtonite ore" desc = "Extremely explosive if struck with mining equipment, Gibtonite is often used by miners to speed up their work by using it as a mining charge. This material is illegal to possess by unauthorized personnel under space law." icon = 'icons/obj/mining.dmi' @@ -212,12 +230,16 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ var/attacher = "UNKNOWN" var/det_timer -/obj/item/twohanded/required/gibtonite/Destroy() +/obj/item/gibtonite/ComponentInitialize() + . = ..() + AddComponent(/datum/component/two_handed, require_twohands=TRUE) + +/obj/item/gibtonite/Destroy() qdel(wires) wires = null return ..() -/obj/item/twohanded/required/gibtonite/attackby(obj/item/I, mob/user, params) +/obj/item/gibtonite/attackby(obj/item/I, mob/user, params) if(!wires && istype(I, /obj/item/assembly/igniter)) user.visible_message("[user] attaches [I] to [src].", "You attach [I] to [src].") wires = new /datum/wires/explosive/gibtonite(src) @@ -245,20 +267,20 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ return ..() -/obj/item/twohanded/required/gibtonite/attack_self(user) +/obj/item/gibtonite/attack_self(user) if(wires) wires.interact(user) else ..() -/obj/item/twohanded/required/gibtonite/bullet_act(obj/item/projectile/P) +/obj/item/gibtonite/bullet_act(obj/item/projectile/P) GibtoniteReaction(P.firer) . = ..() -/obj/item/twohanded/required/gibtonite/ex_act() +/obj/item/gibtonite/ex_act() GibtoniteReaction(null, 1) -/obj/item/twohanded/required/gibtonite/proc/GibtoniteReaction(mob/user, triggered_by = 0) +/obj/item/gibtonite/proc/GibtoniteReaction(mob/user, triggered_by = 0) if(!primed) primed = TRUE playsound(src,'sound/effects/hit_on_shattered_glass.ogg',50,1) @@ -281,7 +303,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ log_bomber(user, "has primed a", src, "for detonation", notify_admins) det_timer = addtimer(CALLBACK(src, .proc/detonate, notify_admins), det_time, TIMER_STOPPABLE) -/obj/item/twohanded/required/gibtonite/proc/detonate(notify_admins) +/obj/item/gibtonite/proc/detonate(notify_admins) if(primed) switch(quality) if(GIBTONITE_QUALITY_HIGH) diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm index f48df08b00805..8f837880ef7d5 100644 --- a/code/modules/mining/satchel_ore_boxdm.dm +++ b/code/modules/mining/satchel_ore_boxdm.dm @@ -8,16 +8,21 @@ desc = "A heavy wooden box, which can be filled with a lot of ores." density = TRUE pressure_resistance = 5*ONE_ATMOSPHERE + var/static/list/typecache_to_take - - +/obj/structure/ore_box/Initialize() + . = ..() + if(!typecache_to_take) + typecache_to_take = typecacheof(/obj/item/stack/ore) /obj/structure/ore_box/attackby(obj/item/W, mob/user, params) if (istype(W, /obj/item/stack/ore)) user.transferItemToLoc(W, src) + ui_update() else if(SEND_SIGNAL(W, COMSIG_CONTAINS_STORAGE)) - SEND_SIGNAL(W, COMSIG_TRY_STORAGE_TAKE_TYPE, /obj/item/stack/ore, src) + SEND_SIGNAL(W, COMSIG_TRY_STORAGE_TAKE_TYPE, typecache_to_take, src) to_chat(user, "You empty the ore in [W] into \the [src].") + ui_update() else return ..() @@ -84,14 +89,12 @@ /obj/structure/ore_box/ui_act(action, params) if(..()) return - if(!Adjacent(usr)) - return - add_fingerprint(usr) - usr.set_machine(src) + switch(action) if("removeall") dump_box_contents() to_chat(usr, "You open the release hatch on the box..") + . = TRUE /obj/structure/ore_box/deconstruct(disassembled = TRUE, mob/user) var/obj/item/stack/sheet/mineral/wood/WD = new (loc, 4) diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index 6dcdd076357c2..7f5b31ac3987e 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -12,13 +12,14 @@ INITIALIZE_IMMEDIATE(/mob/dead) stack_trace("Warning: [src]([type]) initialized multiple times!") flags_1 |= INITIALIZED_1 tag = "mob_[next_mob_id++]" - GLOB.mob_list += src + add_to_mob_list() prepare_huds() - if(length(CONFIG_GET(keyed_list/cross_server))) - verbs += /mob/dead/proc/server_hop + if(length(CONFIG_GET(keyed_list/server_hop))) + add_verb(/mob/dead/proc/server_hop) set_focus(src) + become_hearing_sensitive() return INITIALIZE_HINT_NORMAL /mob/dead/canUseStorage() @@ -39,27 +40,26 @@ INITIALIZE_IMMEDIATE(/mob/dead) loc = destination Moved(oldloc, NONE, TRUE) -/mob/dead/Stat() - ..() +/mob/dead/get_stat_tab_status() + var/list/tab_data = ..() - if(!statpanel("Status")) - return - stat(null, "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]") + tab_data["Game Mode"] = GENERATE_STAT_TEXT("[SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]") if(SSticker.HasRoundStarted()) - return + return tab_data var/time_remaining = SSticker.GetTimeLeft() if(time_remaining > 0) - stat(null, "Time To Start: [round(time_remaining/10)]s") + tab_data["Time To Start"] = GENERATE_STAT_TEXT("[round(time_remaining/10)]s") else if(time_remaining == -10) - stat(null, "Time To Start: DELAYED") + tab_data["Time To Start"] = GENERATE_STAT_TEXT("DELAYED") else - stat(null, "Time To Start: SOON") + tab_data["Time To Start"] = GENERATE_STAT_TEXT("SOON") - stat(null, "Players: [SSticker.totalPlayers]") + tab_data["Players"] = GENERATE_STAT_TEXT("[SSticker.totalPlayers]") if(client.holder) - stat(null, "Players Ready: [SSticker.totalPlayersReady]") + tab_data["Players Ready"] = GENERATE_STAT_TEXT("[SSticker.totalPlayersReady]") + return tab_data /mob/dead/proc/server_hop() set category = "OOC" @@ -67,11 +67,11 @@ INITIALIZE_IMMEDIATE(/mob/dead) set desc= "Jump to the other server" if(notransform) return - var/list/csa = CONFIG_GET(keyed_list/cross_server) + var/list/csa = CONFIG_GET(keyed_list/server_hop) var/pick switch(csa.len) if(0) - verbs -= /mob/dead/proc/server_hop + remove_verb(/mob/dead/proc/server_hop) to_chat(src, "Server Hop has been disabled.") if(1) pick = csa[1] @@ -88,7 +88,7 @@ INITIALIZE_IMMEDIATE(/mob/dead) var/client/C = client to_chat(C, "Sending you to [pick].") - new /obj/screen/splash(C) + new /atom/movable/screen/splash(C) notransform = TRUE sleep(29) //let the animation play diff --git a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm index 887556500cba7..97e97bb31e0e6 100644 --- a/code/modules/mob/dead/new_player/login.dm +++ b/code/modules/mob/dead/new_player/login.dm @@ -4,21 +4,21 @@ client.set_db_player_flags() if(!mind) mind = new /datum/mind(key) - mind.active = 1 - mind.current = src + mind.active = TRUE + mind.set_current(src) ..() var/motd = global.config.motd if(motd) - to_chat(src, " [motd] ", handle_whitespace=FALSE)
+ to_chat(src, "[motd] ", handle_whitespace=FALSE, allow_linkify = TRUE)
if(GLOB.admin_notice)
to_chat(src, "Admin Notice:\n \t [GLOB.admin_notice]")
var/spc = CONFIG_GET(number/soft_popcap)
if(spc && living_player_count() >= spc)
- to_chat(src, "Server Notice:\n \t [CONFIG_GET(string/soft_popcap_message)]")
+ to_chat(src, "Server Notice:\n \t [CONFIG_GET(string/soft_popcap_message)]", allow_linkify = TRUE)
sight |= SEE_TURFS
diff --git a/code/modules/mob/dead/new_player/logout.dm b/code/modules/mob/dead/new_player/logout.dm
index a70800d35b535..b2f37376a3c62 100644
--- a/code/modules/mob/dead/new_player/logout.dm
+++ b/code/modules/mob/dead/new_player/logout.dm
@@ -3,5 +3,6 @@
..()
if(!spawning)//Here so that if they are spawning and log out, the other procs can play out and they will have a mob to come back to.
key = null//We null their key before deleting the mob, so they are properly kicked out.
+ QDEL_NULL(mind) //Clean out mind, yes this fucking sucks
qdel(src)
return
\ No newline at end of file
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 6491c692ebdd9..e47c097febdde 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -15,7 +15,7 @@
/mob/dead/new_player/Initialize()
if(client && SSticker.state == GAME_STATE_STARTUP)
- var/obj/screen/splash/S = new(client, TRUE, TRUE)
+ var/atom/movable/screen/splash/S = new(client, TRUE, TRUE)
S.Fade(TRUE)
if(length(GLOB.newplayer_start))
@@ -140,8 +140,8 @@
LateChoices()
return
- if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.admin_datums)))
- if(IS_PATRON(src.ckey))
+ if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap))
+ if(IS_PATRON(src.ckey) || (client in GLOB.admins))
LateChoices()
return
to_chat(usr, "[CONFIG_GET(string/hard_popcap_message)]")
@@ -169,7 +169,7 @@
to_chat(usr, "There is an administrative lock on entering the game!")
return
- if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums))
+ if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums) && !IS_PATRON(ckey(key)))
if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1]))
to_chat(usr, "Server is full.")
return
@@ -193,12 +193,14 @@
vote_on_poll_handler(poll, href_list)
//When you cop out of the round (NB: this HAS A SLEEP FOR PLAYER INPUT IN IT)
-/mob/dead/new_player/proc/make_me_an_observer()
+/mob/dead/new_player/proc/make_me_an_observer(force_observe=FALSE)
if(QDELETED(src) || !src.client)
ready = PLAYER_NOT_READY
return FALSE
- var/this_is_like_playing_right = alert(src,"Are you sure you wish to observe? You will not be able to play this round!","Player Setup","Yes","No")
+ var/this_is_like_playing_right = "Yes"
+ if(!force_observe)
+ this_is_like_playing_right = alert(src,"Are you sure you wish to observe? You will not be able to play this round!","Player Setup","Yes","No")
if(QDELETED(src) || !src.client || this_is_like_playing_right != "Yes")
ready = PLAYER_NOT_READY
@@ -308,9 +310,9 @@
if(job && !job.override_latejoin_spawn(character))
SSjob.SendToLateJoin(character)
if(!arrivals_docked)
- var/obj/screen/splash/Spl = new(character.client, TRUE)
+ var/atom/movable/screen/splash/Spl = new(character.client, TRUE)
Spl.Fade(TRUE)
- character.playsound_local(get_turf(character), 'sound/voice/ApproachingTG.ogg', 25)
+ character.playsound_local(get_turf(character), 'sound/voice/welcomeBee.ogg', 50)
character.update_parallax_teleport()
@@ -382,7 +384,7 @@
SSjob.prioritized_jobs -= prioritized_job
dat += "
|